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

一文搞懂 Linux 驱动并发与竞争(学习笔记)

1. 基本概念:

对于linux的系统编程,博主做过linux AI项目后,比较了解了,所以就不过多赘述, 如果时间允许后续会将系统的笔记发出来;

  • 竞争产生的原因

    • 多线程访问

    • 中断访问

    • 抢占访问

    • 多核并发访问

  • 共享资源

    • 全局变量

    • 内核缓冲区

    • 设备结构体

  • 保护共享资源

    • 原子操作

    • 自旋锁

    • 互斥锁

    • 信号量

2. 原子操作:

2.1 概念:

  • 原子就是借用化学的概念,不可再分

  • 原子操作就是Linux下,该操作在执行完之前不会被任何事物打断(这个操作不可再分)。

  • 原子操作分类:用于整形变量和位的保护

    • 整型原子操作

    • 位原子操作

2.2 API:

  • 头文件:#include <linux/atomic.h>

  • atomic_t 结构体:

typedefstruct{intcounter;}atomic_t;// 用于32位#ifdefCONFIG_64BITtypedefstruct{longcounter;}atomic64_t;// 用于64位#endif
  • 32位的, 64位只在函数上加个64:(用到来查)


  • 例子:

2.3 code:

  • 因为多个设备打开这个驱动程序,存在竞争的情况,所以使用原子操作实现打开时互斥
/* 1- 定义一个原子变量: 原子变量初始化,用于实现设备单开(同一时间仅允许一个进程打开) */staticatomic64_tv=ATOMIC64_INIT(1);/* 设备私有数据结构体 */structchrdev_test{dev_tdev_num;// 设备号intmajor,minor;// 主/次设备号structcdevcdev;// 字符设备结构体structclass*class;// 设备类};staticstructchrdev_testdev1;staticcharkbuf[10]={0};// 内核数据缓冲区/************************* 设备操作函数实现 *************************//* 2- 用原子变量实现互斥打开: 打开设备:原子变量判断,实现单开 */staticintopen_test(structinode*inode,structfile*file){if(atomic64_read(&v)!=1){return-EBUSY;// 设备已被占用,返回忙}atomic64_set(&v,0);// 标记设备已打开return0;}/* 读设备:内核 -> 用户 拷贝数据 */staticssize_tread_test(structfile*file,char__user*ubuf,size_tlen,loff_t*off){intret;chardata[]="topeet";printk(KERN_INFO"read_test: enter\n");ret=copy_to_user(ubuf,data,strlen(data));if(ret){printk(KERN_ERR"read_test: copy_to_user error\n");}else{printk(KERN_INFO"read_test: copy_to_user ok\n");}return0;}/* 写设备:用户 -> 内核 拷贝数据,并根据内容延时 */staticssize_twrite_test(structfile*file,constchar__user*ubuf,size_tlen,loff_t*off){intret;ret=copy_from_user(kbuf,ubuf,len);if(ret){printk(KERN_ERR"write_test: copy_from_user error\n");return-EFAULT;}printk(KERN_INFO"write_test: received data: %s\n",kbuf);/* 根据写入内容执行不同延时 */if(!strcmp(kbuf,"topeet")){ssleep(4);}elseif(!strcmp(kbuf,"itop")){ssleep(2);}returnlen;// 返回实际写入长度}/* 3- 关闭设备:释放原子变量 */staticintrelease_test(structinode*inode,structfile*file){atomic64_set(&v,1);// 标记设备空闲return0;}

3. 自旋锁:

3.1 概念:

  • 死等的现象: 当一个线程尝试获取自旋锁而发现该锁已被其他线程持有时, 它不会进入睡眠状态等待, 而是会持续循环地尝试获取锁, 直到成功为止。这称为自旋

  • 适用于自旋锁锁持有时间短且线程不希望在重新调度上花费过多时间的情况

  • 因为死等,特别浪费CPU时间

3.2 API:

  • 头文件: #include <linux/spinlock.h>

  • 用到再记:

3.3 code

  • 仅在 open 和 release 中对共享标志 flag 使用自旋锁
// 1- 自旋锁 + 设备状态标志(实现设备同一时间仅能被一个进程打开)staticspinlock_tspinlock_test;staticintflag=1;// 1=设备空闲 0=设备已被占用// 设备私有数据结构体structchrdev_test{dev_tdev_num;// 设备号intmajor,minor;// 主/次设备号structcdevcdev_test;// 字符设备structclass*class_test;// 设备类};staticstructchrdev_testdev1;staticcharkbuf[10]={0};// 内核缓冲区/************************* 设备操作函数 *************************/// 打开设备:自旋锁保护 flag,实现互斥单开staticintopen_test(structinode*inode,structfile*file){spin_lock(&spinlock_test);// 2-加锁if(flag!=1){spin_unlock(&spinlock_test);return-EBUSY;// 设备忙}flag=0;// 标记设备已占用spin_unlock(&spinlock_test);// 解锁return0;}// 读设备:内核 -> 用户 拷贝 "topeet"staticssize_tread_test(structfile*file,char__user*ubuf,size_tlen,loff_t*off){intret;chardata_buf[]="topeet";printk(KERN_INFO"this is read_test\n");ret=copy_to_user(ubuf,data_buf,strlen(data_buf));if(ret)printk(KERN_ERR"copy_to_user error\n");elseprintk(KERN_INFO"copy_to_user ok\n");return0;}// 写设备:用户 -> 内核 拷贝数据,并根据内容延时staticssize_twrite_test(structfile*file,constchar__user*ubuf,size_tlen,loff_t*off){intret;ret=copy_from_user(kbuf,ubuf,len);if(ret){printk(KERN_ERR"copy_from_user error\n");return-EFAULT;}// 根据写入内容执行延时if(!strcmp(kbuf,"topeet"))ssleep(4);elseif(!strcmp(kbuf,"itop"))ssleep(2);printk(KERN_INFO"copy_from_user buf: %s\n",kbuf);returnlen;}// 关闭设备:释放设备占用标志staticintrelease_test(structinode*inode,structfile*file){printk(KERN_INFO"this is release_test\n");spin_lock(&spinlock_test);flag=1;// 标记设备空闲spin_unlock(&spinlock_test);return0;}// 文件操作集合staticconststructfile_operationsfops_test={.owner=THIS_MODULE,.open=open_test,.read=read_test,.write=write_test,.release=release_test,};/************************* 模块加载/卸载 *************************/staticint__initspinlock_drv_init(void){intret;// 初始化自旋锁spin_lock_init(&spinlock_test);// 动态申请设备号ret=alloc_chrdev_region(&dev1.dev_num,0,1,"chrdev_name");if(ret<0){printk(KERN_ERR"alloc_chrdev_region error\n");returnret;}printk(KERN_INFO"alloc_chrdev_region ok\n");// 获取主/次设备号dev1.major=MAJOR(dev1.dev_num);dev1.minor=MINOR(dev1.dev_num);printk(KERN_INFO"major=%d, minor=%d\n",dev1.major,dev1.minor);// 初始化并添加字符设备cdev_init(&dev1.cdev_test,&fops_test);dev1.cdev_test.owner=THIS_MODULE;cdev_add(&dev1.cdev_test,dev1.dev_num,1);// 创建设备类 + 设备节点(自动生成 /dev/device_test)dev1.class_test=class_create(THIS_MODULE,"class_test");device_create(dev1.class_test,NULL,dev1.dev_num,NULL,"device_test");printk(KERN_INFO"spinlock driver init success\n");return0;}

4. 信号量

4.1 API:

  • 头文件:#include <linux/semaphore.h>

4.2 code:

//1- 二值信号量:实现设备单开(同一时间仅允许一个进程打开)structsemaphoresemaphore_test;// 设备私有数据结构体structchrdev_test{dev_tdev_num;// 设备号intmajor,minor;// 主/次设备号structcdevcdev_test;// 字符设备structclass*class_test;// 设备类};staticstructchrdev_testdev1;staticcharkbuf[10]={0};// 内核数据缓冲区/************************* 设备操作函数实现 *************************/// 2- 打开设备:信号量 P 操作(申请资源)staticintopen_test(structinode*inode,structfile*file){printk(KERN_INFO"this is open_test\n");down(&semaphore_test);// 信号量-1,为0则阻塞等待return0;}// 读设备:内核 -> 用户 拷贝 "topeet"staticssize_tread_test(structfile*file,char__user*ubuf,size_tlen,loff_t*off){intret;chardata_buf[]="topeet";printk(KERN_INFO"this is read_test\n");ret=copy_to_user(ubuf,data_buf,strlen(data_buf));if(ret)printk(KERN_ERR"copy_to_user error\n");elseprintk(KERN_INFO"copy_to_user ok\n");return0;}// 写设备:用户 -> 内核 拷贝数据,并根据内容延时staticssize_twrite_test(structfile*file,constchar__user*ubuf,size_tlen,loff_t*off){intret;ret=copy_from_user(kbuf,ubuf,len);if(ret){printk(KERN_ERR"copy_from_user error\n");return-EFAULT;}// 根据写入内容执行不同延时if(!strcmp(kbuf,"topeet"))ssleep(4);elseif(!strcmp(kbuf,"itop"))ssleep(2);printk(KERN_INFO"copy_from_user buf: %s\n",kbuf);returnlen;}// 3-关闭设备:信号量 V 操作(释放资源)staticintrelease_test(structinode*inode,structfile*file){up(&semaphore_test);// 信号量+1,唤醒等待的进程printk(KERN_INFO"this is release_test\n");return0;}// 文件操作集合staticconststructfile_operationsfops_test={.owner=THIS_MODULE,.open=open_test,.read=read_test,.write=write_test,.release=release_test,};/************************* 模块加载/卸载 *************************/staticint__initsema_drv_init(void){intret;// 初始化信号量,值为 1(二值信号量 = 互斥锁)sema_init(&semaphore_test,1);// 动态申请设备号ret=alloc_chrdev_region(&dev1.dev_num,0,1,"chrdev_name");if(ret<0){printk(KERN_ERR"alloc_chrdev_region error\n");returnret;}printk(KERN_INFO"alloc_chrdev_region ok\n");// 获取主/次设备号dev1.major=MAJOR(dev1.dev_num);dev1.minor=MINOR(dev1.dev_num);printk(KERN_INFO"major=%d, minor=%d\n",dev1.major,dev1.minor);// 初始化并添加字符设备cdev_init(&dev1.cdev_test,&fops_test);dev1.cdev_test.owner=THIS_MODULE;cdev_add(&dev1.cdev_test,dev1.dev_num,1);// 创建设备类 + 设备节点 /dev/device_testdev1.class_test=class_create(THIS_MODULE,"class_test");device_create(dev1.class_test,NULL,dev1.dev_num,NULL,"device_test");printk(KERN_INFO"semaphore driver init success\n");return0;}

5. 互斥量:

5.1 API:

  • #include <linux/mutex.h>

5.2 code

// 1-互斥锁:实现设备单开(同一时间仅允许一个进程打开)structmutexmutex_test;// 设备私有数据结构体structchrdev_test{dev_tdev_num;// 设备号intmajor,minor;// 主/次设备号structcdevcdev_test;// 字符设备structclass*class_test;// 设备类};staticstructchrdev_testdev1;staticcharkbuf[10]={0};// 内核数据缓冲区/************************* 设备操作函数实现 *************************/// 2-打开设备:互斥锁上锁staticintopen_test(structinode*inode,structfile*file){printk(KERN_INFO"this is open_test\n");mutex_lock(&mutex_test);// 上锁,已上锁则阻塞等待return0;}// 读设备:内核 -> 用户 拷贝 "topeet"staticssize_tread_test(structfile*file,char__user*ubuf,size_tlen,loff_t*off){intret;chardata_buf[]="topeet";printk(KERN_INFO"this is read_test\n");ret=copy_to_user(ubuf,data_buf,strlen(data_buf));if(ret)printk(KERN_ERR"copy_to_user error\n");elseprintk(KERN_INFO"copy_to_user ok\n");return0;}// 写设备:用户 -> 内核 拷贝数据,并根据内容延时staticssize_twrite_test(structfile*file,constchar__user*ubuf,size_tlen,loff_t*off){intret;ret=copy_from_user(kbuf,ubuf,len);if(ret){printk(KERN_ERR"copy_from_user error\n");return-EFAULT;}// 根据写入内容执行不同延时if(!strcmp(kbuf,"topeet"))ssleep(4);elseif(!strcmp(kbuf,"itop"))ssleep(2);printk(KERN_INFO"copy_from_user buf: %s\n",kbuf);returnlen;}// 3-关闭设备:互斥锁解锁staticintrelease_test(structinode*inode,structfile*file){mutex_unlock(&mutex_test);// 解锁,唤醒等待的进程printk(KERN_INFO"this is release_test\n");return0;}// 文件操作集合staticconststructfile_operationsfops_test={.owner=THIS_MODULE,.open=open_test,.read=read_test,.write=write_test,.release=release_test,};/************************* 模块加载/卸载 *************************/staticint__initmutex_drv_init(void){intret;// 初始化互斥锁mutex_init(&mutex_test);// 动态申请设备号ret=alloc_chrdev_region(&dev1.dev_num,0,1,"chrdev_name");if(ret<0){printk(KERN_ERR"alloc_chrdev_region error\n");returnret;}printk(KERN_INFO"alloc_chrdev_region ok\n");// 获取主/次设备号dev1.major=MAJOR(dev1.dev_num);dev1.minor=MINOR(dev1.dev_num);printk(KERN_INFO"major=%d, minor=%d\n",dev1.major,dev1.minor);// 初始化并添加字符设备cdev_init(&dev1.cdev_test,&fops_test);dev1.cdev_test.owner=THIS_MODULE;cdev_add(&dev1.cdev_test,dev1.dev_num,1);// 创建设备类 + 设备节点 /dev/device_testdev1.class_test=class_create(THIS_MODULE,"class_test");device_create(dev1.class_test,NULL,dev1.dev_num,NULL,"device_test");printk(KERN_INFO"mutex driver init success\n");return0;}
http://www.jsqmd.com/news/862059/

相关文章:

  • 2026年工业胶粘材料国产化趋势白皮书:PI 金手指胶带的高温性能与应用突破
  • 深入拆解 MySQL InnoDB 隔离级别:从 MVCC 到临键锁
  • Go语言内存管理:从tcmalloc到GC优化
  • 2026年AI写作辅助网站实测排行,哪款真正适合写论文?
  • AI 术语通俗词典:LSTM
  • 注释与常用快捷键
  • Harness Engineering:智能体异常处理机制
  • 080.领域自适应:当你的YOLO在新车间“水土不服”时
  • 算法28,前缀和,寻找数组中的中心下标
  • C语言06(操作符)
  • VxWorks网络通信模块:网络协议栈解析(第五部分)
  • 鸿蒙备考题库页面构建:错题本、小组榜单与备考提示模块详解
  • QQ家园迷你屋单机版下载:复刻05年经典网页社区,像素风直接拉满
  • ComfyUI全面掌握-知识点详解——ComfyUI 开发与扩展基础(开发指南+环境搭建)
  • 海量分布式储能节点云边协同架构:边缘网关异步心跳注册与状态上报Python实战
  • 输出函数print
  • 内存管理
  • 【RAG】【retrievers08】基于Together.ai长上下文嵌入的混合检索
  • 4 类国产企业即时通讯平台推荐榜:如何为安全协同构建私有化底
  • AI 大模型技术架构演进与应用落地瓶颈分析
  • 西门子PLC对接须知:从通信到编程的实战指南
  • 用LLM从零搭3D小世界编辑器|小白也能搞定的AI Native开发实录
  • 【RHCA+】info命令(模块化的命令帮助文档)
  • 【RAG】【retrievers09】Pathway检索器:实时数据索引与检索
  • 适配多层级组织管理,科学运用 360 度反馈打造公平高效绩效文化
  • 2026年整箱榨菜厂家精选合集 - 行业平台推荐
  • 第2章:文档加载与智能分块——RAG的第一步
  • HTTP状态码与请求方式全解析【个人八股】
  • VGG16猫狗二分类
  • 工程实战:基于 GPIO 物理旁路极速部署机器人电梯调度系统的设计