Skip to content

Commit cb2aaf2

Browse files
authored
Linux内核编译与开发
1 parent 8d54d45 commit cb2aaf2

File tree

1 file changed

+327
-0
lines changed

1 file changed

+327
-0
lines changed

Diff for: 文章/Linux内核编译与开发.md

+327
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,327 @@
1+
## **1、Linux内核简介**
2+
3+
**linux kernel map:**
4+
5+
![img](https://pic3.zhimg.com/80/v2-6bacced1d82334518e1b3ef705d840d2_720w.webp)
6+
7+
**linux 系统体系结构:**
8+
9+
![img](https://pic1.zhimg.com/80/v2-7cc8b2e7574aed2dab109c6accdfa9a8_720w.webp)
10+
11+
**linux kernel体系结构:**
12+
arm有7种工作模式,x86也实现了4个不同级别RING0-RING3,RING0级别最高,
13+
这样linux用户代码运行在RING3下,内核运行在RING0,这样系统本身就得到了
14+
充分的保护
15+
16+
**用户空间(用户模式)转到内核空间(系统模式)方法:**
17+
·系统调用
18+
·硬件中断
19+
20+
**linux kernel 体系结构:**
21+
22+
![img](https://pic1.zhimg.com/80/v2-751fa81f9b734d40245468e86b84ce4c_720w.webp)
23+
24+
**虚拟文件系统VFS:**
25+
VFS(虚拟文件系统)隐藏各种文件系统的具体细节,为文件操作提供统一的接口
26+
27+
## **2、Linux内核源代码**
28+
29+
linux内核下载***[http://www.kernel.org](https://link.zhihu.com/?target=http%3A//www.kernel.org)\***
30+
**目录结构:**
31+
解压linux kernel tar后目录
32+
·arch:根据cpu体系结构不同而分的代码
33+
·block:部分块设备驱动程序
34+
·crypto:加密,压缩,CRC校验算法
35+
·documentation:内核文档
36+
·drivers:设备驱动程序
37+
·fs(虚拟文件系统vfs):文件系统
38+
·include:内核所需的头文件,(与平台无关的头文件在include/linux中)
39+
·lib:库文件代码(与平台相关的)
40+
·mm:实现内存管理,与硬件体系结构无关的(与硬件体系结构相关的在arch中)
41+
·net:网络协议的代码
42+
·samples:一些内核编程的范例
43+
·scripts:配置内核的脚本
44+
·security:SElinux的模块
45+
·sound:音频设备的驱动程序
46+
·usr:cpio命令实现,用于制作根文件系统的命令(文件系统与内核放到一块的命令)
47+
·virt:内核虚拟机
48+
49+
**linux DOC 编译生成:**
50+
51+
linux源根目录/Documentation/00-INDEX:目录索引
52+
linux源根目录/Documentation/HOWTO:指南
53+
·生成linux内核帮助文档:在linux源根目录(Documentation) 执行make htmldocs
54+
55+
ubuntu16下需要执行sudo apt-get install xmlto安装插件才可生成doc文档
56+
57+
后面开发中经常要改的是arch,drivers中的代码
58+
59+
## **3、Linux内核配置与编译**
60+
61+
**清理文件(在linux源码根目录):**
62+
·make clean:只清理所有产生的文件
63+
·make mrproper:清理所有产生的文件与config配置文件
64+
·make distclean:清理所有产生的文件与config配置文件,并且编辑过的与补丁文件
65+
66+
**配置(收集硬件信息如cpu型号,网卡等...):**
67+
·make config:基于文本模式的交互配置
68+
·make menuconfig:基于文本模式的菜单模式(推荐使用)
69+
·make oldconfig:使用已有的.config,但会询问新增的配置项
70+
·make xconfig:图形化的配置(需要安装图形化系统)
71+
**配置方法:**
72+
1)使用make menuconfig操作方法:
73+
1>按y:编译>连接>镜像文件
74+
2>按m:编译
75+
3>按n:什么都不做
76+
4>按"空格键":y,n轮换
77+
配置完并保存后会在linux源码根目录下生成一个.config文件
78+
注意:在ubuntu11上要执行apt-get install libncurses5-dev来安装支持包
79+
2)利用已有的配置文件模板(.config)
80+
1>linux源码根目录/arch/<cpu架构>/configs/<具体某一的CPU文件>,把里面对应的文件copy并改名为.config至linux源码根目录下
81+
2>利用当前运行已有的文件(要用ls /boot/ -a查看)把/boot/config-2.6.18-53.e15拷贝并改名为.config至linux源码根目录下执行以上操作就可以用make menuconfig在拷贝
82+
.config文件上面修改文件了
83+
84+
**编译内核:**
85+
1)make zImage
86+
2)make bzImage
87+
区别:在X86平台上,zimage只能用于小于512k的内核
88+
获取详细编译信息:make zimage V=1 或 make bzimage V=1
89+
编译好的内核在:arch/<cpu>/boot/目录下
90+
注意:在把.config配置文件cp到根目录编译内核前,必须进入make menuconfig并保存退出(否则生不了效)
91+
92+
**编译并安装模块:**
93+
94+
1)编译内核模块:make modules
95+
2)安装内核模块:make modules_install INSTALL_MOD_PATH=/lib/modules
96+
更换本机器内核:将编译好的内核模块从内核源码目录copy至/lib/modules下
97+
制作init ramdisk():输入执行命令mkinitrd initrd-2.6.39(任意) 2.6.39(可通过查询/lib/modules下的目录得到)
98+
注意:
99+
mkinitrd命令为redhat里面的,ubuntu的命令为:mkinitramfs -k /lib/modules/模块安装位置 -o initrd-2.6.39(任意) 2.6.39(可通过查询/lib/modules下的目录得到)
100+
如果ubuntu里面没有mkinitramfs命令可以用apt-get install initrd-tools进行安装
101+
102+
**安装内核模块:**
103+
1)手动
104+
1>cp linux根目录/arch/x86/boot/bzImage /boot/mylinux-2.6.39
105+
2>cp linux根目录/initrd-2.6.39 /boot/initrd-2.6.39
106+
最后修改/etc/grub.conf或/etc/lilo.conf文件
107+
2)自动
108+
1>make install:这个命令会自动完成上面的操作(查看当前内核版本:uname -r)
109+
\-----------------------------------------------------------------------------
110+
111+
## **4、linux内核模块开发**
112+
113+
描述:
114+
linux内核组件非常庞大,内核ximage并不包含某组件,而是在该组件需要被使用的时候,动态的添加到正在运行的内核中(也可以卸载),这种机制叫做“内核模块”的机制。内核模块通常通过使用makefile文件对模块进行编译
115+
116+
**模块安装与卸载:**
117+
1)加载:insmod hello.ko
118+
2)卸载:rmmod hello
119+
3)查看:lsmod
120+
4)加载(自动寻找模块依赖):modprobe hello
121+
modprobe会根据文件/lib/modules/version/modules.dep来查看要加载的模块,看它是否还依赖于其他模块,如果是,会先找到这些模块,把它们先加载到内核
122+
123+
**实例分析:**
124+
1)moduleDep/1(一个模块的编译)
125+
126+
```text
127+
#include <linux/module.h>
128+
#include <linux/init.h>
129+
130+
//模块入口函数
131+
//__init:表示代码段中的子段,里面的内容只运行一次并且回收内存.
132+
static int __init hello_init(void)
133+
{
134+
printk(KERN_EMERG "hello world!\n");
135+
return 0;
136+
}
137+
//模块卸载函数
138+
//__exit:
139+
static void __exit hello_exit(void)
140+
{
141+
printk(KERN_EMERG "hello exit!\n");
142+
}
143+
//内核符号导出 函数
144+
int add_integar(int a,int b)
145+
{
146+
return a+b;
147+
}
148+
int sub_integar(int a,int b)
149+
{
150+
return a-b;
151+
}
152+
153+
module_init(hello_init);
154+
module_exit(hello_exit);
155+
//函数导出
156+
EXPORT_SYMBOL(add_integar);
157+
EXPORT_SYMBOL(sub_integar);
158+
```
159+
160+
makefile:
161+
162+
```text
163+
#第一次执行KERNELRELEASE是空的,所以执行else里面的
164+
ifneq ($(KERNELRELEASE),)
165+
166+
obj-m :=hello.o
167+
168+
#else块
169+
else
170+
171+
KDIR:= /lib/modules/2.6.18-53.el5/build
172+
173+
all:
174+
#KDIR 依赖内核模块源代码路径(内核编译安装路径)
175+
#PWD 表示内核代码在哪(当前目录)
176+
#modules 编译的是模块
177+
make -C $(KDIR) M=$(PWD) modules
178+
179+
clean:
180+
rm -f *.ko *.o *.mod.o *.mod.c *.symvers *.order
181+
182+
endif
183+
```
184+
185+
2)moduleDep/2(两个模块的编译)
186+
187+
```text
188+
#include <linux/module.h>
189+
#include <linux/init.h>
190+
//模块可选信息
191+
MODULE_LICENSE("GPL");//许可证声明
192+
MODULE_AUTHOR("liyuan");//作者声明
193+
MODULE_DESCRIPTION("This module is a param example.");//模块描述
194+
MODULE_VERSION("V1.0");//模块别名
195+
MODULE_ALIAS("a simple module");//模块别名
196+
197+
//模块参数
198+
static char *name = "liyuan arg";
199+
static int age = 30;
200+
//S_IRUGO是参数权限,也可以用数字
201+
module_param(age,int,S_IRUGO);
202+
module_param(name,charp,S_IRUGO);
203+
204+
205+
//使用外部文件函数
206+
extern int add(int a,int b);
207+
208+
209+
//声明 外部内核符号 函数
210+
extern int add_integar(int a,int b);
211+
extern int sub_integar(int a,int b);
212+
213+
static int __init mains_init(void)
214+
{
215+
//多文件编译
216+
217+
printk(KERN_EMERG"param hi");
218+
int vle=add(1,2);
219+
printk(KERN_EMERG"add value:%d\n",vle);
220+
//模块参数
221+
222+
printk(KERN_EMERG" name : %s\n",name);
223+
printk(KERN_EMERG" age : %d\n",age);
224+
225+
//使用其他模块的函数(内核符号导出)
226+
int adds=add_integar(3,1);
227+
int subs=sub_integar(3,1);
228+
printk(KERN_EMERG" add_integar : %d\n",adds);
229+
printk(KERN_EMERG" sub_integar : %d\n",subs);
230+
return 0;
231+
}
232+
233+
static void __exit mains_exit(void)
234+
{
235+
printk("param exit!");
236+
}
237+
238+
module_init(mains_init);
239+
module_exit(mains_exit);
240+
```
241+
242+
add.c
243+
244+
```text
245+
int add(int a,int b)
246+
{
247+
return a+b;
248+
}
249+
```
250+
251+
makefile
252+
253+
```text
254+
ifneq ($(KERNELRELEASE),)
255+
#两个以上内核源文件 生成单独的内核模块名ma
256+
257+
#内核ma
258+
obj-m :=ma.o
259+
#下面的ma-objs前面必须和上面一样为ma
260+
ma-objs := mains.o add.o
261+
262+
else
263+
264+
KDIR:= /lib/modules/2.6.18-53.el5/build
265+
266+
all:
267+
make -C $(KDIR) M=$(PWD) modules
268+
clean:
269+
rm -f *.ko *.o *.mod.o *.mod.c *.symvers *.order
270+
271+
endif
272+
```
273+
274+
**运行带参模块**:insmod hello.ko name=yuan age=12
275+
内核符号导出(/proc/kallsyms记录了内核中所有导出的符号的名字与地址):
276+
一个内核模块的运行依赖另一个内核模块的函数实现,必须先运行第一个内核模块,这样就需要进行内核符号导出。
277+
278+
**注意:**
279+
错误信息:disagrees about version of symbol struct_module insmod:error inserting ...
280+
开发内核模块时会出现,内核模块不匹配的情况.是你当前运行的linux内核与编译连接所依赖的
281+
内核版本不匹配,解决方法:
282+
·使用modprobe --force-modversion强行插入
283+
·可使用uname -r进行查看当前运行的内核版本
284+
285+
**printk内核打印:**
286+
在<linux/kernel.h>中printk有8个优先级,按优先级递减的是:
287+
·KERN_EMERG 0
288+
用于紧急的消息,常常是那些崩溃的消息
289+
·KERN_ALERT 1
290+
需要立刻行动的消息
291+
·KERN_CRIT 2
292+
严重情况
293+
·KERN_ERR 3
294+
错误情况
295+
·KERN_WARNING(printk默认级别) 4
296+
有问题的警告
297+
·KERN_NOTICE 5
298+
正常情况,但是仍然值得注意
299+
·KERN_INFO 6
300+
信息消息
301+
·KERN_DEBUG 7
302+
用作调试消息
303+
304+
不管是哪个级别的都会在/var/log/messages里面打印出来(messages可以删除后,运行内核进行测试内核打印情况)控制台打印(优先级配置/proc/sys/kernel/printk)
305+
306+
6 4 1 7
307+
·Console_loglevel
308+
·Default_message_loglevel
309+
·Minimum_console_level
310+
·Default_console_loglevel
311+
312+
在vm+redhat安装2.6.39内核时出现的错误
313+
启动时报could not find filesystem '/dev/root'
314+
解决方法
315+
a.通过make menuconfig选中以下对应的选项
316+
General setup -->
317+
[*] enable deprecated sysfs features to support old userspace tools
318+
成功时下面那个也*了的
319+
b.修改.config文件
320+
修改.config文件中CONFIG_SYSFS_DEPRECATED_V2,将原本被注释掉的
321+
CONFIG_SYSFS_DEPRECATED_V2 改成CONFIG_SYSFS_DEPRECATED_V2=y
322+
323+
----
324+
325+
版权声明:本文为知乎博主「[极致Linux内核](https://www.zhihu.com/people/linuxwang-xian-sheng)」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
326+
327+
原文链接:https://zhuanlan.zhihu.com/p/549065925

0 commit comments

Comments
 (0)