Linux/Android启动 之 (module_init和machine-init函数)
Linux/Android启动之Machine-Init函数
一.基础知识
1.Linux启动过程中驱动模块初始化的位置
LinuxOS的启动过程中将会去创建线程kernel_init,该线程负责Driver初始化等一系列工作。线程kernel_init将会依次调用do_basic_setup() -->do_initcalls()-->do_one_initcall(),并在do_initcalls()中完成对各个驱动模块Init函数的调用。
这部分代码如下:
函数do_basic_setup()如下:
/* *Ok,themachineisnowinitialized.Noneofthedevices *havebeentouchedyet,buttheCPUsubsystemisupand *running,andmemoryandprocessmanagementworks. * *Nowwecanfinallystartdoingsomerealwork.. */ staticvoid__initdo_basic_setup(void) { rcu_init_sched();/*neededbymodule_initstage.*/ init_workqueues(); usermodehelper_init(); driver_init(); init_irq_proc(); do_initcalls(); } |
函数do_initcalls():
externinitcall_t__initcall_start[],__initcall_end[],__early_initcall_end[]; staticvoid__initdo_initcalls(void) { initcall_t*call; for(call=__early_initcall_end;call<__initcall_end;call++) do_one_initcall(*call); /*Makesurethereisnopendingstufffromtheinitcallsequence*/ flush_scheduled_work(); } |
函数do_one_initcall(initcall_tfn)如下:
intinitcall_debug; core_param(initcall_debug,initcall_debug,bool,0644); intdo_one_initcall(initcall_tfn) { intcount=preempt_count(); ktime_tcalltime,delta,rettime; charmsgbuf[64]; structboot_trace_callcall; structboot_trace_retret; if(initcall_debug){ call.caller=task_pid_nr(current); printk("calling%pF@%i/n",fn,call.caller); calltime=ktime_get(); trace_boot_call(&call,fn); enable_boot_trace(); } ret.result=fn(); if(initcall_debug){ disable_boot_trace(); rettime=ktime_get(); delta=ktime_sub(rettime,calltime); ret.duration=(unsignedlonglong)ktime_to_ns(delta)>>10; trace_boot_ret(&ret,fn); printk("initcall%pFreturned%dafter%Ldusecs/n",fn, ret.result,ret.duration); } msgbuf[0]=0; if(ret.result&&ret.result!=-ENODEV&&initcall_debug) sprintf(msgbuf,"errorcode%d",ret.result); if(preempt_count()!=count){ strlcat(msgbuf,"preemptionimbalance",sizeof(msgbuf)); preempt_count()=count; } if(irqs_disabled()){ strlcat(msgbuf,"disabledinterrupts",sizeof(msgbuf)); local_irq_enable(); } if(msgbuf[0]){ printk("initcall%pFreturnedwith%s/n",fn,msgbuf); } returnret.result; } |
二.Machine-Init函数被调用的位置
Machine-Init也将在函数do_one_initcall(initcall_tfn)中伴随其它的模块Init函数一起被调用,不同之处在于Machine-Init的优先级最高。
三.Machine-Init函数相关解析过程
1.重要结构体machine_desc说明
structmachine_desc描述了机器(machine,也就是目标板)对内核初始阶段资源分配至关重要的一些参数。
首先,来看一下结构体machine_des的内容(在arch/arm/include/asm/mach/arch.h):
structmachine_desc{ /* *Note!Thefirstfourelementsareused *byassemblercodeinhead.S,head-common.S */ unsignedintnr;/*architecturenumber,记录体系结构*/ unsignedintphys_io;/*startofphysicalio*/ unsignedintio_pg_offst;/*byteoffsetforio *pagetabeentry*/ constchar*name;/*architecturename,体系结构名字*/ unsignedlongboot_params;/*taggedlist*/ unsignedintvideo_start;/*startofvideoRAM*/ unsignedintvideo_end;/*endofvideoRAM*/ unsignedintreserve_lp0:1;/*neverhaslp0*/ unsignedintreserve_lp1:1;/*neverhaslp1*/ unsignedintreserve_lp2:1;/*neverhaslp2*/ unsignedintsoft_reboot:1;/*softreboot*/ void(*fixup)(structmachine_desc*, structtag*,char**, structmeminfo*); void(*map_io)(void);/*IOmappingfunction*/ void(*init_irq)(void); structsys_timer*timer;/*systemticktimer*/ void(*init_machine)(void); }; |
其中,结构体的最后一个函数指针init_machine会被初始化为Machine-Init的地址。系统中针对每一种CPU都会去定义一个该结构体变量,并在系统的启动过程中进行引用。
2.对应当前开发板的结构体machine_des的初始化
这里以SamsungS3C6410的开发板的BSP为例来进行分析。
SamsungS3C6410的machine_des在文件arch/arm/mach-s3c6410/mach-s3c6410.c中定义,定义方式如下:
MACHINE_START(SMDK6410,"SMDK6410") /*Maintainer:BenDooks<ben@fluff.org>*/ .phys_io=S3C_PA_UART&0xfff00000, .io_pg_offst=(((u32)S3C_VA_UART)>>18)&0xfffc, .boot_params=S3C64XX_PA_SDRAM+0x100, .fixup=smdk6410_fixup, .init_irq=s3c6410_init_irq, .map_io=smdk6410_map_io, .init_machine=smdk6410_machine_init, #ifndefCONFIG_HIGH_RES_TIMERS .timer=&s3c64xx_timer, #else .timer=&sec_timer, #endif/*CONFIG_HIGH_RES_TIMERS*/ MACHINE_END |
Linux中针对各个不同的CPU存在很多个类似于上面的定义,宏定义MACHINE_START的定义如下:
/* *Setofmacrostodefinearchitecturefeatures.Thisisbuiltinto *atablebythelinker. */ #defineMACHINE_START(_type,_name)/ staticconststructmachine_desc__mach_desc_##_type/ __used/ __attribute__((__section__(".arch.info.init")))={/ .nr=MACH_TYPE_##_type,/ .name=_name, #defineMACHINE_END/ }; |
其实,宏定义替换后的就变成了如下的定义方式:
staticconststructmachine_desc__SMDK6410 __used __attribute__((__section__(".arch.info.init")))={/*__section__指定了该结构体被链接的位置*/ .nr=MACH_TYPE_SMDK6410, .name="SMDK6410", phys_io=S3C_PA_UART&0xfff00000, .io_pg_offst=(((u32)S3C_VA_UART)>>18)&0xfffc, .boot_params=S3C64XX_PA_SDRAM+0x100, .fixup=smdk6410_fixup, .init_irq=s3c6410_init_irq, .map_io=smdk6410_map_io, .init_machine=smdk6410_machine_init, #ifndefCONFIG_HIGH_RES_TIMERS .timer=&s3c64xx_timer, #else .timer=&sec_timer, }; |
3.函数setup_machine实现对结构体machine_des的定位
函数setup_machine实现对结构体machine_des位置的判别,其代码代码如下:
staticstructmachine_desc*__initsetup_machine(unsignedintnr) { structmachine_desc*list; /* *locatemachineinthelistofsupportedmachines.可能支持多个cpu哦 */ list=lookup_machine_type(nr); if(!list){ printk("Machineconfigurationbotched(nr%d),unable" "tocontinue./n",nr); while(1); } printk("Machine:%s/n",list->name); returnlist; } |
上面红色标记的函数lookup_machine_type(nr)用汇编实现在文件head.S中,用来查找对应当前设备的machine_desc变量位置,并通过函数lookup_machine_type指向其起始位置,后续就可以直接通过该返回值来对结构体machine_desc变量machine_desc__SMDK6410的值进行读取,如后面读取Machine-Init函数的指针的操作init_machine=mdesc->init_machine。
而函数setup_machine被调用的过程如下(start_kernelàsetup_archàsetup_machine):
在函数setup_machine最后会在全局指针变量init_machine中记录Machine-Init函数的信息。
4.Machine-Init的导出
在文件arch/arm/kernel/setup.c中通过如下的方式将Machine-Init设置为模块的Init函数:
staticvoid(*init_machine)(void)__initdata; staticint__initcustomize_machine(void) { /*customizesplatformdevices,oraddsnewones*/ if(init_machine) init_machine(); return0; } arch_initcall(customize_machine); |
可能不太熟悉驱动优先级的人会觉得,这里怎么使用arch_initcall导出,而不是通常的module_init方式进行导出。
在Linux中,没有办法像CE/Mobile中通过注册表的方式来定义驱动的优先级,只有通过导出函数的Level和Makefile中模块驱动书写的先后顺序来定义。
关于导出函数Level的代码如下:
#define__define_initcall(level,fn)staticinitcall_t__initcall_##fn__attribute_used____attribute__((__section__(".initcall"level".init")))=fn #definecore_initcall(fn)__define_initcall("1",fn) #definepostcore_initcall(fn)__define_initcall("2",fn) #definearch_initcall(fn)__define_initcall("3",fn) #definesubsys_initcall(fn)__define_initcall("4",fn) #definefs_initcall(fn)__define_initcall("5",fn) #definedevice_initcall(fn)__define_initcall("6",fn) #definelate_initcall(fn)__define_initcall("7",fn) #define__initcall(fn)device_initcall(fn) #define__exitcall(fn)staticexitcall_t__exitcall_##fn__exit_call=fn #definemodule_init(x)__initcall(x); #definemodule_exit(x)__exitcall(x); |
可以看到,通常驱动中采用的module_init优先级为6,也即最低优先级,而前面采用的arch_initcall优先级为最高优先级1。
更多相关文章
- Android调整TimePicker和DatePicker大小
- android_定义多个Activity及跳转
- android dataBinding详解
- Android(安卓)自定义打印log
- Java层Binder使用(ServiceManager)
- 【转】高通平台android 环境配置编译及开发经验总结
- Android(安卓)自定义View之自定义评分选择器RatingBar
- Android开发工具——ADB(Android(安卓)Debug Bridge) HOST端
- Android(安卓)Intent原理分析