<input id="ohw05"></input>
  • <table id="ohw05"><menu id="ohw05"></menu></table>
  • <var id="ohw05"></var>
  • <code id="ohw05"><cite id="ohw05"></cite></code>
    <label id="ohw05"></label>
    <var id="ohw05"></var>
  • 關于linux的一點好奇心(一):linux啟動過程

      一直很好奇,操作系統是如何工作的?我們知道平時編程,是如何讓代碼跑起來的,但那些都是比較高層次的東西。越往后,你會越覺得,像是空中樓閣,或者說只是有人幫你鋪平了許多道理,而你卻對此一無所知。

     

    1. 操作系統的困惑

      當然了,也不是真的一無所知。因為有很多的操作系統方面的書籍,教你了解操作系統是如何如何工作的,它的各種原理。但總有一種任督二脈不通的感覺。好像說的都知道一點,但好像又不知道這具體是什么,在哪里用,和為什么了。

      我曾經看過一系列關于一個如何自制操作系統的文章,非常棒。https://wiki.0xffffff.org/  里面完全展示了一個求知者的過程,硬件加載,軟件接管,操作系統,內存,中斷,驅動,線程等等方面的知識。可以說,是一個用于解惑,不可多得的文章了。應該說,很多關于操作系統的困惑,在這里找到答案,當然你也得自己總結下。

      但是,我還是會有那么一種感覺,原理看得再多,還是很空虛的。上面那個demo雖然把所有的東西都講了一遍,好像已經把所有問題都講了,但還畢竟只是demo。也許實際情況并非如此呢?至少不會那么簡單。這著實困擾著自己跳動的心。

      再后來,遇到了一篇講關于epoll的文章: https://bbs.gameres.com/thread_842984_1_1.html  。 經過這篇文章的講解,可以說把整個io的原理講得非常之透徹了。而我本人的確也從這里出發,給團隊內部做了一次分享。不知道他們感覺怎么樣,反正我是感覺挺通透的。這關于io東西,可以說是操作系統中的一個小點。但我個人覺得,框架也許只需要你了解一次就好,但小點卻是需要反復琢磨的。我們需要帶著問題去找答案,這個問題往往是關于小點的多。

     

    2. 敢不敢啃一啃操作系統的硬骨頭?

      說實話,我是不敢的。原因是,它太復雜,太宏大,這是比較大方向的困難。其次是,我單就語言這一關,可能就難以過去,因為你至少匯編、C之類的語言要足夠好才行,而自己卻只算是皮毛。正所謂一生清貧怎敢入繁華,兩袖清風怎敢誤佳人。

      難道就這樣得過且過么?但心里總是有一些疑問,不知道怎么去解決。一是問不了許多人,二是自己也不知道咋問,三是即使別人告訴了你你就能懂嗎?(就像教書一樣)

      所以,還是自己找答案吧。其實網上有太多零零散散的答案,好像都能看懂,但又好像都不是很明白。

      最好的文檔,都在官方資料里。最好的答案,都在代碼里。所以,去看看又何妨。

     

    3. linux內核源碼地址

      也許大家一般都是在github上去看這些源碼。但在國內,github的速度實在是不敢恭維。

      gitee地址: https://gitee.com/mirrors/linux

      github地址: https://github.com/torvalds/linux

      至于閱讀工具嘛,純粹打醬油的,使用 sublime 之類的就可以了,如果想更好一點,就eclipse也行,當然可能還要設置其他好些環境問題。

     

    4. linux框架結構

      關于閱讀技巧,可參考文章:http://www.tnepal.com/fanzhidongyzby/archive/2013/03/20/2970624.html

      整體目錄結構如下:

      細節簡略描述如下:

        arch——與體系結構相關的代碼。 對應于每個支持的體系結構,有一個相應的目錄如x86、 arm、alpha等。每個體系結構子目錄下包含幾個主要的子目錄: kernel、mm、lib。
        Documentation——內核方面的相關文檔。
        drivers——設備驅動代碼。每類設備有相應的子目錄,如char、 block、net等 fs 文件系統代碼。每個支持文件系統有相應的子目錄, 如ext2、proc等。
        fs——文件系統實現。如fat, ext4...
        include——內核頭文件。 對每種支持的體系結構有相應的子目錄,如asm-x86、 asm-arm、asm-alpha等。
        init——內核初始化代碼。提供main.c,包含start_kernel函數。
        ipc——進程間通訊代碼。
        kernel——內核管理代碼。
        lib——與體系結構無關的內核庫代碼,特定體系結構的庫代碼保存在arch/*/lib目錄下。
        mm——內存管理代碼。
        net——內核的網絡代碼。
        samples——一些使用功能接口的樣例,有點類似于單元測試。
        scripts——此目錄包含了內核設置時用到的腳本。
        security——安全相關的實現。
        tools——一些附帶工具類實現。

      其中,Documentation目錄可能是源碼不相關的目錄,但對我們理解系統卻是非常重要的地方。(因為我們多半只能看得懂文字的表面意思)

     

    5. linux-86啟動過程

      以x86的實現為例,其啟動過程大致如下:以 header.S 開始,以main.c結束(我們自認為看得懂的地方)。

        /arch/x86/boot/header.S
            -> calll main    ->    /arch/x86/boot/main.c
            -> go_to_protected_mode()    ->    /arch/x86/boot/pmjump.S
            -> jmpl    *%eax    ->    /arch/x86/kernel/head_32.S
            -> .long i386_start_kernel    ->    /arch/x86/kernel/head32.c
            -> start_kernel()    ->    /init/main.c    (C語言入口)

      細節代碼樣例如下:

    // /arch/x86/boot/header.S
    #include <asm/segment.h>
    #include <asm/boot.h>
    #include <asm/page_types.h>
    #include <asm/setup.h>
    #include <asm/bootparam.h>
    #include "boot.h"
    #include "voffset.h"
    #include "zoffset.h"
    ...
    6:
    
    # Check signature at end of setup
        cmpl    $0x5a5aaa55, setup_sig
        jne    setup_bad
    
    # Zero the bss
        movw    $__bss_start, %di
        movw    $_end+3, %cx
        xorl    %eax, %eax
        subw    %di, %cx
        shrw    $2, %cx
        rep; stosl
    
    # Jump to C code (should not return)
        calll    main
    
    
    // /arch/x86/boot/main.c
    void main(void)
    {
        /* First, copy the boot header into the "zeropage" */
        copy_boot_params();
    
        /* Initialize the early-boot console */
        console_init();
        if (cmdline_find_option_bool("debug"))
            puts("early console in setup code\n");
    
        /* End of heap check */
        init_heap();
    
        /* Make sure we have all the proper CPU support */
        if (validate_cpu()) {
            puts("Unable to boot - please use a kernel appropriate "
                 "for your CPU.\n");
            die();
        }
    
        /* Tell the BIOS what CPU mode we intend to run in. */
        set_bios_mode();
    
        /* Detect memory layout */
        detect_memory();
    
        /* Set keyboard repeat rate (why?) and query the lock flags */
        keyboard_init();
    
        /* Query Intel SpeedStep (IST) information */
        query_ist();
    
        /* Query APM information */
    #if defined(CONFIG_APM) || defined(CONFIG_APM_MODULE)
        query_apm_bios();
    #endif
    
        /* Query EDD information */
    #if defined(CONFIG_EDD) || defined(CONFIG_EDD_MODULE)
        query_edd();
    #endif
    
        /* Set the video mode */
        set_video();
    
        /* Do the last things and invoke protected mode */
        go_to_protected_mode();
    }
    
    
    // /arch/x86/boot/pmjump.S
    /*
     * The actual transition into protected mode
     */
    
    #include <asm/boot.h>
    #include <asm/processor-flags.h>
    #include <asm/segment.h>
    #include <linux/linkage.h>
    
        .text
        .code16
    ...
    2:    .long    in_pm32            # offset
        .word    __BOOT_CS        # segment
    ENDPROC(protected_mode_jump)
    
        .code32
        .section ".text32","ax"
    GLOBAL(in_pm32)
        # Set up data segments for flat 32-bit mode
        movl    %ecx, %ds
        movl    %ecx, %es
        movl    %ecx, %fs
        movl    %ecx, %gs
        movl    %ecx, %ss
        # The 32-bit code sets up its own stack, but this way we do have
        # a valid stack if some debugging hack wants to use it.
        addl    %ebx, %esp
    
        # Set up TR to make Intel VT happy
        ltr    %di
    
        # Clear registers to allow for future extensions to the
        # 32-bit boot protocol
        xorl    %ecx, %ecx
        xorl    %edx, %edx
        xorl    %ebx, %ebx
        xorl    %ebp, %ebp
        xorl    %edi, %edi
    
        # Set up LDTR to make Intel VT happy
        lldt    %cx
    
        jmpl    *%eax            # Jump to the 32-bit entrypoint
    ENDPROC(in_pm32)
    
    
    // /arch/x86/kernel/head_32.S
    .text
    #include <linux/threads.h>
    #include <linux/init.h>
    #include <linux/linkage.h>
    #include <asm/segment.h>
    #include <asm/page_types.h>
    #include <asm/pgtable_types.h>
    #include <asm/cache.h>
    #include <asm/thread_info.h>
    #include <asm/asm-offsets.h>
    #include <asm/setup.h>
    #include <asm/processor-flags.h>
    #include <asm/msr-index.h>
    #include <asm/cpufeatures.h>
    #include <asm/percpu.h>
    #include <asm/nops.h>
    #include <asm/bootparam.h>
    #include <asm/export.h>
    #include <asm/pgtable_32.h>
    ...
    /*
     * 32-bit kernel entrypoint; only used by the boot CPU.  On entry,
     * %esi points to the real-mode code as a 32-bit pointer.
     * CS and DS must be 4 GB flat segments, but we don't depend on
     * any particular GDT layout, because we load our own as soon as we
     * can.
     */
    __HEAD
    ENTRY(startup_32)
    ...
    hlt_loop:
        hlt
        jmp hlt_loop
    ENDPROC(early_ignore_irq)
    
    __INITDATA
        .align 4
    GLOBAL(early_recursion_flag)
        .long 0
    
    __REFDATA
        .align 4
    ENTRY(initial_code)
        .long i386_start_kernel
    ENTRY(setup_once_ref)
        .long setup_once
        
    // /arch/x86/kernel/head32.c
    #include <linux/init.h>
    #include <linux/start_kernel.h>
    #include <linux/mm.h>
    #include <linux/memblock.h>
    
    #include <asm/desc.h>
    #include <asm/setup.h>
    #include <asm/sections.h>
    #include <asm/e820/api.h>
    #include <asm/page.h>
    #include <asm/apic.h>
    #include <asm/io_apic.h>
    #include <asm/bios_ebda.h>
    #include <asm/tlbflush.h>
    #include <asm/bootparam_utils.h>
    ...
    asmlinkage __visible void __init i386_start_kernel(void)
    {
        /* Make sure IDT is set up before any exception happens */
        idt_setup_early_handler();
    
        cr4_init_shadow();
    
        sanitize_boot_params(&boot_params);
    
        x86_early_init_platform_quirks();
    
        /* Call the subarch specific early setup function */
        switch (boot_params.hdr.hardware_subarch) {
        case X86_SUBARCH_INTEL_MID:
            x86_intel_mid_early_setup();
            break;
        case X86_SUBARCH_CE4100:
            x86_ce4100_early_setup();
            break;
        default:
            i386_default_early_setup();
            break;
        }
    
        start_kernel();
    }
    
    
    // /init/main.c
    #define DEBUG        /* Enable initcall_debug */
    
    #include <linux/types.h>
    #include <linux/extable.h>
    #include <linux/module.h>
    #include <linux/proc_fs.h>
    #include <linux/binfmts.h>
    #include <linux/kernel.h>
    #include <linux/syscalls.h>
    #include <linux/stackprotector.h>
    #include <linux/string.h>
    #include <linux/ctype.h>
    #include <linux/delay.h>
    #include <linux/ioport.h>
    #include <linux/init.h>
    #include <linux/initrd.h>
    #include <linux/bootmem.h>
    #include <linux/acpi.h>
    #include <linux/console.h>
    #include <linux/nmi.h>
    #include <linux/percpu.h>
    #include <linux/kmod.h>
    #include <linux/vmalloc.h>
    #include <linux/kernel_stat.h>
    #include <linux/start_kernel.h>
    #include <linux/security.h>
    #include <linux/smp.h>
    #include <linux/profile.h>
    #include <linux/rcupdate.h>
    #include <linux/moduleparam.h>
    #include <linux/kallsyms.h>
    #include <linux/writeback.h>
    #include <linux/cpu.h>
    #include <linux/cpuset.h>
    #include <linux/cgroup.h>
    #include <linux/efi.h>
    #include <linux/tick.h>
    #include <linux/sched/isolation.h>
    #include <linux/interrupt.h>
    #include <linux/taskstats_kern.h>
    #include <linux/delayacct.h>
    #include <linux/unistd.h>
    #include <linux/utsname.h>
    #include <linux/rmap.h>
    #include <linux/mempolicy.h>
    #include <linux/key.h>
    #include <linux/buffer_head.h>
    #include <linux/page_ext.h>
    #include <linux/debug_locks.h>
    #include <linux/debugobjects.h>
    #include <linux/lockdep.h>
    #include <linux/kmemleak.h>
    #include <linux/pid_namespace.h>
    #include <linux/device.h>
    #include <linux/kthread.h>
    #include <linux/sched.h>
    #include <linux/sched/init.h>
    #include <linux/signal.h>
    #include <linux/idr.h>
    #include <linux/kgdb.h>
    #include <linux/ftrace.h>
    #include <linux/async.h>
    #include <linux/sfi.h>
    #include <linux/shmem_fs.h>
    #include <linux/slab.h>
    #include <linux/perf_event.h>
    #include <linux/ptrace.h>
    #include <linux/pti.h>
    #include <linux/blkdev.h>
    #include <linux/elevator.h>
    #include <linux/sched_clock.h>
    #include <linux/sched/task.h>
    #include <linux/sched/task_stack.h>
    #include <linux/context_tracking.h>
    #include <linux/random.h>
    #include <linux/list.h>
    #include <linux/integrity.h>
    #include <linux/proc_ns.h>
    #include <linux/io.h>
    #include <linux/cache.h>
    #include <linux/rodata_test.h>
    #include <linux/jump_label.h>
    #include <linux/mem_encrypt.h>
    
    #include <asm/io.h>
    #include <asm/bugs.h>
    #include <asm/setup.h>
    #include <asm/sections.h>
    #include <asm/cacheflush.h>
    // 平臺無關啟動代碼入口
    asmlinkage __visible void __init start_kernel(void)
    {
        char *command_line;
        char *after_dashes;
    
        set_task_stack_end_magic(&init_task);
        smp_setup_processor_id();
        debug_objects_early_init();
    
        cgroup_init_early();
    
        local_irq_disable();
        early_boot_irqs_disabled = true;
    
        /*
         * Interrupts are still disabled. Do necessary setups, then
         * enable them.
         */
        boot_cpu_init();
        page_address_init();
        pr_notice("%s", linux_banner);
        setup_arch(&command_line);
        /*
         * Set up the the initial canary and entropy after arch
         * and after adding latent and command line entropy.
         */
        add_latent_entropy();
        add_device_randomness(command_line, strlen(command_line));
        boot_init_stack_canary();
        mm_init_cpumask(&init_mm);
        setup_command_line(command_line);
        setup_nr_cpu_ids();
        setup_per_cpu_areas();
        smp_prepare_boot_cpu();    /* arch-specific boot-cpu hooks */
        boot_cpu_hotplug_init();
    
        build_all_zonelists(NULL);
        page_alloc_init();
    
        pr_notice("Kernel command line: %s\n", boot_command_line);
        parse_early_param();
        after_dashes = parse_args("Booting kernel",
                      static_command_line, __start___param,
                      __stop___param - __start___param,
                      -1, -1, NULL, &unknown_bootoption);
        if (!IS_ERR_OR_NULL(after_dashes))
            parse_args("Setting init args", after_dashes, NULL, 0, -1, -1,
                   NULL, set_init_arg);
    
        jump_label_init();
    
        /*
         * These use large bootmem allocations and must precede
         * kmem_cache_init()
         */
        setup_log_buf(0);
        vfs_caches_init_early();
        sort_main_extable();
        trap_init();
        mm_init();
    
        ftrace_init();
    
        /* trace_printk can be enabled here */
        early_trace_init();
    
        /*
         * Set up the scheduler prior starting any interrupts (such as the
         * timer interrupt). Full topology setup happens at smp_init()
         * time - but meanwhile we still have a functioning scheduler.
         */
        sched_init();
        /*
         * Disable preemption - early bootup scheduling is extremely
         * fragile until we cpu_idle() for the first time.
         */
        preempt_disable();
        if (WARN(!irqs_disabled(),
             "Interrupts were enabled *very* early, fixing it\n"))
            local_irq_disable();
        radix_tree_init();
    
        /*
         * Set up housekeeping before setting up workqueues to allow the unbound
         * workqueue to take non-housekeeping into account.
         */
        housekeeping_init();
    
        /*
         * Allow workqueue creation and work item queueing/cancelling
         * early.  Work item execution depends on kthreads and starts after
         * workqueue_init().
         */
        workqueue_init_early();
    
        rcu_init();
    
        /* Trace events are available after this */
        trace_init();
    
        if (initcall_debug)
            initcall_debug_enable();
    
        context_tracking_init();
        /* init some links before init_ISA_irqs() */
        early_irq_init();
        init_IRQ();
        tick_init();
        rcu_init_nohz();
        init_timers();
        hrtimers_init();
        softirq_init();
        timekeeping_init();
        time_init();
        sched_clock_postinit();
        printk_safe_init();
        perf_event_init();
        profile_init();
        call_function_init();
        WARN(!irqs_disabled(), "Interrupts were enabled early\n");
        early_boot_irqs_disabled = false;
        local_irq_enable();
    
        kmem_cache_init_late();
    
        /*
         * HACK ALERT! This is early. We're enabling the console before
         * we've done PCI setups etc, and console_init() must be aware of
         * this. But we do want output early, in case something goes wrong.
         */
        console_init();
        if (panic_later)
            panic("Too many boot %s vars at `%s'", panic_later,
                  panic_param);
    
        lockdep_info();
    
        /*
         * Need to run this when irqs are enabled, because it wants
         * to self-test [hard/soft]-irqs on/off lock inversion bugs
         * too:
         */
        locking_selftest();
    
        /*
         * This needs to be called before any devices perform DMA
         * operations that might use the SWIOTLB bounce buffers. It will
         * mark the bounce buffers as decrypted so that their usage will
         * not cause "plain-text" data to be decrypted when accessed.
         */
        mem_encrypt_init();
    
    #ifdef CONFIG_BLK_DEV_INITRD
        if (initrd_start && !initrd_below_start_ok &&
            page_to_pfn(virt_to_page((void *)initrd_start)) < min_low_pfn) {
            pr_crit("initrd overwritten (0x%08lx < 0x%08lx) - disabling it.\n",
                page_to_pfn(virt_to_page((void *)initrd_start)),
                min_low_pfn);
            initrd_start = 0;
        }
    #endif
        page_ext_init();
        kmemleak_init();
        debug_objects_mem_init();
        setup_per_cpu_pageset();
        numa_policy_init();
        acpi_early_init();
        if (late_time_init)
            late_time_init();
        calibrate_delay();
        pid_idr_init();
        anon_vma_init();
    #ifdef CONFIG_X86
        if (efi_enabled(EFI_RUNTIME_SERVICES))
            efi_enter_virtual_mode();
    #endif
        thread_stack_cache_init();
        cred_init();
        fork_init();
        proc_caches_init();
        uts_ns_init();
        buffer_init();
        key_init();
        security_init();
        dbg_late_init();
        vfs_caches_init();
        pagecache_init();
        signals_init();
        seq_file_init();
        proc_root_init();
        nsfs_init();
        cpuset_init();
        cgroup_init();
        taskstats_init_early();
        delayacct_init();
    
        check_bugs();
    
        acpi_subsystem_init();
        arch_post_acpi_subsys_init();
        sfi_init_late();
    
        if (efi_enabled(EFI_RUNTIME_SERVICES)) {
            efi_free_boot_services();
        }
    
        /* Do the rest non-__init'ed, we're now alive */
        rest_init();
    }

      本篇不做深入探討,僅為梳理來龍去脈。其中深意還需各自領悟了。反正大致就是,上電啟動后,進入BIOS,交權限轉交特殊地址,然后轉到系統啟動處,加載對應平臺匯編指令,做各種硬件設置,最后轉到我們熟悉一點的C代碼入口的過程。這個過程中,更多的是內存地址,寄存器之類的操作,可以說涉及到的東西相當廣泛,所以我們不能要求太多可能也沒有必要要求太多。

      老鐵,linux之旅愉快啊!

    posted @ 2022-01-03 21:12  等你歸去來  閱讀(364)  評論(1編輯  收藏  舉報
    国产美女a做受大片观看