linux内核的自旋锁spin_lock和互斥锁mutex_lock

2023-11-08

访问共享资源的代码区域称作临界区。自旋锁(spin_lock)和互斥锁(mutex_lock)是保护内核临界区的两种基本机制。我们逐个分析。

自旋锁可以确保在同时只有一个线程进入临界区。其他想进入临界区的线程必须不停地原地打转,直到第1个线程释放自旋锁。注意:这里所说的线程不是内核线程,而是执行的线程。

理解自旋锁最简单的方法是把它作为一个变量看待,该变量把一个临界区或者标记为“我当前在运行,请稍等一会”或者标记为“我当前不在运行,可以被使用” 。如果 A 执行单元首先进入例程,它将持有自旋锁;当 B 执行单元试图进入同一个例程时,将获知自旋锁已被持有,需等到 A 执行单元释放后才能进入。

驱动工程师应谨慎使用自旋锁,而且在使用中还要特别注意如下几个问题。
1、自旋锁实际上是忙等锁,当锁不可用时,CPU 一直循环执行“测试并设置”该锁直到可用而取得该锁, CPU 在等待自旋锁时不做任何有用的工作, 仅仅是等待。 因此, 只有在占用锁的时间极短的情况下, 使用自旋锁才是合理的。当临界区很大或有共享设备的时候,需要较长时间占用锁,使用自旋锁会降低系统的性能。
2、自旋锁可能导致系统死锁。 引发这个问题最常见的情况是递归使用一个自旋锁,即如果一个已经拥有某个自旋锁的 CPU 想第二次获得这个自旋锁,则该 CPU 将死锁。此外,如果进程获得自旋锁之后再阻塞,也有可能导致死锁的发生。copy_from_user()、copy_to_user()和 kmalloc()等函数都有可能引起阻塞,因此在自旋锁的占用期间不能调用这些函数。

自旋锁的基本用法:

	//添加头文件
	#include <linux/spinlock.h> 

	//定义和初始化自旋锁spinlock
	spinlock_t my_lock; //定义自旋锁spinlock
	spin_lock_init(&my_lock); //初始化自旋锁spinlock

	spin_lock(&my_lock); //获取指定的自旋锁spinlock,保护临界区

	... /*临界资源*/

	spin_unlock(&my_lock); //释放指定的锁spinlock

与自旋锁不同的是,互斥锁在进入一个被占用的临界区之前不会原地打转,而是使当前线程进入睡眠状态。如果要等待的时间较长,互斥锁比自旋锁更合适,因为自旋锁会消耗CPU资源。在使用互斥锁的场合,多于2次进程切换时间都可被认为是长时间,因此一个互斥锁会引起本线程睡眠,而当其被唤醒时,它需要被切换回来。

因此,在很多情况下,决定使用自旋锁还是互斥锁相对来说很容易:

(1) 如果临界区需要睡眠,只能使用互斥锁,因为在获得自旋锁后进行调度、抢占以及在等待队列上睡眠都是非法的;

(2) 由于互斥锁会在面临竞争的情况下将当前线程置于睡眠状态,因此,在中断处理函数中,只能使用自旋锁。

互斥锁使用的基本方法:

	//添加头文件
	#include <linux/mutex.h> 
 
	//函数mutex_init()原型:void mutex_init(struct mutex *mutex);

	//定义和初始化互斥锁mutex
	struct mutex my_mutex;    //定义互斥锁mutex
	mutex_init(&my_mutex);    //初始化互斥锁mutex 

	mutex_lock(&my_mutex);    //获取互斥锁mutex 

	... /*临界资源*/

	mutex_unlock(&my_mutex); //释放互斥锁mutex
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

linux内核的自旋锁spin_lock和互斥锁mutex_lock 的相关文章

随机推荐