news 2026/6/23 17:45:54

两段驱动代码的区别

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
两段驱动代码的区别

1

#include <linux/module.h> #include <linux/kernel.h> static int __init helloworld_init(void) //驱动入口函数 { printk(KERN_EMERG "helloworld_init\r\n");//注意: 内核打印用 printk 而不是 printf return 0; } static void __exit helloworld_exit(void) //驱动出口函数 { printk(KERN_EMERG "helloworld_exit\r\n"); } module_init(helloworld_init); //告诉linux模块入口函数,加载模块代码到操作系统 module_exit(helloworld_exit); //卸载 MODULE_LICENSE("GPL v2"); //同意 GPL 开源协议 MODULE_VERSION("1.0"); //驱动的版本 MODULE_AUTHOR("Cavium Inc"); MODULE_DESCRIPTION("helloworld Driver"); //lsmod

2

#include <linux/init.h> #include <linux/module.h> #include <linux/fs.h> #include <linux/kdev_t.h> #include <linux/cdev.h> static dev_t dev_num;//定义dev_t类型(32位大小)的变量dev_num,用来存放设备号 static struct cdev cdev_test;//定义cdev结构体类型的变量cdev_test static struct file_operations cdev_test_ops = { .owner=THIS_MODULE,//将owner字段指向本模块,可以避免在模块的操作正在被使用时卸载该模块 };//定义file_operations结构体类型的变量cdev_test_ops static int __init module_cdev_init(void)//驱动入口函数 { int ret;//定义int类型变量ret,进行函数返回值判断 int major,minor;//定义int类型的主设备号major和次设备号minor ret = alloc_chrdev_region(&dev_num,0,1,"chrdev_name");//自动获取设备号,设备名为chrdev_name if (ret < 0){ printk("alloc_chrdev_region is error\n"); } printk("alloc_register_region is ok\n"); major = MAJOR(dev_num);//使用MAJOR()函数获取主设备号 minor = MINOR(dev_num);//使用MINOR()函数获取次设备号 printk("major is %d\n",major); printk("minor is %d\n",minor); cdev_init(&cdev_test,&cdev_test_ops);//使用cdev_init()函数初始化cdev_test结构体,并链接到cdev_test_ops结构体 cdev_test.owner = THIS_MODULE;//将owner字段指向本模块,可以避免在模块的操作正在被使用时卸载该模块 ret = cdev_add(&cdev_test,dev_num,1);//使用cdev_add()函数进行字符设备的添加 if(ret < 0 ){ printk("cdev_add is error\n"); } printk("cdev_add is ok\n"); return 0; } static void __exit module_cdev_exit(void)//驱动出口函数 { cdev_del(&cdev_test);//使用cdev_del()函数进行字符设备的删除 unregister_chrdev_region(dev_num,1);//释放字符驱动设备号 printk("module exit \n"); } module_init(module_cdev_init);//注册入口函数 module_exit(module_cdev_exit);//注册出口函数 MODULE_LICENSE("GPL v2");//同意GPL开源协议

一、先分别明确两段代码的核心功能

1. 第一段(helloworld 驱动):最基础的 “空” 内核模块

这段代码是 Linux 内核模块的入门模板,没有任何实际的设备操作功能,仅完成 “模块加载” 和 “模块卸载” 的基础流程:

  • 加载模块时:通过printk输出日志(helloworld_init
  • 卸载模块时:通过printk输出日志(helloworld_exit
  • 额外补充了模块信息(版本、作者、描述),但这些信息仅用于标识模块,无实际功能。

2. 第二段(字符设备驱动):实用设备驱动的 “雏形”

这段代码是字符设备驱动的核心框架,在 helloworld 基础上,新增了 “字符设备注册 / 管理” 的核心逻辑,目的是为用户空间程序提供 “访问硬件设备” 的内核层入口,具备了实际设备驱动的基础骨架。

二、核心区别对比

对比维度第一段(helloworld 驱动)第二段(字符设备驱动)
功能定位内核模块入门演示,无实际功能字符设备驱动核心框架,为硬件操作提供内核入口
复杂程度极简(仅包含模块入口 / 出口 + 日志输出)中等(新增设备号、cdev、文件操作集等核心组件)
涉及内核组件仅基础模块框架(module_init/module_exit1. 设备号管理(dev_t/alloc_chrdev_region)2. 字符设备管理(cdev/cdev_init/cdev_add)3. 文件操作集(file_operations
实际用途验证内核模块编译、加载、卸载的基本流程作为实际字符设备(如串口、LED、按键)驱动的基础,后续可扩展读写、控制等设备操作
关键函数(独有)无独有核心函数(仅基础日志输出)alloc_chrdev_region(分配设备号)、cdev_init(初始化字符设备)、cdev_add(注册字符设备)、unregister_chrdev_region(释放设备号)、cdev_del(删除字符设备)
与硬件 / 用户空间的关联无任何关联(既不操作硬件,也不提供用户空间访问接口)搭建了 “用户空间→内核空间” 的访问桥梁(通过设备号和设备节点),后续可扩展硬件操作逻辑
日志输出目的仅验证模块是否成功加载 / 卸载除验证流程外,还排查设备号分配、字符设备注册的错误(如alloc_chrdev_region is error

三、关键差异的通俗解释

1. 核心差异:是否 “关联设备”

  • 第一段代码就像一个 “空盒子”:你把它放进内核(加载模块),它只告诉你 “我进来了”,把它拿出来(卸载模块),它只告诉你 “我走了”,盒子里没有任何东西。
  • 第二段代码就像一个 “带接口的设备盒子”:它放进内核后,会先向内核 “申请编号”(设备号),再把 “盒子接口”(cdev 字符设备)注册到内核,用户空间程序可以通过这个 “编号” 和 “接口” 访问盒子内部(硬件设备),卸载时还会归还 “编号” 并销毁 “接口”。

2. 组件差异:新增的核心结构体 / 函数的意义

  • dev_t dev_num:设备的 “身份证号”(主设备号 + 次设备号),用户空间通过这个编号找到对应的设备。
  • struct cdev:字符设备的 “管理结构体”,相当于设备的 “管家”,负责统筹设备的操作逻辑。
  • struct file_operations:设备的 “操作手册”,定义了用户空间可以对设备执行的操作(如读、写),第二段代码中仅初始化了owner字段,后续可添加read/write等函数实现实际操作。
  • alloc_chrdev_region/unregister_chrdev_region:向内核 “申请 / 归还” 设备号,避免设备号冲突。
  • cdev_init/cdev_add/cdev_del:初始化、注册、销毁字符设备,让内核识别并管理该设备。

3. 递进关系

第一段代码是第二段代码的基础,第二段代码是在第一段代码的框架上,新增了 “字符设备管理” 的核心逻辑,是从 “入门演示” 到 “实际驱动开发” 的关键递进。

简单说:helloworld 驱动是 “热身”,字符设备驱动是 “正式开始干活”

总结

  1. 功能定位不同:helloworld 驱动是无实际功能的入门模板,字符设备驱动是具备设备管理能力的实用驱动雏形。
  2. 组件复杂度不同:字符设备驱动新增了设备号、cdev 结构体、文件操作集等核心组件,是实际硬件驱动的基础框架。
  3. 用途不同:helloworld 仅验证模块加载 / 卸载流程,字符设备驱动为用户空间访问硬件提供了内核层入口,可直接扩展为实际设备驱动。
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/23 17:34:19

LangFlow与社交媒体API集成:自动发布与监控评论

LangFlow与社交媒体API集成&#xff1a;自动发布与监控评论 在数字营销和品牌运营日益依赖实时互动的今天&#xff0c;企业对社交媒体内容的自动化管理需求正以前所未有的速度增长。想象这样一个场景&#xff1a;一款新产品刚刚上线&#xff0c;市场团队需要在多个平台同步发布…

作者头像 李华
网站建设 2026/6/23 7:46:08

LangFlow与股票行情接口结合:金融信息实时推送

LangFlow与股票行情接口结合&#xff1a;金融信息实时推送 在金融市场的快节奏环境中&#xff0c;信息就是优势。一个交易员是否能在股价异动的第一时间捕捉到信号&#xff0c;并迅速理解其背后可能的原因&#xff0c;往往决定了策略的成败。然而&#xff0c;传统的工作流中&am…

作者头像 李华
网站建设 2026/6/23 4:24:49

VirtualBox虚拟机运行卡顿问题

本机是华为笔记本前提&#xff1a;WIN11 24H2&#xff0c;关闭内核完整性&#xff0c;打开电脑虚拟化之后&#xff0c;发现虚拟机无法开启无法启用AMD-V或INTEL VT-X可能的原因是 Windows 11 24H2 的虚拟化功能&#xff08;如 VBS&#xff09;与某些 Virtualbox 版本冲突。打开…

作者头像 李华
网站建设 2026/6/20 21:46:45

AP0316语音模组深度解析:一站式解决降噪消回音,音频项目党必藏!

&#x1f449; 做音频项目的兄弟集合&#xff01;是不是总被这些问题卡壳&#xff1a;环境噪音盖过人声、麦克风和喇叭离太近全是回音、模拟/数字音频接口不兼容、调试半天还是出问题&#xff1f;今天给大家安利一款“音频全能王”——AP0316全功能降噪回音消除模组&#xff0c…

作者头像 李华
网站建设 2026/6/22 20:54:06

18、网络流量路由与过滤全解析

网络流量路由与过滤全解析 路由算法对比 在网络路由中,距离向量协议和链路状态算法是两种重要的路由算法。 距离向量协议存在一些缺点。它会导致路由表不断增大,因为它会周期性地向其他路由器通告自己的路由表,即使网络收敛后也是如此,这就增加了网络流量。而且,大型互联…

作者头像 李华
网站建设 2026/6/22 11:55:54

unity中利用MRTK添加全息面板并部署到HoloLens 2中

1.添加 MRTK 基础场景设置&#xff0c;点击 「Add to Scene and Configure...」 选项 【这个选项会自动帮你完成以下操作&#xff1a;删除场景中默认的 Main Camera、Directional Light&#xff1b;生成 MRTK 的核心容器&#xff08;MixedRealityPlayspace&#xff0c;包含适配…

作者头像 李华