
运行Linux系统时,内存通常至少为32MB、64MB,甚至1GB、2GB,因此无需像单片机那样受资源限制。在Linux系统中,整个内核都运行在内存中,而非Flash上;内存中的内核代码会划分成多个段,有单片机开发经验的同学对“分段”应该不陌生。
即便内存容量庞大,内核占用的内存区域仍会进一步细分出多个功能段。如果某个段(如初始化相关的段)在使用后确认不再需要,内核就可以释放该段内存,回收资源。之前提到的module_init、module_exit所指定的段,同样位于内存中,而非Flash。
驱动程序有两种部署方式:一是编进内核,二是编译为.ko模块,通过insmod动态加载。两者功能上没有区别,但在调试、升级和系统启动效率上差异显著:
此外,为提升系统启动速度,应尽量精简内核,仅保留必要驱动(如显示驱动,确保上电后快速亮屏、启动核心应用);其他非必要驱动可编译为模块,待系统启动完成后再逐一加载。需要注意的是,驱动程序有bug与“污染内核”并非同一概念。
static int major = 0;,设为0表示让系统自动分配主设备号;
staticstructfile_operationsfops = {
.owner = THIS_MODULE, // 固定赋值为THIS_MODULE,标识驱动所属模块
};编译时出现“register_chrdev未声明”的错误,说明缺少对应头文件。register_chrdev函数定义在<linux/fs.h>中,需添加包含语句:#include <linux/fs.h>。
sudo insmod hello_driver.ko,通过lsmod命令可查看已装载的驱动程序;cat /proc/devices命令,查看字符设备列表,可找到“hello_driver”及系统分配的主设备号(如241);sudo rmmod hello_driver,再次执行cat /proc/devices,会发现hello_driver已从列表中消失。此时注册的字符设备驱动,file_operations结构体中未实现open、read、write等函数。接下来我们思考:应用程序能否调用open、read、write访问该驱动?
应用程序调用open、read、write等系统调用时,最终会进入内核的sys_open、sys_read、sys_write函数,进而调用do_sys_open等底层函数。内核会判断驱动程序的file_operations结构体是否实现了对应的接口函数:
内核的这一容错机制,确保了即使驱动未实现某些非必需接口,也不会导致内核崩溃。
为打通数据链路,我们在file_operations结构体中实现read和write函数,仅添加打印功能和固定返回值(验证调用关系)。
// read函数:应用程序调用read时触发
staticssize_thello_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
{
printk("hello_read is called\n");
return1; // 返回1,表示成功读取1个字节(实际未处理数据)
}
// write函数:应用程序调用write时触发
staticssize_thello_write(struct file *file, constchar __user *buf, size_t count, loff_t *ppos)
{
printk("hello_write is called\n");
return1; // 返回1,表示成功写入1个字节(实际未处理数据)
}
staticstructfile_operationsfops = {
.owner = THIS_MODULE,
.read = hello_read, // 关联read函数
.write = hello_write, // 关联write函数
};sudo insmod hello_driver.ko;sudo ./hello_driver_test read,打印“open成功,读的返回值是1”,通过dmesg可查看“hello_read is called”;sudo ./hello_driver_test write "abcdef",打印“open成功,写的返回值是1”,通过dmesg可查看“hello_write is called”。测试结论:应用程序调用read、write时,成功触发了驱动程序中对应的hello_read、hello_write函数,“应用程序-驱动程序”的调用关系已打通。当前驱动程序仅实现了调用验证,未实际处理数据(读未返回有效数据,写未接收应用程序数据),后续我们会进一步完善数据处理逻辑。
应用程序调用read/write时,会传入数据缓冲区(buffer)和数据长度(len):read是将驱动数据读入缓冲区,write是将缓冲区数据写入驱动。但驱动程序无法直接访问应用程序的缓冲区——两者处于不同的地址空间(后续详细讲解地址空间概念)。
数据传递需通过专门的内核函数实现: