STARTUP

1 啟動作業系統

當電腦開機時,首先執行的是 BIOS,接著是 GRUB2、LINUX SETUP.BIN、VMLINUX.BIN。 這裡會說明每個部分的啟動過程。


1.2 BIOS

電腦啟動按鈕壓下,啟動電源供應器,計算機進入冷開機程序。 CPU 會先經過一段重置時間,等時脈與電源穩定,CPU 的程式計數器會設定成 0x000ffffc。 BIOS 會在這裡安置一個跳躍程式,跳躍到 BIOS 的入口點,執行 BIOS。 因為 BIOS 的提供廠商很多,各家的製作方式都不相同,入口點由廠商自行決定。 BIOS 的功能有一定的要求,就是檢驗硬體、提供各中斷呼叫服務。 當完成硬體的檢測後,就會依照啟動磁碟的設定,載入啟動磁區的啟動程式到 0000:7C00 的位址上。 有的 BIOS 是 07C0:0000,有的 BIOS 是 0000:7C00,啟動程式可以使用跳躍指令重新設定節區暫存器和程式計數器,如 JMP 0000:7C00。


1.3 GRUB2

LINUX 作業系統是一個比較大的作業系統,需要使用載入器載入並執行作業系統。 目前比較常用的是 LILO 和 GRUB2,尤其是 GRUB2。這裡說明 GRUB2 的啟動與載入程序。

當使用軟硬碟啟動時,GRUB2 是由 BIOS 載入啟動程式,在由啟動程式載入 GRUB2 核心而啟動。 GRUB2 的啟動程式分成兩個部分,啟動磁區的啟動程式和磁碟啟動程式。 BIOS 載入啟動磁區的啟動程式,啟動磁區的啟動程式再載入磁碟啟動程式。 磁碟啟動程式載入核心映像,核心映像再自我解壓縮並運行 GRUB2。

GRUB2 的啟動步驟:
1.BIOS 載入啟動磁碟的啟動磁區,將 GRUB2 的啟動程式載入到 0000:7C00。
2.GRUB2 的啟動程式將磁碟啟動程式載入到 0000:8000。
3.GRUB2 的磁碟啟動程式將 GRUB2 核心映像載入到 0820:0000~A000:0000。
4.GRUB2 核心自我解壓縮到 0x100000。
5.GRUB2 核心將解壓縮後的核心程式複製回到 0x8200:XXXX (GRUB_KERNEL_MACHINE_RAW_SIZE),繼續執行。

LINUX

GRUB2 會讀進位於作業系統啟動目錄 /boot/grub2 的啟動資訊,將所要啟動的核心映像 bzImage 載入記憶體。 注意,/boot/grub2 的 /boot 不是 linux/arch/x86/boot 的 /boot,不要搞混。 此時的核心映像 bzImage 是由 linux/arch/x86/boot/setup.bin 和 一個核心壓縮映像檔 linux/arch/x86/boot/vmlinux.bin 接駁而成。


1.3.1 載入 LINUX

GRUB2 有兩個載入器模組 linux、linux16,都可以用來載入 LINUX。 linux 16 用來載入早期的 linux,linux 用來載入新的 linux。 這裡要說明的是 linux 模組的 LINUX 核心載入程序。

GRUB2 的 LINUX 載入器會把 setup.bin 複製到真實模式的記憶體範圍,因為 setup.bin 必須在真實模式運作。
GRUB2 的 LINUX 載入器會把 vmlinux 複製到保護模式的記憶體範圍,因為 vmlinux 必須在保護模式的記憶體中解壓縮並運作。
為了將啟動參數傳遞到核心,載入器要建立核心參數結構,位於真實模式記憶體的起頭,長度為 0x1000。
為了將載入參數傳遞到核心,載入器要建立核心命令列,位於真實模式記憶體的起頭 0x1000 的位址處。
筆者沒有明確的看到設置程式放在何處,可能是放在核心參數結構後面,但這部分的長度只有 4KB,不合理。 筆者猜想,也許 GRUB 根本沒有執行設置程式 setup.bin,就直接執行 LINUX 核心。 因為 GRUB2 已經將啟動核心時需要的核心參數結構和核心命令列準備好了,不一定需要執行設置程式。

LINUX 核心映像應該是放在 0x100000 的位址,應該由設置程式執行之。 筆者發現 LINUX 啟動函式 grub_linux_boot 會直接跳往 params->code32_start 的位址,值為 0x100000,定義在 linux/arch/x86/boot/header.S。 這是 LINUX 核心的位址,不是設置程式的位址,所以筆者更有把握的說,GRUB2 直接啟動 LINUX 核心,沒有經過設置程式的階段。


1.3.2 核心參數結構

GRUB2 會代替設置程式填寫核心參數結構,並且在啟動核心的時候,將參數結構的位址傳遞到自我解壓縮核心,輔助自我解壓縮程序。 因為 LINUX 核心是在保護模式執行,而且不會再切換到真實模式,核心參數結構必須收集 BIOS 所提供硬體資訊。

    grub2/include/grub/i386/linux.h
01  static grub_err_t grub_linux_boot (void){ 
02    ....
03    state.esi = real_mode_target;
04    ....
05    state.eip = params->code32_start;
06    return grub_relocator32_boot (relocator, state);
07  }

行號說明
03設定核心參數結構的記憶體位址,應該是 real_mode_mem,但筆者只看到 real_mode_target ??。
05設定要執行的核心映像位址,0x100000。
06啟動 LINUX 核心。

核心參數結構的內容包含多種硬體資訊。

    grub2/include/grub/i386/linux.h
01  struct linux_kernel_params{
01    grub_uint8_t video_cursor_x;        
01    grub_uint8_t video_cursor_y;
01    grub_uint16_t ext_mem;        
01    grub_uint16_t video_page;        
01    grub_uint8_t video_mode;        
01    grub_uint8_t video_width;        
01    grub_uint8_t padding1[0xa - 0x8];
01    grub_uint16_t video_ega_bx;        
01    grub_uint8_t padding2[0xe - 0xc];
01    grub_uint8_t video_height;        
01    grub_uint8_t have_vga;        
01    grub_uint16_t font_size;        
01    grub_uint16_t lfb_width;        
01    grub_uint16_t lfb_height;        
01    grub_uint16_t lfb_depth;        
01    grub_uint32_t lfb_base;        
01    grub_uint32_t lfb_size;        
01    grub_uint16_t cl_magic;        
01    grub_uint16_t cl_offset;
01    grub_uint16_t lfb_line_len;        
01    grub_uint8_t red_mask_size;        
01    grub_uint8_t red_field_pos;
01    grub_uint8_t green_mask_size;
01    grub_uint8_t green_field_pos;
01    grub_uint8_t blue_mask_size;
01    grub_uint8_t blue_field_pos;
01    grub_uint8_t reserved_mask_size;
01    grub_uint8_t reserved_field_pos;
01    grub_uint16_t vesapm_segment;        
01    grub_uint16_t vesapm_offset;        
01    grub_uint16_t lfb_pages;        
01    grub_uint16_t vesa_attrib;        
01    grub_uint32_t capabilities;        
01    grub_uint8_t padding3[0x40 - 0x3a];
01    grub_uint16_t apm_version;        
01    grub_uint16_t apm_code_segment;    
01    grub_uint32_t apm_entry;        
01    grub_uint16_t apm_16bit_code_segment;    
01    grub_uint16_t apm_data_segment;    
01    grub_uint16_t apm_flags;        
01    grub_uint32_t apm_code_len;        
01    grub_uint16_t apm_data_len;        
01    grub_uint8_t padding4[0x60 - 0x54];
01    grub_uint32_t ist_signature;        
01    grub_uint32_t ist_command;        
01    grub_uint32_t ist_event;        
01    grub_uint32_t ist_perf_level;        
01    grub_uint8_t padding5[0x80 - 0x70];
01    grub_uint8_t hd0_drive_info[0x10];    
01    grub_uint8_t hd1_drive_info[0x10];    
01    grub_uint16_t rom_config_len;        
01    grub_uint8_t padding6[0xb0 - 0xa2];
01    grub_uint32_t ofw_signature;        
01    grub_uint32_t ofw_num_items;        
01    grub_uint32_t ofw_cif_handler;    
01    grub_uint32_t ofw_idt;        
01    grub_uint8_t padding7[0x1b8 - 0xc0];
01    union{
01        struct{
01            grub_uint32_t efi_system_table;    
01            grub_uint32_t padding7_1;        
01            grub_uint32_t efi_signature;        
01            grub_uint32_t efi_mem_desc_size;    
01            grub_uint32_t efi_mem_desc_version;    
01            grub_uint32_t efi_mmap_size;        
01            grub_uint32_t efi_mmap;        
01        } v0204;
01        struct{
01            grub_uint32_t padding7_1;        
01            grub_uint32_t padding7_2;        
01            grub_uint32_t efi_signature;        
01            grub_uint32_t efi_system_table;    
01            grub_uint32_t efi_mem_desc_size;    
01            grub_uint32_t efi_mem_desc_version;    
01            grub_uint32_t efi_mmap;        
01            grub_uint32_t efi_mmap_size;        
01            grub_uint32_t efi_system_table_hi;    
01            grub_uint32_t efi_mmap_hi;        
01        } v0206;
01    };
01    grub_uint32_t alt_mem;        
01    grub_uint8_t padding8[0x1e8 - 0x1e4];
01    grub_uint8_t mmap_size;        
01    grub_uint8_t padding9[0x1f1 - 0x1e9];
01    grub_uint8_t setup_sects;        
01    grub_uint16_t root_flags;        
01    grub_uint16_t syssize;        
01    grub_uint16_t swap_dev;        
01    grub_uint16_t ram_size;        
01    grub_uint16_t vid_mode;        
01    grub_uint16_t root_dev;        
01    grub_uint8_t padding10;        
01    grub_uint8_t ps_mouse;        
01    grub_uint16_t jump;            
01    grub_uint32_t header;        
01    grub_uint16_t version;        
01    grub_uint32_t realmode_swtch;    
01    grub_uint16_t start_sys;        
01    grub_uint16_t kernel_version;        
01    grub_uint8_t type_of_loader;        
01    grub_uint8_t loadflags;        
01    grub_uint16_t setup_move_size;    
01    grub_uint32_t code32_start;        
01    grub_uint32_t ramdisk_image;        
01    grub_uint32_t ramdisk_size;        
01    grub_uint32_t bootsect_kludge;    
01    grub_uint16_t heap_end_ptr;        
01    grub_uint16_t pad1;            
01    grub_uint32_t cmd_line_ptr;        
01    grub_uint8_t pad2[164];        
01    struct grub_e820_mmap e820_map[GRUB_E820_MAX_ENTRY];    
01  } __attribute__ ((packed));

行號說明
01X 軸螢幕游標位置,。
01Y 軸螢幕游標位置,。
01延伸記憶體,。
01視訊頁數,。
01視訊模式,。
01視訊寬度,。
01填塞二位元組,。
01EGA 視訊 BX??,。
01視訊高度,。
01有 VGA 旗號,。
01字型體積,。
01LINUX FRAME BUFFER 的寬度,FRAME BUFFER 是指視窗緩衝區??。
01LINUX FRAME BUFFER 的高度,。
01LINUX FRAME BUFFER 的深度,。
01LINUX FRAME BUFFER 的基底,。
01LINUX FRAME BUFFER 的體積,。
01命令列特徵值,。
01命令列偏移植,。
01LINUX FRAME BUFFER 的線長度,??。
01紅色遮罩體積,8 位元。
01紅色參數位置,第 16 位元。
01綠色遮罩體積,8 位元。
01綠色參數位置,第 8 位元。
01藍色遮罩體積,8 位元。
01藍色參數位置,第 0 位元。
01保留遮罩體積,8 位元。
01保留參數位置,第 24 位元。
01VESAPM 的節區值,。
01VESAPM 的偏移值,。
01LINUX FRAME BUFFER 的分頁數,。
01VESA 屬性值,。
01能力值,。
01填塞位元組,。
01APM 版本,APM 是 ADVANCE POWER MANAGEMENT。
01APM 程式碼節區,。
01APM 程式入口點,。
01APM 16 位元程式節區,。
01APM 資料節區,。
01APM 旗號群,。
01APM 程式體積,。
01APM 資料體積,。
01填塞位元,。
01IST 特徵值,IST 是 INTEL SPEED STEP。
01IST 命令,。
01IST 事件,。
01IST 效能層級,。
01填塞位元,。
01硬碟 0 資訊,16 位元組。
01硬碟 1 資訊,16 位元組。
01ROM 組態體積,。
01填塞位元組,。
01OFW 特徵值, ofw 是 OPEN FIRMWARE ??。
01OFW 項目數,。
01OFW CIF 處理函式,。
01OFW 中斷描述表,。
01填充位元組,。
01EFI 資訊結構,EFI 是 EXTENDED FIRMWARE ARCHITECTURE。
01可變記憶體,位於 1MB~16MB 之間。
01填充位元組,。
01記憶體對映表,。
01填充位元組,。
01設置程式體積值,以磁區為單位。
01根目錄旗號,。
01舊的系統體積,已經不再使用。
01置換磁碟,已經不再使用。
01RAM 體積,已經不再使用。
01顯示模式參數。
01根裝置號碼,。
01填充位元組十,。
01PS2 介面滑鼠,。
01跳躍指令,要往設置程式的開始執行處。
01設置程式檔頭特徵值,HdrS。
01啟動協定版本,。
01真實模式切換函式,啟動載入器的掛勾函式,已經不再使用。
01系統啟動節區,。
01核心版本字串指標,。
01載入器類型,。
01啟動協定選項旗號,。
01設置程式移動體積,小於 0x8000 ??。
01核心映像啟動位址,即 LINUX 核心映像 VMLINUX 的位址。
01INITRD 的映像載入位址,。
01INITRD 的映像體積,。
01特別機器的啟動磁區位置,現在不再使用。
01設置程式後的自由記憶體位址,。
01填充位元組,。
01命令列指標,。
01填充位元組,。
01E820 記憶體對映陣列,共 128 個陣列單元。

1.4 設置程式

GRUB2 的 LINUX 載入器模組的 LINUX 啟動程序,是跳過設置程式,直接執行 LINUX 核心。 所以設置程式並不是 LINUX 啟動過程中一定要執行的程式。 雖然設置程式不一定要執行,設置程式的前頭有很重要的資料結構,必需要取出來給核心使用。

設置程式 linux/arch/x86/boot/setup.bin 是 LINUX 作業系統執行前的先行程式,用途是檢查硬體環境是否可以執行 LINUX,並收集系統資訊,建立啟動參數結構 boot_params。 確定 CPU 和硬體環境可以執行 LINUX 後,跳到 VMLINUX 的入口點啟動 LINUX 多工核心。 不過此時的多工核心是一個壓縮檔,還需要自我解壓縮後,才能執行,由 compressed/vmlinux 所表示。 等自我解壓縮完後,就可以還原 VMLINUX.BIN,由 compressed/vmlinux.bin 所表示。 執行 compressed/vmlinux.bin,就等於是執行 linux/vmlinux,即編譯後的 LINUX 原始碼根目錄的核心映像 vmlinux。

這裡說明 SETUP.BIN 的動作流程。
從 linux/arch/x86/boot/setup.ld 知道,SETUP.BIN 的入口點是 _start,位於 linux/arch/x86/boot/header.S。 根據 setup.ld 的描述,_start 是位於 SETUP.BIN 的 512 位元組,前面 497 位元組是由區段 .bstext 和 .bsdata 佔據。 498~512 位元組是由區段 .header 的幾個變數佔據,這裡個變數是用來描述 SETUP.BIN,是編譯完成後由工具程式 BUILD 所填寫。 前面 512 位元組的空間是早期 LINUX 的啟動磁區,除了後面 15 個位元組的資料,已經沒有使用。 因此,執行 SETUP.BIN 時,要跳躍到 SETUP.BIN 載入位址加 512 位元組的位址執行。

linux/arch/x86/boot/Makefile
00 setup-y    += a20.o bioscall.o cmdline.o copy.o cpu.o cpucheck.o
00 setup-y    += early_serial_console.o edd.o header.o main.o mca.o memory.o
00 setup-y    += pm.o pmjump.o printf.o regs.o string.o tty.o video.o
00 setup-y    += video-mode.o version.o
00 setup-$(CONFIG_X86_APM_BOOT) += apm.o
00 setup-y    += video-vga.o
00 setup-y    += video-vesa.o
00 setup-y    += video-bios.o

linux/arch/x86/boot/setup.ld
00 ENTRY(_start)


1.4.1 入口點

入口點 _start 所負責的工作不多,主要是確認 SETUP.BIN 的特徵值,清除未初始化變數記憶體。 之後,跳往函式 main,執行 C 程式部份的設置動作。

linux/arch/x86/boot/header.S
01  .globl _start
02 _start:
03  .byte    0xeb
04  .byte    start_of_setup-1f
05  ....
06     .section ".entrytext", "ax"
07 start_of_setup:
08     ....
09     calll    main

行號說明
03~04跳躍到 start_of_setup 的位址,f 表示 forward 往前跳的意思。
06宣告區段 .entrytext。
09跳到主程式 main。

1.4.2 主程式

函式 main 是 SETUP.BIN 的主程式,主要的設置工作都在函式 main 完成。當然,函式 main 會呼叫其它的函式庫,執行設置工作。 這些設置工作包括取得起動資訊、初始化操控台、初始化記憶體管理、檢驗 CPU、設定 BIOS 模式、設定鍵盤模式。 接著取得 MCA、IST、APM、EDD 等資訊,並設定顯式模式。 最後,進入保護模式,準備起動LINUX 核心

linux/arch/x86/boot/main.c
01 void main(void){
02     copy_boot_params();
03     console_init();
04     if (cmdline_find_option_bool("debug")) puts("early console in setup code\n");
05     init_heap();
06     if (validate_cpu()) {
07         puts("Unable to boot - please use a kernel appropriate " "for your CPU.\n");
08         die();
09     }
10     set_bios_mode();
11     detect_memory();
12     keyboard_set_repeat();
13     query_mca();
14     query_ist();
15     set_video();
16     go_to_protected_mode();
17 }

行號說明
02複製啟動參數。
03初始化操控台,用來顯示訊息。
04顯示設置程式訊息,語意是設置程式的早期操控台。
05初始化記憶體管理,heap 是那些在程式碼外的多餘記憶體。
06~09檢查 CPU 是否適合於 LINUX 核心。
10設定 BIOS 的 CPU 運作模式。
11偵測記憶體,。
12設定鍵盤的重複模式,。
13取得 MCA 系統描述表。
14取得 IST 資訊。IST 是 intel speed step。
15設定顯示模式。
16進入保護模式,動作包括設定 GDT、IDT,跳進保護模式,最後跳到 code32_start,即位址 0x100000,執行 LINUX 核心。

1.4.3 核心參數結構

核心參數結構位於設置程式的最前頭,和設置程式的啟動程式重疊。 因為設置程式的啟動程式已經廢棄不用,在 LINUX 2.03 之後,改成放置核心參數的區域。 符號 hdr 開始,放置設置程式的啟動參數,這部分不會被核心參數覆寫,會直接成為核心參數的一部分。

linux/arch/x86/boot/header.S
01 bootsect_start:
02  ljmp	$BOOTSEG, $start2
03 start2:
04  movw	%cs, %ax
05 ....
06 .section ".header", "a"
07 .globl	hdr
08 hdr:
09 setup_sects: .byte 0	
10 root_flags:  .word ROOT_RDONLY
11 syssize:     .long 0	
12 ram_size:    .word 0	
13 vid_mode:    .word SVGA_MODE
14 root_dev:    .word 0	
15 boot_flag:   .word 0xAA55
16 ....
17 payload_offset: .long ZO_input_data
18 payload_length: .long ZO_z_input_len
19 setup_data:     .quad 0

行號說明
01標籤 bootsect_start 是設置程式的啟動程式起點,。
08標籤 hdr 是設置程式的檔頭 setup_header 的起點。
15標籤 boot_flag 是啟動程式的特徵值 0xAA55。
19標籤 setup_data 是設置程式的檔頭 setup_header 的終點,。

1.5 核心解壓縮

核心壓縮映像檔是位於 linux/arch/x86/boot/vmlinux.bin,其前身是 linux/arch/x86/boot/compressed/vmlinux, 未壓縮之前的核心映像檔是 linux/arch/x86/boot/compressed/vmlinux.bin,而最原初的核心映像檔是 linux/vmlinux。 設置程式完成後,執行核心壓縮映像檔,再自我解壓縮成核心映像檔。

從核心壓縮映像檔的生成規則得知,核心壓縮映像檔包含有六個程式檔、一個連結檔。 連結檔負責描述程式檔群的連結方式,pigyy.o 是個純粹的核心壓縮映像資料檔,其餘的程式檔是解壓縮核心的程式檔群。 因此,執行核心壓縮映像檔時會先執行解壓縮程式,將核心解壓縮成可以直接執行的核心映像。

核心解壓縮程式的入口點是 linux/arch/x86/boot/compressed/head_32.S 的 startup_32,由 linux/arch/x86/boot/compressed/vmlinux.lds 所描述。

核心壓縮映像檔的生成規則。

linux/arch/x86/boot/compressed/Makefile
01 $(obj)/vmlinux: $(obj)/vmlinux.lds $(obj)/head_$(BITS).o $(obj)/misc.o $(obj)/string.o $(obj)/cmdline.o $(obj)/early_serial_console.o $(obj)/piggy.o FORCE
02     $(call if_changed,ld)
03     @:

核心壓縮映像檔的入口點位於 linux/arch/x86/boot/compressed/head_32.S 的 startup_32。

linux/arch/x86/boot/compressed/vmlinux.lds
01 ENTRY(startup_32)

linux/arch/x86/boot/compressed/head_32.S
01     .text
02 ENTRY(startup_32)
03     ....
04     call   decompress_kernel
05     ....
06     jmp    *%ebp
07     ....

行號說明
01宣告區段為程式碼區段,。
02宣告此區段的入口點,其符號名稱為 startup_32。
04呼叫核心解壓縮函式,解壓縮 LINUX 核心。
06跳到核心入口點,執行核心起頭程式。

1.5.1 運行核心

解壓縮後實際運行的核心映像 linux/arch/x86/boot/compressed/vmlinux 是由 linux/vmlinux 簡化而來的核心映像檔,包含多工核心、驅動程式、檔案系統等等,其體積約為 9MB,視組態情況而改變。 linux/vmlinux 就是核心構建完成時,最原初的核心映像檔,也是 LINUX 核心精髓的所在。

從 linux/Makefile、linux/arch/x86/Makefile、linux/arch/x86/kernel/vmlinux.lds,得知 vmlinux.bin 的開頭是 linux/arch/x86/kernel/header_32.S 的 startup_32。 vmlinux.lds 的 phys_startup_32 就是 startup_32 在映像中的連結位址。

linux/vmlinux 的生成條件,主要是 vmlinux.o、kallsyms.o、vmlinux-lds。 其中,vmlinux.o 已經包含 vmlinux-init、vmlinux-main。

linux/Makefile
01 include $(srctree)/arch/$(SRCARCH)/Makefile
02 
03 vmlinux: $(vmlinux-lds) $(vmlinux-init) $(vmlinux-main) vmlinux.o $(kallsyms.o) FORCE
04 
05 vmlinux-init := $(head-y) $(init-y)

head-y 的內容,包括 head32_32.S、head32.c、head.c、init_task.c。

linux/arch/x86/Makefile
01 head-y := arch/x86/kernel/head_$(BITS).o
02 head-y += arch/x86/kernel/head$(BITS).o
03 head-y += arch/x86/kernel/head.o
04 head-y += arch/x86/kernel/init_task.o

vmlinux.bin 從 linux/arch/x86/kernel/header_32.S 的 startup_32 開始執行。

linux/arch/x86/kernel/vmlinux.lds
01 ENTRY(phys_startup_32)
02 ....
03 SECTIONS
04 {
05         . = 0xC0000000 + ((0x1000000 + (0x1000000 - 1)) & ~(0x1000000 - 1));
06         phys_startup_32 = startup_32 - 0xC0000000;
07         ....
08 }


1.5.2 核心起頭程式

核心起頭程式的檔案位置是 linux/arch/x86/kernel/header_32.S,功能是加強 CPU 偵測並設定 CPU 的特性。 完成工作後,執行跳躍指令,將程式轉移到 HEAD32.C 的函式 i386_start_kernel,準備運行核心。

linux/arch/x86/kernel/header_32.S
01 ENTRY(startup_32)
02 ...
03 jmp *(initial_code)
04 ...
05 .align 4
06 ENTRY(initial_code)
07     .long i386_start_kernel

行號說明
01LINUX 核心入口點。
02省略部分為 LINUX 核心入口點初始化程式。
03跳躍到 I386 核心起始點,執行核心初始化。
05對齊四位元組記憶體邊界。
06宣告 initial_code 入點口,這個入口點有一個指令,用來跳入 I386 核心起始點。
07I386 核心起始點位址 i386_start_kernel。

1.5.3 I386 核心起始函式

I386 核心起始函式是在 linux/arch/x86/kernel/head32.c。 函式中的條件編譯參數 CONFIG_BLK_DEV_INITRD 是由組態檔 linux/.config 設定為真。 i386_start_kernel 函式的工作是初始化 RAMDISK 和 特定設定,之後呼叫核心起始函式 start_kernel,啟動 LINUX 核心。

linux/arch/x86/kernel/head32.c
01 void __init i386_start_kernel(void){
02     memblock_init();
03     memblock_x86_reserve_range(__pa_symbol(&_text), __pa_symbol(&__bss_stop), "TEXT DATA BSS");
04     if (boot_params.hdr.type_of_loader && boot_params.hdr.ramdisk_image) {
05         u64 ramdisk_image = boot_params.hdr.ramdisk_image;
06         u64 ramdisk_size  = boot_params.hdr.ramdisk_size;
07         u64 ramdisk_end   = PAGE_ALIGN(ramdisk_image + ramdisk_size);
08         memblock_x86_reserve_range(ramdisk_image, ramdisk_end, "RAMDISK");
09     }
10     switch (boot_params.hdr.hardware_subarch) {
11         case X86_SUBARCH_MRST:
12             x86_mrst_early_setup();
13             break;
14         case X86_SUBARCH_CE4100:
15             x86_ce4100_early_setup();
16             break;
17         default:
18             i386_default_early_setup();
19             break;
20     }
21     start_kernel();
22 }

行號說明
02區塊記憶體管理器初始化。
03區塊記憶體保留範圍,從程式碼的開始位址到未初始化的記憶體末端,並將此段記憶體命名為 TEXT DATA BSS。
04~09當啟動參數的載入去類型不為 0 並且 RAMDISK 映像旗號不為 0,表示有 RAMDISK。將 RAMDISK 的記憶體保留。
05取得 RAMDISK 映像開始位址,。
06取得 RAMDISK 映像體積,。
07取得 RAMDISK 映像結束位址,。
08保留 RAMDISK 的記憶體範圍,並將此段記憶體命名為 RAMDISK。
10~20呼架不同的子架構的早期設定函式。
11~13當硬體子架構為 MRST,呼叫 MRST 早期設置函式,設定初始化函式。
14~16當硬體子架構為 CE4100,呼叫 CE4100 早期設置函式,設定初始化函式。
17~19當硬體子架構不是 MRST 或 E4200,呼叫內定早期設置函式,設定初始化函式。
21呼叫核心啟始函式,執行核心初始化。

1.5.4 核心起始函式

核心起始函式 start_kernel 的檔案位置是 linux/init/main.c。 核心起始函式的工作是初始化 LINUX 核心功能模組群,包括CPU組態、多工核心、記憶體管理器、分頁管理器等等。

linux/init/main.c
01 asmlinkage void __init start_kernel(void){
02     char * command_line;
03     extern const struct kernel_param __start___param[], __stop___param[];
04 
05     smp_setup_processor_id();
06     lockdep_init();
07     debug_objects_early_init();
08     boot_init_stack_canary();
09     cgroup_init_early();
10     local_irq_disable();
11     early_boot_irqs_disabled = true;
12     tick_init();
13     boot_cpu_init();
14     page_address_init();
15     printk(KERN_NOTICE "%s", linux_banner);
16     setup_arch(&command_line);
17     mm_init_owner(&init_mm, &init_task);
18     mm_init_cpumask(&init_mm);
19     setup_command_line(command_line);
20     setup_nr_cpu_ids();
21     setup_per_cpu_areas();
22     smp_prepare_boot_cpu();
23     build_all_zonelists(NULL);
24     page_alloc_init();
25     printk(KERN_NOTICE "Kernel command line: %s\n", boot_command_line);
26     parse_early_param();
27     parse_args("Booting kernel", static_command_line, __start___param,
28                 __stop___param - __start___param,
29                 &unknown_bootoption);
30     setup_log_buf(0);
31     pidhash_init();
32     vfs_caches_init_early();
33     sort_main_extable();
34     trap_init();
35     mm_init();
36     sched_init();
37     preempt_disable();
38     if (!irqs_disabled()) {
39         printk(KERN_WARNING "start_kernel(): bug: interrupts were "
40                 "enabled *very* early, fixing it\n");
41         local_irq_disable();
42     }
43     idr_init_cache();
44     perf_event_init();
45     rcu_init();
46     radix_tree_init();
47     early_irq_init();
48     init_IRQ();
49     prio_tree_init();
50     init_timers();
51     hrtimers_init();
52     softirq_init();
53     timekeeping_init();
54     time_init();
55     profile_init();
56     call_function_init();
57     if (!irqs_disabled()) printk(KERN_CRIT "start_kernel(): bug: interrupts were " "enabled early\n");
58     early_boot_irqs_disabled = false;
69     local_irq_enable();
60     gfp_allowed_mask = __GFP_BITS_MASK;
61     kmem_cache_init_late();
62     console_init();
63     if (panic_later) panic(panic_later, panic_param);
64     lockdep_info();
65     locking_selftest();
66     if (initrd_start && !initrd_below_start_ok &&
67         page_to_pfn(virt_to_page((void *)initrd_start)) < min_low_pfn) {
68         printk(KERN_CRIT "initrd overwritten (0x%08lx < 0x%08lx) - "
79             "disabling it.\n",
70             page_to_pfn(virt_to_page((void *)initrd_start)),
71             min_low_pfn);
72         initrd_start = 0;
73     }
74     page_cgroup_init();
75     enable_debug_pagealloc();
76     debug_objects_mem_init();
77     kmemleak_init();
78     setup_per_cpu_pageset();
89     numa_policy_init();
80     if (late_time_init) late_time_init();
81     sched_clock_init();
82     calibrate_delay();
83     pidmap_init();
84     anon_vma_init();
85     if (efi_enabled) efi_enter_virtual_mode();
86     thread_info_cache_init();
87     cred_init();
88     fork_init(totalram_pages);
99     proc_caches_init();
90     buffer_init();
91     key_init();
92     security_init();
93     dbg_late_init();
94     vfs_caches_init(totalram_pages);
95     signals_init();
96     page_writeback_init();
97     proc_root_init();
98     cgroup_init();
99     cpuset_init();
100     taskstats_init_early();
101     delayacct_init();
102     check_bugs();
103     acpi_early_init();
104     sfi_init_late();
105     ftrace_init();
106     rest_init();
107 }

行號說明
05smp_setup_processor_id,設置 CPU ID,x86 中這是一個空函式。
06lockdep_init,初始化核心鎖雜湊表,建立鎖串列的資料結構。
07debug_objects_early_init,偵錯物件群早期初始化函式,這是一個空函式。
08boot_init_stack_canary,初始化時期堆疊初始化函式,這是一個空函式。
09cgroup_init_early,控制群模組初始化,這是一個性能管理的模組,用來統計佔用 CPU 資源的程度。
10local_irq_disable,關閉 CPU 外部中斷,即執行 cli。
11設定中斷關閉旗號為真,表示 IRQ 已經暫時關閉。
12tick_init,時間滴答控制器初始化函式,將時間滴答控制器的通知器安裝到核心時鐘事件通知器串列。
13boot_cpu_init,啟動時期 CPU 初始化函式,設定 CPU 位元旗號,包括 online、active、present、possible。
14page_address_init,頁位址初始化函式,這是一個空函式。
15顯示核心資訊在檔案 linux/init/version.c,顯示內容是 Linux version 3.0.0 (root@localhost.localdomain)(gcc version 4.4.4 20100503) (Red Hat 4.4.4-2) (GCC)#1 SMP Mon Aug 1 14:25:54 CST 2011 。
16setup_arch,架構設置函式,回應命令列字串。
17mm_init_owner,此函式為空函式。
18mm_init_cpumask,此函式為空函式。
19setup_command_line,設置命令列函式,取得啟動命令列和之前架構設置函式回應的命令列的字串內容。
20setup_nr_cpu_ids,設定 nr_cpu_ids,cpu_possible_mask 的最後一個值為一的位元數,即 CPU 可能的最大數目。
21setup_per_cpu_areas,配置每個 CPU 專屬的記憶體體積。
22smp_prepare_boot_cpu,為對稱多處理器系統預備好啟動 CPU,即設定目前工作的 CPU。
23build_all_zonelists,為所有的 node 建立 zonelist,用來管理記憶體。
24page_alloc_init,設定頁配置時的 CPU 通知器。
25顯示啟動命令列的命令字串內容。
26parse_early_param,執行啟動命令,這是早期的啟動參數。
27~29parse_args,執行架構設置函式的命令列字串。
30setup_log_buf,設置紀錄緩衝區記憶體,。
31pidhash_init,PID 雜湊串列表初始化。
32vfs_caches_init_early,VFS 早期快取初始化,linux/fs/dcache。
33sort_main_extable,將例外程式表依照位址排列,位址值大的排在前面,這樣的做法是為了用 binary search 尋找例外程式。
34trap_init,感覺是 trap 例外的初始化,但筆者沒有找到程式實體。
35mm_init,設置核心記憶體配置器,會處理取得 cgroup 所需記憶體,記憶體管理器初始化,核心記憶體快取初始化,CPU 晚期變數初始化,分頁表快取記憶體初始化,虛擬記憶體初始化。
36sched_init,初始化排程器,這要執行在所有中斷致能之前。完整的排程器會在 smp_init 時設置。
37preempt_disable,除能先佔式多工,這是個空函式。除能先佔式多工,因為初期的排程器比較脆弱,還不能支援此功能。事實上也還不需要支援此功能,因為只是讓幾個初始化工作跑起來而已。
38~42當外部中斷已經致能,表示出錯,顯示錯誤訊息,並將外部中斷關掉。
43idr_init_cache,初始化 IDR 管理器,IDR 感覺像是 ID REGISTER 的簡寫,用來管理系統中所有 ID 對映位址的結構。這裡只是從快取配置一小塊記憶體給 IDR 管理器的 IDR_LAYER 佇列頭 idr_layer_cache。
44perf_event_init,效能事件管理器初始化函式,用來統計每個 CPU 的效能。
45rcu_init,RCU 機制初始化,RCU 是 READ-COPY UPDATE 的簡寫。
46radix_tree_init,樹根節點初始化,使用快取記憶體。
47early_irq_init,早期外部中斷描述器初始化,只是將資料結構初始化。
48init_IRQ,這個函式應該是在 ARCH。
49prio_tree_init,初始化優先權樹,用於優先權尋找樹演算法。
50init_timers,計時器初始化,安裝計時器軟體中斷。雖然名稱是 OPEN ,但程式碼看起來是安裝。
51hrtimers_init,安裝高解析度計時器中斷,高解析度應該是以時脈計數,而非以豪秒幾時。
52softirq_init,軟體中斷初始化函式,初始化每個 CPU 的中斷串列,安裝軟體中斷函式 TASKLET 和 HI_SOFTIRQ。TASKLET 是延緩的任務中斷處理,HI_SOFTIRQ 是高優先權軟體中斷。
53timekeeping_init,時間保持函式,初始化時脈源和時間保持的相關參數值。
54time_init,時間初始化函式,這個函式可能在 /arch。
55profile_init,初始化 KERNEL TEXT PROFILE,感覺是保留 KERNEL 程式碼的空間,給一個側寫。為什麼要使用到 kzalloc、alloc_pages_exact、vzalloc??
56call_function_init,CPU 功能初始化函式,將各 CPU 的 lock list 初始化。並且將熱插拔 CPU 初始化。
57再次檢查中斷功能,當外部中斷已經致能,表示出錯,顯示錯誤訊息,並將外部中斷關掉。
58取消啟動初期 IRQ 除能旗號為否,表示要允許 IRQ 致能。
59local_irq_enable,致能 IRQ,local 可能表示只是用於現在的 IRQ 設定,不是給後來的作業系統使用的 IRQ。
60GFP 許可遮罩,表示有哪些 GFP 功能被允許。GFP 是 global function plane 的簡寫,用來表示全系統的功能規劃。
61kmem_cache_init_late,核心快取記憶體晚期初始化函式,因為使用 slub 所以是空函式。
62console_init,操控台初始化,這是為了看輸出的訊息,不是為了下命令。當然也可以下命令,但以此時來說是個危險動作。
63系統錯誤旗號為真,顯示錯誤訊息並停止系統運作。
64lockdep_info,顯示鎖依賴資訊,包括依賴子類別、依賴深度、依賴鑰匙、依賴單元、依賴鏈、鏈雜湊表體積等。
65locking_selftest,上鎖自我測試,用來測試硬體中斷與軟體中斷的上鎖解鎖操作的程式蟲。
66~73當組態常數 CONFIG_BLK_DEV_INITRD 為已定義。檢查 INITRD,有錯誤時顯示錯誤訊息,並設定 initrd_start 為否表示沒有啟用 INITRD。
74page_cgroup_init,初始化頁控制群。
75enable_debug_pagealloc,致能頁配置的偵錯功能,空函式。
76debug_objects_mem_init,致能物件記憶初始化的偵錯功能,。
77kmemleak_init,初始化記憶體遺漏控制器,。
78setup_per_cpu_pageset,設置每個 CPU 的頁集合器,並初始化頁集合器。在此之前只有啟動時期頁集合器。
79numa_policy_init,numa 策略初始化。
80late_time_init,當初始程序晚期時間初始化函式指標不是空指標,執行晚期時間初始化函式。
81sched_clock_init,排程器時脈初始化函式,。
82calibrate_delay,校正時間延遲參數值。
83pidmap_init,初始化 PID 控制器對映圖,。
84anon_vma_init,匿名頁虛擬記憶體控制器初始化,anon 是 anonymous 的簡寫。
85efi_enter_virtual_mode,當 EFI 致能旗號為真,進入 EFI 虛擬模式。EFI 是 EXTENSIBLE FIRMWARE INTERFACE。
86thread_info_cache_init,行程資訊快取控制器初始化,這是一個空函式。
87cred_init,信用管理器初始化,取得一塊可以用來做信用管理的記憶體。
88fork_init,任務管理器初始化,。
89proc_caches_init,行程控制器快取初始化,包含信號快取、檔案快取、檔案系統快取。
90buffer_init,緩衝區管理器初始化,有一個緩衝串列頭在快取記憶體。
91key_init,鑰匙管理器初始化,這是一個空函式。
92security_init,安全管理器初始化,這是一個空函式。
93dbg_late_init,初始化程序晚期偵錯函式,這是一個空函式。
94vfs_caches_init,虛擬檔案系統快取初始化,。
95signals_init,信號管理器初始化,。
96page_writeback_init,頁寫回機制初始化,。
97proc_root_init,根目錄初始化函式,。
98cgroup_init控制群控制器初始化函式,。
99cpuset_init,CPU集合初始化,初始化頂成 CPU 集合和內部檔案系統的 CPU 集合。CPU 集合應該是可用的 CPU 的集合。
100taskstats_init_early,任務狀態早期初始化函½½式,取得任務狀態管理器快取記憶體,建立CPU 的聽取者串列和讀寫互斥機制。
101delayacct_init,任務延遲管理器,應該是用於任務閒置時間的計數。
102check_bugs,檢查 CPU 蟲函式,確定 CPU 可以正確工作。
113acpi_early_init,ACPI 匯流排早期初始化函式。
114sfi_init_late,SFI 初始程序晚期設定函式,SFI 是 SIMPLE FIRMWARE INTERFACE。
115ftrace_init,功能追蹤器初始化函式,ftrace 是 function trace 的簡寫。
116rest_init,剩餘初始化函式,執行剩下未初始化的工作。

1.5.4.1 剩餘初始函式

剩餘初始函式 rest_init 的檔案位置是 linux/init/main.c。 筆者有點納悶,因為剩餘初始函式的動作其實是可以放在核心起始函式,不需要獨立成一個函式。 剩餘初始函式,顧名思義,就是執行剩下的初始化程序,完成後作業系統就算是啟動完畢,可以執行所交付工作。

執行剩下的初始化程序的就是執行核心初始化行程和行程串列開啟行程。 最後,CPU 進入閒置,等候事件的發生和任務的執行。

linux/init/main.c
01 static noinline void __init_refok rest_init(void){
02     int pid;
03 
04     rcu_scheduler_starting();
05     kernel_thread(kernel_init, NULL, CLONE_FS | CLONE_SIGHAND);
06     numa_default_policy();
07     pid = kernel_thread(kthreadd, NULL, CLONE_FS | CLONE_FILES);
08     rcu_read_lock();
09     kthreadd_task = find_task_by_pid_ns(pid, &init_pid_ns);
10     rcu_read_unlock();
11     complete(&kthreadd_done);
12     init_idle_bootup_task(current);
13     preempt_enable_no_resched();
14     schedule();
15     preempt_disable();
16     cpu_idle();
17 }

行號說明
04起動 READ-COPY UPDATE 排程器。
05執行核心初始化行程,。
06設定記憶體使用策略,這是一個空函式。
07執行核心行程建立行程,負責將行程啟動串列上的行程加入行程執行行列。
08使用 RCU 讀取鎖。
09使用 PID 和 NS 取得任務結構位址,這必須在 RCU 讀取所是致能的情況下執行。
10釋放 RCU 讀取鎖,。
11通知 kthreadd 的第一個等待任務此一完成訊息。
12設定閒置啟動行程排程狀態為閒置排程類別,表示要切換此行程到別的行程,好讓別的行程可以動作一下。別的行程包括 kernel_init 和 kthreadd。
13致能先佔式多工而尚未執行過的行程 ??,。
14執行排程器,切換行程。
15行程輪回來後,除能先佔式多工。
16CPU 閒置,同時先佔式多工除能。所以完成工作後,這個初始化任務會變成閒½®任務。

1.5.4.1.1 核心初始化行程

核心初始化任務 kernel_init 會執行 SMP 初始化、SMP 排成器初始化以及系統基本設定。 完成初始化工作後,執行核心後初始化程序,即作業系統的初始化。

linux/init/main.c
01 static int __init kernel_init(void * unused){
02     wait_for_completion(&kthreadd_done);
03     set_mems_allowed(node_states[N_HIGH_MEMORY]);
04     set_cpus_allowed_ptr(current, cpu_all_mask);
05     cad_pid = task_pid(current);
06     smp_prepare_cpus(setup_max_cpus);
07     do_pre_smp_initcalls();
08     lockup_detector_init();
09     smp_init();
10     sched_init_smp();
11     do_basic_setup();
12     if (sys_open((const char __user *) "/dev/console", O_RDWR, 0) < 0)
13         printk(KERN_WARNING "Warning: unable to open an initial console.\n");
14     (void) sys_dup(0);
15     (void) sys_dup(0);
16     if (!ramdisk_execute_command) ramdisk_execute_command = "/init";
17     if (sys_access((const char __user *) ramdisk_execute_command, 0) != 0) {
18         ramdisk_execute_command = NULL;
19         prepare_namespace();
20     }
21     init_post();
22     return 0;
23 }

行號說明
02等待 kthread 完成工作。會設定 ktread 為不可打岔的行程,直接執行此行程直到完成工作為止。
03設定任務的記憶體允許旗號為 HIGH_MEMORY,表示該任務可以使用該記憶體節點的記憶體。
04設定任務的 CPU 允許旗號為 cpu_all_mask,表示可以跑在任意一個 CPU 上。這是 2.4 版以後的核心有的任務旗號。
05設定 CTRL-ALT-DEL 按鍵作用的任務 ID。
06設定可用的 CPU 數量。
07執行 SMP 模式下的初始化串列。
08確定 CPU 可以執行時,將該 CPU 的 NFB 註冊到核心。
09SMP 初始化,即啟動 APIC。APIC 是先進可程式中斷控制器,用於控制外部中斷與多 CPU。這是因為 CPU 靠中斷與彼此通信。
10初始化 SMP 排程器。
11執行基本設定工作,例如 CPUSET、KHELPER、TMPFS、IRQ等。
12~13建立操控台 /dev/console,即人機介面。
14建立系統標準輸入串流,導向操控台 /dev/console。
15建立系統標準輸出串流,導向操控台 /dev/console。
16當 RAMDISK 執行命令不存在,將 RAMDISK 執行命令設定為 /init。
18執行 RAMDISK 執行命令,失敗時設定 RAMDISK 執行命令為空命令。
19呼叫命名空間準備函式,重新連結檔案系統。
21執行核心初始化後的處理程序,。
22這行應該執行不到。

1.5.4.1.1.1 作業系統基本設定

系統基本設定函式 do_basic_setup 執行幾個工作,包括 SMP 的 CPUSET 初始化、TMPFS 檔案系統初始化、驅動程式架構初始化、IRQ 中斷描述子初始化、執行核心建構子串列和初始函式串列。

linux/init/main.c
01 static void __init do_basic_setup(void){
02     cpuset_init_smp();
03     usermodehelper_init();
04     init_tmpfs();
05     driver_init();
06     init_irq_proc();
07     do_ctors();
08     do_initcalls();
09 }

行號說明
02頂層 CPU 集合初始化,這要在 CPU 初始化之後才執行。
03核心 khelper 工作串列初始化,用來執行使用者模式的程式。khelper 的意思是 kernel helper,語意為幫助 kernel 執行使用者程式。
04初始化 tmpfs 檔案系統。
05驅動程式模型初始化,先初始化驅動程式架構核心(tmpfs、devices、buses、classes、firmware、hypervisor),再初始化驅動程式子系統(plateform bus、system bus、cpu、memory)。
06初始化中斷請求處理函式,即設定中斷描述器和中斷處理程序。
07執行核心建構子串列,筆者覺得這是個空的串列。
08執行核心初始程序串列,筆者覺得這是個空的串列。

1.5.4.1.1.2 作業系統的後初始化

作業系統的後初始化函式 init_post 執行幾個工作,包括同步所有非同步函式、釋放初始化記憶體、設定核心記憶體為唯讀記憶體、設定系統為執¡¡Œ中。 接著再執行預先設定 RAMDISK 執行命令,用來處理 RAMDISK 的資料內容。 接著在執行預先設定的執行命令,??。 最後,執行作業系統的初始化描述,即檔案 /sbin/init、/etc/init、/bin/init、/bin/sh。

linux/init/main.c
01 static noinline int init_post(void){
02     async_synchronize_full();
03     free_initmem();
04     mark_rodata_ro();
05     system_state = SYSTEM_RUNNING;
06     numa_default_policy();
07     current->signal->flags |= SIGNAL_UNKILLABLE;
08     if (ramdisk_execute_command) {
09         run_init_process(ramdisk_execute_command);
10         printk(KERN_WARNING "Failed to execute %s\n",
11                 ramdisk_execute_command);
12     }
13     if (execute_command) {
14         run_init_process(execute_command);
15         printk(KERN_WARNING "Failed to execute %s.  Attempting "
16                     "defaults...\n", execute_command);
17     }
18     run_init_process("/sbin/init");
19     run_init_process("/etc/init");
20     run_init_process("/bin/init");
21     run_init_process("/bin/sh");
22     panic("No init found.  Try passing init= option to kernel. "
23           "See Linux Documentation/init.txt for guidance.");
24 }

行號說明
02同步所有非同步的函式呼叫,即等待所有非同步的函式執行完成,也就是等前面的工作都完成再繼續後面的工作。
03將初始化程序的分頁記憶體全數釋放,因為非同步的初始化工作都已經完成,筆者猜想應該是 linux/init 程式所佔用的核心記憶體。
04將核心記憶體設定為唯讀,即不可寫入也不可刪除。把整個核心記憶體保護起來,因為這是作業系統最核心的部分。
05設定系統的執行狀態為執行中。
06將目前程序的記憶體設定為基本策略。
07將目前程序的狀態設定為不可刪除,筆者猜想這是因為系統至少要有一個程序活著。
08~12執行 RAMDISK 執行命令,筆者找不到此命令何時設定。
13~17執行"執行命令",筆者找不到此命令何時設定。
18執行 /sbin/init,成功的話就轉移到該程序上執行。
19/etc/init 是一個目錄,應該不能執行。
20/bin/init 是一支 bin 的初始化程序,筆者要查一下它的用途。
21/bin/sh 市一個目錄,成功的話就轉移到該程序上執行。
22~23如果上面的初始化行程都找不到或無法啟動,那表示系統有問題,顯示錯誤訊息並停止作業系統的運作。

1.5.4.1.2 核心行程建立行程

行程建立行程會建立 kthread_create_list 上的所有行程。 這是一個不會停止的行程,只要核心行程開啟串列上有行程時,就會將該串列上的行程加入排程器執行。

linux/init/main.c
01 int kthreadd(void *unused){
02     struct task_struct *tsk = current;
03 
04     set_task_comm(tsk, "kthreadd");
05     ignore_signals(tsk);
06     set_cpus_allowed_ptr(tsk, cpu_all_mask);
07     set_mems_allowed(node_states[N_HIGH_MEMORY]);
08     current->flags |= PF_NOFREEZE | PF_FREEZER_NOSIG;
09     for (;;) {
10         set_current_state(TASK_INTERRUPTIBLE);
11         if (list_empty(&kthread_create_list)) schedule();
12         __set_current_state(TASK_RUNNING);
13         spin_lock(&kthread_create_lock);
14         while (!list_empty(&kthread_create_list)) {
15             struct kthread_create_info *create;
16 
17             create = list_entry(kthread_create_list.next,struct kthread_create_info, list);
18             list_del_init(&create->list);
19             spin_unlock(&kthread_create_lock);
20             create_kthread(create);
21             spin_lock(&kthread_create_lock);
22         }
23         spin_unlock(&kthread_create_lock);
24     }
25     return 0;
26 }

行號說明
04設定行程命令名稱為 kthreadd。
05忽略行程的所有信號,設定所有信號為信號忽略,清除等待信號隊伍等。
06遮罩行程的 CPU 偏好設定,。
07設定行程的記憶體允許層級為 HIGH_MEMORY。
08設定行程旗號為行程不可凍結,並且行程凍結管理器不可以送凍結訊號給此行程。
09~24行程建立迴圈,。
10設定行程狀態為不可中斷,。
11當行程串列為空串列,執行排程器,將此行程切換掉。
12設定行程為執行中,。
13取得核心行程建立鎖。
14~22行程建立迴圈,建立在行程串列上的所有行程。
17取得一個行程,。
18初始化該行程的串列,做法是先從串列頭移除一個串列成員,再初始化該串列頭。筆者不解為什麼要先做移除的動作。
19解除核心行程建立鎖,。
20建立核心行程,。
21取得核心行程建立鎖,。
23解除核心行程建立鎖。
25回返值為 0,這行永遠不會執行,因為上面的 for 迴圈沒有 break 的機制。