Linux内核设计与实现(七)—— 内核同步方法(基于Linux 2.6内核)

文章介绍了多线程编程中的关键概念,包括临界区及其可能导致的并发问题,原子操作确保操作的完整性,自旋锁用于短暂同步,信号量用于处理长时间锁持有,以及互斥体作为另一种同步机制。此外,还提到了屏障在保证指令执行顺序中的作用,这些都是解决并发控制和资源同步的重要工具。
摘要由CSDN通过智能技术生成

一、临界区

临界区就是访问和操作临界资源的代码段。多个线程并发访问共享资源是不安全的,所以操作系统提供了一些方法来避免在临界区中并发访问。如下面这道大疆的面试题:

有一个全局变量 i,两个线程并发地执行 i++,请问在两个线程各执行 100 次后,i 的结果为多少?

答案为 100 ~ 200 之间都有可能(不相信的小伙伴们可以自己去试一下)!因为 i++ 看似只有一段代码,但翻译成汇编代码就有三段!这就是为什么我们需要用一些手段来对临界资源进行保护。

二、原子操作

原子操作可以保证操作不被打断(要么执行成功,要么执行失败)。

通常是内联函数,往往是通过内嵌汇编指令来实现。

原子操作的顺序性通过屏障(barrier)指令来实现。 

三、自旋锁(spin lock)

自旋锁最多只能被一个可执行线程持有。其他想要获取该锁的线程会一直进行忙循环-旋转-等待锁重新可用。

自旋锁不应该被长时间持有,因为其他想要获取该锁的线程会循环等待。可以使用信号量机制来使得在发生争用时,等待的线程能投入睡眠,而不是旋转。

体系结构相关代码定义在<asm/spinlock.h>,实际需要用到的接口定义在文件<linux/spinlock.h>中。使用形式如下:

DEFINE_SPINLOCK(mr_lock);
spin_lock(&mr_lock)
/* 临界区 */
spin_unlock(&mr_lock)

自旋锁是不可递归的! 如果一个线程试图获取自己持有的锁,那么它自己会自旋,并且没有机会释放锁,导致自己被永远锁死。

自旋锁可以用在中断处理程序中(此处不能使用信号量,因为信号量机制会导致睡眠,前面说过中断处理程序要求尽快执行完)

但是,使用锁可能会导致死锁,比如上面提到的一个线程试图去请求自己已经持有的锁,又或者是每个线程都持有一个锁,并且请求下一个进程所持有的锁,形成一个循环。所以,预防死锁的发生非常重要:

  • 按顺序请求锁。只要所有线程按照一定的顺序去请求锁,就可以避免上面第二种情况的死锁。
  • 防止发生饥饿。
  • 不要重复请求自己已经拥有的锁。 

四、信号量(semaphore)

Linux中信号量是一种睡眠锁。如果有一个线程试图获得一个已经被占用的信号量时,信号量会将其推进一个等待队列,然后让其睡眠。

信号量适用于锁会被长时间持有的情况。

信号量不能在中断程序中使用。因为在中断上下文中是不能进行调度的。

在请求信号量的时候不能占用自旋锁,因为持有自旋锁时是不能睡眠的。

信号量可以同时允许任意数量的锁持有者(可以自己声明),而自旋锁在一个时刻最多允许一个任务持有它。

P() 和 V() 操作,P为获得一个信号量,计数减一,如果减一后计数大于等于0,则获得锁,进入临界区,若小于0则放入等待队列;V为释放一个信号量,计数加一,若等待队列不为空,则等待的任务在被唤醒的同时获得该信号量。

互斥信号量

在一个时刻仅允许一个锁持有者,相当于可以睡眠的自旋锁。此时计数为1。

计数信号量

可以允许多个锁持有者,计数为大于1的非0值。

信号量被定义在文件<asm/semaphore.h>中,可以通过以下方式静态地声明信号量:

struct semaphore name;
sema_init(&name, count);

其中name为变量名,count为计数值。

创建互斥信号量的快捷方式:

static DECLARE_MUTEX(name);

五、互斥体(mutex)

和互斥信号量一样,静态定义互斥体:

static DECLARE_MUTEX(name);

动态初始化:

mutex_init(&mutex)

上锁和解锁:

mutex_lock(&mutex);
/* 临界区 */
mutex_unlock(&mutex);

当持有一个mutex时,进程不可退出(exit)。

mutex不能在中断或者中断下半部中使用。

六、顺序和屏障

当处理多处理器之间或硬件设备之间的同步问题时,有时需要在程序代码中以指定的顺序发出读内存和写内存指令。但是编译器和处理器为了提高效率,可能对读和写重新排序。所以处理器提供了一种机器指令来确保顺序,并且也可以指示编译器不要给定点周围的指令序列重新排序,这种指令称为屏障(barriers)

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值