Linux下的“锁”事儿
作者:网络转载 发布时间:[ 2016/6/30 14:09:43 ] 推荐标签:操作系统 Linux
原由
之所以写这篇文章当然还是在面试中涉及了对本文标题的相关问题-互斥锁和自旋锁的区别。听到这个问题的时候,我是比较忐忑的。互斥锁我还能简单说一些,但是对于自旋锁的了解几乎为零。为此,将总结Linux下的相关锁-那些“锁”事儿。知之为知之,不知为不知,是知也。不懂的地方,尽快查漏补缺!
简介
我们晓得在Linux内核中,同步机制是一大特性。比较经典的有原子操作、spin_lock(自旋锁)、mutex(互斥锁)、semaphore(信号量)等。
原子操作
原子操作,也是数据库事务的一大特性。是该操作绝不会在执行完之前被任何任务或者事件终止,要不全部执行,要不全部不执行。它是小的执行单位,原子操作需要硬件的支持,因此是架构相关的。它的API和原子类型定义都在内核源码树的include/asm/atomic.h文件中,都是用汇编语言实现。
原子操作主要用于实现资源计数,很多引用计数是通过原子操作实现的。
原子类型定义:
typedef struct{
volatile int counter;
}atomic_t;
volatile关键字修饰的字段可以通知gcc不要对该类型的数据做优化处理,对它的访问都是对内存的访问,而不是对寄存器的访问。
原子操作的API:
1)atomic_read(atomic_t *v);
作用:对原子类型的变量进行原子读操作,它返回原子类型的变量v的值。
2)atomic_set(atomic_t *v,int i);
作用:设置原子类型的变量v的值为i。
3)atomic_add(int i,atomic_t *v);
作用:给原子类型的变量v增加值i。
4)atomic_sub(int i,atomic_t *v);
作用:从原子类型的变量v中减去i。
5)atomic_sub_and_test(int i, atomic_t *v);
作用:从原子类型的变量v中减去i,并判断结果是否为0,如果为0,返回真,否则返回假。
6)atomic_inc(atomic_t *v);
作用:对原子类型变量v原子地增加1。
7)atomic_dec(atomic_t *v);
作用:对原子类型的变量v原子地减1。
8)atomic_dec_and_test(atomic_t *v);
作用:对原子类型的变量v原子地减1,并判断结果是否为0,如果为0,返回真,否则返回假。
9)int atomic_inc_and_test(atomic_t *v);
作用:对原子类型的变量v原子地增加1,并判断结果是否为0,如果为0,返回真,否则返回假。
10)atomic_add_negative(int i, atomic_t*v);
作用:对原子类型的变量v原子地增加i,并判断结果是否为负数,如果是,返回真,否则返回假。
11)atomic_add_return(int i, atomic_t *v);
作用:对原子类型的变量v原子地增加i,并且返回指向v的指针。
12)int atomic_sub_return(int i, atomic_t *v);
作用:从原子类型的变量v中减去i,并且返回指向v的指针。
13)int atomic_inc_return(atomic_t * v);
作用:对原子类型的变量v原子地增加1并且返回指向v的指针。
14)int atomic_dec_return(atomic_t * v);
作用:对原子类型的变量v原子地减1并且返回指向v的指针。
原子操作通常用于实现资源的引用计数,在TCP/IP协议栈的IP碎片处理中,使用了引用计数,碎片队列结构structipq描述了一个IP碎片,字段refcnt是引用计数器,它的类型为atomic_t,当创建IP碎片时(在函数ip_frag_create中),使用atomic_set函数把它设置为1,当引用该IP碎片时,使用函数atomic_inc把引用计数加1,当不需要引用该IP碎片时,使用函数ipq_put来释放该IP碎片,ipq_put使用函数atomic_dec_and_test把引用计数减1并判断引用计数是否为0,如果是释放Ip碎片。函数ipq_kill把IP碎片从ipq队列中删除,并把该删除的IP碎片的引用计数减1(通过使用函数atomic_dec实现)。
信号量
信号量的本质也是一个计数器,用来记录对某个资源(如共享内存)的存取状况。用来协调不同进程间的数据对象,主要的应用是共享内存方式的进程间通信。
一般情况,为了获取共享资源,进程需要执行如下步骤:
1)测试控制该资源的信号量;
2)如果该信号量为正,允许使用该资源,进程将型号量减一;
3)如果为0,则该资源目前不可用,进程sleep,知道信号量值大于0,才能被唤醒,从步骤1)开始执行;
4)当进程不再使用某信号量控制的资源时,信号量值加1,。如果此时有进程在sleep并等待此信号量,则可以唤醒该进程。
信号量的定义在头文件/usr/src/linux/include/linux/sem.h 中,信号量是一个数据集合,用户可以单独使用这一集合中的每个元素。
Linux2.6.26下定义的信号量结构体:
struct semaphore {
spinlock_t lock;
unsigned int count;
struct list_head wait_list;
};
从以上信号量的定义中,可以看到信号量底层使用到了spinlock的锁定机制,这个spinlock主要用来确保对count成员的原子性的操作(count--)和测试(count > 0)。
信号量的pv操作
信号量的P操作
(1)void down(struct semaphore *sem);
(2)int down_interruptible(struct semaphore *sem);
(3)int down_trylock(struct semaphore *sem);
(1)中的函数根据2.6.26中的代码注释,这个函数已经out了(Use of this function is deprecated),所以从实用角度,彻底忘了它吧。
(2)常用,函数原型:
/**
* down_interruptible - acquire the semaphore unless interrupted
* @sem: the semaphore to be acquired
*
* Attempts to acquire the semaphore. If no more tasks are allowed to
* acquire the semaphore, calling this function will put the task to sleep.
* If the sleep is interrupted by a signal, this function will return -EINTR.
* If the semaphore is successfully acquired, this function returns 0.
*/
int down_interruptible(struct semaphore *sem)
{
unsigned long flags;
int result = 0;
spin_lock_irqsave(&sem->lock, flags);
if (likely(sem->count > 0))
sem->count--;
else
result = __down_interruptible(sem);
spin_unlock_irqrestore(&sem->lock, flags);
return result;
}
函数说明:在保证原子操作的前提下,先测试count是否大于0,如果是说明可以获得信号量,这种情况下需要先将count--,以确保别的进程能否获得该信号量,然后函数返回,其调用者开始进入临界区。如果没有获得信号量,当前进程利用struct semaphore 中wait_list加入等待队列,开始睡眠。
对于需要休眠的情况,在__down_interruptible()函数中,会构造一个struct semaphore_waiter类型的变量struct semaphore_waiter定义如下:
struct semaphore_waiter
{
struct list_head list;
struct task_struct *task;
int up;
};
将当前进程赋给task,并利用其list成员将该变量的节点加入到以sem中的wait_list为头部的一个列表中,假设有多个进程在sem上调用down_interruptible,则sem的wait_list上形成的队列如下图:
(注:将一个进程阻塞,一般的经过是先把进程放到等待队列中,接着改变进程的状态,比如设为TASK_INTERRUPTIBLE,然后调用调度函数schedule(),后者将会把当前进程从cpu的运行队列中摘下)
相关推荐
更新发布
功能测试和接口测试的区别
2023/3/23 14:23:39如何写好测试用例文档
2023/3/22 16:17:39常用的选择回归测试的方式有哪些?
2022/6/14 16:14:27测试流程中需要重点把关几个过程?
2021/10/18 15:37:44性能测试的七种方法
2021/9/17 15:19:29全链路压测优化思路
2021/9/14 15:42:25性能测试流程浅谈
2021/5/28 17:25:47常见的APP性能测试指标
2021/5/8 17:01:11