从0开始学linux韦东山教程Linux驱动入门实验班(3)
本人从0开始学习linux,使用的是韦东山的教程,在跟着课程学习的情况下的所遇到的问题的总结,理论虽枯燥但是是基础。本人将前几章的内容大致学完之后,考虑到后续驱动方面得更多的开始实操,后续的内容将以韦东山教程Linux驱动入门实验班的内容为主,学习其中的代码并手敲。做到锻炼动手能力的同时钻研其中的理论知识点。
摘要:这篇文档主要介绍的是以下几个方面,bear make指令的使用以及其相关知识点,开发板上运行驱动程序会生成的临时文件有哪些,设备节点是啥是啥时候生成的,通过逐行解析代码,明白其工作原理,对device设备节点等相关知识有一些列的了解。
摘要关键词:bear make、临时文件、device设备节点
本文详细介绍以下问题,如果你遇到了以下问题,看看我的方案能否解决。
1.bear make指令的使用
2. ls -a 指令的使用以及运行驱动程序
3.hello临时文件
4.驱动写入数据
5.注释逐行解析代码
6.device设备节点相关知识点
1.bear make指令的使用
首先 bear make 编译生成编译文件。可以看到生成了很多的文件,但是最关注的是compile_commands.json文件,这个文件中写着编译器,编译路径以及头文件查找地址等信息。
Ctrl+H
全局搜索cc,将编译替换成arm内核
2. ls -a 指令的使用以及运行驱动程序
arm-buildroot-linux-gnueabihf-gcc
ls -a
ls -a 命令用于列出当前目录下的所有文件,包括以点(.)开头的隐藏文件
insmod hello_drv.ko
cat /proc/devices
ls /dev/hello -l
insmod hello_drv.ko:insmod 是 Linux 系统中用于动态加载内核模块(Kernel Module)的命令,hello_drv.ko 是一个编译好的内核模块文件(.ko 是 “Kernel Object” 的缩写)。这行命令的核心作用是将 hello_drv 内核模块加载到当前运行的 Linux 内核中,使其成为内核的一部分,从而扩展内核的功能。
cat /proc/devices:/proc 是 Linux 内核提供的虚拟文件系统(procfs),用于向用户空间暴露内核运行时的状态信息(如进程、内存、设备等)。/proc/devices 是其中一个特殊文件,记录了当前内核中已注册的所有字符设备(Character Device)和块设备(Block Device)的信息。
小结:也就是当你输入insmod hello_drv.ko会执行这个模块驱动代码,但是不会执行应用程序,insmod hello_drv.ko 命令的作用是将内核模块(驱动代码)加载到内核空间并执行其初始化逻辑,但不会直接执行用户空间的应用程序。它只会加载模块到内核,执行模块初始化函数。
3.hello临时文件
ls /dev
hello 是自定义设备文件。
pps1 与高精度时钟同步信号有关,通常用于 GPS 或类似的同步设备。
tty10 和 tty41 是虚拟终端设备文件。
v4l 代表 Video4Linux,是一个视频设备接口,用于访问视频捕捉设备。
这些文件通常用于与硬件或虚拟设备交互,这就是以上代码定义的文件类型。
4.驱动写入数据
./hello_test /dev/hello hello,roudragon
驱动读取数据测试,可以看到读取到的数据为100ask。
./hello_test /dev/hello
5.注释逐行解析代码
//静态结构体将 class结构体赋予给hello_class指针结构体,具体如何使用看后续代码
static struct class *hello_class;
static int major;
static unsigned char hello_buf[100];
// 读取函数,读取文件中的内容
static ssize_t hello_read (struct file *filp, char __user *buf, size_t size, loff_t *offset)
{unsigned long len = size > 100 ? 100 : size; //当读取到的内容数据长度超过100位时,读取前100位printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);copy_to_user(buf, hello_buf, len);return len;
}
这个函数的作用也就是将hello_buf中的数据上传到buf中。将驱动程序中的数据上传到应用程序中。
static ssize_t hello_write(struct file *filp, const char __user *buf, size_t size, loff_t *offset)
{unsigned long len = size > 100 ? 100 : size;printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);copy_from_user(hello_buf, buf, len);return len;
}
这个函数的作用也就是将hello_buf中的数据拷贝到buf中。将驱动程序中的数据拷贝到应用程序中。
static int hello_release (struct inode *node, struct file *filp)
{printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);return 0;
}
释放应用程序空间,release 函数通常在文件描述符被关闭时被调用,常用于释放与文件相关的资源。
/* 1. create file_operations */
static const struct file_operations hello_drv = {.owner = THIS_MODULE,.read = hello_read,.write = hello_write,.open = hello_open,.release = hello_release,
};
将file_operations 结构体中的内容给了hello_drv,可以看出这个结构体是结构体函数,它调用的是hello_read这一系列自定义的函数。
/* 3. entry function */
static int hello_init(void)
{major = register_chrdev(0, "100ask_hello", &hello_drv);hello_class = class_create(THIS_MODULE, "hello_class");if (IS_ERR(hello_class)) {printk("failed to allocate class\n");return PTR_ERR(hello_class);}device_create(hello_class, NULL, MKDEV(major, 0), NULL, "hello"); /* /dev/hello */return 0;
}
register_chrdev(0, “100ask_hello”, &hello_drv):这一行注册了一个字符设备。register_chrdev() 是一个内核函数,它用于动态分配主设备号并注册一个字符设备。
0:表示内核将自动分配一个主设备号。
“100ask_hello”:这是设备的名称,通常用于日志中标识该设备。
&hello_drv:这是设备的操作函数结构体指针,通常包含对设备文件的操作,如打开、读取、写入等。
小结也就是这里创建了节点,已注册的字符设备的主设备号和设备名显示。241也就是0:内核将自动分配的,hello_drv也就会自动链接到上面那个结构体那。
class_create(THIS_MODULE, "hello_class"):这一行创建了一个设备类,设备类是一种组织设备文件的方式。在 Linux 中,设备文件通常位于 /dev 目录下,而设备类则用来管理设备文件的创建与销毁。
THIS_MODULE:表示当前模块的指针,通常用来标识设备类是属于当前内核模块的。
“hello_class”:这是设备类的名称,用于标识该类。
device_create():这行代码在 /dev 目录下创建一个设备文件,供用户空间应用程序访问。
“hello”:设备文件的名称,最终会创建 /dev/hello 设备文件,改动和保存的文件也就是它。
/* 4. exit function */
static void hello_exit(void)
{device_destroy(hello_class, MKDEV(major, 0));class_destroy(hello_class);unregister_chrdev(major, "100ask_hello");
}module_init(hello_init);
module_exit(hello_exit);
MODULE_LICENSE("GPL");
Linux 内核模块的卸载部分,主要用于在模块退出时清理系统中创建的资源
该函数用于销毁通过 device_create 创建的设备文件。在模块初始化时,设备文件会通过device_create 创建,这里通过 device_destroy 将其删除。
6.device设备节点相关知识点
到此你会不会想一个问题,理论上上面那个函数会清空100ask_hello,但是为什么我在device设备节点上还是看到了它呢?如上图所示
其实它的执行流程图如下图所示。当你输入命令 insmode hello_drv.ko时,程序会创建设备节点创建类目录,当你输入rmmod hello_drv时设备节点删除,临时动态文件删除。
rmmod hello_drv.ko
cat /proc/device
可以看到240节点设备已经没有了。
ls /dev
hello文件也没有了
个人是这么理解的这段程序,这就好比打开了一个APP,打开APP后会创建一个节点,当其运行时也就会产生一系列的临时文件占用你的动态内存,当你删除APP模块可以释放内存,当你关闭APP也可以释放内存。
“打开APP” | 加载内核模块(insmod) |
---|---|
“创建节点” | device_create()创建/dev/hello |
“运行时产生临时文件” | 模块全局数据(hello_buf),存在整个生命周期 |
“删除APP释放内存” | rmmod卸载模块,释放所有内存 |
“关闭APP释放内存” | 关闭设备文件(close)只调用release函数 |