linux中的同步机制

内核抢占

定义:进程正在执行内核函数时,即它在内核态运行时,允许发生内核切换(即当前进程被新的进程替换)。
关键点:
不管是抢占内核还是非抢占内核,运行在内核态的进程都可以自动放弃cpu。这种称之为计划性进程切换。比如无法获取到资源而转入睡眠状态。
在抢占型内核中,由异步事件引起的进程切换,称之为强制性进程切换。比如中断处理函数唤醒了高优先等级的进程。
禁止抢占的条件:
如果current_thread_info->tHRead_info->preempt_count的值大于0就禁止抢占。在以下任意一种情况下都是大于0的。(ps:和每个进程的抢占字段相关,并不是所有的进程都是一样的)
1.内核在执行中断服务例程。
2.可延迟函数被禁止时(表示内核正在执行软中断或者tasklets,即中断下半部)?
3.把计数器值设置为正数显示的禁止内核抢占。
此外,内核本地中断必须为打开状态,否则无法完成抢占。

Include/linux/preempt.h
preempt_count()							//选中tHRead_info中的preempt_count
preempt_disable()						//抢占计数器值加1
preempt_enable_no_resched()				//抢占计数器值减1
preempt_enable()						//抢占计数器值减1,如果thread_info中TIF_NEED_RESCHED标志被设置,将调用preempt_schedule( )函数。

Include/linux/smp.h
/* preempt_disable()类似,但要返回本地cpu的序列号 */
#define get_cpu()		({ preempt_disable(); smp_processor_id(); })	
#define put_cpu()		preempt_enable()							//相同的
#define put_cpu_no_resched()	preempt_enable_no_resched()			//相同的

注意:内核抢占会引起不容忽视的开销,在编译内核的时候可以通过选项来禁止或者使能内核抢占。

原子操作

基本原理:计算机某些指令只能一次连续而不被中断的执行,也即所谓的原子操作。

整形原子操作

  • 设置原子量值
ATOMIC_INIT(int i) At declaration, initialize to i.
void atomic_set(atomic_t *v, int i) Atomically set v equal to i.
  • 获取原子量值
int atomic_read(atomic_t *v) Atomically read the integer value of v.
  • 增加/减小原子变量
void atomic_add(int i, atomic_t *v) Atomically add i to v.
void atomic_sub(int i, atomic_t *v) Atomically subtract i from v.
  • 原子变量自增/自减
void atomic_inc(atomic_t *v) Atomically add one to v.
void atomic_dec(atomic_t *v) Atomically subtract one from v.
  • 操作并测试
int atomic_sub_and_test(int i, atomic_t *v) Atomically subtract i from v and return true if the result is zero;otherwise false.
int atomic_dec_and_test(atomic_t *v) Atomically decrement v by one and return true if zero; false otherwise.
int atomic_inc_and_test(atomic_t *v) Atomically increment v by one and return true if the result is zero;false otherwise.
  • 操作并返回
int atomic_add_return(int i, atomic_t *v) Atomically add i to v and return the result.
int atomic_sub_return(int i, atomic_t *v) Atomically subtract i from v and return the result.
int atomic_inc_return(int i, atomic_t *v) Atomically increment v by one and return the result.
int atomic_dec_return(int i, atomic_t *v) Atomically decrement v by one and return the result.
  • 其它操作
int atomic_add_negative(int i, atomic_t *v) Atomically add i to v and return true if the result is negative;otherwise false.

位原子操作

  • 设置位
void set_bit(int nr, void *addr) Atomically set the nr-th bit starting from addr.
  • 清除位
void clear_bit(int nr, void *addr) Atomically clear the nr-th bit starting from addr.
  • 改变位
void change_bit(int nr, void *addr) Atomically flip the value of the nr-th bit starting from addr.
  • 测试并设置位
int test_and_set_bit(int nr, void *addr) Atomically set the nr-th bit starting from addr and return the previous value.
  • 设置并清除位
int test_and_clear_bit(int nr, void *addr) Atomically clear the nr-th bit starting from addr and return the previous value.
  • 测试并改变位
int test_and_change_bit(int nr, void *addr) Atomically flip the nr-th bit starting from addr and return the previous value.
  • 测试位
int test_bit(int nr, void *addr) Atomically return the value

自旋锁

1.自旋锁的原理就像许多人要拿钥匙进房间一样,自旋锁是一种运行在多处理器环境下的特殊锁;
2.在自旋锁保护的临界区禁止内核抢占,在单cpu系统中,锁本身是没有用的,它仅仅是禁止/使能内核抢占,在自旋锁忙等待期间,内核抢占是使能的,因此,在等待自旋锁的进程可能被更高等级的进程取代。
3.自旋锁所有的宏都是基于原子操作,这样可以确保即使自旋锁在不同处理器上的多个进程试图修改锁时也可以被正确的更新。
4.自旋锁是全局变量,因此要保证它们不被并发访问。

自旋锁的使用步骤

  1. 定义自旋锁变量 spinlock_t testlock;
  2. 初始化锁 spin_lock_init(&testlock);
    以上两步也可以通过DEFINE_SPINLOCK(testlock)来实现
  3. 获取锁 spin_lock/spin_lock_irq/spin_lock_irqsave(&xlock)
  4. 释放锁 spin_unlock/spin_unlock_irq/spin_unlock_restore(&xlock)
    eg:
    自旋锁方法:
    使用的接口定义在<linux/spinlock.h>,自旋锁的基本使用形式如下:
 DEFINE_SPINLOCK(mylock);
 spin_lock(&mylock);
 /* 临界区 */
 spin_unlock(&mylock);

内核提供的禁止中断同时请求锁的接口如下:

DEFINE_SPINLOCK(mylock);
unsigned long flags;
spin_lock_irqsave(&mylock, flags);
 /* 临界区 */
spin_lock_irqrestore(&mylock, flags);

互斥体

和自旋锁最大的不同:
1.在等待资源的时候,进程会转入睡眠。
2.等待的信号量可以通过链表记录多个在等待的进程。

和信号量的区别:
1.更简单,更高效,开销更小。
2.可以进行调试,而信号量是不能调试的,即便是约束性调试也不可能。
3.使用场景更小,即约束性更强。
ps:互斥体是信号量的特例,互斥体只有两种状态。

内核参考文档:Documentation/locking/mutex-design.txt
内核源码:/kernel/locking/mutex.c
定义及头文件:/include/linux/mutex.h

1、初始化

mutex_init(&mutex); //动态初始化互斥锁
DEFINE_MUTEX(mutexname); //静态定义和初始化互斥锁

2、上锁

void mutex_lock(struct mutex *lock);
无法获得锁时,睡眠等待,不会被信号中断。
int mutex_trylock(struct mutex *lock);
此函数是 mutex_lock()的非阻塞版本,成功返回1,失败返回0。
int mutex_lock_interruptible(struct mutex *lock);
和mutex_lock()一样,也是获取互斥锁。在获得了互斥锁或进入睡眠直到获得互斥锁之后会返回0。如果在等待获取锁的时候进入睡眠状态收到一个信号(被信号打断睡眠),则返回_EINIR。

3、解锁

void mutex_unlock(struct mutex *lock);
用处:代表进程获取互斥数据的时候,比如:file_read()

4、测试

mutex_is_locked (struct mutex *) 
Returns one if the lock is locked and zero otherwise

eg:

Locking and unlocking the mutex :
mutex_lock(&mutex);
/* critical region ... */
mutex_unlock(&mutex);

注意:
1.在同一个上下上锁解锁
2.不能递归使用
3.当持有mutex时,进程不能退出。
4.不能在中断或者下半部使用,即使使用mutex_trylock()也不行。
5.只能通过官方api来初始化。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值