Linux内核模块开发难吗?
摘要:Linux内核模块开发 零、关于 1、概述 最近在学习Linux相关的东西,学习了U-Boot的编译,Linux的编译,能够在开发板上运行自己编译的U-Boot和Linux了,那么接下来就是在自己编译的Linux上做应用级或者系统级的开发了
Linux内核模块开发
零、关于
1、概述
最近在学习Linux相关的东西,学习了U-Boot的编译,Linux的编译,能够在开发板上运行自己编译的U-Boot和Linux了,那么接下来就是在自己编译的Linux上做应用级或者系统级的开发了。本文以字符设备驱动为例介绍如何开发Linux内核的模块,包括静态编译、动态加载和模块之间的依赖等内容。
若要继续实践下面的内容,需要以你能够自己编译Linux内核为前提。
2、内核模块
Linux内核模块是用于扩展内核功能的一些代码。本质上是.ko格式的独立目标文件,内核模块通过与内核链接,实现对硬件驱动、文件系统、网络协议等功能的灵活扩展。
内核模块分为设备驱动程序、文件系统模块、网络协议模块、网络服务模块、硬件架构与系统支持模块、内核子系统扩展模块和特殊功能模块等类型,我们可以针对某个部分对内核进行扩展。
3、处理方式
在Linux中有两种内核模块的处理方式,一种是静态编译,另一种是动态加载。
其中,静态编译是指我们在编译Linux时把我们的模块代码一起编译进系统可执行文件中去,使其成为内核不可分割的一部分。这样做的好处是在Linux启动时我们的代码就被加载到Linux内核系统中运行了,而且不用生成另外的模块文件,方便分发。但是缺点是我们需要对内核模块代码进行改动时需要重新编译Linux,内核运行期间也无法单独卸载或更新我们的内核模块,这样不方便随时修改。
而动态加载则是指我们的内核模块代码不会与Linux内核代码一起编译,而是另外再单独编译。这样会生成一个后缀为.ko(Kernel Object)的内核模块文件。我们需要使用此内核模块时,可以使用加载内核模块命令(insmod/modprobe)把内核模块加载到Linux内核中,不需要使用此模块时,可以使用卸载内核模块命令(rmmod)把内核模块从Linux内核中卸载,加载操作和卸载操作都无需重启内核。
壹、代码模板
在介绍如何编译和使用内核模块之前,我们需要对内核模块的代码有一个大致的了解。
1、内核模块代码
下面的代码模板构建了一个简单的Linux内核模块,在模块加载时会输出hello_yu init消息,在模块卸载时会输出hello_yu exit消息。通过printk函数,这些消息会被记录到内核日志中,可使用dmesg命令查看。
一个简单的内核模块代码文件hello.c内容如下:
#include <linux/module.h>
#include <linux/kernel.h>
int __init hello_yu_init(void)
{
printk("hello_yu init\n");
return 0;
}
void __exit hello_yu_exit(void)
{
printk("hello_yu exit\n");
}
MODULE_LICENSE("GPL");
module_init(hello_yu_init);
module_exit(hello_yu_exit);
下面对其做一个更详细的介绍:
1)、头文件包含
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/module.h>:这个头文件定义了构建内核模块所需的函数和宏,像module_init、module_exit以及MODULE_LICENSE等。
#include <linux/kernel.h>:此头文件包含了内核编程常用的函数和数据结构,例如printk函数等。
2)、模块初始化函数
int __init hello_yu_init(void)
{
printk("hello_yu init\n");
return 0;
}
int __init hello_yu_init(void):这是模块的初始化函数,当模块被加载到内核时会调用此函数。__init是一个宏,其作用是告知编译器该函数仅在模块初始化时使用,之后就可以释放相关内存。
printk("hello_yu init\n");:printk 是内核中的打印函数,功能类似于用户空间的printf。它会把消息输出到内核日志缓冲区,可通过dmesg命令查看。
return 0;:返回值为0表明模块初始化成功。若返回非零值,则意味着初始化失败,模块将无法加载。
