1.请推荐一些好的
Linux内核参考书?
8 }% [/ C) j' h
a.《LinuxDevice
Drivers,SecondEdition》,有中文译本
; ]' a1 F: {4 Ab.《UnderstandingtheLinuxKernel,2ndEdition》
1 S6 o3 B4 q, |$ t
c.《Linux内核
源代码情景分析》,分上下两册
2 g. t- }/ g7 z9 `d.《边干边学-Linux内核指导》
: \7 Z% R( O0 p/ M" g6 {- `. S- | y
, y7 Z" r5 x& ^9 G1 W; ~$ s% a+ `5 ]0 t f, D# [4 s
2.内核源代码问题
% x, H; E- d. F2.1如何得到某一版本的Linux内核源代码?
% K1 P, M3 o* `3 a9 O3 a5 `+ B
a.http://www.kernel.org或ftp://ftp.kernel.org,这是Linux内核版本的发布
Q6 R* c& S6 q) m
网站。
! c1 j0 @3 f1 vb.很多镜像或本地网站也提供部分Linux内核版本的
下载,多用ftp搜索引擎。
3 B/ e* Z* E& {0 \c.一般的Linux发行版如
Redhat之
类会随盘提供相应的内核源代码,不过这个源代
. P- R) m! e; X# \0 F; `% q. i码往往是改动过的,与同版本的
标准Linux内核可能有些差异。
: B/ R9 m) }; Z' `+ J8 u
3 S3 m1 r y& u, A2 L6 A" r. T' A2.2请推荐一些源代码查看工具?
, s: M" H8 f5 B" Y! M5 Za.
Windows系统可以用SourceInsight,Linux系统可以用SourceNavigator。
A/ ~2 Q9 u Q2 v. U$ X# U
b.vim或emacs
编辑器,配合cscope、ctags、etags等交叉索引工具。
7 K! T p0 J% Sc.vim或emacs编辑器,配合grep、egrep等文本搜索工具,不过最好要对源代码目
4 c7 T* [8 ^+ D6 O T C0 V
录
结构有所熟悉
1 O- x3 m; Z [* {/ `$ L
d.LXR,以网页的形式通过
浏览器浏览,安装复杂,可从http://lxr.linux.no/下
+ C; Z/ L; d+ \: F8 k* d
载该工具也可以直接访问http://lxr.linux.no/source/在线阅读Linux内核源
& E. p) _" E+ v/ U3 p
代码。
$ O6 z. Y; n& g* \( x& Y& r' Z1 l' q+ A' T4 }3 Y4 @1 b
2.3xx结构的定义在哪个内核源文件中?
$ O9 l- I- }" |a.请使用源码查看工具,见问题2.2。
6 N+ t1 P( `9 N! X* ab.如果用grep等文本搜索工具,主要在include/linux和include/asm两个目录下
& ~; [$ y; \( q* |7 Q0 ?搜索。
8 B1 F: c8 Y; `& k9 c6 e
/ s. U% E6 g2 G0 Z! D4 D/ I2.4volatile和__volatile__是什么意思?
% C& w$ I6 ~! K5 va.volatile是C语言定义的关键字,gcc为了需要又定义了__volatile__,它和
* g* @7 @) \$ L" z( t ~
volatile表达的是同一意思。
+ V; m9 {5 O+ r3 b. Ib.volatile的本意是"易变的",由于访问寄存器的
速度快于访存,所以编译器一般
- A, a' R- f/ O- G( Q- v9 l7 ~0 a
都会作优化以减少访存。如果变量加上volatile修饰,则编译器就不会对此变量
% y! Y9 m. l& G% F) i6 W; k2 [- p的读写操作进行优化,即不通过寄存器缓冲而直接访存。
5 X( W* o6 c- P3 B$ T6 g5 N; Uc.__asm____volatile__一起指示编译器不要改动优化后面的汇编语句。
4 }. c7 }0 z0 }* v, E' R6 @) T/ H. p# r* s/ D3 K h! C' J
2.5do{...}while(0)是什么意思?
+ E7 v+ M6 |- S; o! d3 {% p" s
a.主要是为了避免宏在不同情况展开可能会出现的一些错误。
# y( C1 Z. w* Z8 I
b.在http://www.kernelnewbies.org/faq/上有详细介绍。
; p9 L4 L+ p8 S: z1 N w9 l( Z
. x4 K" i. t5 Z7 Z2.6list_entry的定义是怎么回事?
# m3 ?$ p. O3 F [7 \" W3 @6 _a.list_entry的定义在内核源文件include/linux/list.h中:
" J# v7 y1 P. l. O3 A! l V+ t#definelist_entry(ptr,
type,member)\
( r& v% F; `0 V- m3 m+ h) u; n
((type*)((char*)(ptr)-(unsignedlong)(%26((type*)0)-%26gt;member)))
1 u1 t) l0 U9 e2 x7 ?
b.其
功能是根据list_head型指针ptr换算成其宿主结构的起始地址,该宿主结构是
! D9 U: t1 }4 n) y) v# w& r' C* N3 q
type型的,而ptr在其宿主结构中定义为member成员。如下图:
_, `3 Y+ {! s4 H* R; \
. |& _6 E# J3 H u, S; H% [% breq--%26gt;│type型
对象起始地址
1 e% j9 o- R* W" }│
! \& ?6 }( T: p: b3 R' n0 u! {
│......
. |# d8 p$ ~8 d2 L' ^ptr--%26gt;│ptr指针所指的member成员地址
9 R& F& s! h; b( @
│
. g5 _' a; n% h
│......
; ~' y9 g ]* a$ s- x+ v+ m w- N7 d( E$ H4 _$ r' q
ptr指向图中所示的位置,通过(unsignedlong)(%26((type*)0)-%26gt;member)得到ptr
* a8 C4 e- j9 S! @: i% R3 H# [" k
和req之间的差值,ptr减去这个差值就得到了type型宿主结构的指针req,返回
' a! X7 ]/ m0 y( N* |# s! a ]+ e4 }
类型为(type*)。
/ v. O( I( b7 e8 }- J3 U: i
2 e. ]9 U- U0 h; Z2 x( D/ }, f1 O* y, ^+ W* I3 ]6 \# {* ?
3.模块
编程问题
" P6 G" \" o, W: ]( ]' ^$ _( v
3.1模块编程需要注意什么?
* {: Z- {& q0 | U$ ~
a.在gcc编译选项中增加-c
/ V1 [; Z5 }/ D: ~0 D) N7 vb.在gcc编译选项中定义两个宏:-DMODULE-D__KERENL__
3 D; A3 S; w' L5 ?9 f0 M" k% f L6 Z
或直接在源文件中定义这两个宏:
m1 R9 _2 ?- b8 I. i- @$ T! j0 k#defineMODULE
p* W% C9 f }. m! t! s. j
#define__KERNEL__
- e1 U% l$ {. Y
c.在源文件中包括module.h文件:
( y' B s) n2 s# ^4 b7 a3 d1 ~
#include%26lt;linux/module.h%26gt;
) q E' M5 J" j J1 |
d.如果要用inline功能,需要在gcc编译选项中增加-O2
% E/ ^6 g0 F, w, g
& O/ m8 o' K x6 B: K* d9 C2 n
3.2为什么insmod一个模块时显示版本不匹配?
: i3 D. `5 c9 V2 h6 k假定你现在运行的内核的源码目录绝对路径是MyKernelSrcPath,在gcc编译时
" C1 g; n& O8 E% R) w5 k5 ? Z增加选项:
$ p* d) O& p3 D3 ~4 ^" X# z1 R) K
-I$MyKernelSrcPath/include
1 @! v0 L+ D- A, k3 K8 ^0 l
" P% B3 J. N: Y) K
3.3为什么出现UnresolvedSymbol?
% y! m0 h* @4 o. Ca.首先查看文件/proc/ksyms,看内核有没有输出这个符号,不同的内核版本如
3 D5 U" s7 f# B
2.2和2.4输出的符号会有些变化。
% D# \6 \' \. b
b.如果内核输出的符号带有版本控制
信息如符号printk_R12345678,则性质同
" `5 d" h8 t' p; }; H2 ?3 j, f问题3.2。
9 F7 g3 T5 R( Z' {) K; i
( o" \ ]+ d: Z6 h2 O" F
3.4为什么出现nolicense错误?
# s9 S% b) Y& r) z在源文件加入下面一行:
; I0 L9 o4 }" h% E" NMODULE_LICENSE("GPL");
5 ]2 y- ~* N# ~& L( K. ?- D2 y+ ]
" W6 i" W# F5 k' i, v
3.5为什么看不到用printk打印的信息?
7 z( s3 A! D* {# h
a.打印消息受级别的限制,消息级别可以通过printk设置,如:
2 G% R% P/ _1 s& \4 O9 h' \* X+ Z& Wprintk("%26lt;n%26gt;something");/*其中0%26lt;=n%26lt;=7*/
" h; X8 q, a3 ~% h- O假设控制台的消息级别为m,当n%26lt;m时消息打印到控制台,否则不打印。
# D/ `+ p$ m2 B- s9 F这样一方面可以提高要打印消息本身的级别(数字越小级别越高),
6 S% j/ X$ L% a V6 E+ r5 W
另一方面可以改变控制台的消息级别(可从1到8),如改为8可用以下命令:
4 e: Z. W3 A' j1 G: K0 l
#echo"8"%26gt;/proc/sys/kernel/printk
# [& e/ Z* Y# V: Q$ M+ y
b.用dmesg命令看。
8 v; }% A" b! l# y9 Pc.当系统运行klogd和syslogd时,内核消息就会由klogd分发到syslogd,
$ v, j, }+ Q' y5 t$ Q Z/ H" isyslogd会根据配置文件/etc/syslog.conf作相应处理,具体可以查看syslogd
9 i% H0 j9 h6 o0 ]0 N+ y
和syslog.conf的man页。
5 o9 S% F# @# u$ G! _# F' w# a& R% x, U
$ G( q' J3 W1 }
4.内核开发问题
$ z- p/ z$ O& ?% Y
4.1怎么制作、使用patch文件?
. r7 q0 [( ~* s
patch文件是由diff命令生成的,使用patch文件用patch命令,具体可查看diff和
7 r' @6 v0 E% P$ A4 ?9 g% {8 v/ Opatch的man页和info。
- x/ y) b: n' A7 J) L3 S% g
, p/ @# d4 w" C0 j+ v4.2在内核中可以使用系统调用吗?
- h" X% I( ]3 e2 }: f6 L1 ca.可以。内核源代码中就有使用系统调用的例子,如open()、execve()等。
; I( i) o$ x8 k0 b" s( B; yb.在内核中使用系统调用必须要在源文件中包括以下两行:
$ T- I# @- J; H9 c#define__KERNEL_SYSCALLS__
+ w9 N7 r. H. Z8 B3 ^+ L#include%26lt;linux/unistd.h%26gt;
! E; F, r' g8 S# A3 M
c.内核中使用系统调用的相关定义可查看文件include/asm/unistd.h。
# e/ ], I' O+ ~
如果要用的系统调用该文件中没有定义,可以按照其格式自行添加。
8 L3 e/ |0 C4 K( \7 \6 o
1 e/ q7 f% o0 N: ?7 h) j3 e G
4.3在内核中怎么打开并操作一个文件?
1 l9 A+ n1 L+ D+ `2 o5 i5 @
a.直接用open()、read()等系统调用,见问题4.2。
% w6 _' e, Z5 t
b.用filp_open()函数打开文件,得到structfile*的指针fp。
& N i2 V! o ^& n" Q0 j使用指针fp进行相应操作,如读文件可以用fp-%26gt;f_ops-%26gt;read。
4 _2 u7 l5 j3 q5 ~# t2 t( h
最后用filp_close()函数关闭文件。
/ w( ~5 i( `" e' B% a- Q0 |. ofilp_open()、filp_close()函数在fs/open.c定义,在include/linux/fs.h中
! p! M8 D. @! R+ \& V% u声明。
( U8 [( l% ~/ O: Z
c.自己写包装函数,可参照文件fs/exec.c中的open_exec()和kernel_read()函数。
D) }+ s2 B4 q
在http://www.linuxforum.net/forum/showflat.php?Cat=%26Board=linuxK
q" Q% [, M# n* e5 J+ b%26Number=363455%26page=%26view=%26sb=%26o=%26vc=1上有些代码可以参照。
! {" n7 j1 t# P( w# d
* ?1 F7 t' o5 D
4.4在内核中读写文件时为什么会出现EFAULT错误?
) g/ U4 J8 o7 l! f! R4 O0 C1 @
a.内核文件系统提供的read()和write()之类的函数,期望是对用户态
程序服务的,
- i9 O) ? P1 V$ P
所以它会验证读写缓冲区不超过用户
空间的上限即0xC0000000。但现在内核中
9 U: e( q6 H/ W要读写文件,缓冲区在内核中即地址会超过0xC0000000。
' b0 {& ^. N9 V5 ]1 c# M- k! p- l
b.在读写文件前先得到当前fs:mm_segment_told_fs=get_fs();
# s* o+ m# @' r! F) ~并设置当前fs为内核fs:set_fs(KERNEL_DS);
% o4 n+ k0 i: ?5 g在读写文件后再恢复原先fs:set_fs(old_fs);
! J: j" \4 I" P3 v( {set_fs()、get_fs()等相关宏在文件include/asm/uaccess.h中定义。
9 j/ l- i/ t C2 Q7 ~
: m- n8 a E9 p
" a4 q$ b# Q- t' }
5.其它
4 O$ p7 E# Q; C* I
) x' e: D: O3 p. {, }5 ?0 D5.1请问xx命令、xx库的源码是哪个文件?
" y1 F" ^0 J8 Z/ ^a.一个系统除了内核以外,还需要有
shell、gcc等一系列工具和命令以及C库等一
( W( C% z; J) \5 C; b4 B
系列库,这些作为应用程序其源代码都不在内核中,需要另外下载相应的源代码。
( k% V2 g2 i4 b! c# @! ?b.对于Redhat系统,可以用rqm-qf命令来查找某一命令所在的
软件包,然后再找
" Z& {9 c) j s/ q+ _9 B
相应的源代码包安装。