当前位置:文档之家› Linux内核源代码导读

Linux内核源代码导读

Linux内核源代码导读
Linux内核源代码导读

4.1 实模式setup阶段

setup用于体系结构相关的硬件初始化工作,在arch目录中的各个系统结构的平台相关都有类似功能的代码。在32位的x86平台中,setup的入口点是arch/x86/boot/header.S 中的_start。

代码片段 4.1.节自arch/x86/boot/header.S

1 .code16

2 section ".bstext", "ax"

3

4 .global bootsect_start

5 bootsect_start:

6

7 # Normalize the start address

8 ljmp $BOOTSEG, $start2

9

10 start2:

11 movw %cs, %ax

12 movw %ax, %ds

13 movw %ax, %es

14 movw %ax, %ss

15 xorw %sp, %sp

16 sti

17 cld

18

19 movw $bugger_off_msg, %si

20

21 msg_loop:

22 lodsb

23 andb %al, %al

24 jz bs_die

25 movb $0xe, %ah

26 movw $7, %bx

27 int $0x10

28 jmp msg_loop

29 .......

30 .section ".bsdata", "a"

31 bugger_off_msg:

32 .ascii "Direct booting from floppy is no longer supported\r\n"

33 .ascii "Please use a boot loader program instead.\r\n"

34 .ascii "\n"

35 .ascii "Remove disk and press any key to reboot . . .\r\n"

37

38 .section ".header", "a"

39

40 .globl hdr

41 hdr:

42 setup_sects: .byte SETUPSECTS

43 root_flags: .word ROOT_RDONLY

44 syssize: .long SYSSIZE

45 ram_size: .word RAMDISK

46 vid_mode: .word SVGA_MODE

47 root_dev: .word ROOT_DEV

48 boot_flag: .word 0xAA55

49

50 # offset 512, entry point

51 .globl _start

52 _start:

53 # Explicitly enter this as bytes, or the assembler

54 # tries to generate a 3-byte jump here, which causes

55 # everything else to push off to the wrong offset.

56 .byte 0xeb # short (2-byte) jump

57 .byte start_of_setup-1f

58 1:

59 ......

60 code32_start: # here loaders can put a different

61 # start address for 32-bit code.

62 #ifndef __BIG_KERNEL__

63 .long 0x1000 # 0x1000 = default for zImage

64 #else

65 .long 0x100000 # 0x100000 = default for big kernel

66 #endif

从第48行可以看出,第一个引导扇区以0x55AA 结尾,现在的内核都需要由Boot Loader 来加载。如果是由BIOS 直接加载这个扇区,上面这段代码就会打印出错信息。setup_sects 在第一个扇区内的偏移为0x1F1,数据结构hdr 用来传递一些信息给之后运行的代码。细心的读者可能会有疑问,从文件开始到第48行的0xAA55 似乎没有512 字节?这要从链接脚本setup.ld 说起。

代码片段 4.2.节自arch/x86/boot/setup.ld

1 ......

2 # 入口点。

3 ENTRY(_start)

4

5 SECTIONS

7 # 从0偏移开始存放上面的.bstext和bsdata段。

8 . = 0;

9 .bstext : { *(.bstext) }

10 .bsdata : { *(.bsdata) }

11

12 # header段从497(0x1F1)开始。

13 . = 497;

14 .header : { *(.header) }

15 ......

16 }

在第13行通过.=497 指定了header 这个段的起始地址是0x1F1(497),读者可以从前面代码片段4.1header.S 中第38行的.section ".header"开始,数一数后面的字节数,填充到这里正好512 个字节。从0x1F1 偏移开始的这些是传递给内核的参数,有几个是很常用的,其中setup_sects 表示该扇区之后的实模式代码占用几个扇区。后面的code32_start 表示32 位保护模式代码的入口点,也就是bzImage 的第二部分的入口地址。在图4.2中,Boot Loader把setup 加载到X 处后,DS 寄存器设置为X,再跳转到DS:200 把控制权交到_start,这里是硬编码一条跳转指令,它跳转到start_of_setup 处。

代码片段 4.3.节自arch/x86/boot/header.S

1 ......

2 .section ".inittext", "ax"

3 start_of_setup:

4 #ifdef SAFE_RESET_DISK_CONTROLLER

5 # Reset the disk controller.

6 movw $0x0000, %ax # Reset disk controller

7 movb $0x80, %dl # All disks

8 int $0x13

9 #endif

10

11 # Force %es = %ds

12 movw %ds, %ax

13 movw %ax, %es

14 cld

15 ......

16 # Invalid %ss, make up a new stack

17 movw $_end, %dx

18 testb $CAN_USE_HEAP, loadflags

19 jz 1f

20 movw heap_end_ptr, %dx

21 ......

22 # We will have entered with %cs = %ds+0x20, normalize %cs so

23 # it is on par with the other segments.

24 pushw %ds

25 pushw $6f

26 lretw

27 6:

28 ......

29

30 # Zero the bss

31 movw $__bss_start, %di

32 movw $_end+3, %cx

33 xorl %eax, %eax

34 subw %di, %cx

35 shrw $2, %cx

36 rep; stosl

37 # Jump to C code (should not return)

38 call main

39 ......

程序的起始地址在链接脚本中被设置为0,如果setup被加载到其他地方(起始地址不为0 的地方),那么指令里面访问的全局符号都会有重定位的问题。由于Boot Loader 跳转到上面这段代码的时候,把DS设置为setup加载的基地址,而第17行访问_end默认是用数据段寄存器DS的,所以不会有重定位的问题。但是在38行转移指令用的CS寄存器,而符号main 的地址是在链接期决定的,现在加载的基地址改变了,那肯定会出问题了。所以事先在第24行把CS设置为和DS一样,这样就解决了重定位的问题了。代码的最后跳转到了arch/x86/boot/main.c中的main函数中继续执行。

代码片段 4.4.节自arch/x86/boot/main.c

1 void main(void)

2 {

3 /* First, copy the boot header into the "zeropage" */

4 copy_boot_params();

5

6 /* End of heap check */

7 if (boot_params.hdr.loadflags & CAN_USE_HEAP) {

8 heap_end = (char *)(boot_params.hdr.heap_end_ptr

9 +0x200-STACK_SIZE);

10 } else {

11 /* Boot protocol 2.00 only, no heap available */

12 puts("WARNING: Ancient bootloader,

13 some functionality may be limited!\n");

14 }

15

16 /* Make sure we have all the proper CPU support */

17 if (validate_cpu()) {

18 puts("Unable to boot - please use a

19 kernel appropriate for your CPU.\n");

20 die();

21 }

22

23 /* Tell the BIOS what CPU mode we intend to run in. */

24 set_bios_mode();

25

26 /* Detect memory layout */

27 detect_memory();

28

29 /* Set keyboard repeat rate (why?) */

30 keyboard_set_repeat();

31

32 /* Set the video mode */

33 set_video();

34

35 /* Query MCA information */

36 query_mca();

37

38 /* Voyager */

39 #ifdef CONFIG_X86_VOYAGER

40 query_voyager();

41 #endif

42

43 /* Query Intel SpeedStep (IST) information */

44 query_ist();

45

46 /* Query APM information */

47 #if defined(CONFIG_APM) || defined(CONFIG_APM_MODULE)

48 query_apm_bios();

49 #endif

50

51 /* Query EDD information */

52 #if defined(CONFIG_EDD) || defined(CONFIG_EDD_MODULE)

53 query_edd();

54 #endif

55 /* Do the last things and invoke protected mode */

56 go_to_protected_mode();s

57 }

这个main 函数先(调用copy_boot_params函数)把位于第一个扇区的参数复制到boot_params 变量中,boot_params 位于setup 的数据段。其后是一些设置和获取系统硬件参数有关的代码。笔者建议除非特别需要,否则不要花费过多的精力去分析这些代码。例如detect_memory()通过BIOS 调用查询系统内存信息。虽然可以通过查阅BIOS 相关资料

得知如何获取内存信息。但是这里有许多历史因素,而且通过BIOS 获取内存信息也只是一种方法,在不同的平台上方法是不一样的,重点需要分析的是这些信息收集好之后如何传递给内核,这样在嵌入式平台开发过程中,就可以根据自己平台的系统设计,把相关信息传递给内核。笔者在初学阶段花费了过多的时间精力在BIOS 调用、A20 等这些历史问题的细节上面,这些东西既不是“真理”也不是设计上的“精华”,等到恍然大悟时才后悔莫及,当然这只是笔者的个人建议。

main 函数中收集到的信息全部存放在setup 的数据段中的boot_params中,结构体boot_params是在include/asm-x86/bootparam.h内定义的。

代码片段 4.5.节自include/asm-x86/bootparam.h

1 /* The so-called "zeropage" */

2 struct boot_params {

3 /*0x000*/ struct screen_info screen_info;

4 /*0x040*/ struct apm_bios_info apm_bios_info;

5 /*0x054*/ __u8 _pad2[12];

6 /*0x060*/ struct ist_info ist_info;

7 /*0x070*/ __u8 _pad3[16];

8 /*0x080*/ __u8 hd0_info[16]; /* obsolete! */

9 /*0x090*/ __u8 hd1_info[16]; /* obsolete! */

10 /*0x0a0*/ struct sys_desc_table sys_desc_table;

11 /*0x0b0*/ __u8 _pad4[144];

12 /*0x140*/ struct edid_info edid_info;

13 /*0x1c0*/ struct efi_info efi_info;

14 /*0x1e0*/ __u32 alt_mem_k;

15 /*0x1e4*/ __u32 scratch; /* Scratch field! */

16 /*0x1e8*/ __u8 e820_entries;

17 /*0x1e9*/ __u8 eddbuf_entries;

18 /*0x1ea*/ __u8 edd_mbr_sig_buf_entries;

19 /*0x1eb*/ __u8 _pad6[6];

20 /*0x1f1*/ struct setup_header hdr; /* setup header */

21 __u8 _pad7[0x290-0x1f1-sizeof(struct setup_header)];

22 /*0x290*/ __u32 edd_mbr_sig_buffer[EDD_MBR_SIG_MAX];

23 /*0x2d0*/ struct e820entry e820_map[E820MAX];

24 /*0xcd0*/ __u8 _pad8[48];

25 /*0xd00*/ struct edd_info eddbuf[EDDMAXNR];

26 /*0xeec*/ __u8 _pad9[276];

27 } __attribute__((packed));

其中比较重要的有setup_header和e820_map,e820_map我们以后再探讨,这里先看看setup_header。它的定义和boot_params结构在同一个文件。

代码片段 4.6.节自include/asm-x86/bootparam.h

1 struct setup_header {

2 /* 实模式代码setup镜像的扇区个数。*/

3 __u8 setup_sects;

4 __u16 root_flags;

5 __u32 syssize;

6 __u16 ram_size;

7 #define RAMDISK_IMAGE_START_MASK 0x07FF

8 #define RAMDISK_PROMPT_FLAG 0x8000

9 #define RAMDISK_LOAD_FLAG 0x4000

10 __u16 vid_mode;

11 __u16 root_dev;

12 __u16 boot_flag;

13 __u16 jump;

14 __u32 header;

15 __u16 version;

16 /*

17 * realmode_swtch用来设置Hook函数,

18 * setup在进入保护模式前调用该函数。

19 */

20 __u32 realmode_swtch;

21 __u16 start_sys;

22 __u16 kernel_version;

23

24 /* Boot Loader 的类别,不同的Boot Loader有一个唯一的编号。*/

25 __u8 type_of_loader;

26 __u8 loadflags;

27 #define LOADED_HIGH (1<<0)

28 #define KEEP_SEGMENTS (1<<6)

29 #define CAN_USE_HEAP (1<<7)

30 __u16 setup_move_size;

31

32 /* 保护模式代码的入口。*/

33 __u32 code32_start;

34 /*

35 * Boot Load把Ramdisk镜像文件加载到内存中,

36 * 并设置该参数指向内存的起始地址。

37 */

38 __u32 ramdisk_image;

39 /* Ramdisk镜像文件的大小。*/

40 __u32 ramdisk_size;

41 __u32 bootsect_kludge;

42 __u16 heap_end_ptr;

43 __u16 _pad1;

44

45 /* Boot Loader存放内核参数的地址。*/

46 __u32 cmd_line_ptr;

47 __u32 initrd_addr_max;

48 __u32 kernel_alignment;

49 __u8 relocatable_kernel;

50 __u8 _pad2[3];

51 __u32 cmdline_size;

52 __u32 hardware_subarch;

53 __u64 hardware_subarch_data;

54 } __attribute__((packed));

现在来看看copy_boot_params 函数。

代码片段 4.7.节自arch/x86/boot/main.c

1 static void copy_boot_params(void)

2 {

3 struct old_cmdline {

4 u16 cl_magic;

5 u1

6 cl_offset;

6 };

7 const struct old_cmdline * const oldcmd =

8 (const struct old_cmdline *)OLD_CL_ADDRESS;

9

10 BUILD_BUG_ON(sizeof boot_params != 4096);

11 /* 第二个参数是在header.S中。*/

12 memcpy(&boot_params.hdr, &hdr, sizeof hdr);

13

14 if (!boot_params.hdr.cmd_line_ptr &&

15 oldcmd->cl_magic == OLD_CL_MAGIC) {

16 /* Old-style command line protocol. */

17 u16 cmdline_seg;

18

19 /* Figure out if the command line falls in the region of memory

20 that an old kernel would have copied up to 0x90000... */

21 if (oldcmd->cl_offset < boot_params.hdr.setup_move_size)

22 cmdline_seg = ds();

23 else

24 cmdline_seg = 0x9000;

25

26 boot_params.hdr.cmd_line_ptr =

27 (cmdline_seg << 4) + oldcmd->cl_offset;

28 }

29

30 printf("kernel command line: %s at 0x%x\n",

31 boot_params.hdr.cmd_line_ptr - (ds()<<4),

32 boot_params.hdr.cmd_line_ptr);

33 }

这里主要是把header.S中的hdr 结构中的参数复制到boot_params的hdr中,Boot Loader 可以向内核传递参数,它把参数放在适当的位置,并且把内核参数的指针存放到hdr.cmd_line_ptr 中。最后一行是笔者增加的,读者可以把这一行添加到内核中,之后编译运行看看效果。需要注意的是这个指针是32保护模式下的线性地址,在后面我们将看到进入保护模式后,启动阶段的临时段基地址为0,实模式下的代码默认会用DS<<4+Offset 来访问,所以这里在printf中传递的参数可以把这个指针减去ds()<<4访问到正确的命令行参数。如果读者不能理解这行代码,就需要及时补充相关基础知识了。

main 函数的最后是通过调用go_to_protected_mode()进入保护模式。

代码片段 4.8.节自arch/x86/boot/pm.c

1 void go_to_protected_mode(void)

2 {

3 /* Hook before leaving real mode, also disables interrupts */

4 realmode_switch_hook();

5

6 /* Move the kernel/setup to their final resting places */

7 move_kernel_around();

8

9 /* Enable the A20 gate */

10 if (enable_a20()) {

11 puts("A20 gate not responding, unable to boot...\n");

12 die();

13 }

14

15 /* Reset coprocessor (IGNNE#) */

16 reset_coprocessor();

17

18 /* Mask all interrupts in the PIC */

19 mask_all_interrupts();

20

21 /* Actual transition to protected mode... */

22 setup_idt();

23 setup_gdt();

24 protected_mode_jump(boot_params.hdr.code32_start,

25 (u32)&boot_params + (ds() << 4));

26 }

realmode_switch_hook()在进入保护模式前调用hdr中的realmode_swtch Hook函数。Boot Loader可以利用realmode_swtch抓住在实模式下执行某些代码的最后机会。setup_gdt在全局描述符表中建立临时的数据段和代码供保护模式使用,其段基址为0。boot_params.hdr.code32_start保存着32 位保护模式下的代码入口,Boot Loader把bzImage的第二部分搬到合适的地方,再设置该参数。boot_params是setup镜像的数据段中的参数。读者不妨思考一下,为什么这里要加上(ds() << 4)。可以参考代码片段4.7中的第30行,笔者添加的打印命令行参数的相关解释。protected_mode_jump最后开启CR0 寄存器的PE位进入保护模式,跳转到code32_start继续执行,读者可以做一个试验,把code32_start的值打印出来结合图4.2思考一下。

代码片段 4.9.节自arch/x86/boot/pmjump.S

1 /*

2 * void protected_mode_jump(u32 entrypoint, u32 bootparams);

3 */

4 protected_mode_jump:

5 # edx = bootparams,eax = entrypoint.

6 # esi指向boot_params。

7 # Pointer to boot_params table

8 movl %edx, %esi

9 # Patch ljmpl instruction

10 movl %eax, 2f

11

12 movw $__BOOT_DS, %cx

13 # Per the 32-bit boot protocol

14 xorl %ebx, %ebx

15 # Per the 32-bit boot protocol

16 xorl %ebp, %ebp

17 # Per the 32-bit boot protocol

18 xorl %edi, %edi

19

20 movl %cr0, %edx

21 # Protected mode (PE) bit

22 orb $1, %dl

23 movl %edx, %cr0

24 # 这个跳转用于清除流水线队列。

25 # Short jump to serialize on 386/486

26 jmp 1f

27 1:

28

29 movw %cx, %ds

30 movw %cx, %es

31 movw %cx, %fs

32 movw %cx, %gs

33 movw %cx, %ss

34

35 # Jump to the 32-bit entrypoint

36 # ljmpl opcode

37 .byte 0x66, 0xea

38 2: .long 0 # offset

39 .word __BOOT_CS # segment

第37行是硬编码的跳转指令,其目标跳转地址是第10行通过eax传递进来的入口参数改写的。调用的时候是C 代码,而这里是汇编代码,需要确定C代码的参数和汇编代码中的寄存器的对应关系。这里可以通过.cmd文件的命令行参数和gcc的-S参数来分析。另外如果要用objdump来反汇编分析,则需要指定其指令是8086,其命令是objdump -d –M i8086 arch/x86/boot/setup.elf。

4.2 保护模式startup_32

实模式的protected_mode_jump后,跳出了bzImage 的第一部分,Boot Loader默认把第二部分放在0x100000处,但是在允许重定位内核的情况下这个地址可以是变化的。这个入口是startup_32,内核代码中有两个startup_32,一个位于arch/x86/kernel/head_32.S,另外一个位于arch/x86/boot/compressed/head_32.S。这里跳转的入口是哪一个呢?在对bzImage和vmlinux关系的分析中,我们得知arch/x86/kernel/head_32.S是编译到内核vmlinux中,vmlinux被objcopy 处理再经过压缩,最后被当成数据段的内容和arch/x86/boot/compressed/head_32.o链接。所以先执行的是compressed/head_32.S 中的startup_32。它把数据段中的压缩内核解压缩后,最后还是要跳转到arch/x86/kernel/head_32.S 中的startup_32。

代码片段 4.10.节自arch/x86/kernel/head_32.S

1 .section .text.head,"ax",@progbits

2 ENTRY(startup_32)

3 ......

4 /* Set segments to known values. */

5 # 加载全局描述符表以及初始化段选择子。

6 1: lgdt boot_gdt_descr - __PAGE_OFFSET

7 movl $(__BOOT_DS),%eax

8 movl %eax,%ds

9 movl %eax,%es

10 movl %eax,%fs

11 movl %eax,%gs

12 2:

13 /*

14 * Clear BSS first so that there are no surprises...

15 */

16 # BSS是未初始化变量区,把这个区初始化为0。

17 cld

18 xorl %eax,%eax

19 movl $__bss_start - __PAGE_OFFSET,%edi

20 movl $__bss_stop - __PAGE_OFFSET,%ecx

21 subl %edi,%ecx

22 shrl $2,%ecx

23 rep ; stosl

24 /*

25 * Copy bootup parameters out of the way.

26 * Note: %esi still has the pointer to the real-mode data.

27 * With the kexec as boot loader, parameter segment might

28 * be loaded beyond kernel image and might not even be

29 * addressable by early boot page tables. (kexec on panic case).

30 * Hence copy out the parameters before initializing page tables.

31 */

32 movl $(boot_params - __PAGE_OFFSET),%edi

33 movl $(PARAM_SIZE/4),%ecx

34 cld

35 rep

36 movsl

37 movl boot_params - __PAGE_OFFSET + NEW_CL_POINTER,%esi

38 andl %esi,%esi

39 jz 1f # No comand line

40 movl $(boot_command_line - __PAGE_OFFSET),%edi

41 movl $(COMMAND_LINE_SIZE/4),%ecx

42 rep

43 movsl

44 1:

由于在链接脚本中的起始地址是0xC0100000,如果直接访问其中的符号,其地址必然是在0xC0100000之上。现在还没有启用分页机制,所以访问内核中的符号都要减去__PAGE_OFFSET,也就是0xC0100000。boot_gdt_descr被定义在同一个文件中:

代码片段 4.11.节自arch/x86/kernel/head_32.S

1 boot_gdt_descr:

2 .word __BOOT_DS+7

3 .long boot_gdt - __PAGE_OFFSET

4

5 # 数据段和代码段的基地址都是0。

6 ENTRY(boot_gdt)

7 .fill GDT_ENTRY_BOOT_CS,8,0

8

9 /* kernel 4GB code at 0x00000000 */

10 .quad 0x00cf9a000000ffff

11

12 /* kernel 4GB data at 0x00000000 */

13 .quad 0x00cf92000000ffff

在前面代码片段4.10中的第32行开始,这里的boot_params并不是第一部分setup 中的boot_params,这个boot_params定义在arch/x86/kernel/setup_32.c文件中,它被链接到vmlinux 的数据段中,寄存器esi指向setup中的boot_params参数。这里把1MB 以下的setup保存的参数复制到vmlinux数据区的boot_params结构中。同样内核命令行参数也在代码片段4.10的第40行中被复制到init/main.c中的boot_command_line中。

接下来的工作就是初始化页表,为开启分页机制做好准备。

代码片段 4.12.节自arch/x86/kernel/head_32.S

1 ......

2 page_pde_offset = (__PAGE_OFFSET >> 20);

3

4 default_entry:

5 movl $(pg0 - __PAGE_OFFSET), %edi

6 movl $(swapper_pg_dir - __PAGE_OFFSET), %edx

7 /* 0x007 = PRESENT+RW+USER */

8 movl $0x007, %eax

9 10:

10 /* Create PDE entry */

11 leal 0x007(%edi),%ecx

12 /* Store identity PDE entry */

13 movl %ecx,(%edx)

14 /* Store kernel PDE entry */

15 movl %ecx,page_pde_offset(%edx)

16 addl $4,%edx

17 movl $1024, %ecx

18 11:

19 stosl

20 addl $0x1000,%eax

21 loop 11b

22

23 /*

24 * End condition: we must map up to and including

25 * INIT_MAP_BEYOND_END

26 * bytes beyond the end of our own page tables;

27 * the +0x007 is the attribute bits

28 */

29 leal (INIT_MAP_BEYOND_END+0x007)(%edi),%ebp

30 cmpl %ebp,%eax

31 jb 10b

32 movl %edi,(init_pg_tables_end - __PAGE_OFFSET)

33

34 ......

35 /*

36 * Enable paging

37 */

38 movl $swapper_pg_dir-__PAGE_OFFSET,%eax

39 /* set the page table pointer.. */

40 movl %eax,%cr3

41 movl %cr0,%eax

42 orl $0x80000000,%eax

43 /* ..and set paging (PG) bit */

44 movl %eax,%cr0

45 /* Clear prefetch and normalize %eip */

46 ljmp $__BOOT_CS,$1f

47 1:

48 /* Set up the stack pointer */

49 lss stack_start,%esp

50

51 ......

52 .data

53 ENTRY(stack_start)

54 .long init_thread_union+THREAD_SIZE

55 .long __BOOT_DS

56 ......

swapper_pg_dir是arch/x86/kernel/head_32.S定义的,它是内核的页目录的起始地址,而pg0是在链接脚本vmlinux.lds中定义的,它位于内核数据段结束的地方。这里把物理地址0开始的一段内存同时映射到虚拟地址空间0x00000000 和0xC0000000开始的地方。前面提到内核使用的是从0xC0000000开始大小为1GB虚拟地址空间,这里为什么要把0开始的物理地址同时映射到0x00000000呢?这是因为当开启分页机制后,在执行以某一个符号为转移目标的指令之前1,EIP还是使用物理地址取指令。在上面的第44行执行之后,分页机制被开启,但是EIP中还是物理地址,取下面的一条指令(EIP+4)会去查页表,所以把0x00000000开始的内存也临时地做相应的映射就不会有问题了。

1符号的地址是在编译链接期决定的,内核中的符号起始偏移是0xC0100000,所以执行到以某个符号为目标的转移指令后,EIP就会被更新为正确的虚拟地址。

图 4.1 启动时建立的页面映射

第4行开始,用于建立如图4.3所示的页表映射关系。pg0-__PAGE_OFFSET得到页表的起始物理地址并存放到edi寄存器,swapper_pg_dir-__PAGE_OFFSET得到页目录的起始物理地址并存放到edx寄存器,0x007是低12位标志。leal 0x007(%edi), %ecx把页表的起始物理地址加上低12 位标志的页表项存入ecx寄存器。movl %ecx, (%edx)把第一个页表项存入第一个页目录中,movl %ecx, page_pde_offset(%edx)把第一个页表存入第768 个页目录中。所以虚拟地址0x00000000和0xC0000000的高10位在页目录中索引得到的页表地址是相同的。mov $4, %edx,将edx寄存器指向页目录中的下一项,为下一次做好准备。之后,movl $1024,%ecx,stosl,addl $0x1000, %ecx 把页表中的第0,1,2...1023 项分别指向物理地址为0,4K,8K... 4M的物理页面。一个目录项可以映射4M的内存,如果不够的话,jb 10b跳回去再建立一个新的页表项目,再映射4M。从这里可以看出来,访问0 地址是不会有问题的,因为为虚拟地址0建立了映射,这样就检测不到NULL指针引用错误了。所以在后面我们可以看到,这个映射很快就会被撤销,在内核中虚拟地址空间从0开始的一段区域是不映射,专门用于NULL指针检测的。开启分页机制前,要准备好页表映射多大的空间呢?整个内核镜像vmlinux肯定是需要映射,划分了1GB的虚拟地址空间,将来要把更多的物理内存映射到这个虚拟地址空间上来,所以用于映射这1GB虚拟地址空间的页表也要提前建立好映射。否则开启分页机制后,内核要映射其他的物理页面时,需要填写页表项,所以进入保护模式前必须保证页表项本身的内存能够通过页机制访问到。所以INIT_MAP_BEYOND_END定义为BOOTBITMAP_SIZE + (PAGE_TABLE_SIZE + ALLOCATOR_SLOP)*PAGE_SIZE_asm,PAGE_SIZE_asm 是页大小,这里PAGE_TABLE_SIZE 就是用做页表的大小,定义为1024,它能够映射4G 的地址空间;ALLOCATOR_SLOP定义为4,保险起见多映射几个。进入start_kenel 后,内核要临时设立一个启动时的内存管理机制bootmem_allocator,BOOTBITMAP_SIZE是bootmem_allocator需要使用的空间大小。读者请自己思考一下,为什么这里INIT_MAP_BEYOND_END中没有包括vmlinux的大小呢?(提示:请接合vmlinux.lds 中对pg0的定义)。

第49行使用lss指令为内核设置新的堆栈指针,可以看到堆栈段选择子ss为

__BOOT_DS,和数据段在同一个段,栈顶指针esp为init_thread_union + THREAD_SIZE,THREAD_SIZE默认是8K。考虑到堆栈是向低地址增长,因此堆栈的底部是init_thread_union,它是在arch/x86/kernel/init_task.c 文件中定义的:

代码片段 4.13.节自arch/x86/kernel/init_task.c

union thread_union init_thread_union

__attribute__((__section__(".data.init_task"))) =

{ INIT_THREAD_INFO(init_task) };

堆栈本身被设置在vmlinux 镜像的.data.init_task 段中,它是在vmlinux.lds 中定义的:

代码片段 4.14.节自arch/x86/kernel/vmlinux.lds

. = ALIGN((8192)); /* init_task */

.data.init_task : AT(ADDR(.data.init_task) - 0xC0000000) {

*(.data.init_task)

}

以后我们将会看到内核会利用这个堆栈“手工”建立一个进程,最终在这个“手工”建立的进程环境中调用kernel_thread()创建init 进程。

最终建立的虚拟地址到物理地址的映射关系如图4.4所示。当然在允许内核重定位的情况下,图4.4中的vmlinux不一定是从1M的地方开始的。在设置好页目录和页表后,把页表结束的物理地址保存到init_pg_tables_end变量中,这个变量是在arch/x86/kernel/setup_32.c中定义的,以后内核会用到这个变量,它记录了内核目前使用到的最大物理内存地址。最后通过置位CR0寄存器中的PE位进入分页模式。

图 4.2 启动时虚拟地址和物理地址映射关系

页表初始化完成后,就需要对中断进行初始化了,首先,内核在arch/x86/kernel/traps_32.c文件中定义了一个中断描述符表,一共256项,每一项8个字节,其定义如下:

代码片段 4.15.节自arch/x86/kernel/traps_32.c

struct desc_struct idt_table[256]

__attribute__((__section__(".data.idt"))) = { {0, 0}, };

idt_table位于内核数据段中的.data.idt段中,被初始化为0。现在,需要把对应的中断处理函数的首地址填充到idt_table中,在启动过程之初,所有的中断处理程序都被临时设置为ignore_int。

代码片段 4.16.节自arch/x86/kernel/head_32.S

1 setup_idt:

2 lea ignore_int,%edx

3 movl $(__KERNEL_CS << 16),%eax

4 /* selector = 0x0010 = cs */

5 movw %dx,%ax

6 /* interrupt gate - dpl=0, present */

7 movw $0x8E00,%dx

8

9 lea idt_table,%edi

10 mov $256,%ecx

11 rp_sidt:

12 movl %eax,(%edi)

13 movl %edx,4(%edi)

14 addl $8,%edi

15 dec %ecx

16 jne rp_sidt

17

18 ......

19 /* 跳转到start_kernel处继续执行。*/

20 jmp start_kernel

有了中断描述符表后,还需要把这个表的内存地址告诉CPU,因此在head_32.S文件中定义了idt_descr。

代码片段 4.17.节自arch/x86/kernel/head_32.S

idt_descr:

# idt contains 256 entries

.word IDT_ENTRIES*8-1 // IDT段界限

.long idt_table // IDT基地址

然后通过lidt idt_descr指令,把idt_descr加载到IDTR 寄存器中,现在CPU就可以在中断时,根据IDTR 寄存器找到中断描述符表,然后利用中断号在描述符表中索引到中断处理程序的地址。最后在第20行跳转到start_kernel()函数。

4.3 内核启动start_kernel()

start_kernel是内核初始化的主要函数,在这个函数中负责对内核的各个模块进行初始化,包括内存,中断等,在这里我们先对start_kernel进行总体上的介绍,对某个具体模块初始化的介绍放到相关的具体章节中。

代码片段 4.18.节自init/main.c

1 asmlinkage void __init start_kernel(void)

2 {

3 char * command_line;

4 extern struct kernel_param __start___param[], __stop___param[];

5

6 ......

7 /* 打印Linux内核版本信息。*/

8 printk(linux_banner);

9 /* 根据BIOS和Boot Loader传递的参数收集系统硬件信息。*/

10 setup_arch(&command_line);

11 ......

12 /* 复制参数。*/

13 setup_command_line(command_line);

14 ......

15 /* 初始化进程调度模块。*/

16 sched_init();

17 ......

18 /* 内存管理相关的初始化。*/

19 build_all_zonelists();

20 page_alloc_init();

21

22 /* 在终端上打印内核参数。*/

23 printk(KERN_NOTICE "Kernel command line: %s\n",

24 boot_command_line);

25

26 /* 处理内核参数。*/

27 parse_early_param();

28 parse_args("Booting kernel",

29 static_command_line,

30 __start___param,

31 __stop___param - __start___param,

32 &unknown_bootoption);

33 ......

34 /* 中断处理初始化。*/

35 trap_init();

36 init_IRQ();

37 ......

38 /* 软中断初始化。*/

39 softirq_init();

40 timekeeping_init();

41 /* 系统时钟相关初始化。*/

42 time_init();

43 /* OProfile系统性能分析模块的初始化。*/

44 profile_init();

45 ......

46 /* 控制台初始化。*/

47 console_init();

48 ......

49 /* 内存管理相关的初始化。*/

50 mem_init();

51 kmem_cache_init();

52 ......

53 /* 最后初始化其他的。*/

54 rest_init();

55 }

这里各个初始化函数我们将在后面的相关章节进行讨论。rest_init()主要调用kernel_thread()建立两个内核进程继续对内核进行初始化。

代码片段 4.19.节自init/main.c

1 static void noinline __init_refok rest_init(void)

2 {

3 /* 建立kernel_init内核线程。*/

4 kernel_thread(kernel_init, NULL, CLONE_FS | CLONE_SIGHAND);

5 ......

6

7 /* 建立kthreadd内核线程。*/

8 pid = kernel_thread(kthreadd, NULL, CLONE_FS | CLONE_FILES);

9 kthreadd_task = find_task_by_pid(pid);

10 unlock_kernel();

11 /*

12 * The boot idle thread must execute schedule()

13 * at least once to get things moving:

14 */

15 init_idle_bootup_task(current);

16 preempt_enable_no_resched();

17 schedule();

18 preempt_disable();

19

20 /* Call into cpu_idle with preempt disabled */

21 cpu_idle();

22 }

kernel_init将继续初始化内核,并建立init进程。

代码片段 4.20.节自init/main.c

1 static int __init kernel_init(void * unused)

2 {

3 ......

4 /* do_basic_setup中最后主要调用do_initcalls.*/

5 do_basic_setup();

6 ......

7 init_post();

8 }

9

10 static void __init do_initcalls(void)

11 {

12 initcall_t *call;

13 ......

14

15 for (call = __initcall_start; call < __initcall_end; call++) {

16 ktime_t t0, t1, delta;

17 char *msg = NULL;

18 char msgbuf[40];

19 int result;

20

21 /* 打印调用的函数名。*/

22 if (initcall_debug) {

23 printk("Calling initcall 0x%p", *call);

24 print_fn_descriptor_symbol(":%s()", (unsigned long)*call);

25 printk("\n");

26 t0 = ktime_get();

27 }

28

29 /* 调用具体的初始化函数。*/

30 result = (*call)();

31 ......

32 }

33 ......

34 }

35

36 static int noinline init_post(void)

37 {

38 ......

Linux常用命令

(1)基于虚拟机的Linux操作系统的使用 1.启动VMware Workstation应用程序,启动Windows以后,选择开始—程序——VMware—VMware Workstation,出现如图7—1所示窗口。 2.选择File—Open选择安装好的Linux虚拟机文件,或者直接单击Start this virtual machine 启动Linux操作系统。 3.启动系统后需要输入用户名和密码,如图7—2所示,用户名为“root”,密码为“jsjxy308”。 4.启动终端,成功进入系统后,选择“应用程序”—“系统工具”—“终端”。 5.在终端中执行Linux 命令,终端运行以后,就可以在这里输入Linux命令,并按回车键执行。 (2)文件与目录相关命令的使用 1.查询/bin目录,看一看有哪些常用的命令文件在该目录下: ll /bin 2.进入/tmp目录下,新建目录myshare: cd /tmp mkdir myshare ls –ld myshare/ 3.用pwd命令查看当前所在的目录: pwd 4.新建testfile文件: touch testfile ls -l 5.设置该文件的权限模式: chomd 755 testfile ls -l testfile 6.把该文件备份到/tmp/myshare目录下,并改名为testfile.bak: cp testfile myshare/testfile.bak ls -l myshare/ 7.在/root目录下为该文件创建1个符号连接: ln -s /tmp/testfile /root/testfile.ln ls -l /root/testfile.ln 8.搜索inittab 文件中含有initdefault字符串的行: cat /etc/inittab | grep initdefault (3)磁盘管理与维护命令的使用 1)Linux 下使用光盘步骤 1.确认光驱对应的设备文件: ll /dev/cdrom 2.挂载光盘: mout -t iso9600/dev/cdrom test/dir 1.查询挂载后的目录: ll /media/cdrom 2.卸载光盘: umount /dev/cdrom 2)Linux下USB设备的使用

linux网络操作系统和实训课后习题答案解析(1)

练习题 一、选择题 1. Linux 最早是由计算机爱好者 B 开发的。 A. Richard Petersen B. Linus Torvalds C. Rob Pick D. Linux Sarwar 2. 下列 C 是自由软件。 A. Windows XP B. UNIX C. Linux D. Windows 2000 3. 下列 B 不是Linux 的特点。 A. 多任务 B. 单用户 C. 设备独立性 D. 开放性 4. Linux 的内核版本2.3.20 是 A 的版本。 A. 不稳定 B. 稳定的 C. 第三次修订 D. 第二次修订 二、填空题 1. GUN 的含义是:GNU's Not UNIX。 2. Linux 一般有 3 个主要部分:内核(kernel)、命令解释层(Shell 或其他操作环境)、 实用工具。 三、简答题(略) 1. 简述Red Hat Linux 系统的特点。 2. 简述一些较为知名的Linux 发行版本。 练习题 一、选择题 1. Linux 安装过程中的硬盘分区工具是 D 。 A. PQmagic B. FDISK C. FIPS D. Disk Druid 2. Linux 的根分区系统类型是 C 。 A. FATl6 B. FAT32 C. ext3 D. NTFS 二、填空题 1. 安装Linux 最少需要两个分区,分别是 swap 交换分区和/(根)分区。 2. Linux 默认的系统管理员账号是 root 。 3. X-Window System 由三部分构成:X Server、X Client 和通信通道。 三、简答题(略) 1. Linux 有哪些安装方式 2. 安装Red Hat Linux 系统要做哪些准备工作 3. 安装Red Hat Linux 系统的基本磁盘分区有哪些 4. Red Hat Linux 系统支持的文件类型有哪些 练习题 一、选择题 1. C 命令能用来查找在文件TESTFILE 中包含四个字符的行 A. grep’’TESTFILE B. grep’….’TESTFILE C. grep’^$’TESTFILE D. grep’^….$’TESTFILE 2. B 命令用来显示/home 及其子目录下的文件名。 A. ls -a /home B. ls -R /home C. ls -l /home D. ls -d /home 3. 如果忘记了ls 命令的用法,可以采用 C 命令获得帮助 A. ls B. help ls C. man ls D. get ls 4. 查看系统当中所有进程的命令是 D 。 A. ps all B. ps aix C. ps auf D. ps aux

Linux操作系统源代码详细分析

linux源代码分析:Linux操作系统源代码详细分析 疯狂代码 https://www.doczj.com/doc/123762426.html,/ ?:http:/https://www.doczj.com/doc/123762426.html,/Linux/Article28378.html 内容介绍: Linux 拥有现代操作系统所有功能如真正抢先式多任务处理、支持多用户内存保护虚拟内存支持SMP、UP符合POSIX标准联网、图形用户接口和桌面环境具有快速性、稳定性等特点本书通过分析Linux内核源代码充分揭示了Linux作为操作系统内核是如何完成保证系统正常运行、协调多个并发进程、管理内存等工作现实中能让人自由获取系统源代码并不多通过本书学习将大大有助于读者编写自己新 第部分 Linux 内核源代码 arch/i386/kernel/entry.S 2 arch/i386/kernel/init_task.c 8 arch/i386/kernel/irq.c 8 arch/i386/kernel/irq.h 19 arch/i386/kernel/process.c 22 arch/i386/kernel/signal.c 30 arch/i386/kernel/smp.c 38 arch/i386/kernel/time.c 58 arch/i386/kernel/traps.c 65 arch/i386/lib/delay.c 73 arch/i386/mm/fault.c 74 arch/i386/mm/init.c 76 fs/binfmt-elf.c 82 fs/binfmt_java.c 96 fs/exec.c 98 /asm-generic/smplock.h 107 /asm-i386/atomic.h 108 /asm- i386/current.h 109 /asm-i386/dma.h 109 /asm-i386/elf.h 113 /asm-i386/hardirq.h 114 /asm- i386/page.h 114 /asm-i386/pgtable.h 115 /asm-i386/ptrace.h 122 /asm-i386/semaphore.h 123 /asm-i386/shmparam.h 124 /asm-i386/sigcontext.h 125 /asm-i386/siginfo.h 125 /asm-i386/signal.h 127 /asm-i386/smp.h 130 /asm-i386/softirq.h 132 /asm-i386/spinlock.h 133 /asm-i386/system.h 137 /asm-i386/uaccess.h 139 //binfmts.h 146 //capability.h 147 /linux/elf.h 150 /linux/elfcore.h 156 /linux/errupt.h 157 /linux/kernel.h 158 /linux/kernel_stat.h 159 /linux/limits.h 160 /linux/mm.h 160 /linux/module.h 164 /linux/msg.h 168 /linux/personality.h 169 /linux/reboot.h 169 /linux/resource.h 170 /linux/sched.h 171 /linux/sem.h 179 /linux/shm.h 180 /linux/signal.h 181 /linux/slab.h 184 /linux/smp.h 184 /linux/smp_lock.h 185 /linux/swap.h 185 /linux/swapctl.h 187 /linux/sysctl.h 188 /linux/tasks.h 194 /linux/time.h 194 /linux/timer.h 195 /linux/times.h 196 /linux/tqueue.h 196 /linux/wait.h 198 init/.c 198 init/version.c 212 ipc/msg.c 213 ipc/sem.c 218 ipc/shm.c 227 ipc/util.c 236 kernel/capability.c 237 kernel/dma.c 240 kernel/exec_do.c 241 kernel/exit.c 242 kernel/fork.c 248 kernel/info.c 255 kernel/itimer.c 255 kernel/kmod.c 257 kernel/module.c 259 kernel/panic.c 270 kernel/prk.c 271 kernel/sched.c 275 kernel/signal.c 295 kernel/softirq.c 307 kernel/sys.c 307 kernel/sysctl.c 318 kernel/time.c 330 mm/memory.c 335 mm/mlock.c 345 mm/mmap.c 348 mm/mprotect.c 358 mm/mremap.c 361 mm/page_alloc.c 363 mm/page_io.c 368 mm/slab.c 372 mm/swap.c 394 mm/swap_state.c 395 mm/swapfile.c 398 mm/vmalloc.c 406 mm/vmscan.c 409

linux内核IMQ源码实现分析

本文档的Copyleft归wwwlkk所有,使用GPL发布,可以自由拷贝、转载,转载时请保持文档的完整性,严禁用于任何商业用途。 E-mail: wwwlkk@https://www.doczj.com/doc/123762426.html, 来源: https://www.doczj.com/doc/123762426.html,/?business&aid=6&un=wwwlkk#7 linux2.6.35内核IMQ源码实现分析 (1)数据包截留并重新注入协议栈技术 (1) (2)及时处理数据包技术 (2) (3)IMQ设备数据包重新注入协议栈流程 (4) (4)IMQ截留数据包流程 (4) (5)IMQ在软中断中及时将数据包重新注入协议栈 (7) (6)结束语 (9) 前言:IMQ用于入口流量整形和全局的流量控制,IMQ的配置是很简单的,但很少人分析过IMQ的内核实现,网络上也没有IMQ的源码分析文档,为了搞清楚IMQ的性能,稳定性,以及借鉴IMQ的技术,本文分析了IMQ的内核实现机制。 首先揭示IMQ的核心技术: 1.如何从协议栈中截留数据包,并能把数据包重新注入协议栈。 2.如何做到及时的将数据包重新注入协议栈。 实际上linux的标准内核已经解决了以上2个技术难点,第1个技术可以在NF_QUEUE机制中看到,第二个技术可以在发包软中断中看到。下面先介绍这2个技术。 (1)数据包截留并重新注入协议栈技术

(2)及时处理数据包技术 QoS有个技术难点:将数据包入队,然后发送队列中合适的数据包,那么如何做到队列中的数

激活状态的队列是否能保证队列中的数据包被及时的发送吗?接下来看一下,激活状态的队列的 证了数据包会被及时的发送。 这是linux内核发送软中断的机制,IMQ就是利用了这个机制,不同点在于:正常的发送队列是将数据包发送给网卡驱动,而IMQ队列是将数据包发送给okfn函数。

《LINUX网络操作系统》试题及其答案

《LINUX网络操作系统》试题及其答 案 最新资料,word文档,可以自编辑!!精品文档下载【本页是封面,下载后可以删除!】广州工程技术职业学院信息工程院《LINUX操作系统》期末考试复习题理论部分: 一、单项选择题:1.Linux的创始人是谁A、Turbolinux B、AT&T Bell Laboratry C、University of Helsinki D、Linus Torvalds 2.在Linux 中,命令解释器是哪个A、管道B、分级文件系统C、字符型处理器D、shell 3.Linux安装程序提供了两个引导装载程序( C ) A、GROUP和LLTO B、DIR和COID C、GRUB和LILO D、以上都不是4.如果当前目录是/home/sea/china,那么“china”的父目录是哪个目录A、

/home/sea B、/home/C、/D、/sea 5.在Linux中,要删除abc目录及其全部内容的命令为:A、rm abc B、rm -r abc C、rmdir abc D、rmdir -r abc 6.在Linux环境下,能实现域名解析的功能软件模块是:A、apache B、dhcpd C、BIND D、SQUID 7.创建的辅助DNS服务器无法获得主DNS区域信息,哪种方法可以解决A、重新启动主DNS服务器B、手动从主DNS服务器复制区域文件到辅助DNS服务器C、重新启动辅助DNS服务器D、在主DNS服务器中利用allow-transfer命令设置“允许区域复制” 8.假定kernel支持vfat分区,下面哪一个操作是将/dev/hda1,一个Windows 分区加载到/win目录? A、mount -t windows /win /dev/hda1 B、mount -fs=msdos /dev/hda1 /win C、mount -s win/dev/hda1 /win D、mount –t vfat/dev/hda1

红帽linux常用操作命令

红帽linux常用操作命令 1.查看硬件信息 # uname -a # 查看内核/操作系统/CPU信息 # head -n 1 /etc/issue # 查看操作系统版本 # cat /proc/cpuinfo # 查看CPU信息 # hostname # 查看计算机名 # lspci -tv # 列出所有PCI设备 # lsusb -tv # 列出所有USB设备 # lsmod # 列出加载的内核模块 # env # 查看环境变量资源 # free -m # 查看内存使用量和交换区使用量# df -h # 查看各分区使用情况 # du -sh # 查看指定目录的大小 # grep MemTotal /proc/meminfo # 查看内存总量 # grep MemFree /proc/meminfo # 查看空闲内存量 # uptime # 查看系统运行时间、用户数、负载# cat /proc/loadavg # 查看系统负载磁盘和分区 # mount | column -t # 查看挂接的分区状态 # fdisk -l # 查看所有分区 # swapon -s # 查看所有交换分区 # hdparm -i /dev/hda # 查看磁盘参数(仅适用于IDE设备) # dmesg | grep IDE # 查看启动时IDE设备检测状况网络# ifconfig # 查看所有网络接口的属性 # iptables -L # 查看防火墙设置 # route -n # 查看路由表 # netstat -lntp # 查看所有监听端口 # netstat -antp # 查看所有已经建立的连接 # netstat -s # 查看网络统计信息进程

读Linux内核源代码

Linux内核分析方法 Linux的最大的好处之一就是它的源码公开。同时,公开的核心源码也吸引着无数的电脑爱好者和程序员;他们把解读和分析Linux的核心源码作为自己的最大兴趣,把修改Linux源码和改造Linux系统作为自己对计算机技术追求的最大目标。 Linux内核源码是很具吸引力的,特别是当你弄懂了一个分析了好久都没搞懂的问题;或者是被你修改过了的内核,顺利通过编译,一切运行正常的时候。那种成就感真是油然而生!而且,对内核的分析,除了出自对技术的狂热追求之外,这种令人生畏的劳动所带来的回报也是非常令人着迷的,这也正是它拥有众多追随者的主要原因: ?首先,你可以从中学到很多的计算机的底层知识,如后面将讲到的系统的引导和硬件提供的中断机制等;其它,象虚拟存储的实现机制,多任务机制,系统保护机制等等,这些都是非都源码不能体会的。 ?同时,你还将从操作系统的整体结构中,体会整体设计在软件设计中的份量和作用,以及一些宏观设计的方法和技巧:Linux的内核为上层应用提供一个与具体硬件不相关的平台; 同时在内核内部,它又把代码分为与体系结构和硬件相关的部分,和可移植的部分;再例如,Linux虽然不是微内核的,但他把大部分的设备驱动处理成相对独立的内核模块,这样减小了内核运行的开销,增强了内核代码的模块独立性。 ?而且你还能从对内核源码的分析中,体会到它在解决某个具体细节问题时,方法的巧妙:如后面将分析到了的Linux通过Botoom_half机制来加快系统对中断的处理。 ?最重要的是:在源码的分析过程中,你将会被一点一点地、潜移默化地专业化。一个专业的程序员,总是把代码的清晰性,兼容性,可移植性放在很重要的位置。他们总是通过定义大量的宏,来增强代码的清晰度和可读性,而又不增加编译后的代码长度和代码的运行效率; 他们总是在编码的同时,就考虑到了以后的代码维护和升级。甚至,只要分析百分之一的代码后,你就会深刻地体会到,什么样的代码才是一个专业的程序员写的,什么样的代码是一个业余爱好者写的。而这一点是任何没有真正分析过标准代码的人都无法体会到的。 然而,由于内核代码的冗长,和内核体系结构的庞杂,所以分析内核也是一个很艰难,很需要毅力的事;在缺乏指导和交流的情况下,尤其如此。只有方法正确,才能事半功倍。正是基于这种考虑,作者希望通过此文能给大家一些借鉴和启迪。 由于本人所进行的分析都是基于2.2.5版本的内核;所以,如果没有特别说明,以下分析都是基于i386单处理器的2.2.5版本的Linux内核。所有源文件均是相对于目录/usr/src/linux的。 方法之一:从何入手 要分析Linux内核源码,首先必须找到各个模块的位置,也即要弄懂源码的文件组织形式。虽然对于有经验的高手而言,这个不是很难;但对于很多初级的Linux爱好者,和那些对源码分析很

《Linux网络操作系统配置与管理》试卷D[3页]

《Linux 管理与服务器配置》试卷D 第1页,共3页 密 密 封 线 内 不 得 答 题 XXXXX 职业技术学院2018年7月考试 《Linux 网络操作系统配置与管理》试卷D (考时:90分钟) 适用专业: 课程代号: 一、填空题(每空2分,共30分) 1. GNOME 桌面环境包括顶部工具栏、 和桌面。 2. 使用通配符 每次可以匹配一个字符。 3. Linux 内核引导时,从文件 中读取要加载的文件系统。 4. 若要改变一个文件的所有者,可通过 命令来实现。 5. 在RHEL7中设定限制用户使用磁盘空间的软件工具包是 。 6. 结束后台进程的命令是 。 7. 把文件file1和file2合并成file3的命令是 。 8. 当umask=027时,创建的目录的初始权限为___________。 9. 在使用手工的方法配置网络时,可通过修改 文件来改变主机名。 10. 命令“rpm -qa |grep vsftpd”的作用是 。 11. 挂载NFS 服务器192.168.1.12的共享目录/tmp 到本地目录/mnt/share 的命令是: 。 12. 在RHEL7系统中,启动DHCP 服务的命令是: 。 13. 在设置Apache 服务器时,一般将服务的默认端口绑定到系统的 端口上。 14. 登录IP 为192.168.89.64的FTP 服务器的命令是 。 15. Postfix 服务器的主配置文件的名称是 。 二、选择题(下列各题的四个选项中,请将正确选项的序号填入。每小题2分,共30分) 1. 下面说法错误的是( ) A .Linux 操作系统不限制应用程序可用内存的大小 B .Linux 操作系统是免费软件,可以通过网络下载 C .Linux 操作系统部分符合UNIX 标准,可以将Linux 上完成的程序经过重新修改后移植到UNIX 主机上运行。 D .Linux 操作系统支持多用户,在同一时间可以有多个用户使用主机 2. RHEL7系统中重启系统的命令是( ) A .systemctl halt B .shutdown C .systemctl reboot D .init 0 3. 使用命令chmod 的数字设置,可以改变( ) A .文件的访问特权 B .目录的访问特权 C .文件/目录的访问特权 D .以上说法都不对 4. 下面哪些命令可以显示一个文件的具体内容( ) A .vim B .cat C .head D .more 5. 要删除用户user 并同时删除该用户的主目录,可以使用命令( ) A .userdel user B .deluser user C .userdel -r user D .deluser -r user

linux 网络配置命令

Linux设置用永久静态IP 配置文件位于: /etc/sysconfig/network-script s/ifcfg-eth0 DEVICE=eth0 ONBOOT=yes BOOTPROTO=static IPADDR=192.168.0.3 NETMASK=255.255.255.0 GATEWAY=192.168.0.1 使IP地址生效: /sbin/ifdown eth0 /sbin/ifup eth0 通知网关更新信息: /etc/init.d/network restart 如果想给该网卡设置多IP则: 1.执行“cp ifcfg-eth0 ifcfg-eth0:1”复制eth0的配置文件,文件名在原文件名后加":1". 2.编辑修改ifcfg-eth0:1中相应的字段,具体如下:

懂得网络配置命令是一般技术人员必备的技术,经过一段时间的研究和学习,总结了一些常用的命令和示例以便日后查阅. 传统的在1--3点,ip高级路由命令在4--12点,两者部分可以通用,并达到同样的目的,但ip 的功能更强大,可以实现更多的配置目的. 首先,先了解传统的网络配置命令: 1. 使用ifconfig命令配置并查看网络接口情况 示例1: 配置eth0的IP,同时激活设备: # ifconfig eth0 192.168.4.1 netmask 255.255.255.0 up 示例2: 配置eth0别名设备eth0:1 的IP,并添加路由 # ifconfig eth0:1 192.168.4.2 # route add –host 192.168.4.2 dev eth0:1 示例3:激活(禁用)设备 # ifconfig eth0:1 up(down) 示例4:查看所有(指定)网络接口配置 # ifconfig (eth0) 2. 使用route 命令配置路由表 示例1:添加到主机路由 # route add –host 192.168.4.2 dev eth0:1 # route add –host 192.168.4.1 gw 192.168.4.250 示例2:添加到网络的路由 # route add –net IP netmask MASK eth0 # route add –net IP netmask MASK gw IP # route add –net IP/24 eth1

Linux内核源代码阅读与工具介绍

Linux的内核源代码可以从很多途径得到。一般来讲,在安装的linux系统下,/usr/src/linux 目录下的东西就是内核源代码。另外还可以从互连网上下载,解压缩后文件一般也都位于linux目录下。内核源代码有很多版本,目前最新的版本是2.2.14。 许多人对于阅读Linux内核有一种恐惧感,其实大可不必。当然,象Linux内核这样大而复杂的系统代码,阅读起来确实有很多困难,但是也不象想象的那么高不可攀。只要有恒心,困难都是可以克服的。任何事情做起来都需要有方法和工具。正确的方法可以指导工作,良好的工具可以事半功倍。对于Linux内核源代码的阅读也同样如此。下面我就把自己阅读内核源代码的一点经验介绍一下,最后介绍Window平台下的一种阅读工具。 对于源代码的阅读,要想比较顺利,事先最好对源代码的知识背景有一定的了解。对于linux内核源代码来讲,基本要求是:⑴操作系统的基本知识;⑵对C语言比较熟悉,最好要有汇编语言的知识和GNU C对标准C的扩展的知识的了解。另外在阅读之前,还应该知道Linux内核源代码的整体分布情况。我们知道现代的操作系统一般由进程管理、内存管理、文件系统、驱动程序、网络等组成。看一下Linux内核源代码就可看出,各个目录大致对应了这些方面。Linux内核源代码的组成如下(假设相对于linux目录): arch这个子目录包含了此核心源代码所支持的硬件体系结构相关的核心代码。如对于X86平台就是i386。 include这个目录包括了核心的大多数include文件。另外对于每种支持的体系结构分别有一个子目录。 init此目录包含核心启动代码。 mm此目录包含了所有的内存管理代码。与具体硬件体系结构相关的内存管理代码位于arch/*/mm目录下,如对应于X86的就是arch/i386/mm/fault.c。 drivers系统中所有的设备驱动都位于此目录中。它又进一步划分成几类设备驱动,每一种也有对应的子目录,如声卡的驱动对应于drivers/sound。 ipc此目录包含了核心的进程间通讯代码。 modules此目录包含已建好可动态加载的模块。 fs Linux支持的文件系统代码。不同的文件系统有不同的子目录对应,如ext2文件系统对应的就是ext2子目录。 kernel主要核心代码。同时与处理器结构相关代码都放在arch/*/kernel目录下。 net核心的网络部分代码。里面的每个子目录对应于网络的一个方面。 lib此目录包含了核心的库代码。与处理器结构相关库代码被放在arch/*/lib/目录下。

《Linux网络操作系统项目教程(RHEL7.4--CentOS-7.4)(第3版))》习题及答案

《Linux网络操作系统项目教程 (RHEL7.4/CentOS 7.4)(第3版)》 课后习题答案 1.11 练习题 一、填空题 1.GNU的含义是。 2.Linux一般有3个主要部分:、、。 3.目前被称为纯种的UNIX指的就是以及这两套操作系统。 4.Linux是基于的软件模式进行发布的,它是GNU项目制定的通用公共许可证,英文是。 5.史托曼成立了自由软件基金会,它的英文是。 6.POSIX是的缩写,重点在规范核心与应用程序之间的接口,这是由美国电气与电子工程师学会(IEEE)所发布的一项标准。 7.当前的Linux常见的应用可分为与两个方面。 8.Linux的版本分为和两种。 9.安装Linux最少需要两个分区,分别是。 10.Linux默认的系统管理员账号是。 1. GNU's Not Unix的递归缩写(GNU计划一个自由软件组织) 2. 内核(kernel)、命令解释层(Shell或其他操作环境)、实用工具 3. System V BSD 4. Copyleft(无版权)General Public License,GPL) 5. FSF,Free Software Foundation 6. 便携式操作系统接口(Portable Operating System Interface) 7. 企业应用个人应用 8. 内核版本发行版本 9. swap交换分区/(根)分区 10. root 二、选择题 1.Linux最早是由计算机爱好者()开发的。

A.Richard Petersen B.Linus Torvalds C.Rob Pick D.Linux Sarwar 2.下列中()是自由软件。 A.Windows XP B.UNIX C.Linux D.Windows 2008 3.下列中()不是Linux的特点。 A.多任务B.单用户C.设备独立性D.开放性4.Linux的内核版本2.3.20是()的版本。 A.不稳定B.稳定的C.第三次修订D.第二次修订5.Linux安装过程中的硬盘分区工具是()。 A.PQmagic B.FDISK C.FIPS D.Disk Druid 6.Linux的根分区系统类型可以设置成()。 A.FATl6 B.FAT32 C.ext4 D.NTFS 1.B 2.C 3.B 4.A 5. D 6. C 三、简答题(部分) 1.简述Linux的体系结构。 2.使用虚拟机安装Linux系统时,为什么要先选择稍后安装操作系统,而不是去选择RHEL 7系统镜像光盘? 答:在配置界面中若直接选择了RHEL 7系统镜像,则VMware Workstation虚拟机会使用内置的安装向导自动进行安装,最终安装出来的系统跟我们后续进行实验所需的系统环境会不一样。 3.简述RPM与Yum软件仓库的作用。 答:RPM是为了简化安装的复杂度,而Yum软件仓库是为了解决软件包之间的依赖关系。 4.安装Red Hat Linux系统的基本磁盘分区有哪些? 5.Red Hat Linux系统支持的文件类型有哪些? 6.丢失root口令如何解决? 7.RHEL 7系统采用了systemd作为初始化进程,那么如何查看某个服务的运行状态? 答:执行命令“systemctl status 服务名.service”可查看服务的运行状态,其中服务名后的.service可以省略。

linux源代码分析实验报告格式

linux源代码分析实验报告格式

Linux的fork、exec、wait代码的分析 指导老师:景建笃 组员:王步月 张少恒 完成日期:2005-12-16

一、 设计目的 1.通过对Linux 的fork 、exec 、wait 代码的分析,了解一个操作系统进程的创建、 执行、等待、退出的过程,锻炼学生分析大型软件代码的能力; 2.通过与同组同学的合作,锻炼学生的合作能力。 二、准备知识 由于我们选的是题目二,所以为了明确分工,我们必须明白进程的定义。经过 查阅资料,我们得知进程必须具备以下四个要素: 1、有一段程序供其执行。这段程序不一定是进程专有,可以与其他进程共用。 2、有起码的“私有财产”,这就是进程专用的系统堆栈空间 3、有“户口”,这就是在内核中有一个task_struct 结构,操作系统称为“进程控制 块”。有了这个结构,进程才能成为内核调度的一个基本单位。同时,这个结构又 是进程的“财产登记卡”,记录着进程所占用的各项资源。 4、有独立的存储空间,意味着拥有专有的用户空间:进一步,还意味着除前述的 系统空间堆栈外,还有其专用的用户空间堆栈。系统为每个进程分配了一个 task_struct 结构,实际分配了两个连续的物理页面(共8192字节),其图如下: Struct task_struct (大约1K) 系统空间堆栈 (大约7KB )两个 连续 的物 理页 面 对这些基本的知识有了初步了解之后,我们按老师的建议,商量分工。如下: 四、 小组成员以及任务分配 1、王步月:分析进程的创建函数fork.c ,其中包含了get_pid 和do_fork get_pid, 写出代码分析结果,并画出流程图来表示相关函数之间的相互调用关系。所占工作 比例35%。 2、张少恒:分析进程的执行函数exec.c,其中包含了do_execve 。写出代码分析结 果,并画出流程图来表示相关函数之间的相互调用关系。所占工作比例35% 。 3、余波:分析进程的退出函数exit.c,其中包含了do_exit 、sys_wait4。写出代码 分析结果,并画出流程图来表示相关函数之间的相互调用关系。所占工作比例30% 。 五、各模块分析: 1、fork.c 一)、概述 进程大多数是由FORK 系统调用创建的.fork 能满足非常高效的生灭机制.除了 0进程等少数一,两个进程外,几乎所有的进程都是被另一个进程执行fork 系统调 用创建的.调用fork 的进程是父进程,由fork 创建的程是子进程.每个进程都有一

《LINUX网络操作系统》试题及其答案

广州工程技术职业学院 信息工程(系)院(2009--2010 学年第 2 学期) 《LINUX操作系统》期末考试复习题 理论部分: 一、单项选择题: 1.Linux 的创始人是谁(D ) A、Turbolinux B 、AT&T Bell Laboratry C、University of Helsinki D、Linus Torvalds 2.在Linux 中,命令解释器是哪个(D ) A、管道 B、分级文件系统 C、字符型处理器 D shell 3.Linux 安装程序提供了两个引导装载程序( C ) A、GROU和LLTO B、DIR和COID C GRU岳口LILO D、以 上都不是 4.如果当前目录是/home/sea/china ,那么“ china ”的父目录是哪个目录( A )

A、/home/sea B 、/home/ C 、/ D 、/sea 5.在Linux 中,要删除abc 目录及其全部内容的命令为:(B ) A、rm abc B、rm -r abc C 、rmdir abc D 、rmdir -r abc 6.在Linux 环境下,能实现域名解析的功能软件模块是:(C ) A、apache B 、dhcpd C、BIND D 、SQUID 7. 创建的辅助DNS服务器无法获得主DNS区域信息,哪种方法可以 解决( D ) A、重新启动主DNS服务器 B手动从主DNS服务器复制区域文件到辅助DN&艮务器 C重新启动辅助DN&艮务器 D在主DNS服务器中利用allow-transfer 命令设置“允许区域复制” 8. 假定kernel 支持vfat 分区,下面哪一个操作是将/dev/hda1 ,一 个Windows分区加载到/win目录(D )

Linux命令 linuc 常用命令

Linux必学的60个命令 IPv4 技术在网络发展中起到了巨大的作用,不过随着时间的流逝它无论在网络地址的 提供、服务质量、安全性等方面都越来越力不从心,IPv6呼之欲出。Linux是所有操作系统中最先支持IPv6的,一般Linux基于2.4内核的Linux发行版本都可以直接使用IPv6,不过主 要发行版本没有加载IPv6模块,可以使用命令手工加载,需要超级用户的权限。 (1)加载IPv6模块 使用命令检测,其中inet6 addr: fe80::5054:abff:fe34:5b09/64,就是eth0网卡的 IPv6地址。 # modprobe IPv6 #ifconfig eth0 li nk encap:Ethernet HWaddr 52:54:AB:34:5B:09 inet addr:192.168.1.2 Bcast:192.168.1.255 Mask:255.255.255.0 inet6 addr: fe80::5054:abff:fe34:5b09/64 Scope ink UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1 RX packets:0 errors:0 dropped:0 overruns:0 frame.:0 TX packets:21 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:100 RX bytes:0 (0.0 b) TX bytes:1360 (1.3 Kb) Interrupt:5 base address:0xec00 (2)使用ping命令检测网卡的IPv6地址是否有效 #ping6 -I eth0 -c 2 fe80::200:e8ff:fea0:2586 和IPv4不一样,使用ping6命令时必须指定一个网卡界面,否则系统不知道将数据包发 送到哪个网络设备。I表示Interface、eth0是第一个网卡,-c表示回路,2表示ping6操作 两次。结果见图1所示。 图1 IPv6网络下的ping6命令 (3)使用ip命令在IPv6下为eth0增加一个IP地址 #ip -6 addr add 3ffe:ffff:0:f101::1/64 dev eth0 使用ifconfig命令,查看网卡是否出现第二个IPv6地址。 Linux网络的主要优点是能够实现资源和信息的共享,并且用户可以远程访问信息。 Linux提供了一组强有力的网络命令来为用户服务,这些工具能够帮助用户进行网络设定、 检查网络状况、登录到远程计算机上、传输文件和执行远程命令等。 上面介绍了Linux中比较重要的网络命令,其实Linux还有许多命令需要学习。Linux网 络操作命令的一个特点就是命令参数选项很多,并不要求全部记住,关键在于理解命令的主 要用途和学会使用帮助信息。 Linux必学的60个命令(5)-网络安全 虽然Linux和Windows NT/2000系统一样是一个多用户的系统,但是它们之间有不少重要 的差别。对于很多习惯了Windows系统的管理员来讲,如何保证Linux操作系统安全、可靠将

Linux源代码分析_存储管理

文章编号:1004-485X (2003)03-0030-04 收稿日期:2003-05-10 作者简介:王艳春,女(1964 ),副教授,主要从事操作系统、中文信息处理等方面的研究工作。 Linux 源代码分析 存储管理 王艳春 陈 毓 葛明霞 (长春理工大学计算机科学技术学院,吉林长春130022) 摘 要:本文剖析了Linux 操作系统的存储管理机制。给出了Linux 存储管理的特点、虚存的实现方法,以及主要数据结构之间的关系。 关键词:Linux 操作系统;存储管理;虚拟存储中图分类号:T P316 81 文献标识码:A Linux 操作系统是一种能运行于多种平台、源代码公开、免费、功能强大、与Unix 兼容的操作系统。自其诞生以来,发展非常迅速,在我国也受到政府、企业、科研单位、大专院校的重视。我们自2000年开始对Linux 源代码(版本号是Linux 2 2 16)进行分析,首先剖析了进程管理和存储管理部分,本文是有关存储管理的一部分。主要介绍了Linux 虚存管理所用到的数据结构及其相互间的关系,据此可以更好地理解其存储管理机制,也可以在此基础上对其进行改进或在此后的研究中提供借鉴作用。作为一种功能强大的操作系统,Linux 实现了以虚拟内存为主的内存管理机制。即能够克服物理内存的局限,使用户进程在透明方式下,拥有比实际物理内存大得多的内存。本文主要阐述了Linux 虚存管理的基本特点和主要实现技术,并分析了Linux 虚存管理的主要数据结构及其相互关系。 1 Lin ux 虚存管理概述 Linux 的内存管理采用虚拟页式管理,使用多级页表,动态地址变换。进程在运行过程中可以动态浮动和扩展,为用户提供了透明的、灵活有效的内存使用方式。 1)32 bit 虚拟地址 在Linux 中,进程的4GB 虚存需通过32 bit 地址进行寻址。Linux 中虚拟地址与线性地址为同一概念,虚拟地址被分成3个子位段,而大小为4k,如图1所示。 2)Linux 的多级页表结构 图1 32位虚拟地址 标准的Linux 的虚存页表为三级页表,依次为页目录(Pag e Directory PGD)、中间页目录(Pag e Middle Directory PMD )、页表(Page Table PT E )。在i386机器上Linux 的页表结构实际为两级,PGD 和PMD 页表是合二为一的。所有有关PMD 的操作关际上是对PGD 的操作。所以源代码中形如*_pgd _*()和*_pmd_*()函数实现的功能也是一样的。 页目录(PGD)是一个大小为4K 的表,每一个进程只有一个页目录,以4字节为一个表项,分成1024个表项(或称入口点),表项的索引即为32位虚拟地址的页目录,该表项的值为所指页表的起始地址。页表(PTE)的每一个入口点的值为此表项所指的一页框(page frame),页表项的索引即为32位虚拟地址中的页号。页框(page reame)并不是物理页,它指的是虚存的一个地址空间。 3) 页表项的格式 图2 Linux 中页目录项和页表项格式 4)动态地址映射 Linux 虚存采用动态地址映射方式,即进程的地址空间和存储空间的对应关系是在程序的执行过 第26卷第3期长春理工大学学报 Vol 26N o 32003年9月 Journal of Changchun University of Science and T echnology Sep.2003

《Linux网络操作系统配置与管理》试卷B含答案

《Linux 管理与服务器配置》试卷B 第1页,共4页 密 密 封 线 内 不 得 答 题 《Linux 网络操作系统配置与管理》试卷B (闭卷) (考时:90分钟) 适用专业: 17级计算机网络技术 课程代号: 110011 一、填空题(每空2分,共30分) 1.Red Hat Linux 支持多种安装方式,包括:光盘安装、硬盘安装、NFS 映像安装、FTP 安装和 等。 2.Red Hat Enterprise Linux 7中提供的默认的图形桌面环境是: 。 3.可以用ls –l 命令来观察文件的权限,每个文件的权限都用10位表示,并分为四段,其中第一段占1位,表示文件类型,第二段占3位,表示 对该文件的权限。 4.Linux 内核引导时,从文件 中读取要加载的文件系统。 5.Linux 内核引导时,从文件 中读取系统中的用户组信息。 6.在Red Hat Enterprise Linux 7中查询网卡信息的命令是 。 7.Samba 服务的主配置文件是 。 8.执行带 选项的fdisk 命令可以查看当前主机中磁盘的分区表信息。 9.静态路由设定后,若网络拓扑结构发生变化,需 修改路由的设置。 10.DHCP 是动态主机配置协议的简称,其作用是:为网络中的主机分配 。 11.DNS 实际上是分布在Internet 上的主机信息的数据库,其作用是实现 之间的转换。 12.apache 的守护进程是 13.若使用vsftpd 的默认配置,使用匿名账户登录FTP 服务器,所处的目录是 。 14.新安装的MariaDB 只有一个名称为 的管理员帐户。 15.Dovecot 邮件服务器的主配置文件的名称是 。 二、选择题(下列各题的四个选项中,请将正确选项的序号填在下表中。每小题2分,共30分) 1.若一台计算机的内存为256MB ,则交换分区的大小通常是( )。 A 、64M B B 、128MB C 、256MB D 、512MB 2.文件exer1的访问权限为rw-r--r--,现要增加所有用户的执行权限和同组用户的写权限,下列命令正确的是( )。 A 、chmod a+x g+w exer1 B 、chmod 765 exer1 C 、chmod o+x exer1 D 、chmod g+w exer1 3.设超级用户root 当前目录为:/usr/local ,键入cd 命令后,用户当前目录为 ( ) A 、/home B 、/root C 、/home/root D 、/usr/local 4.Linux 有三个查看文件的命令,若希望在查看文件内容过程中可以用光标上下移动来查看文件内容,应使用的命令是( )。 A 、cat B 、more C 、less D 、menu 5.将光盘CD-ROM (hdc )挂载到/mnt/cdrom 目录下的命令是 ( ) A 、mount /mnt/cdrom B 、mount /mnt/cdrom /dev/hdc

相关主题
文本预览
相关文档 最新文档