分析
__define_initcall(level,fn,id) 宏定义 Y/ V' g4 s" l7 s, ?2 U
% K# d- u'
x& {* X
1) 这个宏的定义位于inlclude\linux\init.h中:
2 s+ d1 X% g' d& I5 X0 z
8 A/ ]; W# n1 x4 M4 I& q#define __define_initcall(level,fn,id) \
static initcall_t __initcall_##fn##id __used \
__attribute__((__section__(".initcall" level ".init"))) = fn
其中
initcall_t 是一个函数指针类型:
/ X/ J" N) ]; U5 b! ^5 ?3 ?' N! H# C9 |; O+
K6 k* k
typedef int (*initcall_t)(void);
4 F( Z! O: Y) |8 U9 l' b- j( J
+ ^: b+ u5 [ r" a8 q 而属性
__attribute__((__section__())) 则表示把对象放在一个这个& u r6 |) g) |6 Z* D1 u% ?
由括号中的名称所指代的section中。8 c' F7 `$ p/ S- B. k+ x: ~
: o% R6 H% k3
M* N- ~9 n: }4 \! E
所以这个宏定义的的含义是:
1) 声明一个名称为__initcall_##fn的函数指针(其中##表示替换连接,);
2) 将这个函数指针初始化为fn;
: ?+ Q% D3 M&
3) 编译8 r o6 `3 n! X$ P. Y的时候需要把这个函数指针变量放置到名称为 ".initcall" level ".init"- H! s' k' C4 x( u2 g
的section中(比如level="1",代表这个section的名称是 ".initcall1.init")。
, ~' `! v1 j7 P0 N3 s$ y& c" K3 {
: E- x6 H7 V" X2 j! v" i& Y 2)
举例:__define_initcall(6, pci_init,6)
5 Z1 E) r# b: P, O. s0 v
% n6 q+ e( ^& r- m
上述宏调用的含义是:
1) 声明一个函数指针__initcall_pic_init = pci_init;
2)
这个指针变量__initcall_pic_init 需要放置到名称为 .initcall6.init
/ Y0 ]9 k) O$ v- Q6 } K7 t! [1 v的section中( 其实质就是将这个函数pic_init的首地址放置到了这个; J$ o7 i e" }1 D* l# |section中)。
3)
这个宏一般并不直接使用,而是被定义成下述其他更简单的7个衍生宏
7 V) ?; F; d, Z0 m!( A$ i! R; f, x P
这些衍生宏宏的定义也位于 inlclude\linux\Init.h 中:; w7 _- D* r- P0 ]1 p4 f* Y; N/ A2 c* o
+ i3 y5 y4 s5 P: p2 x4 I! I
4 G8 c# S3 R" v5 ^0 q: J6 X#define pure_initcall(fn) __define_initcall("0",fn,0)
#define core_initcall(fn) __define_initcall("1",fn,1)
#define core_initcall_sync(fn) __define_initcall("1s",fn,1s)
#define postcore_initcall(fn) __define_initcall("2",fn,2)
#define postcore_initcall_sync(fn) __define_initcall("2s",fn,2s)
#define arch_initcall(fn) __define_initcall("3",fn,3)
#define arch_initcall_sync(fn) __define_initcall("3s",fn,3s)
#define subsys_initcall(fn) __define_initcall("4",fn,4)
#define subsys_initcall_sync(fn) __define_initcall("4s",fn,4s)
#define fs_initcall(fn) __define_initcall("5",fn,5)
#define fs_initcall_sync(fn) __define_initcall("5s",fn,5s)
#define rootfs_initcall(fn) __define_initcall("rootfs",fn,rootfs)
#define device_initcall(fn) __define_initcall("6",fn,6)
#define device_initcall_sync(fn) __define_initcall("6s",fn,6s)
#define late_initcall(fn) __define_initcall("7",fn,7)
#define late_initcall_sync(fn) __define_initcall("7s",fn,7s)
因此通过宏 core_initcall() 来声明的函数指针,将放置到名称为* z) Y7 Q4 c5 ^: ?6 A- A
.initcall1.init的section中,而通过宏 postcore_initcall() 来0 j- |& D* W0
D Z* Z$ V1 f
声明的函数指针,将放置到名称为.initcall2.init的section中,
依次类推。
, O; L2 c/ t0 e5 c+ L
& ]6 P4 V- L7 r9 U6 K
1 ?1 [ S6 H7 a( v; y' v; S6 Y
4) 举例:device_initcall(pci_init)) h4 s' N+ U7 @. d) ?* B" G
1 g, V% V# x(
Q# ?1 x
解释同上 1-2)。
9 R: N. h% ^+ W7 P' O3 U
5 v: q5 v3 l# x5 a/ u; p% P& C2、
与初始化调用有关section--initcall.init被分成了7个子section
$ V1 \8 I! v2 W7 D( q4 |
7 m2 {+ i: e2 \/ o1 a9 T 1)
它们依次是.initcall1.init、.initcall2.init、...、.initcall7.init3 C0 [8 N' ^9 S7 y"
m, G
9 ]: k5 D&
H6 P3 Y+ k' }
2) 按照先后顺序依次排列6
n$ w* w! e4 {, l) p; T
3)
它们的定义在文件vmlinux.lds.S中
4 |( R, z. c" p+ x ~( ?: y$ A* ~3 m1 K
\! A! {0 c' b2 K- \& Z 例如 在include/asm-generic/vmlinux.lds.h中有:0 {9 I+ b5 L( P& |3 y& p4 D% N: A
$ T8 p9 J; ?+
I7 f8 {* g
#define INITCALLS \
*(.initcallearly.init) \
VMLINUX_SYMBOL(__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)
#define INIT_CALLS \
VMLINUX_SYMBOL(__initcall_start) = .; \
INITCALLS \
VMLINUX_SYMBOL(__initcall_end) = .;
4) 在这7个section总的开始位置被标识为__initcall_start,
而在结尾被标识为__initcall_end。
% ?+ l* v5 a$ w4 f1 x, q: d, L* Z3 O/ R& J
x( J6 g) P* ]. s9 a3、
内核初始化函数do_basic_setup(): do_initcalls() 将从.initcall.init
中,也就是这7个section中依次取出所有的函数指针,并调用这些
3 O2 D; b, J4 M, Q3 w7 d4 D- I8 I/ |; d- {
函数指针所指向的函数,来完成内核的一些相关的初始化。
$ T( F4 G/ V o# H$ c& p1 f/ m7 K+ o# O; E1 ~0
d; x/ n
这个函数的定义位于init\main.c中:# ~$ B! s& F. |3 w1 I- a5 I* E
extern initcall_t __initcall_start[], __initcall_end[], __early_initcall_end[];
8 Y, U9 }, a1 J. y4 a$ d
static void __init do_initcalls(void)
{
initcall_t *call;
for (call = __early_initcall_end; call < __initcall_end; call++)
do_one_initcall(*call);
/* Make sure there is no pending stuff from the initcall sequence */
flush_scheduled_work();
}
这些函数指针指向的函数就是通过宏__define_initcall(level,fn,id)- x& c9 A8 c$ M& m" G9 K/ u! \/
s
赋值的函数fn,他们调用的顺序就是放置在这些section中的顺序,
这个顺序很重要,
这就是这个宏__define_initcall(level,fn,id)的作用。
% q; V }3 K% Q
注意到,这里__initcall_start 和 __initcall_end 就是section
% M# o$ H1 Q+ z6 k: U& R. x2 R0 Z% r$
x# g }
initcall.init的头和尾。2 C% ?; ]1 T* b
: P; K6 W2 P9
U3 }' f
4、 归纳之
1 [/ k4 F/ s7 `/ v, b! M/ P8 X8 m# T8 [ ~; h% E0 }+
G& y
1) __define_initcall(level,fn,id)的作用就是指示编译器把一些初始化函数/ ]& t9 F0 d;
N f7 v
的指针(即:函数起始地址)按照顺序放置一个名为 .initcall.init 的
section中,这个section又被分成了7个子section,它们按顺序排列。
9 k7 X, V& f( M' m3 @, M3 l, O o
在内核初始化阶段,这些放置到这个section中的函数指针将供
4 @% h$ n& r* K* J
do_initcalls() 按顺序依次调用,来完成相应初始化。
7 h5 ~ R J S6 O, C* T& b1 X* j5 _
6 w5 j- B6 S/ N+ q8 a8 M& T
5 o* U9 Q% ^8 T, S6 T3 S$ Z 2)
函数指针放置到的子section由宏定义的level确定,对应level较小的
子section位于较前面。而位于同一个子section内的函数指针顺序不定,
# M- `0 O& y* s! j! Y
将由编译器按照编译的顺序随机指定。
! E1 b* W$ j% @: Y u6 E! C
9 [" R ~, b+ Y0 p! e+ I' k) c
: _$ A# l+ o( o% M. J5 ^ 3)
因此,如果你希望某个初始化函数在内核初始化阶段就被调用,那么你, I7 G% [; N. E* G) h- J: _
就应该使用宏__define_initcall(level,fn,id) 或 其7个衍生宏 把这个
函数fn的对应的指针放置到按照初始化的顺序放置到相关的 section 中。
; ?" p% x- p8 C2 U4 o- j u; I, G Y5 R) a* y. o
同时,如果某个初始化函数fn_B需要依赖于另外一个初始化函数fn_A的
完成,那么你应该把fn_B放在比fn_A对应的level值较大的子section中,
7 D5 G A- q8 q% X8 I- `2 L& v0 d9 P5 p
这样,do_initcalls()将在fn_A之后调用fn_B。
阅读(1261) | 评论(0) | 转发(0) |