在线看毛片网站电影-亚洲国产欧美日韩精品一区二区三区,国产欧美乱夫不卡无乱码,国产精品欧美久久久天天影视,精品一区二区三区视频在线观看,亚洲国产精品人成乱码天天看,日韩久久久一区,91精品国产91免费

<menu id="6qfwx"><li id="6qfwx"></li></menu>
    1. <menu id="6qfwx"><dl id="6qfwx"></dl></menu>

      <label id="6qfwx"><ol id="6qfwx"></ol></label><menu id="6qfwx"></menu><object id="6qfwx"><strike id="6qfwx"><noscript id="6qfwx"></noscript></strike></object>
        1. <center id="6qfwx"><dl id="6qfwx"></dl></center>

            新聞中心

            EEPW首頁(yè) > 嵌入式系統(tǒng) > 設(shè)計(jì)應(yīng)用 > Arm linux 內(nèi)核移植及系統(tǒng)初始化過(guò)程分析

            Arm linux 內(nèi)核移植及系統(tǒng)初始化過(guò)程分析

            作者: 時(shí)間:2016-11-09 來(lái)源:網(wǎng)絡(luò) 收藏
            本文主要介紹內(nèi)核移植過(guò)程中涉及文件的分布及其用途,以及簡(jiǎn)單介紹系統(tǒng)的初始化過(guò)程。整個(gè)arm linux內(nèi)核的啟動(dòng)可分為三個(gè)階段:第一階段主要是進(jìn)行cpu和體系結(jié)構(gòu)的檢查、cpu本身的初始化以及頁(yè)表的建立等;第二階段主要是對(duì)系統(tǒng)中的一些基礎(chǔ)設(shè)施進(jìn)行初始化;最后則是更高層次的初始化,如根設(shè)備和外部設(shè)備的初始化。了解系統(tǒng)的初始化過(guò)程,有益于更好地移植內(nèi)核。

            1. 內(nèi)核移植2. 涉及文件分布介紹
            2.1. 內(nèi)核移植2.2. 涉及的頭文件
            /linux-2.6.18.8/include
            [root@localhost include]# tree -L 1
            .
            |-- Kbuild
            |-- acpi
            |-- asm -> asm-arm
            |-- asm-alpha
            |-- asm-arm ------------------------------->(1)
            |-- asm-sparc
            |-- asm-sparc64
            |-- config
            |-- keys
            |-- linux ------------------------------->(2)
            |-- math-emu
            |-- media
            |-- mtd
            |-- net
            |-- pcmcia
            |-- rdma
            |-- rxrpc
            |-- scsi
            |-- sound
            `-- video

            本文引用地址:http://www.biyoush.com/article/201611/317956.htm

            內(nèi)核移植過(guò)程中涉及到的頭文件包括處理器相關(guān)的頭文件(1)和處理器無(wú)關(guān)的頭文件(2)。

            2.3. 內(nèi)核移植2.4. 涉及的源文件
            /linux-2.6.18.8/arch/arm
            [root@localhost arm]# tree -L 1
            .
            |-- Kconfig
            |-- Kconfig-nommu
            |-- Kconfig.debug
            |-- Makefile
            |-- boot ------------------------------->(2)
            |-- common
            |-- configs
            |-- kernel ------------------------------->(3)
            |-- lib
            |-- mach-at91rm9200
            ……
            |-- mach-omap1
            |-- mach-omap2
            |-- mach-realview
            |-- mach-rpc
            |-- mach-s3c2410 ------------------------------->(4)
            |-- mach-sa1100
            |-- mach-versatile
            |-- mm ------------------------------->(5)
            |-- nwfpe
            |-- oprofile
            |-- plat-omap
            |-- tools ------------------------------->(1)
            `-- vfp

            (1)
            /linux-2.6.18.8/arch/arm/tools
            [root@localhost tools]# tree -L 1
            .
            |-- Makefile
            |-- gen-mach-types
            `-- mach-types

            Mach-types 文件定義了不同系統(tǒng)平臺(tái)的系統(tǒng)平臺(tái)號(hào)。移植linux內(nèi)核到新的平臺(tái)上需要對(duì)新的平臺(tái)登記系統(tǒng)平臺(tái)號(hào)。

            Mach-types文件格式如下:
            # machine_is_xxx CONFIG_xxxx MACH_TYPE_xxx number
            s3c2410 ARCH_S3C2410 S3C2410 182
            smdk2410 ARCH_SMDK2410 SMDK2410 193

            之所以需要這些信息,是因?yàn)槟_本文件linux/arch/arm/tools/gen-mach-types需要linux/arch/tools/mach-types來(lái)產(chǎn)生linux/include/asm-arm/mach-types.h文件,該文件中設(shè)置了一些宏定義,需要這些宏定義來(lái)為目標(biāo)系統(tǒng)選擇合適的代碼。

            (2)
            linux-2.6.18.8/arch/arm/boot/compressed
            [root@localhost compressed]# tree -L 1
            .
            |-- Makefile
            |-- Makefile.debug
            |-- big-endian.S
            |-- head-at91rm9200.S
            |-- head.S
            |-- ll_char_wr.S
            |-- misc.c
            |-- ofw-shark.c
            |-- piggy.S
            `-- vmlinux.lds.in

            Head.s 是內(nèi)核映像的入口代碼,是自引導(dǎo)程序。自引導(dǎo)程序包含一些初始化程序,這些程序都是體系結(jié)構(gòu)相關(guān)的。在對(duì)系統(tǒng)作完初始化設(shè)置工作后,調(diào)用misc.c文件中的decompress_kernel()函數(shù)解壓縮內(nèi)核映像到指定的位置,然后跳轉(zhuǎn)到kernel的入口地址。

            Vmlinux.lds.in用來(lái)生成內(nèi)核映像的內(nèi)存配置文件。

            (3)
            linux-2.6.18.8/arch/arm/kernel
            [root@localhost kernel]# tree -L 1
            .
            |-- Makefile
            |-- apm.c
            |-- armksyms.c
            |-- arthur.c
            |-- asm-offsets.c
            |-- bios32.c
            |-- calls.S
            |-- dma.c
            |-- ecard.c
            |-- entry-armv.S
            |-- entry-common.S
            |-- entry-header.S
            |-- fiq.c
            |-- head-common.S
            |-- head-nommu.S
            |-- head.S
            |-- init_task.c
            |-- io.c
            |-- irq.c
            |-- isa.c
            |-- module.c
            |-- process.c
            |-- ptrace.c
            |-- ptrace.h
            |-- semaphore.c
            |-- setup.c
            |-- smp.c
            |-- sys_arm.c
            |-- time.c
            |-- traps.c
            `-- vmlinux.lds.S

            內(nèi)核入口處也是由一段匯編語(yǔ)言實(shí)現(xiàn)的,由head.s和head-common.s兩個(gè)文件組成。
            Head.s 是內(nèi)核的入口文件, 在head.s的末尾處 #include "head-common.S"。 經(jīng)過(guò)一系列的初始化后,跳轉(zhuǎn)到linux-2.6.18.8/init/main.c中的start_kernel()函數(shù)中,開(kāi)始內(nèi)核的基本初始化過(guò)程。


            /linux-2.6.18.8/init
            [root@localhost init]# tree
            .
            |-- Kconfig
            |-- Makefile
            |-- calibrate.c
            |-- do_mounts.c
            |-- do_mounts.h
            |-- do_mounts_initrd.c
            |-- do_mounts_md.c
            |-- do_mounts_rd.c
            |-- initramfs.c
            |-- main.c
            `-- version.c

            (4)
            /linux-2.6.18.8/arch/arm/mach-s3c2410
            [root@localhost mach-s3c2410]# tree -L 1
            .
            |-- Kconfig
            |-- Makefile
            |-- Makefile.boot
            |-- bast-irq.c
            |-- bast.h
            |-- clock.c
            |-- clock.h
            |-- common-smdk.c
            |-- common-smdk.h
            |-- cpu.c
            |-- cpu.h
            |-- devs.c
            |-- devs.h
            |-- dma.c
            |-- gpio.c
            |-- irq.c
            |-- irq.h
            |-- mach-anubis.c
            |-- mach-smdk2410.c
            |-- pm-simtec.c
            |-- pm.c
            |-- pm.h
            |-- s3c2400-gpio.c
            |-- s3c2400.h
            |-- s3c2410-clock.c
            |-- s3c2410-gpio.c
            |-- s3c2410.c
            |-- s3c2410.h
            |-- sleep.S
            |-- time.c
            |-- usb-simtec.c
            `-- usb-simtec.h

            這個(gè)目錄中的文件都是板級(jí)相關(guān)的,其中比較重要是如下幾個(gè):
            linux/arch/arm/mach-s3c2410/cpu.c
            linux/arch/arm/mach-s3c2410/common-smdk.c
            linux/arch/arm/mach-s3c2410/devs.c
            linux/arch/arm/mach-s3c2410/mach-smdk2410.c
            linux/arch/arm/mach-s3c2410/Makefile.boot
            linux/arch/arm/mach-s3c2410/s3c2410.c

            3. 處理器和設(shè)備4.
            這里主要介紹處理器和設(shè)備的描述和操作過(guò)程。設(shè)備描述在linux/arch/arm/mach-s3c2410/devs.c和linux/arch/arm/mach-s3c2410/common-smdk.c中實(shí)現(xiàn)。最后以nand flash為例具體介紹。
            4.1. 處理器、設(shè)備4.2. 描述
            設(shè)備描述主要兩個(gè)結(jié)構(gòu)體完成:struct resource和struct platform_device。
            先來(lái)看看著兩個(gè)結(jié)構(gòu)體的定義:
            struct resource {
            resource_size_t start;
            resource_size_t end;
            const char *name;
            unsigned long flags;
            struct resource *parent, *sibling, *child;
            };

            Resource結(jié)構(gòu)體主要是描述了設(shè)備在系統(tǒng)中的起止地址、名稱、標(biāo)志以及為了鏈?zhǔn)矫枋龇奖阒赶虮窘Y(jié)構(gòu)體類型的指針。Resource定義的實(shí)例將被添加到platform_device結(jié)構(gòu)體對(duì)象中去。

            struct platform_device {
            const char * name;
            u32 id;
            struct device dev;
            u32 num_resources;
            struct resource * resource;
            };

            Platform_device結(jié)構(gòu)體包括結(jié)構(gòu)體的名稱、ID號(hào)、平臺(tái)相關(guān)的信息、設(shè)備的數(shù)目以及上面定義的resource信息。Platform_device結(jié)構(gòu)對(duì)象將被直接通過(guò)設(shè)備操作函數(shù)注冊(cè)導(dǎo)系統(tǒng)中去。具體注冊(cè)和注銷過(guò)程在下一節(jié)介紹。

            4.3. 處理器、設(shè)備4.4. 操作
            (1) int platform_device_register(struct platform_device * pdev); 注冊(cè)設(shè)備
            (2) void platform_device_unregister(struct platform_device * pdev); 注銷設(shè)備
            (3) int platform_add_devices(struct platform_device devs, int num);添加設(shè)備,通過(guò)調(diào)用上面兩個(gè)函數(shù)實(shí)現(xiàn)。
            4.5. 添加Nand flash設(shè)備4.6.
            下面以nand flash 設(shè)備的描述為例,具體介紹下設(shè)備的描述和注冊(cè)過(guò)程。

            // resource結(jié)構(gòu)體實(shí)例s3c_nand_resource 對(duì)nand flash 控制器描述,包括控制器的起止地址和標(biāo)志。
            static struct resource s3c_nand_resource[] = {
            [0] = {
            .start = S3C2410_PA_NAND,
            .end = S3C2410_PA_NAND + S3C24XX_SZ_NAND - 1,
            .flags = IORESOURCE_MEM,
            }
            };

            //platform_device結(jié)構(gòu)體實(shí)例s3c_device_nand定義了設(shè)備的名稱、ID號(hào)并把resource對(duì)象作為其成員之一。
            struct platform_device s3c_device_nand = {
            .name = "s3c2410-nand",
            .id = -1,
            .num_resources = ARRAY_SIZE(s3c_nand_resource),
            .resource = s3c_nand_resource,
            };

            // nand flash 的分區(qū)情況,由mtd_partition結(jié)構(gòu)體定義。
            static struct mtd_partition smdk_default_nand_part[] = {
            [0] = {
            .name = "Boot Agent",
            .size = SZ_16K,
            .offset = 0,
            },
            [1] = {
            .name = "S3C2410 flash partition 1",
            .offset = 0,
            .size = SZ_2M,
            },
            [2] = {
            .name = "S3C2410 flash partition 2",
            .offset = SZ_4M,
            .size = SZ_4M,
            },
            [3] = {
            .name = "S3C2410 flash partition 3",
            .offset = SZ_8M,
            .size = SZ_2M,
            },
            [4] = {
            .name = "S3C2410 flash partition 4",
            .offset = SZ_1M * 10,
            .size = SZ_4M,
            },
            [5] = {
            .name = "S3C2410 flash partition 5",
            .offset = SZ_1M * 14,
            .size = SZ_1M * 10,
            },
            [6] = {
            .name = "S3C2410 flash partition 6",
            .offset = SZ_1M * 24,
            .size = SZ_1M * 24,
            },
            [7] = {
            .name = "S3C2410 flash partition 7",
            .offset = SZ_1M * 48,
            .size = SZ_16M,
            }
            };

            static struct s3c2410_nand_set smdk_nand_sets[] = {
            [0] = {
            .name = "NAND",
            .nr_chips = 1,
            .nr_partitions = ARRAY_SIZE(smdk_default_nand_part),
            .partitions = smdk_default_nand_part,
            },
            };

            /* choose a set of timings which should suit most 512Mbit
            * chips and beyond.
            */

            static struct s3c2410_platform_nand smdk_nand_info = {
            .tacls = 20,
            .twrph0 = 60,
            .twrph1 = 20,
            .nr_sets = ARRAY_SIZE(smdk_nand_sets),
            .sets = smdk_nand_sets,
            };

            /* devices we initialise */
            // 最后將nand flash 設(shè)備加入到系統(tǒng)即將注冊(cè)的設(shè)備集合中。
            static struct platform_device __initdata *smdk_devs[] = {
            &s3c_device_nand,
            &smdk_led4,
            &smdk_led5,
            &smdk_led6,
            &smdk_led7,
            };

            然后通過(guò)smdk_machine_init()函數(shù),調(diào)用設(shè)備添加函數(shù)platform_add_devices(smdk_devs, ARRAY_SIZE(smdk_devs)) 完成設(shè)備的注冊(cè)。具體過(guò)程參見(jiàn)系統(tǒng)初始化的相關(guān)部分。
            5. 系統(tǒng)初始化
            5.1. 系統(tǒng)初始化的主干線
            Start_kernel() èsetup_arch() èreset_init() è kernel_thread(init …) è init() è do_basic_setup() èdriver_init() è do_initcall()

            Start_kernel()函數(shù)負(fù)責(zé)初始化內(nèi)核各個(gè)子系統(tǒng),最后調(diào)用reset_init(),啟動(dòng)一個(gè)叫做init的內(nèi)核線程,繼續(xù)初始化。Start_kernel()函數(shù)在init/main.c中實(shí)現(xiàn)。

            asmlinkage void __init start_kernel(void)
            {
            char * command_line;
            extern struct kernel_param __start___param[], __stop___param[];

            smp_setup_processor_id();

            /*
            * Need to run as early as possible, to initialize the
            * lockdep hash:
            */
            lockdep_init();

            local_irq_disable();
            early_boot_irqs_off();
            early_init_irq_lock_class();

            /*
            * Interrupts are still disabled. Do necessary setups, then
            * enable them
            */
            lock_kernel();
            boot_cpu_init();
            page_address_init();
            printk(KERN_NOTICE);
            printk(linux_banner);
            setup_arch(&command_line);
            //setup processor and machine and destinate some pointers for do_initcalls() functions
            // for example init_machine pointer is initialized with smdk_machine_init() function , and //init_machine() function is called by customize_machine(), and the function is processed by //arch_initcall(fn). Therefore smdk_machine_init() is issured. by edwin
            setup_per_cpu_areas();
            smp_prepare_boot_cpu(); /* arch-specific boot-cpu hooks */

            /*
            * 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();
            build_all_zonelists();
            page_alloc_init();
            printk(KERN_NOTICE "Kernel command line: %s/n", saved_command_line);
            parse_early_param();
            parse_args("Booting kernel", command_line, __start___param,
            __stop___param - __start___param,
            &unknown_bootoption);
            sort_main_extable();
            unwind_init();
            trap_init();
            rcu_init();
            init_IRQ();
            pidhash_init();
            init_timers();
            hrtimers_init();
            softirq_init();
            timekeeping_init();
            time_init();
            profile_init();
            if (!irqs_disabled())
            printk("start_kernel(): bug: interrupts were enabled early/n");
            early_boot_irqs_on();
            local_irq_enable();

            /*
            * HACK ALERT! This is early. Were enabling the console before
            * weve 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(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();

            #ifdef CONFIG_BLK_DEV_INITRD
            if (initrd_start && !initrd_below_start_ok &&
            initrd_start < min_low_pfn << PAGE_SHIFT) {
            printk(KERN_CRIT "initrd overwritten (0x%08lx < 0x%08lx) - "
            "disabling it./n",initrd_start,min_low_pfn << PAGE_SHIFT);
            initrd_start = 0;
            }
            #endif
            vfs_caches_init_early();
            cpuset_init_early();
            mem_init();
            kmem_cache_init();
            setup_per_cpu_pageset();
            numa_policy_init();
            if (late_time_init)
            late_time_init();
            calibrate_delay();
            pidmap_init();
            pgtable_cache_init();
            prio_tree_init();
            anon_vma_init();
            #ifdef CONFIG_X86
            if (efi_enabled)
            efi_enter_virtual_mode();
            #endif
            fork_init(num_physpages);
            proc_caches_init();
            buffer_init();
            unnamed_dev_init();
            key_init();
            security_init();
            vfs_caches_init(num_physpages);
            radix_tree_init();
            signals_init();
            /* rootfs populating might need page-writeback */
            page_writeback_init();
            #ifdef CONFIG_PROC_FS
            proc_root_init();
            #endif
            cpuset_init();
            taskstats_init_early();
            delayacct_init();

            check_bugs();

            acpi_early_init(); /* before LAPIC and SMP init */

            /* Do the rest non-__inited, were now alive */
            rest_init();
            }

            分析start_kernel()源碼, 其中setup_arch() 和 reset_init()是兩個(gè)比較關(guān)鍵的函數(shù)。下面將具體分析這兩個(gè)函數(shù)。
            5.2. setup_arch()函數(shù)分析
            首先我們來(lái)分析下setup_arch()函數(shù)。
            Setup_arch()函數(shù)主要工作是安裝cpu和machine,并為start_kernel()后面的初始化函數(shù)指針指定值。
            其中setup_processor()函數(shù)調(diào)用linux/arch/arm/kernel/head_common.S 中的lookup_processor_type函數(shù)查詢處理器的型號(hào)并安裝。

            Setup_machine()函數(shù)調(diào)用inux/arch/arm/kernel/head_common.S 中的lookup_machine_type(__machine_arch_type)函數(shù)根據(jù)體系結(jié)構(gòu)號(hào)__machine_arch_type,在__arch_info_begin和__arch_info_end段空間查詢體系結(jié)構(gòu)。問(wèn)題是__machine_arch_type是在什么時(shí)候賦的初值?__arch_info_begin和__arch_info_end段空間到底放的是什么內(nèi)容?
            __machine_arch_type是一個(gè)全局變量,在linux/boot/decompress/misc.c的解壓縮函數(shù)中得以賦值。
            decompress_kernel(ulg output_start, ulg free_mem_ptr_p, ulg free_mem_ptr_end_p, int arch_id)
            {
            __machine_arch_type = arch_id;
            }

            __arch_info_begin和__arch_info_end段空間到底放的內(nèi)容由鏈接器決定,存放是.arch.info.init段的內(nèi)容。這個(gè)段是通過(guò)段屬性__attribute__指定的。Grep一下.arch.info.init 得到./include/asm/mach/arch.h:53: __attribute__((__section__(".arch.info.init"))) = { / 在linux/include/asm-arm/mach/arch.h 中發(fā)現(xiàn)MACHINE_START宏定義。

            #define MACHINE_START(_type,_name) /
            static const struct machine_desc __mach_desc_##_type /
            __attribute_used__ /
            __attribute__((__section__(".arch.info.init"))) = { /
            .nr = MACH_TYPE_##_type, /
            .name = _name,

            #define MACHINE_END /
            };

            inux/arch/arm/mach-s3c2410/mach-smdk2410.c中對(duì).arch.info.init段的初始化如下。
            MACHINE_START(SMDK2410, "SMDK2410") /* @TODO: request a new identifier and switch
            * to SMDK2410 */
            /* Maintainer: Jonas Dietsche */
            .phys_io = S3C2410_PA_UART,
            .io_pg_offst = (((u32)S3C24XX_VA_UART) >> 18) & 0xfffc,
            .boot_params = S3C2410_SDRAM_PA + 0x100,
            .map_io = smdk2410_map_io,
            .init_irq = s3c24xx_init_irq,
            .init_machine = smdk_machine_init,
            .timer = &s3c24xx_timer,
            MACHINE_END

            由此可見(jiàn)在.arch.info.init段內(nèi)存放了__desc_mach_desc_SMDK2410結(jié)構(gòu)體。初始化了相應(yīng)的初始化函數(shù)指針。問(wèn)題又來(lái)了, 這些初始化指針函數(shù)是什么時(shí)候被調(diào)用的呢?
            分析發(fā)現(xiàn),不一而同。
            如s3c24xx_init_irq()函數(shù)是通過(guò)start_kernel()里的init_IRQ()函數(shù)調(diào)用init_arch_irq()實(shí)現(xiàn)的。因?yàn)樵贛ACHINE_START結(jié)構(gòu)體中 .init_irq = s3c24xx_init_irq,而在setup_arch()函數(shù)中init_arch_irq = mdesc->init_irq, 所以調(diào)用init_arch_irq()就相當(dāng)于調(diào)用了s3c24xx_init_irq()。
            又如smdk_machine_init()函數(shù)的初始化。在MACHINE_START結(jié)構(gòu)體中,函數(shù)指針賦值,.init_machine = smdk_machine_init。而init_machine()函數(shù)被linux/arch/arm/kernel/setup.c文件中的customize_machine()函數(shù)調(diào)用并被arch_initcall(Fn)宏處理,arch_initcall(customize_machine)。 被arch_initcall(Fn)宏處理過(guò)函數(shù)將linux/init/main.c
            do_initcalls()函數(shù)調(diào)用。 具體參看下邊的部分。

            void __init setup_arch(char cmdline_p)
            {
            struct tag *tags = (struct tag *)&init_tags;
            struct machine_desc *mdesc;
            char *from = default_command_line;

            setup_processor();
            mdesc = setup_machine(machine_arch_type);//machine_arch_type =SMDK2410 by edwin
            machine_name = mdesc->name;

            if (mdesc->soft_reboot)
            reboot_setup("s");

            if (mdesc->boot_params)
            tags = phys_to_virt(mdesc->boot_params);

            /*
            * If we have the old style parameters, convert them to
            * a tag list.
            */
            if (tags->hdr.tag != ATAG_CORE)
            convert_to_tag_list(tags);
            if (tags->hdr.tag != ATAG_CORE)
            tags = (struct tag *)&init_tags;

            if (mdesc->fixup)
            mdesc->fixup(mdesc, tags, &from, &meminfo);

            if (tags->hdr.tag == ATAG_CORE) {
            if (meminfo.nr_banks != 0)
            squash_mem_tags(tags);
            parse_tags(tags);
            }

            init_mm.start_code = (unsigned long) &_text;
            init_mm.end_code = (unsigned long) &_etext;
            init_mm.end_data = (unsigned long) &_edata;
            init_mm.brk = (unsigned long) &_end;

            memcpy(saved_command_line, from, COMMAND_LINE_SIZE);
            saved_command_line[COMMAND_LINE_SIZE-1] = /0;
            parse_cmdline(cmdline_p, from);
            paging_init(&meminfo, mdesc);
            request_standard_resources(&meminfo, mdesc);

            #ifdef CONFIG_SMP
            smp_init_cpus();
            #endif

            cpu_init();

            /*
            * Set up various architecture-specific pointers
            */
            init_arch_irq = mdesc->init_irq;
            system_timer = mdesc->timer;
            init_machine = mdesc->init_machine;

            #ifdef CONFIG_VT
            #if defined(CONFIG_VGA_CONSOLE)
            conswitchp = &vga_con;
            #elif defined(CONFIG_DUMMY_CONSOLE)
            conswitchp = &dummy_con;
            #endif
            #endif
            }
            5.3. rest_init()函數(shù)分析
            下面我們來(lái)分析下rest_init()函數(shù)。
            Start_kernel()函數(shù)負(fù)責(zé)初始化內(nèi)核各子系統(tǒng),最后調(diào)用reset_init(),啟動(dòng)一個(gè)叫做init的內(nèi)核線程,繼續(xù)初始化。在init內(nèi)核線程中,將執(zhí)行下列init()函數(shù)的程序。Init()函數(shù)負(fù)責(zé)完成根文件系統(tǒng)的掛接、初始化設(shè)備驅(qū)動(dòng)程序和啟動(dòng)用戶空間的init進(jìn)程等重要工作。

            static void noinline rest_init(void)
            __releases(kernel_lock)
            {
            kernel_thread(init, NULL, CLONE_FS | CLONE_SIGHAND);
            numa_default_policy();
            unlock_kernel();

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

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


            static int init(void * unused)
            {
            lock_kernel();
            /*
            * init can run on any cpu.
            */
            set_cpus_allowed(current, CPU_MASK_ALL);
            /*
            * Tell the world that were going to be the grim
            * reaper of innocent orphaned children.
            *
            * We dont want people to have to make incorrect
            * assumptions about where in the task array this
            * can be found.
            */
            child_reaper = current;

            smp_prepare_cpus(max_cpus);

            do_pre_smp_initcalls();

            smp_init();
            sched_init_smp();

            cpuset_init_smp();

            /*
            * Do this before initcalls, because some drivers want to access
            * firmware files.
            */
            populate_rootfs(); //掛接根文件系統(tǒng)

            do_basic_setup(); //初始化設(shè)備驅(qū)動(dòng)程序

            /*
            * check if there is an early userspace init. If yes, let it do all
            * the work //啟動(dòng)用戶空間的init進(jìn)程
            */

            if (!ramdisk_execute_command)
            ramdisk_execute_command = "/init";

            if (sys_access((const char __user *) ramdisk_execute_command, 0) != 0) {
            ramdisk_execute_command = NULL;
            prepare_namespace();
            }

            /*
            * Ok, we have completed the initial bootup, and
            * were essentially up and running. Get rid of the
            * initmem segments and start the user-mode stuff..
            */
            free_initmem();
            unlock_kernel();
            mark_rodata_ro();
            system_state = SYSTEM_RUNNING;
            numa_default_policy();

            if (sys_open((const char __user *) "/dev/console", O_RDWR, 0) < 0)
            printk(KERN_WARNING "Warning: unable to open an initial console./n");

            (void) sys_dup(0);
            (void) sys_dup(0);

            if (ramdisk_execute_command) {
            run_init_process(ramdisk_execute_command);
            printk(KERN_WARNING "Failed to execute %s/n",
            ramdisk_execute_command);
            }

            /*
            * We try each of these until one succeeds.
            *
            * The Bourne shell can be used instead of init if we are
            * trying to recover a really broken machine.
            */
            if (execute_command) {
            run_init_process(execute_command);
            printk(KERN_WARNING "Failed to execute %s. Attempting "
            "defaults.../n", execute_command);
            }
            run_init_process("/sbin/init");
            run_init_process("/etc/init");
            run_init_process("/bin/init");
            run_init_process("/bin/sh");

            panic("No init found. Try passing init= option to kernel.");
            }

            5.3.1. 掛接根文件系統(tǒng)
            Linux/init/ramfs.c
            void __init populate_rootfs(void)
            {
            char *err = unpack_to_rootfs(__initramfs_start,
            __initramfs_end - __initramfs_start, 0);
            if (err)
            panic(err);
            #ifdef CONFIG_BLK_DEV_INITRD
            if (initrd_start) {
            #ifdef CONFIG_BLK_DEV_RAM
            int fd;
            printk(KERN_INFO "checking if image is initramfs...");
            err = unpack_to_rootfs((char *)initrd_start,
            initrd_end - initrd_start, 1);
            if (!err) {
            printk(" it is/n");
            unpack_to_rootfs((char *)initrd_start,
            initrd_end - initrd_start, 0);
            free_initrd();
            return;
            }
            printk("it isnt (%s); looks like an initrd/n", err);
            fd = sys_open("/initrd.image", O_WRONLY|O_CREAT, 0700);
            if (fd >= 0) {
            sys_write(fd, (char *)initrd_start,
            initrd_end - initrd_start);
            sys_close(fd);
            free_initrd();
            }
            #else
            printk(KERN_INFO "Unpacking initramfs...");
            err = unpack_to_rootfs((char *)initrd_start,
            initrd_end - initrd_start, 0);
            if (err)
            panic(err);
            printk(" done/n");
            free_initrd();
            #endif
            }
            #endif
            }

            5.3.2. 初始化設(shè)備5.3.3. 驅(qū)動(dòng)程序
            linux/init/main.c
            static void __init do_basic_setup(void)
            {
            /* drivers will send hotplug events */
            init_workqueues();
            usermodehelper_init();
            driver_init(); /* 初始化驅(qū)動(dòng)程序模型。調(diào)用驅(qū)動(dòng)初始化函數(shù)初始化子系統(tǒng)。 */

            #ifdef CONFIG_SYSCTL
            sysctl_init();
            #endif

            do_initcalls();
            }


            linux/init/main.c
            extern initcall_t __initcall_start[], __initcall_end[];

            static void __init do_initcalls(void)
            {
            initcall_t *call;
            int count = preempt_count();

            for (call = __initcall_start; call < __initcall_end; call++) {
            char *msg = NULL;
            char msgbuf[40];
            int result;

            if (initcall_debug) {
            printk("Calling initcall 0x%p", *call);
            print_fn_descriptor_symbol(": %s()",
            (unsigned long) *call);
            printk("/n");
            }

            result = (*call)();

            ……
            ……
            ……
            }

            /* Make sure there is no pending stuff from the initcall sequence */
            flush_scheduled_work();
            }
            分析上面一段代碼可以看出,設(shè)備的初始化是通過(guò)do_basic_setup()函數(shù)調(diào)用do_initcalls()函數(shù),實(shí)現(xiàn)__initcall_start, __initcall_end段之間的指針函數(shù)執(zhí)行的。而到底是那些驅(qū)動(dòng)函數(shù)怎么會(huì)被集中到這個(gè)段內(nèi)的呢?我們知道系統(tǒng)內(nèi)存空間的分配是由鏈接器ld讀取鏈接腳本文件決定。鏈接器將同樣屬性的文件組織到相同的段里面去,如所有的.text段都被放在一起。在鏈接腳本里面可以獲得某塊內(nèi)存空間的具體地址。我們來(lái)看下linux-2.6.18.8/arch/arm/kernel/vmlinux.lds.S文件。由于文件過(guò)長(zhǎng),只貼出和__initcall_start, __initcall_end相關(guān)的部分。
            __initcall_start = .;
            *(.initcall1.init)
            *(.initcall2.init)
            *(.initcall3.init)
            *(.initcall4.init)
            *(.initcall5.init)
            *(.initcall6.init)
            *(.initcall7.init)
            __initcall_end = .;
            從腳本文件中我們可以看出, 在__initcall_start, __initcall_end之間放置的是屬行為(.initcall*.init)的函數(shù)數(shù)據(jù) 。在linux/include/linux/init.h文件中可以知道,(.initcall*.init)屬性是由__define_initcall(level, fn)宏設(shè)定的。

            #define __define_initcall(level,fn) /
            static initcall_t __initcall_##fn __attribute_used__ /
            __attribute__((__section__(".initcall" level ".init"))) = fn

            #define core_initcall(fn) __define_initcall("1",fn)
            #define postcore_initcall(fn) __define_initcall("2",fn)
            #define arch_initcall(fn) __define_initcall("3",fn)
            #define subsys_initcall(fn) __define_initcall("4",fn)
            #define fs_initcall(fn) __define_initcall("5",fn)
            #define device_initcall(fn) __define_initcall("6",fn)
            #define late_initcall(fn) __define_initcall("7",fn)
            #define __initcall(fn) device_initcall(fn)

            由此可以判斷,所有的設(shè)備驅(qū)動(dòng)函數(shù)都必然通過(guò)*_initcall(fn)宏的處理。以此為入口,可以查詢所有的設(shè)備驅(qū)動(dòng)。
            core_initcall(fn)
            static int __init consistent_init(void) linux/arch/arm/mm/consistent.c
            static int __init v6_userpage_init(void) linux/arch/arm/mm/copypage-v6.c
            static int __init init_dma(void) linux/arch/arm/kernel/dma.c
            static int __init s3c2410_core_init(void) linux/arch/arm/mach-s3c2410/s3c2410.c

            postcore_initcall(fn)
            static int ecard_bus_init(void) linux/arch/arm/kernel/ecard.c

            arch_initcall(fn)
            static __init int bast_irq_init(void) linux/arch/arm/mach-s3c2410/bast-irq.c
            static int __init s3c_arch_init(void) linux/arch/arm/mach-s3c2410/cpu.c
            static __init int pm_simtec_init(void) linux/arch/arm/mach-s3c2410/pm-simtec.c
            static int __init customize_machine(void) linux/arch/arm/kernel/setup.c

            subsys_initcall(fn)
            static int __init ecard_init(void) linux/arch/arm/kernel/ecard.c
            int __init scoop_init(void) linux/arch/arm/common/scoop.c
            static int __init topology_init(void) linux/arch/arm/kernel/setup.c

            fs_initcall(fn)
            static int __init alignment_init(void) linux/arch/arm/mm/alignment.c

            device_initcall(fn)
            static int __init leds_init(void) linux/arch/arm/kernel/time.c
            static int __init timer_init_sysfs(void) linux/arch/arm/kernel/time.c

            late_initcall(fn)
            static int __init crunch_init(void) arch/arm/kernel/crunch.c
            static int __init arm_mrc_hook_init(void) linux/arch/arm/kernel/traps.c

            5.3.4. 啟動(dòng)用戶空間的程序



            評(píng)論


            技術(shù)專區(qū)

            關(guān)閉