Linux设备驱动中的并发控制(三)

简介: Linux设备驱动中的并发控制(三)

信号量

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

信号量与操作系统中的经典概念PV操作对应。

+ P(S):①将信号量S的值减1,即S=S-1;②如果S≥0,则该进程继续执行;否则该进程置为等待状态,排入等待队列。
+ V(S):①将信号量S的值加1,即S=S+1;②如果S>0,唤醒队列中等待信号量的进程。Linux中与信号量相关的操作主要有下面几种。
  • 1.定义信号量
    下列代码定义名称为sem的信号量:
struct semaphore sem;
  • 2.初始化信号量
void sema_init(struct semaphore *sem, int val);

该函数初始化信号量,并设置信号量sem的值为val。

  • 3.获得信号量
void down(struct semaphore * sem);

该函数用于获得信号量sem,它会导致睡眠,因此不能在中断上下文中使用。

int down_interruptible(struct semaphore * sem);

该函数功能与down类似,不同之处为,因为down()进入睡眠状态的进程不能被信号打断,但因为down_interruptible()进入睡眠状态的进程能被信号打断,信号也会导致该函数返回,这时候函数的返回值非0。

int down_trylock(struct semaphore * sem);

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

在使用down_interruptible()获取信号量时,对返回值一般会进行检查,如果非0,通常立即返回-ERESTARTSYS,如:

if (down_interruptible(&sem))
    return  -ERESTARTSYS;
  • 4.释放信号量
void up(struct semaphore * sem);

该函数释放信号量sem,唤醒等待者。作为一种可能的互斥手段,**信号量可以保护临界区,它的使用方式和自旋锁类似。**与自旋锁相同,只有得到信号量的进程才能执行临界区代码。但是,与自旋锁不同的是,当获取不到信号量时,进程不会原地打转而是进入休眠等待状态。用作互斥时,信号量一般这样被使用:

由于新的Linux内核倾向于直接使用mutex作为互斥手段,信号量用作互斥不再被推荐使用。

信号量也可以用于同步,一个进程A执行down()等待信号量,另外一个进程B执行up()释放信号量,这样进程A就同步地等待了进程B。其过程类似:

此外,对于关心具体数值的生产者/消费者问题,使用信号量则较为合适。因为生产者/消费者问题也是一种同步问题。

互斥体

尽管信号量已经可以实现互斥的功能,但是“正宗”的mutex在Linux内核中还是真实地存在着。

下面代码定义了名为my_mutex的互斥体并初始化它:

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);

mutex_lock()与mutex_lock_interruptible()的区别和down()与down_trylock()的区别完全一致,前者引起的睡眠不能被信号打断,而后者可以。

mutex_trylock()用于尝试获得mutex,获取不到mutex时不会引起进程睡眠。

下列函数用于释放互斥体:

void mutex_unlock(struct mutex *lock);

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

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

自旋锁和互斥体都是解决互斥问题的基本手段,面对特定的情况,应该如何取舍这两种手段呢?选择的依据是临界区的性质和系统的特点。

从严格意义上说,互斥体和自旋锁属于不同层次的互斥手段,前者的实现依赖于后者。

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

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

当所要保护的临界区访问时间比较短时,用自旋锁是非常方便的,因为它可节省上下文切换的时间。但是CPU得不到自旋锁会在那里空转直到其他执行单元解锁为止,所以要求锁不能在临界区里长时间停留,否则会降低系统的效率。

由此,可以总结出自旋锁和互斥体选用的3项原则。

  • 1)当锁不能被获取到时,使用互斥体的开销是进程上下文切换时间,使用自旋锁的开销是等待获取自旋锁(由临界区执行时间决定)。若临界区比较小,宜使用自旋锁,若临界区很大,应使用互斥体
  • 2)互斥体所保护的临界区可包含可能引起阻塞的代码,而自旋锁则绝对要避免用来保护包含这样代码的临界区。因为阻塞意味着要进行进程的切换,如果进程被切换出去后,另一个进程企图获取本自旋锁,死锁就会发生。
  • 3)互斥体存在于进程上下文,因此,如果被保护的共享资源需要在中断或软中断情况下使用,则在互斥体和自旋锁之间只能选择自旋锁。当然,如果一定要使用互斥体,则只能通过mutex_trylock()方式进行,不能获取就立即返回以避免阻塞。(防止中断信号打不断睡眠造成了死锁)

完成量

Linux提供了完成量(Completion,关于这个名词,至今没有好的翻译,笔者将其译为“完成量”),它用于一个执行单元等待另一个执行单元执行完某事。

Linux中与完成量相关的操作主要有以下4种。

  • 1.定义完成量
    下列代码定义名为my_completion的完成量:
struct completion my_completion;
  • 2.初始化完成量
    下列代码初始化或者重新初始化my_completion这个完成量的值为0(即没有完成的状态):
init_completion(&my_completion);
reinit_completion(&my_completion)
  • 3.等待完成量
    下列函数用于等待一个完成量被唤醒:
void wait_for_completion(struct completion *c);
  • 4.唤醒完成量
    下面两个函数用于唤醒完成量:
void complete(struct completion *c);
void complete_all(struct completion *c);

前者只唤醒一个等待的执行单元,后者释放所有等待同一完成量的执行单元。

完成量用于同步的流程一般如下:

目录
相关文章
|
22天前
|
安全 Linux 网络虚拟化
Linux网络名称空间和Veth虚拟设备的关系
在讨论Linux网络名称空间和veth(虚拟以太网对)之间的关系时,我们必须从Linux网络虚拟化的核心概念开始。Linux网络名称空间和veth是Linux网络虚拟化和容器化技术的重要组成部分,它们之间的关系密不可分,对于构建隔离、高效的网络环境至关重要。😊
|
22天前
|
Linux 网络安全 网络虚拟化
Linux虚拟网络设备:底层原理与性能优化深度解析
在深入探讨Linux虚拟网络设备的底层原理之前,重要的是要理解这些设备如何在Linux内核中实现,以及它们如何与操作系统的其他部分交互以提供高效且灵活的网络功能。虚拟网络设备在现代网络架构中发挥着关键作用🔑,特别是在云计算☁️、容器化📦和网络功能虚拟化(NFV)环境中。
Linux虚拟网络设备:底层原理与性能优化深度解析
|
22天前
|
Linux 网络虚拟化 虚拟化
Linux虚拟网络设备深度解析:使用场景、分类与开发者指南
Linux虚拟网络设备支撑着各种复杂的网络需求和配置,从基础的网络桥接到高级的网络隔离和加密🔐。以下是对主要Linux虚拟网络设备的介绍、它们的作用以及适用场景的概览,同时提出了一种合理的分类,并指出应用开发人员应该着重掌握的设备。
Linux虚拟网络设备深度解析:使用场景、分类与开发者指南
|
22天前
|
安全 Linux API
Linux设备模型统一:桥接硬件多样性与应用程序开发的关键
在Linux的宏大世界中,各种各样的硬件设备如星辰般繁多。从常见的USB设备到复杂的网络接口卡,从嵌入式设备到强大的服务器,Linux需要在这些差异极大的硬件上运行。这就引出了一个问题:Linux是如何统一这些不同硬件的设备模型的呢?本文将探讨Linux是如何针对不同的硬件统一设备模型的,这一统一的设备模型对于应用程序开发人员来说又有何意义。让我们一探究竟🕵️‍♂️。
Linux设备模型统一:桥接硬件多样性与应用程序开发的关键
|
22天前
|
Cloud Native Linux 网络虚拟化
深入理解Linux veth虚拟网络设备:原理、应用与在容器化架构中的重要性
在Linux网络虚拟化领域,虚拟以太网设备(veth)扮演着至关重要的角色🌐。veth是一种特殊类型的网络设备,它在Linux内核中以成对的形式存在,允许两个网络命名空间之间的通信🔗。这篇文章将从多个维度深入分析veth的概念、作用、重要性,以及在容器和云原生环境中的应用📚。
深入理解Linux veth虚拟网络设备:原理、应用与在容器化架构中的重要性
|
1天前
|
Linux 数据安全/隐私保护 Windows
linux 搭建cloudreve win映射网络驱动器WebDav
linux 搭建cloudreve win映射网络驱动器WebDav
|
2天前
|
传感器 物联网 Linux
物联网设备的操作系统之争:Linux vs RTOS
【6月更文挑战第4天】在遥远的数码星球,物联网城中的Linux先生与RTOS小姐展开激烈角逐,分别在操作系统领域各显神通。Linux先生以其开源、兼容性强、功能丰富占据服务器、桌面及嵌入式设备市场,适合处理复杂任务和需要强大计算能力的设备。而RTOS小姐以实时性、高效响应和低资源占用见长,适用于资源有限、强调实时性的物联网设备。设备制造商在两者间抉择,引发物联网设备操作系统的选择大战。通过Python与FreeRTOS示例,展现了两者在智能家居和生产线控制等场景的应用。在物联网世界,Linux与RTOS共同推动设备智能化,为生活带来更多便捷。
24 3
|
22天前
|
Linux 芯片 Ubuntu
Linux驱动入门 —— 利用引脚号操作GPIO进行LED点灯
Linux驱动入门 —— 利用引脚号操作GPIO进行LED点灯
|
22天前
|
Ubuntu Linux
Linux驱动入门 —— 利用寄存器操作GPIO进行LED点灯-2
Linux驱动入门 —— 利用寄存器操作GPIO进行LED点灯
Linux驱动入门 —— 利用寄存器操作GPIO进行LED点灯-2
|
22天前
|
Linux 芯片
Linux驱动入门 —— 利用寄存器操作GPIO进行LED点灯-1
Linux驱动入门 —— 利用寄存器操作GPIO进行LED点灯
Linux驱动入门 —— 利用寄存器操作GPIO进行LED点灯-1