当前位置: 首页 > news >正文

从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函数

相关文章:

  • python中多线程:线程插队方法join详解、线程停止、通过变量来让线程停止
  • Java面试宝典:基础五
  • 【数据集】中国2016-2022年 城市土地利用数据集 CULU
  • 操作系统学习笔记 | 操作系统常见问题整理
  • AlphaFold3安装报错
  • NumPy 统计函数与矩阵运算指南
  • AI+预测3D新模型百十个定位预测+胆码预测+去和尾2025年6月29日第123弹
  • 理解 Confluent Schema Registry:Kafka 生态中的结构化数据守护者
  • 数据库级联操作详解:级联删除、更新与置空
  • aws(学习笔记第四十八课) appsync-graphql-dynamodb
  • 详解快速排序
  • STM32——HAL库总结
  • acme自签证书
  • docker安装gitlab并配置ssl证书
  • DeepSeek贪吃蛇游戏网页版
  • python打卡 DAY 46 通道注意力(SE注意力)
  • AVL树的简洁写法
  • Linux中ssh无法使用配置的环境变量,ssh(非登录环境)环境变量和登录环境变量不同步问题
  • 《伴时匣》app开发技术分享--用户登录(3)
  • 7类茶叶嫩芽图像分类数据集