1.并发介绍

一般来说,操作系统都是支持并发执行能力的,多个执行单元访问同一个模块时,如果不能支持并发,则会让这个模块功能紊乱,像读写操作时。两个用户同时读写,那么可能一个用户执行读操作时,另一个用户可以执行了它的写操作,这就会出现功能不协调的情况。因此这里通过并发,将代码放在临界区,通过特定的互斥机制来对这一块进行保护,使得多个执行单元访问这块时只能执行一个,其余都需要等待或其他。

2.并发处理方法

(1)中断屏蔽

CPU一般都具备屏蔽中断和打开中断的功能,这项功能可以保证正在执行的内核执行路径不被中断处理程序所抢占,防止某些竞态条件的发生。具体而言,中断屏蔽将使得中断与进程之间的并发不再发生,而且,由于Linux内核的进程调度等操作都依赖中断来实现,内核抢占进程之间的并发也得以避免了。

local_irq_disable() /* 屏蔽中断 */
. . .
critical section /* 临界区*/
. . .

local_irq_enable() /* 开中断*/

(2)自旋锁

自旋锁(Spin Lock)是一种典型的对临界资源进行互斥访问的手段,其名称来源于它的工作方式。为了获得一个自旋锁,在某CPU上运行的代码需先执行一个原子操作,该操作测试并设置(Test-And-Set)某个内存变量

spinlock_t lock;        /* 定义一个自旋锁*/
spin_lock_init(&lock);   /* 初始化一个自旋锁*/
spin_lock (&lock) ;   /* 获取自旋锁,保护临界区 */
. . ./* 临界区*/

spin_unlock (&lock) ;   /* 解锁*/

自旋锁主要针对SMP或单CPU但内核可抢占的情况,对于单CPU和内核不支持抢占的系统,自旋锁退化为空操作

(3)信号量

信号量(Semaphore)是操作系统中最典型的用于同步和互斥的手段,信号量的值可以是0、1或者n。信号量与操作系统中的经典概念PV操作对应。

P(S):①将信号量S的值减1,即S=S-1;
②如果S≥0,则该进程继续执行;否则该进程置为等待状态,排入等待队列。
V(S):①将信号量S的值加1,即S=S+1;
②如果S>0,唤醒队列中等待信号量的进程。
Linux中与信号量相关的操作主要有下面几种。

struct semaphore sem;    /* 定义一个信号量 */

void sema_init(struct semaphore *sem, int val);  //初始化型号量,并设置信号量的值为val

/*获得信号量*/

void down(struct semaphore * sem); //获得信号量,它会导致睡眠,因此不能在中断上下文中使用。

int down_interruptible(struct semaphore * sem);   //获得信号量,进入睡眠状态的进程能被信号打断

int down_trylock(struct semaphore * sem);   /*该函数尝试获得信号量sem,如果能够立刻获得,它就获得该信号量并返回0,否则,返回非0值。。它不会导致调用者睡眠,可以在中断上下文中使用。*/

/*释放信号量*/

void up(struct semaphore * sem);

(4)互斥体

struct mutex my_mutex;   //定义一个互斥体

mutex_init(&my_mutex);   //初始化互斥体

/*获取互斥体*/

void mutex_lock(struct mutex *lock);
int mutex_lock_interruptible(struct mutex *lock);
int mutex_trylock(struct mutex *lock);

/*释放互斥体*/

void mutex_unlock(struct mutex *lock);

mutex的使用方法和信号量用于互斥的场合完全一样:

eg:

struct mutex my_mutex; /* 定义mutex */
mutex_init(&my_mutex); /* 初始化mutex */
mutex_lock(&my_mutex); /* 获取mutex */
... /* 临界资源*/

mutex_unlock(&my_mutex); /* 释放mutex */

互斥体与自旋锁区别:

互斥体和自旋锁属于不同层次的互斥手段,前者的实现依赖于后者。在互斥体本身的实现上,为了保证互斥体结构存取的原子性,需要自旋锁来互斥。所以自旋锁属于更底层的手段。

互斥体是进程级的,用于多个进程之间对资源的互斥,虽然也是在内核中,但是该内核执行路径是以进程的身份,代表进程来争夺资源的。如果竞争失败,会发生进程上下文切换,当前进程进入睡眠状态,CPU将运行其他进程。鉴于进程上下文切换的开销也很大,因此,只有当进程占用资源时间较长时,用互斥体才是较好的选择。

当所要保护的临界区访问时间比较短时,用自旋锁是非常方便的,因为它可节省上下文切换的时间。

由此,可以总结出自旋锁和互斥体选用的3项原则。
1)当锁不能被获取到时,使用互斥体的开销是进程上下文切换时间,使用自旋锁的开销是等待获取自旋锁(由临界区执行时间决定)。若临界区比较小,宜使用自旋锁,若临界区很大,应使用互斥体。
2)互斥体所保护的临界区可包含可能引起阻塞的代码,而自旋锁则绝对要避免用来保护包含这样代码的临界区。因为阻塞意味着要进行进程的切换,如果进程被切换出去后,另一个进程企图获取本自旋锁,死锁就会发生。

3)互斥体存在于进程上下文,因此,如果被保护的共享资源需要在中断或软中断情况下使用,则在互斥体和自旋锁之间只能选择自旋锁。当然,如果一定要使用互斥体,则只能通过mutex_trylock()方式进行,不能获取就立即返回以避免阻塞。

(5)完成量(Completion)

它用于一个执行单元等待另一个执行单元执行完某事。

struct completion my_completion; //定义名为my_completion的完成量

/*  初始化完成量*/

init_completion(&my_completion);

reinit_completion(&my_completion)

/*等待完成量*/

void wait_for_completion(struct completion *c);

/*唤醒完成量*/

void complete(struct completion *c);
void complete_all(struct completion *c);

eg:

linux设备驱动学习(三)——并发控制相关推荐

  1. Linux设备驱动模型三 kset

    Linux设备驱动模型三 kset 1 kset数据结构 kset的定义在前文已有描述,我们再回顾一下: [cpp] view plain copy struct kset { /*与子kobject ...

  2. linux 两个驱动 竞态,第7章 Linux设备驱动中的并发控制之一(并发与竞态)

    本章导读 Linux设备驱动中必须解决的一个问题是多个进程对共享资源的并发访问,并发的访问会导致竞态(竞争状态). Linux提供了多种解决竞态问题的方式,这些方式适合不同的应用场景. 7.1讲解了并 ...

  3. linux设备驱动学习,linux设备驱动学习4

    Linux设备驱动程序学习(4) -高级字符驱动程序操作[(1)ioctl and llseek] 今天进入<Linux设备驱动程序(第3版)>第六章高级字符驱动程序操作的学习. 一.io ...

  4. Linux设备驱动中的并发控制总结

    并发(concurrency)指的是多个执行单元同时.并行被执行.而并发的执行单元对共享资源(硬件资源和软件上的全局.静态变量)的访问则容易导致竞态(race conditions).   SMP是一 ...

  5. Linux设备驱动开发详解:第7章 Linux设备驱动中的并发控制

    7.1并发与竞态 (1).竞态的发生场景:CPU0的进程与CPU1的进程之间.CPU0的中断与CPU1的进程之间.CPU0的中断与CPU1的中断之间: (2).解决竞态问题的途径是保证对共享资源的互斥 ...

  6. 君君学Linux设备驱动第三天之linux内核简简简介

    内核这东西不是一篇博客,日志能说清楚的,但是简要总结一下有利于后面的学习...... 一 内核的演变和发展 Linux是unix的一种克隆系统.它的发展依赖于五大支柱:unix系统(分时操作系统),m ...

  7. Linux设备驱动——第三章字符驱动

    当对幸福的憧憬过于急切,那痛苦就在人的心灵深处升起.--加缪 本章的目的是编写一个完整的字符设备驱动.我们开发一个字符驱动是因为这一类适合大部分简单的硬件设备.字符驱动也比块驱动易于理解.本章的最终目 ...

  8. linux设备驱动学习笔记(1)

    学习了将近半个月的设备驱动程序的编写,也有一些体会,这里写下来也给学习做一个总结,为后面的学习做更好的准备. 首先,个人感觉驱动程序的设计是很有套路的,最基本的要求就是要掌握这些套路.所谓的套路就是一 ...

  9. linux设备驱动学习(四)——阻塞与非阻塞I/O

    1.阻塞与非阻塞I/O介绍 阻塞和非阻塞I/O是设备访问的两种不同模式.阻塞操作是指在执行设备操作时,若不能获得资源,则挂起进程直到满足可操作的条件后再进行操作.被挂起的进程进入睡眠状态,被从调度器的 ...

  10. linux设备驱动学习(二)——字符设备编写及测试

    一.字符设备体结构介绍 1.字符设备作为linux内核三大驱动设备之一,主要完成字节的读写操作,常见的应用有鼠标.键盘等,结构体形式如下所示: struct cdev{ struct kobject ...

最新文章

  1. java 注解scheduler_使用Scheduler
  2. 16. Logging 模块的配置与使用
  3. maven 打jar包将配置文件,和lib包打在外面
  4. “好的软件人员一生必看的六十本书”
  5. cocoJS配置文件:project.json
  6. Vue007_ 表单输入绑定
  7. 【mac开发.NET】No installed provisioning profiles match the installed iOS signing identities
  8. 启动“powershell.exe”时出现错误 0x8007000
  9. linux emule 编译 wx-config --libs,LeezPi-RK3399_Android9编译说明
  10. java url解码_如何从REST WebService 调用中解码路径参数
  11. 练手小程序之约瑟芬杀人法
  12. BUUCTF [CISCN2019 总决赛 Day2 Web1] Easyweb
  13. 999宝藏网ghostxp-sp2(圣诞+纯净版)
  14. 联想笔记本e43l_联想昭阳E43L电脑配置
  15. Not have a lick 没有一丁点儿
  16. WWDC20 苹果发布会
  17. 关于RedisInsight 创建数据库时 connection time out 连接超时的问题
  18. k8s搭建--裸机搭建(Bare Metal)
  19. Python 的 AIML
  20. 互联网用户公众账号信息服务管理规定

热门文章

  1. style 放入css文件失效_React中使用CSS的7种方式
  2. android-ultra-pull-to-refresh list,[Android]Ultra-Pull-To-Refresh之listview下拉刷新、上拉加载的用例...
  3. 十四、JavaScript表单中的验证API
  4. 七、Excel中图表制作和展示
  5. 物理化学 多组成系统热力学
  6. 深度强化学习探索算法最新综述,近200篇文献揭示挑战和未来方向
  7. 算法全覆盖,还能玩星际争霸,开源决策智能平台OpenDILab面世
  8. AU R-CNN:利用专家先验知识进行表情运动单元检测的R-CNN模型
  9. 从三大顶会论文看百变Self-Attention
  10. CVPR 2019 | APDrawingGAN:人脸秒变艺术肖像画