当前位置: 首页 > 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.zskr.cn/news/1342689.html

相关文章:

  • Harness Engineering:智能体异常处理机制
  • 【docker镜像加速器配置】
  • 【Midjourney拟物化风格实战指南】:20年视觉设计专家亲授3大材质渲染公式与5步出图工作流
  • 设计师私藏的11个纹理Prompt原子模块(仅限本周开放下载:含PBR贴图映射表+光照反射系数速查卡)
  • QQ家园迷你屋单机版下载:复刻05年经典网页社区,像素风直接拉满
  • 【RAG】【retrievers08】基于Together.ai长上下文嵌入的混合检索
  • AI 大模型技术架构演进与应用落地瓶颈分析
  • 【RAG】【retrievers09】Pathway检索器:实时数据索引与检索
  • 【RHCA+】info命令(模块化的命令帮助文档)
  • AI 术语通俗词典:LSTM
  • 仪式感,从来与你无关
  • Pure Live:3大平台聚合,打造你的专属纯净直播空间
  • VGG16猫狗二分类
  • 数采网关的应用与特点
  • 慢驴效应(懒驴效应)
  • 算法28,前缀和,寻找数组中的中心下标
  • 11.三层网络VXLAN
  • 【SSD】闪存1
  • 2026年工业胶粘材料国产化趋势白皮书:PI 金手指胶带的高温性能与应用突破
  • 学Simulink——多路输出反激式开关电源(SMPS)交叉调整率改善仿真
  • 手把手教你学Simulink——高频隔离型双向 DC-DC 变换器的软开关(ZVS/ZCS)实现仿真
  • 鸿蒙中的自由流转
  • 2026年4月钢边止水带企业推荐分析,聚乙烯闭孔泡沫板/聚乙烯泡沫棒/钢边止水带/橡胶止水带,钢边止水带生产厂家找哪家 - 品牌推荐师
  • 中画幅风格仅限Pro订阅者可用?不!3个未公开API参数+本地化--seed锁定技巧,让免费账户稳定输出中画幅质感
  • 输出函数print
  • 几十万买的数字孪生低代码平台集体落灰?被隐瞒的落地真相,终于说透了
  • 408 每日一题 Day 2:二叉树的重构与遍历
  • leetcode思路-236 二叉树的最近公共祖先
  • 分布式团队的代码协作规范:从分支策略到提交信息格式
  • Cell Host Microbe | 西奈山伊坎医学院房刚团队揭示肠道微生物的表观遗传“押注对冲“策略