Machine_desc & boot & Kernel_init & initcall & module_init

来源:互联网 发布:毕向东java基础视频 编辑:程序博客网 时间:2024/06/07 05:15

跳转到start_kernel中,与本题相关的主要是setup_arch()和rest_init(),其中前者执行的较早,而后者做为start_kernel执行的最后一个函数。

函数start_kernel()和rest_init()定义在kernel/init/main.c中,函数setup_arch()定义在kernel/arch/arch_name/kernel/setup.c中。

1.setup_arch()在.init.text中,会执行machine_desc.init_very_early和machine_desc.init_early,代码如下,

915void __init setup_arch(char **cmdline_p)916{917struct machine_desc *mdesc;918919unwind_init();920921setup_processor();922mdesc = setup_machine_fdt(__atags_pointer);923if (!mdesc)924mdesc = setup_machine_tags(machine_arch_type);925machine_desc = mdesc;926machine_name = mdesc->name;927928if (mdesc->soft_reboot)929reboot_setup("s");930931init_mm.start_code = (unsigned long) _text;932init_mm.end_code   = (unsigned long) _etext;933init_mm.end_data   = (unsigned long) _edata;934init_mm.brk   = (unsigned long) _end;935936/* populate cmd_line too for later use, preserving boot_command_line */937strlcpy(cmd_line, boot_command_line, COMMAND_LINE_SIZE);938*cmdline_p = cmd_line;939940parse_early_param();941942if (mdesc->init_very_early)943mdesc->init_very_early();944945sanity_check_meminfo();946arm_memblock_init(&meminfo, mdesc);947948paging_init(mdesc);949request_standard_resources(mdesc);950951unflatten_device_tree();952953#ifdef CONFIG_SMP954if (is_smp())955smp_init_cpus();956#endif957reserve_crashkernel();958959cpu_init();960tcm_init();961962#ifdef CONFIG_MULTI_IRQ_HANDLER963handle_arch_irq = mdesc->handle_irq;964#endif965966#ifdef CONFIG_VT967#if defined(CONFIG_VGA_CONSOLE)968conswitchp = &vga_con;969#elif defined(CONFIG_DUMMY_CONSOLE)970conswitchp = &dummy_con;971#endif972#endif973early_trap_init();974975if (mdesc->init_early)976mdesc->init_early();977}

2. rest_init()执行后,创建内核线程kernel_init,这也是内核第一个线程,kernel_init做的与本题相关的初始化是do_pre_smp_initcalls()和do_basic_setup(),然后变成用户态进程init。代码如下,

841static int __init kernel_init(void * unused)842{843/*844 * Wait until kthreadd is all set-up.845 */846wait_for_completion(&kthreadd_done);847/*848 * init can allocate pages on any node849 */850set_mems_allowed(node_states[N_HIGH_MEMORY]);851/*852 * init can run on any cpu.853 */854set_cpus_allowed_ptr(current, cpu_all_mask);855856cad_pid = task_pid(current);857858smp_prepare_cpus(setup_max_cpus);859860do_pre_smp_initcalls();861lockup_detector_init();862863smp_init();864sched_init_smp();865866do_basic_setup();867868/* Open the /dev/console on the rootfs, this should never fail */869if (sys_open((const char __user *) "/dev/console", O_RDWR, 0) < 0)870printk(KERN_WARNING "Warning: unable to open an initial console.\n");871872(void) sys_dup(0);873(void) sys_dup(0);874/*875 * check if there is an early userspace init.  If yes, let it do all876 * the work877 */878879if (!ramdisk_execute_command)880ramdisk_execute_command = "/init";881882if (sys_access((const char __user *) ramdisk_execute_command, 0) != 0) {883ramdisk_execute_command = NULL;884prepare_namespace();885}886887/*888 * Ok, we have completed the initial bootup, and889 * we're essentially up and running. Get rid of the890 * initmem segments and start the user-mode stuff..891 */892893init_post();894return 0;895}

do_pre_smp_initcalls()做.initcallearly.init section函数的执行,早于.initcall0.init-.initcall7.init;在初始化完smp调度后,.initcalln.init函数在do_basic_setup()被调用

do_pre_smp_initcalls()代码如下,

786static void __init do_pre_smp_initcalls(void)787{788initcall_t *fn;789790for (fn = __initcall_start; fn < __initcall0_start; fn++)791do_one_initcall(*fn);792}

do_basic_setup()及调用.initcalln.init的代码如下,

743static void __init do_initcall_level(int level)744{745extern const struct kernel_param __start___param[], __stop___param[];746initcall_t *fn;747748strcpy(static_command_line, saved_command_line);749parse_args(initcall_level_names[level],750   static_command_line, __start___param,751   __stop___param - __start___param,752   level, level,753   repair_env_string);754755for (fn = initcall_levels[level]; fn < initcall_levels[level+1]; fn++)756do_one_initcall(*fn);757}758759static void __init do_initcalls(void)760{761int level;762763for (level = 0; level < ARRAY_SIZE(initcall_levels) - 1; level++)764do_initcall_level(level);765}766767/*768 * Ok, the machine is now initialized. None of the devices769 * have been touched yet, but the CPU subsystem is up and770 * running, and memory and process management works.771 *772 * Now we can finally start doing some real work..773 */774static void __init do_basic_setup(void)775{776cpuset_init_smp();777usermodehelper_init();778shmem_init();779driver_init();780init_irq_proc();781do_ctors();782usermodehelper_enable();783do_initcalls();784}

各.initcalln.init的宏定义如下

168/* initcalls are now grouped by functionality into separate169 * subsections. Ordering inside the subsections is determined170 * by link order.171 * For backwards compatibility, initcall() puts the call in172 * the device init subsection.173 *174 * The `id' arg to __define_initcall() is needed so that multiple initcalls175 * can point at the same handler without causing duplicate-symbol build errors.176 */177178#define __define_initcall(level,fn,id) \179static initcall_t __initcall_##fn##id __used \180__attribute__((__section__(".initcall" level ".init"))) = fn181182/*183 * Early initcalls run before initializing SMP.184 *185 * Only for built-in code, not modules.186 */187#define early_initcall(fn)__define_initcall("early",fn,early)188189/*190 * A "pure" initcall has no dependencies on anything else, and purely191 * initializes variables that couldn't be statically initialized.192 *193 * This only exists for built-in code, not for modules.194 */195#define pure_initcall(fn)__define_initcall("0",fn,0)196197#define core_initcall(fn)__define_initcall("1",fn,1)198#define core_initcall_sync(fn)__define_initcall("1s",fn,1s)199#define postcore_initcall(fn)__define_initcall("2",fn,2)200#define postcore_initcall_sync(fn)__define_initcall("2s",fn,2s)201#define arch_initcall(fn)__define_initcall("3",fn,3)202#define arch_initcall_sync(fn)__define_initcall("3s",fn,3s)203#define subsys_initcall(fn)__define_initcall("4",fn,4)204#define subsys_initcall_sync(fn)__define_initcall("4s",fn,4s)205#define fs_initcall(fn)__define_initcall("5",fn,5)206#define fs_initcall_sync(fn)__define_initcall("5s",fn,5s)207#define rootfs_initcall(fn)__define_initcall("rootfs",fn,rootfs)208#define device_initcall(fn)__define_initcall("6",fn,6)209#define device_initcall_sync(fn)__define_initcall("6s",fn,6s)210#define late_initcall(fn)__define_initcall("7",fn,7)211#define late_initcall_sync(fn)__define_initcall("7s",fn,7s)212213#define __initcall(fn) device_initcall(fn)214215#define __exitcall(fn) \216static exitcall_t __exitcall_##fn __exit_call = fn217218#define console_initcall(fn) \219static initcall_t __initcall_##fn \220__used __section(.con_initcall.init) = fn221222#define security_initcall(fn) \223static initcall_t __initcall_##fn \224__used __section(.security_initcall.init) = fn

machine_desc.init_machine在customize_machine() @ kernel/arch/arm/kernel/setup.c中被调用,而customize_machine指针引用在.initcall3.init中。customize_machine()代码如下,

842static int __init customize_machine(void)843{844/* customizes platform devices, or adds new ones */845if (machine_desc->init_machine)846machine_desc->init_machine();847return 0;848}849arch_initcall(customize_machine);

各module_init(fn)设备驱动在.initcall6.init中被调用,由以下module_init的宏定义可以看出,

259/**260 * module_init() - driver initialization entry point261 * @x: function to be run at kernel boot time or module insertion262 *263 * module_init() will either be called during do_initcalls() (if264 * builtin) or at module insertion time (if a module).  There can only265 * be one per module.266 */267#define module_init(x)__initcall(x);

而__initcall(x)宏定义为

213#define __initcall(fn) device_initcall(fn)

综上,题中提及各初始化的顺序是machine_desc.init_very_early, machine_desc.init_early, .initcallearly.init, machine_desc.init_machine(.initcall3.init), module_init(.initcall6.init) 。

[End]

[参考文章]

http://blog.csdn.net/paomadi/article/details/8611408

一、定义

#define MACHINE_START(_type,_name)\//板类型,板名字static const struct machine_desc __mach_desc_##_type\ __used\ __attribute__((__section__(".arch.info.init"))) = {\.nr= MACH_TYPE_##_type,\.name= _name,#define MACHINE_END\};

MACHINE_START和MACHINE_END框起了一个machine_desc结构体的声明并根据MACHINE_START宏的参数初始化其.nr和.name成员

并将该结构体标记编译到.arch.info.init段

在MACHINE_START和MACHINE_END宏之间可以初始化machine_desc结构体的剩余成员

machine_desc结构体的定义

struct machine_desc {unsigned intnr;/* architecture number 编号*/const char*name;/* architecture name 名字*/unsigned longboot_params;/* tagged list*/unsigned intnr_irqs;/* number of IRQs 中断数*/unsigned intvideo_start;/* start of video RAM */unsigned intvideo_end;/* end of video RAM*/unsigned intreserve_lp0 :1;/* never has lp0*/unsigned intreserve_lp1 :1;/* never has lp1*/unsigned intreserve_lp2 :1;/* never has lp2*/unsigned intsoft_reboot :1;/* soft reboot*/void(*fixup)(struct machine_desc *,struct tag *, char **,struct meminfo *);void(*reserve)(void);/* reserve mem blocks*/void(*map_io)(void);/* IO mapping function io映射函数 */void(*init_irq)(void);/* 中断初始化函数 */struct sys_timer*timer;/* system tick timer 滴答定时器 */void(*init_machine)(void);/* 初始化函数 */};

使用例子:

MACHINE_START(SMDKC110, "SMDKC110")/* Maintainer: Kukjin Kim <kgene.kim@samsung.com> */.boot_params= S5P_PA_SDRAM + 0x100,.init_irq= s5pv210_init_irq,.map_io= smdkc110_map_io,.init_machine= smdkc110_machine_init,.timer= &s3c24xx_timer,MACHINE_END

这里smdkc110_machine_init就是对应的板级初始化函数,s5pv210_init_irq就是板级中断初始化函数,smdkc110_map_io就是板级io初始化函数...
二、调用关系

MACHINE_START宏将machine_desc标记编译到.arch.info.init段, 而/arch/arm/kernel/vmlinux.lds中

  __arch_info_begin = .;   *(.arch.info.init)  __arch_info_end = .;

 当系统启动时在linux启动函数start_kernel中调用了setup_arch(&command_line);

void __init setup_arch(char **cmdline_p){struct tag *tags = (struct tag *)&init_tags;struct machine_desc *mdesc;//声明了一个machine_desc结构体指针char *from = default_command_line;init_tags.mem.start = PHYS_OFFSET;unwind_init();setup_processor();mdesc = setup_machine(machine_arch_type);//0根据machine_arch_type获取machine_descmachine_name = mdesc->name;//设置名字if (mdesc->soft_reboot)//需要软重启?reboot_setup("s");if (__atags_pointer)tags = phys_to_virt(__atags_pointer);else if (mdesc->boot_params) {//处理启动参数#ifdef CONFIG_MMUif (mdesc->boot_params < PHYS_OFFSET ||    mdesc->boot_params >= PHYS_OFFSET + SZ_1M) {printk(KERN_WARNING"Default boot params at physical 0x%08lx out of reach\n",mdesc->boot_params);} else#endif{tags = phys_to_virt(mdesc->boot_params);}}#if defined(CONFIG_DEPRECATED_PARAM_STRUCT)if (tags->hdr.tag != ATAG_CORE)convert_to_tag_list(tags);#endifif (tags->hdr.tag != ATAG_CORE)tags = (struct tag *)&init_tags;if (mdesc->fixup)//若存在fixup方法则调用其方法mdesc->fixup(mdesc, tags, &from, &meminfo);if (tags->hdr.tag == ATAG_CORE) {if (meminfo.nr_banks != 0)squash_mem_tags(tags);save_atags(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;/* parse_early_param needs a boot_command_line */strlcpy(boot_command_line, from, COMMAND_LINE_SIZE);/* populate cmd_line too for later use, preserving boot_command_line */strlcpy(cmd_line, boot_command_line, COMMAND_LINE_SIZE);*cmdline_p = cmd_line;parse_early_param();arm_memblock_init(&meminfo, mdesc);//这里可能会调用reserve方法paging_init(mdesc);//->devicemaps_init(mdesc)->map_io方法request_standard_resources(&meminfo, mdesc);//这里可能会调用video_start方法#ifdef CONFIG_SMPif (is_smp())smp_init_cpus();#endifreserve_crashkernel();cpu_init();tcm_init();arch_nr_irqs = mdesc->nr_irqs;//1设置全局变量 中断个数init_arch_irq = mdesc->init_irq;//2设置全局变量 中断初始化函数system_timer = mdesc->timer;//3设置全局变量 sys_timer结构体init_machine = mdesc->init_machine;//4设置全局变量 板级初始化函数#ifdef CONFIG_VT#if defined(CONFIG_VGA_CONSOLE)conswitchp = &vga_con;#elif defined(CONFIG_DUMMY_CONSOLE)conswitchp = &dummy_con;#endif#endifearly_trap_init();}

 0. 结构machine_desc安装

static struct machine_desc * __init setup_machine(unsigned int nr){extern struct machine_desc __arch_info_begin[], __arch_info_end[];struct machine_desc *p;for (p = __arch_info_begin; p < __arch_info_end; p++)//遍历__arch_info_begin和__arch_info_end之间的machine_desc结构体if (nr == p->nr) {//找到对应的板printk("Machine: %s\n", p->name);//打印板级信息return p;}early_print("\n""Error: unrecognized/unsupported machine ID (r1 = 0x%08x).\n\n""Available machine support:\n\nID (hex)\tNAME\n", nr);for (p = __arch_info_begin; p < __arch_info_end; p++)early_print("%08x\t%s\n", p->nr, p->name);early_print("\nPlease check your kernel config and/or bootloader.\n");while (true)/* can't use cpu_relax() here as it may require MMU setup */;}

1.中断个数

start_kernel->early_irq_init->arch_probe_nr_irqs函数中nr_irqs = arch_nr_irqs ? arch_nr_irqs : NR_IRQS;设置全局nr_irqs变量
2.中断初始化函数

start_kernel->init_IRQ->init_arch_irq()

3.sys_timer结构体

start_kernel->time_init()调用system_timer->init()方法既sys_timer->init()

4.板级初始化函数

static void (*init_machine)(void) __initdata;static int __init customize_machine(void){/* customizes platform devices, or adds new ones */if (init_machine)//全局函数init_machine存在init_machine();//则调用,既mdesc->init_machine()return 0;}arch_initcall(customize_machine);//用arch_initcall修饰customize_machine函数

arch_iniitcall函数在/include/linux/init.h中定义

#define arch_initcall(fn)__define_initcall("3",fn,3)

__define_initcall的定义

#define __define_initcall(level,fn,id) \static initcall_t __initcall_##fn##id __used \__attribute__((__section__(".initcall" level ".init"))) = fn


展开就是static initcall_t __initcall_customize_machine3 __used __attribute__((__section__(".initcall3.init")))=customize_machine

在vmlinux.lds中

__initcall_start = .; *(.initcallearly.init) __early_initcall_end = .; *(.initcall0.init) *(.initcall0s.init) *(.initcall1.init) *(.initcall1s.init) *(.initcall2.init) *(.initcall2s.init) *(.initcall3.init) *(.initcall3s.init) *(.initcall4.init) *(.initcall4s.init) *(.initcall5.init) *(.initcall5s.init) *(.initcallrootfs.init) *(.initcall6.init) *(.initcall6s.init) *(.initcall7.init) *(.initcall7s.init) __initcall_end = .;


标注为.initcall3.init的函数编译进__initcall_start和__initcall_end框起的section中

而在系统启动的时候start_kernel->rest_init()->kernel_thread(kernel_init, NULL, CLONE_FS | CLONE_SIGHAND);创建了kernel线程

kernel_init->do_pre_smp_initcalls()

static void __init do_pre_smp_initcalls(void){initcall_t *fn;for (fn = __initcall_start; fn < __early_initcall_end; fn++)do_one_initcall(*fn);}


该函数遍历__initcall_start和__early_initcall_end中的函数,并调用do_one_initcall

int __init_or_module do_one_initcall(initcall_t fn){int count = preempt_count();int ret;if (initcall_debug)ret = do_one_initcall_debug(fn);elseret = fn();//执行了fn函数也就是customize_machinemsgbuf[0] = 0;if (ret && ret != -ENODEV && initcall_debug)sprintf(msgbuf, "error code %d ", ret);if (preempt_count() != count) {strlcat(msgbuf, "preemption imbalance ", sizeof(msgbuf));preempt_count() = count;}if (irqs_disabled()) {strlcat(msgbuf, "disabled interrupts ", sizeof(msgbuf));local_irq_enable();}if (msgbuf[0]) {printk("initcall %pF returned with %s\n", fn, msgbuf);}return ret;}


http://blog.csdn.net/cxw3506/article/details/8475965

Machine定义以MACHINE_START开始并以MACHINE_END结束,如下mini2440开发板的移植为示例

MACHINE_START(MINI2440, "MINI2440").phys_io= S3C2410_PA_UART,.io_pg_offst= (((u32)S3C24XX_VA_UART) >> 18) & 0xfffc,.boot_params= S3C2410_SDRAM_PA + 0x100,.map_io= mini2440_map_io,.init_machine= mini2440_init,.init_irq= s3c24xx_init_irq,.timer= &s3c24xx_timer,MACHINE_END

MACHINE_START、MACHINE_END都是定义的宏,代码如下

/* * Set of macros to define architecture features.  This is built into * a table by the linker. */#define MACHINE_START(_type,_name)\static const struct machine_desc __mach_desc_##_type\ __used\ __attribute__((__section__(".arch.info.init"))) = {\.nr= MACH_TYPE_##_type,\.name= _name,#define MACHINE_END\};

这两个宏一起定义了一个类型为struct machine_desc的变量,结构体定义如下

struct machine_desc {/* * Note! The first four elements are used * by assembler code in head.S, head-common.S */unsigned intnr;/* architecture number*/unsigned intphys_io;/* start of physical io*/unsigned intio_pg_offst;/* byte offset for io  * page tabe entry*/const char*name;/* architecture name*/unsigned longboot_params;/* tagged list*/unsigned intvideo_start;/* start of video RAM*/unsigned intvideo_end;/* end of video RAM*/unsigned intreserve_lp0 :1;/* never has lp0*/unsigned intreserve_lp1 :1;/* never has lp1*/unsigned intreserve_lp2 :1;/* never has lp2*/unsigned intsoft_reboot :1;/* soft reboot*/void(*fixup)(struct machine_desc *, struct tag *, char **, struct meminfo *);void(*map_io)(void);/* IO mapping function*/void(*init_irq)(void);struct sys_timer*timer;/* system tick timer*/void(*init_machine)(void);};

这个类型的变量放在内核代码段.arch.info.init中,在内核运行初期,被函数lookup_machine_type(此函数用汇编实现,在汇编文件中)取出,读取流程为

Start_kernel() -> setup_arch() -> setup_machine() -> lookup_machine_type()

在函数setup_machine()中,利用这个结构体类型的变量初始化一些全局变量,以备内核运行时使用,比如

       init_arch_irq = mdesc->init_irq;

       system_timer = mdesc->timer;

       init_machine = mdesc->init_machine;

这个结构体中,成员init_machine保存的是开发板资源注册的初始化代码,init_irq保存的是中断初始化指针,timer保存的是一个struct sys_timer类型的指针…..如果我们要给自己的开发板定制内核,那么我们必须自己实现以上成员函数,其中函数init_machine()是我们向内核传递开发板设备信息的重要的常规途径,分析mini2440开发板内核移植代码知道,在这个函数中,注册了开发板所用到的所有设备的相关硬件信息!

static void __init mini2440_init(void){struct mini2440_features_t features = { 0 };int i;printk(KERN_INFO "MINI2440: Option string mini2440=%s\n",mini2440_features_str);/* Parse the feature string */mini2440_parse_features(&features, mini2440_features_str);/* turn LCD on */s3c_gpio_cfgpin(S3C2410_GPC(0), S3C2410_GPC0_LEND);/* Turn the backlight early on */WARN_ON(gpio_request(S3C2410_GPG(4), "backlight"));gpio_direction_output(S3C2410_GPG(4), 1);/* remove pullup on optional PWM backlight -- unused on 3.5 and 7"s */s3c_gpio_setpull(S3C2410_GPB(1), S3C_GPIO_PULL_UP);s3c2410_gpio_setpin(S3C2410_GPB(1), 0);s3c_gpio_cfgpin(S3C2410_GPB(1), S3C2410_GPIO_INPUT);/* Make sure the D+ pullup pin is output */WARN_ON(gpio_request(S3C2410_GPC(5), "udc pup"));gpio_direction_output(S3C2410_GPC(5), 0);/* mark the key as input, without pullups (there is one on the board) */for (i = 0; i < ARRAY_SIZE(mini2440_buttons); i++) {s3c_gpio_setpull(mini2440_buttons[i].gpio, S3C_GPIO_PULL_UP);s3c_gpio_cfgpin(mini2440_buttons[i].gpio, S3C2410_GPIO_INPUT);}if (features.lcd_index != -1) {int li;mini2440_fb_info.displays =&mini2440_lcd_cfg[features.lcd_index];printk(KERN_INFO "MINI2440: LCD");for (li = 0; li < ARRAY_SIZE(mini2440_lcd_cfg); li++)if (li == features.lcd_index)printk(" [%d:%dx%d]", li,mini2440_lcd_cfg[li].width,mini2440_lcd_cfg[li].height);elseprintk(" %d:%dx%d", li,mini2440_lcd_cfg[li].width,mini2440_lcd_cfg[li].height);printk("\n");s3c24xx_fb_set_platdata(&mini2440_fb_info);}s3c24xx_udc_set_platdata(&mini2440_udc_cfg);s3c24xx_mci_set_platdata(&mini2440_mmc_cfg);s3c_nand_set_platdata(&mini2440_nand_info);s3c_i2c0_set_platdata(NULL);i2c_register_board_info(0, mini2440_i2c_devs,ARRAY_SIZE(mini2440_i2c_devs));platform_add_devices(mini2440_devices, ARRAY_SIZE(mini2440_devices));if (features.count)/* the optional features */platform_add_devices(features.optional, features.count);}

那么成员函数init_machine什么时候被调用呢?

在函数setup_machine()中有一条语句init_machine = mdesc->init_machine;其中init_machine为全局函数指针变量,此变量在函数customize_machine()中被调用,代码如下所示:

static int __init customize_machine(void){       /* customizes platform devices, or adds new ones */       if (init_machine)              init_machine();       return 0;}arch_initcall(customize_machine);
原创粉丝点击