这篇文章将为大家详细讲解有关 Linux 内核编译与开发的示例分析,丸趣 TV 小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。
一.Linux 内核简介
linux kernel map:
linux 系统体系结构:
linux kernel 体系结构:
arm 有 7 种工作模式,x86 也实现了 4 个不同级别 RING0-RING3,RING0 级别 ***,
这样 linux 用户代码运行在 RING3 下,内核运行在 RING0, 这样系统本身就得到了
充分的保护
用户空间 (用户模式) 转到内核空间 (系统模式) 方法:
系统调用
硬件中断
linux kernel 体系结构:
虚拟文件系统 VFS:
VFS(虚拟文件系统)隐藏各种文件系统的具体细节,为文件操作提供统一的接口
二.Linux 内核源代码
linux 内核下载 www.kernel.org
目录结构:
解压 linux kernel tar 后目录
arch: 根据 cpu 体系结构不同而分的代码
block: 部分块设备驱动程序
crypto: 加密,压缩,CRC 校验算法
documentation: 内核文档
drivers: 设备驱动程序
fs(虚拟文件系统 vfs): 文件系统
include: 内核所需的头文件,(与平台无关的头文件在 include/linux 中)
lib: 库文件代码(与平台相关的)
mm: 实现内存管理,与硬件体系结构无关的(与硬件体系结构相关的在 arch 中)
net: 网络协议的代码
samples: 一些内核编程的范例
scripts: 配置内核的脚本
security:SElinux 的模块
sound: 音频设备的驱动程序
usr:cpio 命令实现,用于制作根文件系统的命令(文件系统与内核放到一块的命令)
virt: 内核虚拟机
linux DOC 编译生成:
linux 源根目录 /Documentation/00-INDEX: 目录索引
linux 源根目录 /Documentation/HOWTO: 指南
生成 linux 内核帮助文档: 在 linux 源根目录(Documentation) 执行 make htmldocs
ubuntu16 下需要执行 sudo apt-get install xmlto 安装插件才可生成 doc 文档
后面开发中经常要改的是 arch,drivers 中的代码
三.Linux 内核配置与编译
清理文件(在 linux 源码根目录):
make clean: 只清理所有产生的文件
make mrproper: 清理所有产生的文件与 config 配置文件
make distclean: 清理所有产生的文件与 config 配置文件,并且编辑过的与补丁文件
配置(收集硬件信息如 cpu 型号,网卡等 …):
make config: 基于文本模式的交互配置
make menuconfig: 基于文本模式的菜单模式(推荐使用)
make oldconfig: 使用已有的.config, 但会询问新增的配置项
make xconfig: 图形化的配置(需要安装图形化系统)
配置方法:
1)使用 make menuconfig 操作方法:
1 按 y: 编译 连接 镜像文件
2 按 m: 编译
3 按 n: 什么都不做
4 按 空格键 :y,n 轮换
配置完并保存后会在 linux 源码根目录下生成一个.config 文件
注意:在 ubuntu11 上要执行 apt-get install libncurses5-dev 来安装支持包
2)利用已有的配置文件模板(.config)
1 linux 源码根目录 /arch/ cpu 架构 /configs/ 具体某一的 CPU 文件,把里面对应的文件 copy 并改名为.config 至 linux 源码根目录下
2 利用当前运行已有的文件 (要用 ls /boot/ - a 查看) 把 /boot/config-2.6.18-53.e15 拷贝并改名为.config 至 linux 源码根目录下执行以上操作就可以用 make menuconfig 在拷贝
.config 文件上面修改文件了
编译内核:
1)make zImage
2)make bzImage
区别: 在 X86 平台上,zimage 只能用于小于 512k 的内核
获取详细编译信息:make zimage V=1 或 make bzimage V=1
编译好的内核在:arch/ cpu /boot/ 目录下
注意:在把.config 配置文件 cp 到根目录编译内核前,必须进入 make menuconfig 并保存退出(否则生不了效)
编译并安装模块:
1)编译内核模块:make modules
2)安装内核模块:make modules_install INSTALL_MOD_PATH=/lib/modules
更换本机器内核: 将编译好的内核模块从内核源码目录 copy 至 /lib/modules 下
制作 init ramdisk(): 输入执行命令 mkinitrd initrd-2.6.39(任意) 2.6.39(可通过查询 /lib/modules 下的目录得到)
注意:
mkinitrd 命令为 redhat 里面的,ubuntu 的命令为:mkinitramfs -k /lib/modules/ 模块安装位置 -o initrd-2.6.39(任意) 2.6.39(可通过查询 /lib/modules 下的目录得到)
如果 ubuntu 里面没有 mkinitramfs 命令可以用 apt-get install initrd-tools 进行安装
安装内核模块:
1)手动
1 cp linux 根目录 /arch/x86/boot/bzImage /boot/mylinux-2.6.39
2 cp linux 根目录 /initrd-2.6.39 /boot/initrd-2.6.39
*** 修改 /etc/grub.conf 或 /etc/lilo.conf 文件
2)自动
1 make install: 这个命令会自动完成上面的操作(查看当前内核版本:uname -r)
—————————————————————————–
四.linux 内核模块开发
描述:
linux 内核组件非常庞大,内核 ximage 并不包含某组件,而是在该组件需要被使用的时候,动态的添加到正在运行的内核中(也可以卸载),这种机制叫做“内核模块”的机制。内核模块通常通过使用 makefile 文件对模块进行编译
模块安装与卸载:
1)加载:insmod hello.ko
2)卸载:rmmod hello
3)查看:lsmod
4)加载(自动寻找模块依赖):modprobe hello
modprobe 会根据文件 /lib/modules/version/modules.dep 来查看要加载的模块,看它是否还依赖于其他模块,如果是, 会先找到这些模块,把它们先加载到内核
实例分析:
1)moduleDep/1(一个模块的编译)
1 #include linux/module.h 2 #include linux/init.h 3 4 // 模块入口函数 5 //__init: 表示代码段中的子段, 里面的内容只运行一次并且回收内存. 6 static int __init hello_init(void) 7 { 8 printk(KERN_EMERG hello world!\n 9 return 0;10 }11 // 模块卸载函数 12 //__exit:13 static void __exit hello_exit(void)14 {15 printk(KERN_EMERG hello exit!\n 16 }17 // 内核符号导出 函数 18 int add_integar(int a,int b)19 {20 return a+b; 21 }22 int sub_integar(int a,int b)23 {24 return a-b; 25 }26 27 module_init(hello_init);28 module_exit(hello_exit);29 // 函数导出 30 EXPORT_SYMBOL(add_integar);31 EXPORT_SYMBOL(sub_integar);
makefile:
#*** 次执行 KERNELRELEASE 是空的, 所以执行 else 里面的 ifneq ($(KERNELRELEASE),) obj-m :=hello.o #else 块 elseKDIR:= /lib/modules/2.6.18-53.el5/build all: #KDIR 依赖内核模块源代码路径(内核编译安装路径) #PWD 表示内核代码在哪(当前目录) #modules 编译的是模块 make -C $(KDIR) M=$(PWD) modules clean: rm -f *.ko *.o *.mod.o *.mod.c *.symvers *.order endif
2)moduleDep/2(两个模块的编译)
#include linux/module.h #include linux/init.h // 模块可选信息 MODULE_LICENSE(GPL // 许可证声明 MODULE_AUTHOR( liyuan // 作者声明 MODULE_DESCRIPTION( This module is a param example. // 模块描述 MODULE_VERSION( V1.0 // 模块别名 MODULE_ALIAS( a simple module // 模块别名 // 模块参数 static char *name = liyuan arg static int age = 30; //S_IRUGO 是参数权限,也可以用数字 module_param(age,int,S_IRUGO); module_param(name,charp,S_IRUGO); // 使用外部文件函数 extern int add(int a,int b); // 声明 外部内核符号 函数 extern int add_integar(int a,int b); extern int sub_integar(int a,int b); static int __init mains_init(void) { // 多文件编译 printk(KERN_EMERG param hi int vle=add(1,2); printk(KERN_EMERG add value:%d\n ,vle); // 模块参数 printk(KERN_EMERG name : %s\n ,name); printk(KERN_EMERG age : %d\n ,age); // 使用其他模块的函数(内核符号导出) int adds=add_integar(3,1); int subs=sub_integar(3,1); printk(KERN_EMERG add_integar : %d\n ,adds); printk(KERN_EMERG sub_integar : %d\n ,subs); return 0; } static void __exit mains_exit(void) { printk( param exit! } module_init(mains_init);52 module_exit(mains_exit);
add.c
int add(int a,int b) { return a+b; }
makefile
ifneq ($(KERNELRELEASE),) #两个以上内核源文件 生成单独的内核模块名 ma #内核 ma obj-m :=ma.o # 下面的 ma-objs 前面必须和上面一样为 ma ma-objs := mains.o add.oelseKDIR:= /lib/modules/2.6.18-53.el5/build all: make -C $(KDIR) M=$(PWD) modules clean: rm -f *.ko *.o *.mod.o *.mod.c *.symvers *.order endif
运行带参模块:insmod hello.ko name=yuan age=12
内核符号导出(/proc/kallsyms 记录了内核中所有导出的符号的名字与地址):
一个内核模块的运行依赖另一个内核模块的函数实现,必须先运行 *** 个内核模块,这样就需要进行内核符号导出。
注意:
错误信息:disagrees about version of symbol struct_module insmod:error inserting …
开发内核模块时会出现,内核模块不匹配的情况. 是你当前运行的 linux 内核与编译连接所依赖的
内核版本不匹配,解决方法:
使用 modprobe –force-modversion 强行插入
可使用 uname - r 进行查看当前运行的内核版本
printk 内核打印:
在 linux/kernel.h 中 printk 有 8 个优先级,按优先级递减的是:
KERN_EMERG 0
用于紧急的消息,常常是那些崩溃的消息
KERN_ALERT 1
需要立刻行动的消息
KERN_CRIT 2
严重情况
KERN_ERR 3
错误情况
KERN_WARNING(printk 默认级别) 4
有问题的警告
KERN_NOTICE 5
正常情况,但是仍然值得注意
KERN_INFO 6
信息消息
KERN_DEBUG 7
用作调试消息
不管是哪个级别的都会在 /var/log/messages 里面打印出来 (messages 可以删除后,运行内核进行测试内核打印情况) 控制台打印(优先级配置 /proc/sys/kernel/printk)
6 4 1 7
Console_loglevel
Default_message_loglevel
Minimum_console_level
Default_console_loglevel
在 vm+redhat 安装 2.6.39 内核时出现的错误
启动时报 could not find filesystem /dev/root
解决方法
a. 通过 make menuconfig 选中以下对应的选项
General setup —
[*] enable deprecated sysfs features to support old userspace tools
成功时下面那个也 * 了的
b. 修改.config 文件
修改.config 文件中 CONFIG_SYSFS_DEPRECATED_V2,将原本被注释掉的
CONFIG_SYSFS_DEPRECATED_V2 改成 CONFIG_SYSFS_DEPRECATED_V2=y
关于“Linux 内核编译与开发的示例分析”这篇文章就分享到这里了,希望以上内容可以对大家有一定的帮助,使各位可以学到更多知识,如果觉得文章不错,请把它分享出去让更多的人看到。