linux kernel啟動程式碼分析__linux

來源:互聯網
上載者:User

本文分析基於mips架構,linux-3.3.8

一.kernel入口

首先通過連結指令碼找到kernel的入口

1.核心是壓縮過的

/arch/mips/boot/compressed/Makefile  ,將$(obj)/head.o $(obj)/decompress.o $(obj)/dbg.o  $(obj)/piggy.o 連結為vmlinuz,連結指令碼為ld.script vmlinuz: $(src)/ld.script $(vmlinuzobjs-y) $(obj)/calc_vmlinuz_load_addr     $(call cmd,zld)     $(call cmd,strip)
將vmlinux.bin.z採用objcopy工具,作為資料打包為piggy.o targets += piggy.o OBJCOPYFLAGS_piggy.o := --add-section=.image=$(obj)/vmlinux.bin.z \                         --set-section-flags=.image=contents,alloc,load,readonly,data $(obj)/piggy.o: $(obj)/dummy.o $(obj)/vmlinux.bin.z FORCE     $(call if_changed,objcopy)
將vmlinux.bin採用壓縮公用程式壓縮為vmlinux.bin.z tool_$(CONFIG_KERNEL_GZIP)    = gzip tool_$(CONFIG_KERNEL_BZIP2)   = bzip2 tool_$(CONFIG_KERNEL_LZMA)    = lzma tool_$(CONFIG_KERNEL_LZO)     = lzo
targets += vmlinux.bin.z $(obj)/vmlinux.bin.z: $(obj)/vmlinux.bin FORCE     $(call if_changed,$(tool_y))

可見在壓縮是入口為/arch/mips/boot/compressed/head.s

2.核心是未壓縮的

定義makefile指定了vmlinux的連結指令碼:

vmlinux-lds  := arch/$(SRCARCH)/kernel/vmlinux.lds, 由vmlinux.lds.S編譯得到 vmlinux.lds: OUTPUT_ARCH(mips) ENTRY(kernel_entry) PHDRS { text PT_LOAD FLAGS(7); /* RWX */ note PT_NOTE FLAGS(4); /* R__ */ }   jiffies = jiffies_64; SECTIONS { . = 0x80001000; /* read-only */ _text = .; /* Text and read-only data */ .text : {   . = ALIGN(8); *(.text.hot) *(.text) *(.ref.text) *(.devinit.text) *(.devexit.text) *(.cpuinit.text) *(.cpuexit.text) *(.text.unlikely)   . = ALIGN(8); __sched_text_start = .; *(.sched.text) __sched_text_end = .;   . = ALIGN(8); __lock_text_start = .; *(.spinlock.text) __lock_text_end = .;   . = ALIGN(8); __kprobes_text_start = .; *(.kprobes.text) __kprobes_text_end = .;   *(.text.*)   *(.fixup)   *(.gnu.warning) } :text = 0 _etext = .; /* End of text section */

可見入口為kernel_entry

這裡只講解未壓縮的情況,因為壓縮情況解壓之後的跳轉到核心執行過程是一樣的。

 (arch/mips/kernel/head.s) NESTED(kernel_entry, 16, sp)            # kernel entry point
    kernel_entry_setup            # cpu specific setup
    setup_c0_status_pri
    /* We might not get launched at the address the kernel is linked to,        so we jump there.  */     PTR_LA    t0, 0f     jr    t0 0:     PTR_LA        t0, __bss_start        # clear .bss      //將核心的bss段清零     LONG_S        zero, (t0)     PTR_LA        t1, __bss_stop - LONGSIZE 1:     PTR_ADDIU    t0, LONGSIZE     LONG_S        zero, (t0)     bne        t0, t1, 1b
    LONG_S        a0, fw_arg0        # firmware arguments     LONG_S        a1, fw_arg1     LONG_S        a2, fw_arg2     LONG_S        a3, fw_arg3
    MTC0        zero, CP0_CONTEXT    # clear context register     PTR_LA        $28, init_thread_union        //union thread_union init_thread_union,  $28寄存器指向 init_thread_union     /* Set the SP after an empty pt_regs.  */     PTR_LI        sp, _THREAD_SIZE - 32 - PT_SIZE     PTR_ADDU    sp, $28         //sp寄存器指向 init_thread_union中的棧空間     back_to_back_c0_hazard   //同步c0,防止c0冒險,見下文解釋     set_saved_sp    sp, t0, t1    //將sp寄存器的值儲存到kernelsp變數中,即將進程核心態棧指標儲存在kernelsp變數中     PTR_SUBU    sp, 4 * SZREG        # init stack pointer
    j        start_kernel      //啟動c代碼,init/main.c     END(kernel_entry)


kernel_entry_setup的定義位於:

(arch/mips/include/asm/mach-brcmstb/kernel-entry-init.h)     .macro kernel_entry_setup     # save arguments for CFE callback     sw    a0, cfe_handle     //將a0 寄存器的內容儲存至cfe_handle,  為了kernel能夠調用cfe中的函數,取得核心啟動函數和其他硬體設定參數,這些參數儲存在cfe的環境變數中     sw    a2, cfe_entry       //將a2 寄存器的內容儲存至cfe_entry     sw    a3, cfe_seal         //將a3 寄存器的內容儲存至cfe_seal      jal    bmips_enable_xks01     .endm         .macro  smp_slave_setup         .endm

(arch/mips/kernel/bmips_vec.s) /*********************************************************************** * XKS01 support * Certain CPUs support extending kseg0 to 1024MB. ***********************************************************************/ LEAF(bmips_enable_xks01)
#if defined(CONFIG_XKS01)     mfc0    t0, $22, 3     li    t1, 0x1ff0     li    t2, (1 << 12) | (1 << 9)     or    t0, t1     xor    t0, t1     or    t0, t2     mtc0    t0, $22, 3     BARRIER #endif /* defined(CONFIG_XKS01) */
    jr    ra
END(bmips_enable_xks01)


setup_c0_status_pri的定義位於:

 (arch/mips/kernel/head.s)     .macro    setup_c0_status_pri #ifdef CONFIG_64BIT     setup_c0_status ST0_KX 0 #else     setup_c0_status 0 0               //將c0  status寄存器清零 #endif     .endm


arch/mips/include/asm/hazards.h:   _ehb彙編指令,用於同步c0,防止c0冒險 ASMMACRO(back_to_back_c0_hazard,      _ehb     )

current表示當前進程,我們來看看它的定義: #define get_current() (current_thread_info()->task) #define current get_current()
register struct thread_info *__current_thread_info __asm__("$28"); #define current_thread_info()  __current_thread_info
__current_thread_info是一個寄存器變數,$28表示gp寄存器

所以這裡PTR_LA        $28, init_thread_union 實際上就是設定為0號進程,

 PTR_LI        sp, _THREAD_SIZE - 32 - PT_SIZE

 PTR_ADDU    sp, $28

sp= $28 +  _THREAD_SIZE - 32 - PT_SIZE=$28 +8192-32-sizeof(struct pt_regs)

union thread_union {
#ifndef CONFIG_X86
struct thread_info thread_info;
#endif
unsigned long stack[THREAD_SIZE/sizeof(long)];

};

struct thread_info {
struct task_struct *task; /* main task structure */
struct exec_domain *exec_domain; /* execution domain */
unsigned long flags; /* low level flags */
unsigned long tp_value; /* thread pointer */
__u32 cpu; /* current CPU */
int preempt_count; /* 0 => preemptable, <0 => BUG */


mm_segment_t addr_limit; /* thread address space:
   0-0xBFFFFFFF for user-thead
   0-0xFFFFFFFF for kernel-thread
*/
struct restart_block restart_block;
struct pt_regs *regs;

};

$28---------→+----------------+          low address

                       |     *task            |

                       |        ...              |

                       |    *regs            |   ----------------------------------+

 grow    ↑        |   xxxxxxxxx      |                                                      |

              |        |  xxxxxxxxx       |                                                      |

sp--------->   | xxxxxxxxx        |                                                       |

                       |  32 byte         |                                                        |

                       | sizeof(pt_regs)|    ← ------------------------------ +

這樣sp就切換到了0號進程的核心棧上,設定好棧之後就可以執行c定義的函數了

二.進入start_kernel

asmlinkage void __init start_kernel(void)
{
boot_init_stack_canary();
local_irq_disable();

tick_init();

setup_arch(&command_line);

build_all_zonelists(NULL);
page_alloc_init();
trap_init();
mm_init();
sched_init();
init_IRQ();
prio_tree_init();
init_timers();
hrtimers_init();
softirq_init();
timekeeping_init();
time_init();
local_irq_enable();
        ...
/* Do the rest non-__init'ed, we're now alive */
rest_init();

}

static noinline void __init_refok rest_init(void)
{
int pid;
/*
* We need to spawn init first so that it obtains pid 1, however
* the init task will end up wanting to create kthreads, which, if
* we schedule it before we create kthreadd, will OOPS.
*/
kernel_thread(kernel_init, NULL, CLONE_FS | CLONE_SIGHAND);   //建立init進程,即1號進程

rcu_read_lock();
kthreadd_task = find_task_by_pid_ns(pid, &init_pid_ns);
rcu_read_unlock();
complete(&kthreadd_done);

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


/* Call into cpu_idle with preempt disabled */
preempt_disable();
cpu_idle();  //0號進程

}

static int __init kernel_init(void * unused)
{
  

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在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.