do_initcall解析
来源:互联网 发布:进驻淘宝协议 编辑:程序博客网 时间:2024/06/12 00:21
老大提了新需求:
板子上2个PCI网卡,2个CPU自带TSEC;
要求TSEC对应 eth0,eth1, PCI网卡对应 eth2,eth3
网络设备的注册是由register_netdev(struct net_device *dev)来实现的
网络设备驱动加载的时候,会调这个函数,到时就会确定网络设备的接口名称为 eth1或2或3...
在设备驱动代码里强行指定设备接口名?麻烦,改的地方多
忽然想到了驱动加载顺序,看了看 do_initcall,顺便网上查了查 __define_initcall(level,fn)
然后改了下/drivers/net/Makefile
编译,搞定。
--------------------------- 华丽的分割线 -------------------------------------
前言
宏定义__define_initcall(level,fn)对于内核的初始化很重要,它指示
编译器在编译的时候,将一系列初始化函数的起始地址值按照一定的顺序
放在一个section中。在内核初始化阶段,do_initcalls() 将按顺序从该
section中以函数指针的形式取出这些函数的起始地址,来依次完成相应
的初始化。由于内核某些部分的初始化需要依赖于其他某些部分的初始化
的完成,因此这个顺序排列常常非常重要。
下面将从__define_initcall(level,fn) 宏定义的代码分析入手,依次
分析名称为initcall.init的section的结构,最后分析内核初始化函数
do_initcalls()是如何利用宏定义__define_initcall(level,fn)及其相
关的衍生的7个宏宏定义,来实现内核某些部分的顺序初始化的。
1、分析 __define_initcall(level,fn) 宏定义
1) 这个宏的定义位于inlclude\linux\init.h中:
#define __define_initcall(level,fn) \
static initcall_t __initcall_##fn \
__attribute__((__section__(".initcall" level ".init"))) \
= fn
其中 initcall_t 是一个函数指针类型:
typedef int (*initcall_t)(void);
而属性 __attribute__((__section__())) 则表示把对象放在一个这个
由括号中的名称所指代的section中。
所以这个宏定义的的含义是:1) 声明一个名称为__initcall_##fn的函数
指针(其中##表示替换连接,);2) 将这个函数指针初始化为fn;3) 编译
的时候需要把这个函数指针变量放置到名称为 ".initcall" level ".init"
的section中(比如level="1",代表这个section的名称是 ".initcall1.init")。
2) 举例:__define_initcall(6, pci_init)
上述宏调用的含义是:1) 声明一个函数指针__initcall_pic_init = pci_init;
且 2) 这个指针变量__initcall_pic_init 需要放置到名称为 .initcall6.init
的section中( 其实质就是将 这个函数pic_init的首地址放置到了这个
section中)。
3) 这个宏一般并不直接使用,而是被定义成下述其他更简单的7个衍生宏
这些衍生宏宏的定义也位于 inlclude\linux\Init.h 中:
#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)
因此通过宏 core_initcall() 来声明的函数指针,将放置到名称为
.initcall1.init的section中,而通过宏 postcore_initcall() 来
声明的函数指针,将放置到名称为.initcall2.init的section中,
依次类推。
4) 举例:device_initcall(pci_init)
解释同上 1-2)。
2、与初始化调用有关section--initcall.init被分成了7个子section
1) 它们依次是.initcall1.init、.initcall2.init、...、.initcall7.init
2) 按照先后顺序依次排列
3) 它们的定义在文件vmlinux.lds.S中
例如 对于i386+,在i386\kernel\vmlinux.lds.S中有:
__initcall_start = .;
.initcall.init : {
*(.initcall1.init)
*(.initcall2.init)
*(.initcall3.init)
*(.initcall4.init)
*(.initcall5.init)
*(.initcall6.init)
*(.initcall7.init)
}
__initcall_end = .;
而在makefile 中有
LDFLAGS_vmlinux += -T arch/$(ARCH)/kernel/vmlinux.lds.s
4) 在这7个section总的开始位置被标识为__initcall_start,
而在结尾被标识为__initcall_end。
3、 内核初始化函数do_basic_setup(): do_initcalls() 将从.initcall.init
中,也就是这7个section中依次取出所有的函数指针,并调用这些
函数指针所指向的函数,来完成内核的一些相关的初始化。
这个函数的定义位于init\main.c中:
extern initcall_t __initcall_start, __initcall_end;
static void __init do_initcalls(void)
{
initcall_t *call;
....
for (call = &__initcall_start; call < &__initcall_end; call++)
{
....
(*call)();
....
}
....
}
这些函数指针指向的函数就是通过宏__define_initcall(level,fn)
赋值的函数fn,他们调用的顺序就是放置在这些section中的顺序,
这个顺序很重要, 这就是这个宏__define_initcall(level,fn)的作用。
注意到,这里__initcall_start 和 __initcall_end 就是section
initcall.init的头和尾。
4、 归纳之
1) __define_initcall(level,fn)的作用就是指示编译器把一些初始化函数
的指针(即:函数起始地址)按照顺序放置一个名为 .initcall.init 的
section中,这个section又被分成了7个子section,它们按顺序排列。
在内核初始化阶段,这些放置到这个section中的函数指针将供
do_initcalls() 按顺序依次调用,来完成相应初始化。
2) 函数指针放置到的子section由宏定义的level确定,对应level较小的
子section位于较前面。而位于同一个子section内的函数指针顺序不定,
将由编译器按照编译的顺序随机指定。
3) 因此,如果你希望某个初始化函数在内核初始化阶段就被调用,那么你
就应该使用宏__define_initcall(level,fn) 或 其7个衍生宏 把这个
函数fn的对应的指针放置到按照初始化的顺序放置到相关的 section 中。
同事,如果某个初始化函数fn_B需要依赖于另外一个初始化函数fn_A的
完成,那么你应该把fn_B放在比fn_A对应的level值较大的子section中,
这样,do_initcalls()将在fn_A之后调用fn_B。
***********************************************************************
如果你希望某个初始化函数在内核初始化阶段就被调用,那么你
就应该使用宏__define_initcall(level,fn) 或 其7个衍生宏来
把这个初始化函数fn的起始地址按照初始化的顺序放置到相关的
section 中。 内核初始化时的do_initcalls()将从这个section
中按顺序找到这些函数来执行。
如果你希望某个初始化函数在内核初始化阶段就被调用,那么你
就应该使用宏__define_initcall(level,fn) 或 其7个衍生宏来
把这个初始化函数fn的起始地址按照初始化的顺序放置到相关的
section 中。 内核初始化时的do_initcalls()将从这个section
中按顺序找到这些函数来执行。
*******************************************************************
板子上2个PCI网卡,2个CPU自带TSEC;
要求TSEC对应 eth0,eth1, PCI网卡对应 eth2,eth3
网络设备的注册是由register_netdev(struct net_device *dev)来实现的
网络设备驱动加载的时候,会调这个函数,到时就会确定网络设备的接口名称为 eth1或2或3...
在设备驱动代码里强行指定设备接口名?麻烦,改的地方多
忽然想到了驱动加载顺序,看了看 do_initcall,顺便网上查了查 __define_initcall(level,fn)
然后改了下/drivers/net/Makefile
- obj-$(CONFIG_GIANFAR) += gianfar_driver.o #wanghui modify netdev init sequence 2011-03-10
- #(tesc0,tesc1 ===> eth0,eth1 pci0,pci1 ==> eth2,eth3)
- obj-$(CONFIG_E1000) += e1000/
- obj-$(CONFIG_E1000E) += e1000e/
- obj-$(CONFIG_IBM_EMAC) += ibm_emac/
- obj-$(CONFIG_IBM_NEW_EMAC) += ibm_newemac/
- obj-$(CONFIG_IXGBE) += ixgbe/
- obj-$(CONFIG_IXGB) += ixgb/
- obj-$(CONFIG_IP1000) += ipg.o
- obj-$(CONFIG_CHELSIO_T1) += chelsio/
- obj-$(CONFIG_CHELSIO_T3) += cxgb3/
- obj-$(CONFIG_EHEA) += ehea/
- obj-$(CONFIG_CAN) += can/
- obj-$(CONFIG_BONDING) += bonding/
- obj-$(CONFIG_ATL1) += atl1/
- #obj-$(CONFIG_GIANFAR) += gianfar_driver.o # wanghui del 2011-03-10
- obj-$(CONFIG_TEHUTI) += tehuti.o
--------------------------- 华丽的分割线 -------------------------------------
前言
宏定义__define_initcall(level,fn)对于内核的初始化很重要,它指示
编译器在编译的时候,将一系列初始化函数的起始地址值按照一定的顺序
放在一个section中。在内核初始化阶段,do_initcalls() 将按顺序从该
section中以函数指针的形式取出这些函数的起始地址,来依次完成相应
的初始化。由于内核某些部分的初始化需要依赖于其他某些部分的初始化
的完成,因此这个顺序排列常常非常重要。
下面将从__define_initcall(level,fn) 宏定义的代码分析入手,依次
分析名称为initcall.init的section的结构,最后分析内核初始化函数
do_initcalls()是如何利用宏定义__define_initcall(level,fn)及其相
关的衍生的7个宏宏定义,来实现内核某些部分的顺序初始化的。
1、分析 __define_initcall(level,fn) 宏定义
1) 这个宏的定义位于inlclude\linux\init.h中:
#define __define_initcall(level,fn) \
static initcall_t __initcall_##fn \
__attribute__((__section__(".initcall" level ".init"))) \
= fn
其中 initcall_t 是一个函数指针类型:
typedef int (*initcall_t)(void);
而属性 __attribute__((__section__())) 则表示把对象放在一个这个
由括号中的名称所指代的section中。
所以这个宏定义的的含义是:1) 声明一个名称为__initcall_##fn的函数
指针(其中##表示替换连接,);2) 将这个函数指针初始化为fn;3) 编译
的时候需要把这个函数指针变量放置到名称为 ".initcall" level ".init"
的section中(比如level="1",代表这个section的名称是 ".initcall1.init")。
2) 举例:__define_initcall(6, pci_init)
上述宏调用的含义是:1) 声明一个函数指针__initcall_pic_init = pci_init;
且 2) 这个指针变量__initcall_pic_init 需要放置到名称为 .initcall6.init
的section中( 其实质就是将 这个函数pic_init的首地址放置到了这个
section中)。
3) 这个宏一般并不直接使用,而是被定义成下述其他更简单的7个衍生宏
这些衍生宏宏的定义也位于 inlclude\linux\Init.h 中:
#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)
因此通过宏 core_initcall() 来声明的函数指针,将放置到名称为
.initcall1.init的section中,而通过宏 postcore_initcall() 来
声明的函数指针,将放置到名称为.initcall2.init的section中,
依次类推。
4) 举例:device_initcall(pci_init)
解释同上 1-2)。
2、与初始化调用有关section--initcall.init被分成了7个子section
1) 它们依次是.initcall1.init、.initcall2.init、...、.initcall7.init
2) 按照先后顺序依次排列
3) 它们的定义在文件vmlinux.lds.S中
例如 对于i386+,在i386\kernel\vmlinux.lds.S中有:
__initcall_start = .;
.initcall.init : {
*(.initcall1.init)
*(.initcall2.init)
*(.initcall3.init)
*(.initcall4.init)
*(.initcall5.init)
*(.initcall6.init)
*(.initcall7.init)
}
__initcall_end = .;
而在makefile 中有
LDFLAGS_vmlinux += -T arch/$(ARCH)/kernel/vmlinux.lds.s
4) 在这7个section总的开始位置被标识为__initcall_start,
而在结尾被标识为__initcall_end。
3、 内核初始化函数do_basic_setup(): do_initcalls() 将从.initcall.init
中,也就是这7个section中依次取出所有的函数指针,并调用这些
函数指针所指向的函数,来完成内核的一些相关的初始化。
这个函数的定义位于init\main.c中:
extern initcall_t __initcall_start, __initcall_end;
static void __init do_initcalls(void)
{
initcall_t *call;
....
for (call = &__initcall_start; call < &__initcall_end; call++)
{
....
(*call)();
....
}
....
}
这些函数指针指向的函数就是通过宏__define_initcall(level,fn)
赋值的函数fn,他们调用的顺序就是放置在这些section中的顺序,
这个顺序很重要, 这就是这个宏__define_initcall(level,fn)的作用。
注意到,这里__initcall_start 和 __initcall_end 就是section
initcall.init的头和尾。
4、 归纳之
1) __define_initcall(level,fn)的作用就是指示编译器把一些初始化函数
的指针(即:函数起始地址)按照顺序放置一个名为 .initcall.init 的
section中,这个section又被分成了7个子section,它们按顺序排列。
在内核初始化阶段,这些放置到这个section中的函数指针将供
do_initcalls() 按顺序依次调用,来完成相应初始化。
2) 函数指针放置到的子section由宏定义的level确定,对应level较小的
子section位于较前面。而位于同一个子section内的函数指针顺序不定,
将由编译器按照编译的顺序随机指定。
3) 因此,如果你希望某个初始化函数在内核初始化阶段就被调用,那么你
就应该使用宏__define_initcall(level,fn) 或 其7个衍生宏 把这个
函数fn的对应的指针放置到按照初始化的顺序放置到相关的 section 中。
同事,如果某个初始化函数fn_B需要依赖于另外一个初始化函数fn_A的
完成,那么你应该把fn_B放在比fn_A对应的level值较大的子section中,
这样,do_initcalls()将在fn_A之后调用fn_B。
***********************************************************************
如果你希望某个初始化函数在内核初始化阶段就被调用,那么你
就应该使用宏__define_initcall(level,fn) 或 其7个衍生宏来
把这个初始化函数fn的起始地址按照初始化的顺序放置到相关的
section 中。 内核初始化时的do_initcalls()将从这个section
中按顺序找到这些函数来执行。
如果你希望某个初始化函数在内核初始化阶段就被调用,那么你
就应该使用宏__define_initcall(level,fn) 或 其7个衍生宏来
把这个初始化函数fn的起始地址按照初始化的顺序放置到相关的
section 中。 内核初始化时的do_initcalls()将从这个section
中按顺序找到这些函数来执行。
*******************************************************************
0
上一篇:[原创]Linux系统启动过程分析
下一篇:Linux开机启动过程详细分析
相关热门文章
- nginx default跳转
- MySQL新特性之mysql_config_ed...
- CDN 与 CDS 详解
- 提高SDN控制器拓扑发现性能...
- BLE-NRF51822教程4-串口BLE解...
- linux 常见服务端口
- xmanager 2.0 for linux配置
- 【ROOTFS搭建】busybox的httpd...
- openwrt中luci学习笔记
- 什么是shell
- linux dhcp peizhi roc
- 关于Unix文件的软链接
- 求教这个命令什么意思,我是新...
- sed -e "/grep/d" 是什么意思...
- 谁能够帮我解决LINUX 2.6 10...
给主人留下些什么吧!~~
评论热议
0 0
- do_initcall解析
- do_initcall()函数的说明
- 解析
- 解析
- 解析
- 解析
- 解析
- 解析
- 解析
- 解析
- 解析
- 解析[ ]
- 解析
- ffmpeg解析-----解析文件格式
- Xen解析 netfilter解析
- JSON解析,XML解析
- 硬解析 软解析
- 解析xml dom 解析
- while的典型应用例子
- Linux启动过程详解
- Codeforces 217D Bitonix' Patrol (dfs + bitset)
- linux内核启动过程学习总结
- [原创]Linux系统启动过程分析
- do_initcall解析
- Linux开机启动过程详细分析
- Linux的i2c驱动详解
- 我对linux理解之driver_register
- 2016.4.1 启动war包中遇到的问题和没有解决的问题
- Linux使用ssh远程登录服务器反应慢的解决办法
- I2C设备驱动注册
- 设备模型之kobject,kset及其关系
- 数据结构之线性表之顺序表和链表(通过数据结构角度深入理解arrayList和linkedList的特性)
原创粉丝点击
热门IT博客
热门问题
老师的惩罚
人脸识别
我在镇武司摸鱼那些年
重生之率土为王
我在大康的咸鱼生活
盘龙之生命进化
天生仙种
凡人之先天五行
春回大明朝
姑娘不必设防,我是瞎子
真借条假债务怎么办
老丈人借钱不还怎么办
欠货款被起诉怎么办
银行卡被诈骗了怎么办
信用卡欠款40万 怎么办
车牌掉了一个怎么办
工行信用卡盗刷怎么办
银行卡被钓鱼了怎么办
银行卡国外盗刷怎么办
宾馆碰到仙人跳怎么办
神像不供奉了怎么办
电脑出现紫屏怎么办
波音747停产后怎么办
期房烂尾了怎么办
吃螃蟹太寒怎么办
苹果cydia删了怎么办
cydia软件源空白怎么办
ios9.3.3耗电快怎么办
ios9.3.5耗电快怎么办
ifile闪退怎么办9.32
苹果越狱白苹果怎么办
破了沙弥戒怎么办
普通话证件丢了怎么办
宝宝睡觉蹬被子怎么办
小孩睡觉蹬被子怎么办
耳朵进蚂蚁了怎么办
如果蚂蚁进耳朵怎么办
耳朵进蟑螂了怎么办
被蟑螂钻进耳朵怎么办
蟑螂进到耳朵怎么办
蟑螂跑到耳朵里怎么办
耳朵里钻进蟑螂怎么办
卧室里进蟑螂怎么办
蟑螂爬进耳朵怎么办
水龙头漆掉了怎么办
淋浴头不出水怎么办
300英雄好卡怎么办
玩300英雄卡怎么办
五行金旺缺水怎么办
五行缺水缺金怎么办
八字算命克夫怎么办