现在的位置: 首页 > 技术文章 > 正文

linux kernel 信号量、互斥锁、自旋锁(转)

2017年10月27日 技术文章 ⁄ 共 3226字 ⁄ 字号 linux kernel 信号量、互斥锁、自旋锁(转)已关闭评论 ⁄ 阅读 1,822 次

1.信号量

1.1 概念

信号量又称为信号灯(semaphore),它是用来协调不同进程间的数据对象的,本质上信号量是一个计数器,它用来记录对某个资源(如共享内存)的存取状况。一般说来,为了获得共享资源,进程需要执行下列操作:
(1) 测试控制该资源的信号量。
(2) 若此信号量的值为正,则允许进行使用该资源。进程将信号量减1。
(3) 若此信号量为0,则该资源目前不可用,进程进入睡眠状态,直至信号量值大于0,进程被唤醒,转入步骤(1)。
(4) 当进程不再使用一个信号量控制的资源时,信号量值加1。如果此时有进程正在睡眠等待此信号量,则唤醒此进程。

1.2涉及结构、函数


/* 从以上信号量的定义中,可以看到信号量底层使用到了spin lock的锁定机制,这个spinlock主要用来确保对count成员的原子性的操作(count--)和测试(count > 0)。 */
struct semaphore {
spinlock_t lock;
unsigned int count;
struct list_head wait_list;
};

(1)信号量的获取操作


#
(1).void down(struct semaphore *sem); //废弃

 

/* (最常用)
在保证原子操作的前提下,先测试count是否大于0,
~ 如果是说明可以获得信号量,这种情况下需要先将count--,以确保别的进程能否获得该信号量,然后函数返回,其调用者开始进入临界区。
~ 如果没有获得信号量,当前进程利用struct semaphore 中wait_list加入等待队列,开始睡眠。
*/
(2).int down_interruptible(struct semaphore *sem);

 

/* 试图去获得一个信号量,如果没有获得,函数立刻返回1而不会让当前进程进入睡眠状态。 */
(3).int down_trylock(struct semaphore *sem);

(2)信号量的释放操作


/* 如果没有其他线程等待在目前即将释放的信号量上,那么只需将count++即可。如果有其他线程正因为等待该信号量而睡眠,那么调用__up唤醒睡眠的进程*/
void up(struct semaphore *sem);

----------

2.互斥锁

2.1概念

互斥体实现了“互相排斥”(mutual exclusion)同步的简单形式(所以名为互斥体(mutex))。互斥体禁止多个线程同时进入受保护的代码“临界区”(critical section)。因此,在任意时刻,只有一个线程被允许进入这样的代码保护区。mutex实际上是count=1情况下的semaphore

2.2涉及结构、函数


// 结构
struct mutex {
/* 1: unlocked, 0: locked, negative: locked, possible waiters */
atomic_t count;
spinlock_t wait_lock;
struct list_head wait_list;
#ifdef CONFIG_DEBUG_MUTEXES
struct thread_info *owner;
const char *name;
void *magic;
#endif
#ifdef CONFIG_DEBUG_LOCK_ALLOC
struct lockdep_map dep_map;
#endif
};


// 定义互斥锁lock
mutex_init(struct mutex* lock)

// 获取
mutex_lock(struct mutex *lock)

// 释放
mutex_unlock(struct mutex *lock)

3. 自旋锁

3.1 定义

在任何时刻,最多只能有一个保持者,也就说,在任何时刻最多只能有一个执行单元获得锁。但是两者在调度机制上略有不同。
~对于互斥锁,如果资源已经被占用,资源申请者只能进入睡眠状态。
~但是自旋锁不会引起调用者睡眠,如果自旋锁已经被别的执行单元保持,调用者就一直循环在那里看是否该自旋锁的保持者已经释放了锁,”自旋”一词就是因此而得名。
~最大的区别就是自旋锁不会休眠

3.2 涉及结构、函数

(1) 结构


# linux/Spinlock.h
typedef struct spinlock {
union { //联合
struct raw_spinlock rlock;
#ifdef CONFIG_DEBUG_LOCK_ALLOC
# define LOCK_PADSIZE (offsetof(struct raw_spinlock, dep_map))
struct{
u8 __padding[LOCK_PADSIZE];
struct lockdep_map dep_map;
};
#endif
};
} spinlock_t;

(2)定义和初始化


spinlock_t my_lock = SPIN_LOCK_UNLOCKED;
void spin_lock_init(spinlock_t *lock);

(3)操作函数


//加锁一个自旋锁函数
void spin_lock(spinlock_t *lock); //获取指定的自旋锁
void spin_lock_irq(spinlock_t *lock); //禁止本地中断获取指定的锁
void spin_lock_irqsave(spinlock_t *lock, unsigned long flags); //保存本地中断的状态,禁止本地中断,并获取指定的锁
void spin_lock_bh(spinlock_t *lock) //安全地避免死锁, 而仍然允许硬件中断被服务


//释放一个自旋锁函数
void spin_unlock(spinlock_t *lock); //释放指定的锁
void spin_unlock_irq(spinlock_t *lock); //释放指定的锁,并激活本地中断
void spin_unlock_irqrestore(spinlock_t *lock, unsigned long flags); //释放指定的锁,并让本地中断恢复到以前的状态
void spin_unlock_bh(spinlock_t *lock); //对应于spin_lock_bh


//非阻塞锁
int spin_trylock(spinlock_t *lock); //试图获得某个特定的自旋锁,如果该锁已经被争用,该方法会立刻返回一个非0值,
//而不会自旋等待锁被释放,如果成果获得了这个锁,那么就返回0.
int spin_trylock_bh(spinlock_t *lock);
//这些函数成功时返回非零( 获得了锁 ), 否则 0. 没有"try"版本来禁止中断.

//其他
int spin_is_locked(spinlock_t *lock); //和try_lock()差不多

4.自旋锁&互斥锁对比

信号量/互斥体允许进程睡眠属于睡眠锁,自旋锁则不允许调用者睡眠,而是让其循环等待,所以有以下区别应用
1)、信号量和读写信号量适合于保持时间较长的情况,它们会导致调用者睡眠,因而自旋锁适合于保持时间非常短的情况
2)、自旋锁可以用于中断,不能用于进程上下文(会引起死锁)。而信号量不允许使用在中断中,而可以用于进程上下文
3)、自旋锁保持期间是抢占失效的,自旋锁被持有时,内核不能被抢占,而信号量和读写信号量保持期间是可以被抢占的

5.信号量&互斥锁

使用场所:信号量主要适用于进程间通信,当然,也可用于线程间通信。而互斥锁只能用于线程间通信。

linux

linux

转自:http://blog.csdn.net/u012719256/article/details/52670098

×