Linux内核配置编译以及模块开发
Posted _WILLPOWER_
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Linux内核配置编译以及模块开发相关的知识,希望对你有一定的参考价值。
文章目录
- Linux内核简介
- Linux内核模块开发
- 总结-对比应用
- 编译问题
- **编译内核错误:Can't use 'defined(@array)' (Maybe you should just omit the defined()?) at kernel/timeconst.p...**
- **linux/compiler-gcc9.h:没有那个文件或目录**
- **编译内核出现:cc1: error: code model kernel does not support PIC mode**
- 'make menuconfig' requires the ncurses libraries
- /bin/sh: 1: flex: not found
- /bin/sh: 1: bison: not found
- fatal error: openssl/opensslv.h: 没有那个文件或目录
- make[1]: *** 没有规则可制作目标“debian/canonical-certs.pem”
Linux内核简介
Linux体系结构
Linux由用户空间和内核空间组成
现代CPU通常实现了不同的工作模式,以ARM为例,实现了7种工作模式:
用户模式( usr)、快速中断(fiq)、外部中断(irq)、管理模式( svc)、数据访问中止(abt)、系统模式(sys)、未定义指令异常(und)
X86也实现了4个不同的级别:Ring0-Ring3。Ring0下,可以执行特权指令,可以访问IO设备等,在Ring3则有很多限制。Linux系统利用了CPU的这一特性,使用了其中的两级来分别运行Linux内核与应用程序,这样使操作系统本身得到充分的保护。例如:如果使用X86,用户代码运行在Ring3,内核代码运行在Ring0。
内核空间与用户空间是程序执行的两种不同状态,通过系统调用和硬件中断能够完成从用户空间到内核空间的转移。
Linux内核架构
虚拟文件系统
VFS(虚拟文件系统)隐藏各种文件系统的具体细节,为文件操作提供统一的接口。
内核源码结构
arch目录
arch是architecture的缩写。内核所支持的每种CPU体系,在该目录下都有对应的子目录。每个CPU的子目录,又进一步分解为boot,mm,kernel等子目录,分别包含控制系统引导,内存管理,系统调用等。
documentation
内核的文档
drivers目录
设备驱动程序
include目录
内核所需要的头文件。与平台无关的头文件在include/linux子目录下面,与平台有关的头文件则放在对应的子目录中。
fs目录
存放各种文件系统的实现代码。每个子目录对应一种文件系统的实现,公用的源程序用于实现虚拟文件系统vfs.
net目录
网络协议的实现代码
Linux内核配置与编译
-
配置内核
- 为什么需要配置内核
- 如何配置内核
- make config
- make menuconfig
- 内核配置结果
-
编译内核
- make zlmage
- make bzlmage
-
编译内核模块
- make modules
- make modules_install
-
安装内核
-
清理内核
- make clean
- make distclean
为什么要配置内核?
- 硬件的需求
- 软件的需求
选出需要的,去掉不需要的
内核配置方法
make config: 基于文本模式的交互式配置
make menuconfig: 基于文本模式的菜单型配置(更直观高效)
取值方式
<*> 内核镜像直接带有该功能
<m> 内核模块方式导入该功能,模块导入的功能不是随时都要用的,需要的时候才会加入到内存中运行,不需要的时候不加载。
<> 不选择该功能
配置后的结果保存在.config
中
要使用内核配置文件简化配置
编译内核
区别:在X86平台,zImage只能用于小于512K的内核
如需获取详细编译信息,可使用:
make zImage V=1
make bzImage V=1
编译好的内核位干arch//boot/目录下
过程
获取源码
官方网站链接
在这里面选择你想要的源码
在文件目录中
然后将本机的配置文件复制过来,节省自己配置的时间
cp /boot/config-5.11.0-27-generic ./.config
然后使用make menuconfig
然后使用tab键
EXIT即可
然后使用
make bzImage
完成后使用
make modules
然后使用
make module_install
然后制作init ramdisk
在centos中使用mkinitrd
方法:
mkinitrd initrd-\\$version \\$version
例:
mkinitrd initrd-2.6.39 2.6.39
*$version可以通过查询/lib/modules下的目录得到
在ubuntu中使用mkinitramfs
mkinitramfs 5.9.0 -o /boot/initrd-5.9.0
cp arch/x86/boot/bzImage /boot/vmlinuz-5.9.0
将System.map复制至/boot下:
cp System.map /boot/System.map-5.9.0
如果开机没有内核选择界面
Ubuntu 系统开机 Grub 界面的开启和关闭
在 /etc/default/grub 里面找到
GRUB_TIMEOUT_STYLE=hidden
GRUB_HIDDEN_TIMEOUT=0
两个注释掉
然后使用
sudo update-grub2
重启选择advance选项,选择最新的内核
如果开机还是没有,那就再开机进入登录管理器后,选择poweroff,重启即可
然后使用unamr -r
确定当前使用的内核版本
Linux内核模块开发
- 什么是内核模块
- 内核模块设计
- 主体部分设计
- 编译内核模块
- 安装/卸载内核模块
- 可选项使用
- 模块申明
- 模块参数
- 符号输出
- 内核打印
什么是内核模块
Linux内核的整体结构非常庞大,其包含的组件也非常多,如何使用这些组件呢?
方法1:把所有的组件都编译进内核文件,即: zImage或bzImage,但这样会导致一个问题:内核文件过大,占用内存过多.
有没有一种机制能让内核文件本身并不包含某组件,而是在该组件需要被使用的时候,动态地添加到正在运行的内核中呢?
内核模块设计
helloworld.c
#include <linux/module.h>
#include <linux/init.h>
MODULE_LICENSE("GPL");//必须要加这句,不加报错
static int hello_init(void)
{
printk(KERN_WARNING "Hello, world!\\n");
return 0;
}
static void hello_exit(void)
{
printk(KERN_INFO "Goodbye, world\\n");
}
module_init(hello_init);
module_exit(hello_exit);
Makefile
obj-m := hello.o
KDIR := /lib/modules/`uname -r`/build
all:
make -C $(KDIR) M=$(PWD) modules
如果有多个文件要生成内核模块
Makefile
obj-m := test.o
test-objs := hello.o hello1.o
KDIR := /lib/modules/`uname -r`/build
all:
make -C $(KDIR) M=$(PWD) modules
安装与卸载
安装 insmod
insmod hello.ko
卸载 rmmod
rmmod hello
查看 lsmod
模块可选信息
- 模块申明
- 模块参数
- 符号输出
模块申明
- MODULE_LICENSE(“遵守的协议”)
- MODULE_AUTHOR(“作者”)
- MODULE_DESCRIPTION(“模块功能描述”)
- MODULE_VERSION(“V1.0版本”)
模块参数
- 内核模块可以通过命令行输入参数
通过宏module param
指定保存模块参数的变量。模块参数用于在加载模块时传递参数给模块。
module_param(name ,type,perm)
name:变量的名称
type:变量类型,bool:布尔型i nt:整型 charp:字符串型
perm是访问权限。S_IRUGO:读权限 S_IWUSR:写权限
例;
int a = 3;
char *st;
module_param(a , int , S_IRUGO);
module_param(st,charp, S_IRUGO);
#include <linux/module.h>
#include <linux/init.h>
MODULE_LICENSE("willpower");
int a = 9;
module_param(a, int, S_IRUGO);
static int hello_init(void)
{
printk("a is %d\\n", a);
printk(KERN_WARNING "Hello, world!\\n");
return 0;
}
static void hello_exit(void)
{
printk(KERN_INFO "Goodbye, world\\n");
}
module_init(hello_init);
module_exit(hello_exit);
符号导出
为什么到导出符号?
申明此函数是可以其他文件来使用的
内核符号的导出使用宏
EXPORT_SYMBOL(符号名)
EXPORT_SYMBOL_GPL(符号名)
说明:
其中EXPORT_SYMBOL_GPL智能包含用于包含GPL许可证的模块
总结-对比应用
对比应用程序,内核模块具有以下不同:
- 应用程序是从头(main)到尾执行任务,执
行结束后从内存中消失。 - 内核模块的初始化函数结束时,模块仍然
存在于内核中,直到卸载函数被调用,模块才从内核中消失。
内核打印
printk是内核中出现最频繁的函数之一,通过将printk与printf对比,将有助于理解。
相同点:
- 打印信息
不同点: - printk在内核中使用,printf在应用程序中使用
- printk允许根据严重程度,通过附加不同的“优先级”来对消息分类。
在<linux/kernel.h>中定义了8种记录级别。按照优先级递减的顺序分别是:
KERN_EMERG “<0>” 用于紧急消息,常常是那些崩溃前的消息。
KERN_ALERT “<1>" 需要立刻行动的消息。
KERN_CRIT “<2>" 严重情况。
KERN_ERR “<3>" 错误情况。
没有指定优先级的printk默认使用
DEFAULT_MESSAGE_LOGLEVEL优先级,它是一个在kernel/printk.c中定义的整数。
编译问题
在ubuntu16.04中编译成功了linux2.6.39版本,18.04及其以上没有成功
编译内核错误:Can’t use ‘defined(@array)’ (Maybe you should just omit the defined()?) at kernel/timeconst.p…
linux/compiler-gcc9.h:没有那个文件或目录
将include/linux/
的compiler-gcc3.h复制一份,改为compiler-gcc9.h的名字
编译内核出现:cc1: error: code model kernel does not support PIC mode
‘make menuconfig’ requires the ncurses libraries
安装libncurses5-dev
/bin/sh: 1: flex: not found
sudo apt install flex -y
/bin/sh: 1: bison: not found
sudo apt install bison -y
fatal error: openssl/opensslv.h: 没有那个文件或目录
sudo apt install libssl-dev
make[1]: *** 没有规则可制作目标“debian/canonical-certs.pem”
以上是关于Linux内核配置编译以及模块开发的主要内容,如果未能解决你的问题,请参考以下文章