Linux核心啟動過程(2.6.23)

來源:互聯網
上載者:User
[轉載]Linux核心啟動過程(2.6.23)---之一2010/03/19 02:05 P.M.

核心版本號碼:2.6.23

當PC按下電源,CPU加電後,自動從0xFFFF0處開始執行代碼,這個地址位於BIOS中。接著BIOS開始一系列系統檢測,並在記憶體物理地址0處初
始化中斷向量,供Linux核心啟動過程中進行調用。此後,它將啟動裝置的第一個扇區(磁碟開機磁區,512Bytes)讀入記憶體絕對位址0x7C00
處,並跳到這個地方開始執行(arch/i386/boot/header.s)。
注意,現在程式現在運行在16位實模式下,使用64k的段。 segment(段地址) : offset(位移)構成了邏輯地址,段地址乘以 16
再加上 位移 ,就得到 linear
address(線性地址)。header.s(arch/i386/boot/header.s)中包含了大量的.h(標頭檔)。

18 #include <asm/segment.h>
19 #include <linux/utsrelease.h>
20 #include <asm/boot.h>
21 #include <asm/e820.h>
22 #include <asm/page.h>
23 #include <asm/setup.h>
24 #include "boot.h"
這些標頭檔包含了許多重要的宏和聲明,在Linux 核心的其他程式中,都會用到。
.section ".header", "a"
.globl hdr
hdr:
上面開始了header.s的最重要的部分,hdr資料區段儲存了Linux kernel啟動過程所需的資訊資料,它的結構struct
boot_params定義在 include/asm-i386/bootparam.h中。struct
boot_params非常重要,它包含了其他的一些struct,並不是很複雜。下面開始了header.s程式,開始了header.s的真正的執
行。
.section ".inittext", "ax"
start_of_setup:
它首先初始化 Disk Controller(磁碟控制卡),是通過 int 0x13進行的。然後設定寄存器,Zero the
bss(初始化資料區段),接著 call main(調用
main)跳轉到:arch/i386/boot/main.c的main()函數開始執行。(終於見到我們的可親可愛的C了,不過別高興太早喔:~)
下面我們看看main()都做了什麼(註:linux kernel 中有太多main函數,程式並不是總是從main開始執行)
copy_boot_params(); 複製 boot header 到 "zeropage"
validate_cpu(); 確保支援當前啟動並執行CPU
set_bios_mode(); 告訴BIOS什麼CPU我們將要去運行
detect_memory(); 檢測Memory
keyboard_set_repeat(); 設定鍵盤 repeat rate (Why ?)
set_video(); 設定 Video mode
query_mca(); 獲得 MCA 資訊
query_voyager(); Voyager ?
query_ist(); 獲得 Query Intel SpeedStep (IST) 資訊
query_apm_bios(); 獲得APM 資訊
query_edd(); 獲得EDD資訊
go_to_protected_mode
()
; 最後一件事,也是最重要的一件事,進入保護模式
上面只是函數調用的大致順序,並不是真正的程式,通過這些函數我們可以一目瞭然地看到核心的執行過程(還是C好啊,不像彙編,得一行一行地看),當然你要
瞭解更多的細節,可以追蹤到每一個函數中去。它通過上方面的檢測,不斷地填充struct boot_params結構,記住,struct
boot_params 是很重要的喔。其他函數我們就不研究了,單看最後一個,go_to_protected_mode
(),
究竟在哪兒? arch/i386/boot/pm.c
void go_to_protected_mode(void)
依次調用了如下函數:
realmode_switch_hook(); Hook before leaving realmode
move_kernel_around(); 把 Kernel/setup 移到 它們最終的地方
enable_a20() 開啟 a20 門
reset_coprocessor(); 重設 coprocessor(副處理器)
mask_all_interrupts(); Mask(屏蔽所有中斷)
setup_idt(); 開始轉入保護模式……
setup_gdt();
protected_mode_jump
(boot_params.hdr.code32_start, (u32)&boot_params + (ds() << 4));

protected_mode_jump
,傳入了boot_params->hdr ->code32


_start作為第一個參數, 該參數在header.s

(arch/i386/boot/header.s) 設定如下 :

code32_start: # here loaders can put a different
# start address for 32-bit code.
#ifndef __BIG_KERNEL__
.long 0x1000 # 0x1000 = default for zImage
#else
.long 0x100000 # 0x100000 = default for big kernel
#endif
顯然是跳轉到 0x1000或0x100000處繼續執行。
第二個參數,就是 boot_params的線性地址,注意,現在仍是真實位址模式,線性地址為段地址乘16加上位移。

(啊,天哪,又到 彙編裡去了,AT&T彙編),在arch/i386/boot/pmjump.S中,
.globl protected_mode_jump

.type protected_mode_jump, @function

.code16
終於找到了,
arch/i386/boot/pmjump.S並不大,只有54行。我們看看它做了什麼:
xorl %ebx, %ebx # Flag to indicate this is a boot
movl %edx, %esi
# 傳遞過來的boot_params地址,轉移到 ESI寄存器

movl %eax, 2
f # Patch ljmpl instruction 要跳轉的地址 放到 2f

........
1:
movw $__BOOT_DS, %cx

movl %cr0, %edx

orb $1, %dl # Protected mode (PE) bit 設定CR0的PE位,進入保護模式!!


movl %edx, %cr0

很簡單嘛 :~)
# Jump to the 32-bit entrypoin
t 進入32位程式段


.byte 0x66, 0xea # ljmpl opcode 跳轉指令碼 (為什麼不用彙編呢?不太清楚)
2
: .long 0 # offset 位移地址
.word __BOOT_CS # segment 段 類似於 ljmp segment : offset;

.size protected_mode_jump, .-protected_mode_jump
現在我們到哪兒了呢,想必大家都迷住了吧。我找了半天,終於找到了: arch/i386/boot/compressed/head.s
********** head.S contains the 32-bit startup code.
NOTE!!! Startup happens at absolute address 0x00001000, which is also where
* the page directory will exist. The startup code will be overwritten by
* the page directory.
必定是跳轉到這裡了,Startup 被載入到絕對位址 0x00001000處。head.s的作用想必大家已經猜到了吧。既然在compressed檔案夾中,肯定是和壓縮有關的啦。對!

.text

#include <linux/linkage.h>
#include <asm/segment.h>
#include <asm/page.h>
#include <asm/boot.h>

.section ".text.head","ax",@progbits
.globl startup_32 定義了startup_32函數,從這兒開始執行

startup_32: 程式開始……
接下來,startup_32要檢測是不是被載入到預定的位置了呢,怎麼檢查呢,這裡用了一個技巧:
leal (0x1e4+4)(%esi), %esp 還記得 ESI中存的是boot_params的地址嗎?這條指令就是把 棧指標指向boot_params的一個成員,而這個成員 u32 scratch就是專為程式檢查是否被載入到預定位置設定的作 為棧使用的

call 1f 當 call時,一條指令的指標要入棧,就是儲存到 scratch中了
1: popl %ebp 把指令指標放到 EBP中,EBP存的是當前的位置
subl $1b, %ebp 1b指的是指令在程式段中的位移,兩者之差,就是程式被載入的address了

* %ebp contains the address we are loaded at by the boot loader and %ebx
* contains the address where we should move the kernel image temporarily
* for safe in-place decompression.
* 現在 EBP中含有我們被載入的地址,而 EBX 中含有我們應該暫時把核心 image移動到的安全位元置的地址

接著,就開始複製並移動核心,接著跳到移動後的地址開始執行,並為解壓過程設定棧(stack)

* Do the decompression, and jump to the new kernel..
*開始解壓縮,然後跳到新的核心開始執行
movl output_len(%ebx), %eax #將參數依次入棧
pushl %eax
pushl %ebp # output address
movl input_len(%ebx), %eax
pushl %eax # input_len
leal input_data(%ebx), %eax
pushl %eax # input_data
leal _end(%ebx), %eax
pushl %eax # end of the image as third argument
pushl %esi # real mode pointer as second arg
call decompress_kernel


addl $20, %esp 恢複 棧
popl %ecx
如果需要,還要再移動核心,然後:
* Jump to the decompressed kernel.

xorl %ebx,%ebx
jmp *%ebp EBP 中有解壓後新核心的地址

先說下核心的的解壓縮。
核心解壓縮的
decompress_kernel


在arch/i386/boot/compressed/misc.c中定義,
asmlinkage void decompress_kernel(void *rmode, unsigned long end,
uch *input_data, unsigned long input_len, uch *output)
{
……
……
putstr("Uncompressing Linux...
"); 很高興啊,Uncompressing Linux...
gunzip
(); 是專門解壓核心的gzip,它涉及到同目錄下的relocs.c檔案,
還有 lib/inflate.c(主要的解壓常式)
putstr("Ok, booting the kernel.
/n"); 很熟悉吧,Ok,booting the kernel.
return;
}

好了,繼續下去,現在Linux應該是跳轉到新的解壓縮後的位置開始執行了吧(跳來跳去真辛苦啊
)[轉載]Linux核心啟動過程(2.6.23)----之二2010/03/19 02:06 P.M.

到哪兒了呢? 還是彙編~:(

arch/i386/kernel/head.s head.s檔案,578L(行代碼,不小啊,不過這算是最後一個啦,堅持哈)
head.s 雖然大,但很有條理:
重新設定段
把BSS段設定為0
把 boot_params的參數重新COPY到安全的地方
Initialize page tables 初始化頁表
為SMP設定頁表(如果有的話)
* Enable paging
movl $swapper_pg_dir-__PAGE_OFFSET,%eax
movl %eax,%cr3 # set the page table pointer..設定頁指標
movl %cr0,%eax
orl $0x80000000,%eax
movl %eax,%cr0 # ..and set paging (PG) bit 啟動分頁
ljmp $__BOOT_CS,$1f # Clear prefetch and normalize %eip 設定EIP
1:
lss stack_start,%esp #Set up the stack pointer 設定 棧 指標


pushl $0 #
Initialize eflags. 把EFLAGES設定為0

popfl

檢測 CPU 類型
setup_idt 設定 IDT表
include arch/i386/xen/xen-head.S
…………

360(Line) jmp start_kernel
跳轉到 init/main.c
現在,終於熬過了 AT&T彙編 ,是不是很有成就感呢,下面的start_kernel要是也講習懂了,Linux核心的其他部分,就不在話下了,加油啊,我現在水平還不夠,只能列出start_kernel源碼,大家一同分析
513 asmlinkage void __init start_kernel(void)

514 {
515 char * command_line;
516 extern struct kernel_param __start___param[], __stop___param[];
517
518 smp_setup_processor_id();
519
520
524 unwind_init();
525 lockdep_init();
526
527 local_irq_disable();
528 early_boot_irqs_off();
529 early_init_irq_lock_class();
530
531
535 lock_kernel();
536 tick_init();
537 boot_cpu_init();
538 page_address_init();
539 printk(KERN_NOTICE);
540 printk(linux_banner);
541 setup_arch(&command_line);
542 setup_command_line(command_line);
543 unwind_setup();
544 setup_per_cpu_areas();
545 smp_prepare_boot_cpu();
546
547
sched_init();
553
557 preempt_disable();
558 build_all_zonelists();
559 page_alloc_init();
560 printk(KERN_NOTICE "Kernel command line: %s/n", boot_command_line);
561 parse_early_param();
562 parse_args("Booting kernel", static_command_line, __start___param,
563 __stop___param - __start___param,
564 &unknown_bootoption);
565 if (!irqs_disabled()) {
566 printk(KERN_WARNING "start_kernel(): bug: interrupts were "
567 "enabled *very* early, fixing it/n");
568 local_irq_disable();
569 }
570 sort_main_extable();
571 trap_init();
572 rcu_init();
573 init_IRQ();
574 pidhash_init();
575 init_timers();
576 hrtimers_init();
577 softirq_init();
578 timekeeping_init();
579 time_init();
580 profile_init();
581 if (!irqs_disabled())
582 printk("start_kernel(): bug: interrupts were enabled early/n");
583 early_boot_irqs_on();
584 local_irq_enable();
585
586
591 console_init();
592 if (panic_later)
593 panic(panic_later, panic_param);
594
595 lockdep_info();
596
597
602 locking_selftest();
603
604 #ifdef CONFIG_BLK_DEV_INITRD
605 if (initrd_start && !initrd_below_start_ok &&
606 initrd_start < min_low_pfn << PAGE_SHIFT) {
607 printk(KERN_CRIT "initrd overwritten (0x%08lx < 0x%08lx) - "
608 "disabling it./n",initrd_start,min_low_pfn << PAGE_SHIFT);
609 initrd_start = 0;
610 }
611 #endif
612 vfs_caches_init_early();
613 cpuset_init_early();
614 mem_init();
615 kmem_cache_init();
616 setup_per_cpu_pageset();
617 numa_policy_init();
618 if (late_time_init)
619 late_time_init();
620 calibrate_delay();
621 pidmap_init();
622 pgtable_cache_init();
623 prio_tree_init();
624 anon_vma_init();
625 #ifdef CONFIG_X86
626 if (efi_enabled)
627 efi_enter_virtual_mode();
628 #endif
629 fork_init(num_physpages);
630 proc_caches_init();
631 buffer_init();
632 unnamed_dev_init();
633 key_init();
634 security_init();
635 vfs_caches_init(num_physpages);
636 radix_tree_init();
637 signals_init();
638
639 page_writeback_init();
640 #ifdef CONFIG_PROC_FS
641 proc_root_init();
642 #endif
643 cpuset_init();
644 taskstats_init_early();
645 delayacct_init();
646
647 check_bugs();
648
649 acpi_early_init();
650
651
652 rest_init();
653 }

至此,核心正常啟動起來了。太高興了,現在還沒有研究到 start_kernel
內部,再接再厲!!

start_kernel( )程式用於初始化系統核心的各個部分,包括:

*設定記憶體邊界,調用paging_init( )初始化記憶體頁面。
*初始化陷阱,中斷通道和調度。
*對命令列進行文法分析。
*初始化裝置驅動程式和磁碟緩衝區。
*校對延遲迴圈。

前面兩篇,網友喬遷已經很詳細的分析了核心的啟動過程,
最終核心將跑到rest_init();



static void noinline __init_refok rest_init
(void)
__releases(kernel_lock)
{
int pid;

kernel_thread(kernel_init, NULL, CLONE_FS | CLONE_SIGHAND);
numa_default_policy();
pid = kernel_thread(kthreadd, NULL, CLONE_FS | CLONE_FILES);
kthreadd_task = find_task_by_pid_ns(pid, &init_pid_ns);
unlock_kernel();

/*
* The boot idle thread must execute schedule()
* at least once to get things moving:
*/
init_idle_bootup_task(current);
preempt_enable_no_resched();
schedule();
preempt_disable();

/* Call into cpu_idle with preempt disabled */
cpu_idle();

}

最後的function'rest_init' 作了以下工作:

*開闢核心線程'init'
*調用unlock_kernel
*建立核心啟動並執行cpu_idle環, 如果沒有調度,就一直死迴圈

所以start_kernel永遠不能終止.它會無窮地迴圈執行cpu_idle.
最後,系統核心轉向move_to_user_mode( ),以便建立初始化進程(init)。此後,進程0開始進入無限迴圈。
初始化進程開始執行/etc/init、/bin/init 或/sbin
/init中的一個之後,系統核心就不再對程式進行直接控制了。之後系統核心的作用主要是給進程提供系統調用,以及提供非同步中斷事件的處理。多任務機制已
經建立起來,並開始處理多個使用者的登入和fork( )建立的進程。

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.