Ubuntu18.04添加内核模块(字符设备)
虚拟机Ubuntu18.04(内核版本linux-5.4.0-135-generic)
参考
1 编译内核模块
- 创建字符设备代码文件
char_dev.c
:
#include <linux/init.h> //定义了module_init
#include <linux/module.h> //最基本的头文件,其中定义了MODULE_LICENSE这一类宏
#include <linux/fs.h> // file_operations结构体定义在该头文件中
#include <linux/device.h> //class、class_device结构体的定义位置
static const char* devive_name = "first_driver"; // 定义设备名
static struct class *first_class; //定义class结构体
static struct device *first_dev; //定义device结构体
//定义了open函数
static int first_drv_open (struct inode *inode, struct file *file)
{
printk("open\n");
return 0;
}
//定义了write函数
static ssize_t first_drv_write (struct file *file, const char __user *buf, size_t size, loff_t * ppos)
{
printk("write\n");
return 0;
}
//在file_operations中注册open和write函数
static struct file_operations first_drv_fo =
{
.owner = THIS_MODULE,
//将对应的函数关联在file_operations的结构体中
.open = first_drv_open,
.write = first_drv_write,
};
static int dev_id = 0; //初始化的设备号0
//init驱动的入口函数
static int __init first_drv_init(void)
{
//注册设备,实际是将file_operations结构体放到内核的制定数组中,以便管理
//在register_chrdev中制定dev_id作为主设备号,若dev_id为0则自动分配一个主设备号
dev_id = register_chrdev(dev_id, devive_name , &first_drv_fo);
first_class = class_create(THIS_MODULE, "first_drv"); //初始化class结构体,指定设备文件名
first_dev = device_create(first_class, NULL, MKDEV(dev_id, 0), NULL, "first_drv");// 根据class来初始化device,会创建出对应的设备文件 /dev/first_drv
printk("init\n");
return 0;
}
//驱动的出口函数
static void __exit first_drv_exit(void)
{
printk("exit\n");
unregister_chrdev(dev_id, devive_name); //卸载设备,实际是将file_operations结构体从内核维护的相关数组中以主设备号作为索引删除
device_unregister(first_dev); // 后创建的先卸载
class_destroy(first_class);
}
//内核将通过这个宏,来直到这个驱动的入口和出口函数
module_init(first_drv_init);
module_exit(first_drv_exit);
MODULE_AUTHOR("Ethan Lee <4128127@qq.com>");
MODULE_LICENSE("GPL"); //指定协议
- 同目录下创建
Makefile
文件:
obj-m += char_dev.o
KERN_DIR=/usr/src/linux-headers-5.4.0-135-generic
all:
make -C ${KERN_DIR} M=${shell pwd} modules
clean:
rm -f *.ko *.o *.mod.o *.mod.c *.sysvers
- 编译:
make
2 加载内核模块
sudo insmod char_dev.ko
- 查看是否添加成功:
cat /proc/devices
结果如下:
Character devices:
...
189 usb_device
204 ttyMAX
226 drm
240 first_driver #这里是我们添加的模块
241 aux
242 hidraw
...
Block devices:
7 loop
8 sd
9 md
11 sr
65 sd
66 sd
...
- 创建一个测试程序
char_dev_test.c
:
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
int main(int argc, char **argv)
{
int fd; //声明设备描述符
int val = 1; //随便定义变量传入到
fd = open("/dev/first_drv", O_RDWR); //根据设备描述符打开设备
if(fd < 0) //打开失败
printf("can't open\n");
write(fd, &val, 4); //根据文件描述符调用write
return 0;
}
- 编译并运行测试程序:
gcc char_dev_test.c -o char_dev_test
sudo ./char_dev_test
- 查看结果:
$ dmesg | tail -10
[ 1746.094412] CPU3 has been hot-added
[ 1746.094945] CPU4 has been hot-added
[ 1746.097525] CPU5 has been hot-added
[ 1746.098038] CPU6 has been hot-added
[ 1746.098708] CPU7 has been hot-added
[ 2861.264107] char_dev: loading out-of-tree module taints kernel.
[ 2861.264142] char_dev: module verification failed: signature and/or required key missing - tainting kernel
[ 2861.264398] init
[ 3070.234439] open
[ 3070.234441] write
3.卸载内核模块
sudo rmmod char_dev
- 查看结果:
$ dmesg | tail -1
[ 4282.264114] exit