一.中断屏蔽
local_save_flags(flags);
local_irq_disable()
……..critical section…………
local_irq_restore(flags);
二.原子操作
位和整型变量的原子操作都依赖于底层CPU的原子操作,因此这些操作都与CPU架构密切相关。、
整型原子操作:
1.设置原子变量的值:
Void atomic_set(aotmic_v *v,int i) //设置原子变量的值为i
Automic_t v = ATOMIC_INIT(0) //定义原子变量v 并初始化为0
2.获取原子变量的值:
Atomic_read(aotmic_ t *v) //返回原子变量的值。
3.原子变量加/减:
Void atomic_add(int I ,atomic_t *v) //原子量加i
Void atomic_sub(int I ,atomic_t *v) //原子量减i
4.原子变量自增/自减:
Void atomic_inc(atomic_v *v)
Void atomic_dec(atomic_t *v)
5.操作并测试
Int atomic_inc_and_test(atomic_t *v)
Int atomic_dec_and_test(atomic_t *v)
Int atomic_sub_and_test(int I,atomic_t *v)
6.操作并返回
Int atomic_add_return(int I ,atomic_v *v)
Int atomic_sub_return(int I ,atomic_v *v)
Int atomic_inc_return (int I, atomic_v *v)
Int atomic_dec_return(int I,atomic_v *v)
三.自旋锁
自旋锁不关心锁定的临界区究竟在进行什么操作,不管是读还是写,它都一视同仁,即便多个单元同时读取临界资源也会被锁住,自旋锁的衍生锁读写自旋锁可允许读的并发,读写自旋锁是一种比自旋锁烂度更小锁机制,它保留了自旋的概念,但在写操作方面,只能最多有一个写进程,在读操作方面,可以有多个读执行单元,读和写不能同时进行。
自旋锁
1.定义自旋锁
spinlock_t lock;
2.初始化自旋锁
Spin_lock_init(lock)
3.获得自旋锁
Spin_lock(lock)
Spin_trylock(lock)
四.顺序锁
顺序锁是对读写锁的一种优化,若使用顺序锁,读执行单元不会被写执行单元所阻塞,写操作之间互斥,读操作与写操作之间不会被阻塞,如果读执行单元在读操作时,发生了写操作,那么读操作必须重新读,这样会导致读多次。
写执行单元涉及的顺序锁
1。获得顺序锁
Void write_seqlock(seqlock_t *sl)
Int write_tryseqlock(seqlock_t *sl)
Write_seqlock_irqsave(lock, flags)
Write_seqlock_irq(lock)
Write_seqlock_bh(lock)
Write_seqlock_irqsave(lock, flags) = local_irq_save() +write_seqlock()
Write_seqlock_irq(lock) = local_irq_disable()+write_seqlock()
Write_seqlock_bh(lock) = local_bh_disable()+write_seqlock()
2。释放顺序锁
Void write_sequnlock(seqlock_t *sl)
Write_sequnlock_irqstore(lock,flags)
Write_sequnlock_irq(lock)
Write_sequnlock_bh(lock)
Write_sequnlock_irqstore(lock,flags) = write_sequnlock()+local_irq_restore()
Write_sequnlock_irq(lock) = write_sequnlock()+local_irq_enable()
Write_sequnlock_bh(lock) = write_sequnlock()+local_bh_enable()
读执行单元涉及的顺序锁
1.读开始
Unsigned read_seqbegin(const seqlock_t *sl)
Read_seqbegin_irqsave(lock,flags)
读执行单元对被顺序锁sl保护的共享资源访问前需要调用此函数 ,此函数返回顺序锁sl的当前顺序号
2.重读
Int read_seqretry(const seqlock_t *sl,unsigned iv)
Read_seqretry_irqrestore(lock,iv,flags)
读—复制—更新
RCU
不同于自旋锁,RUC的读端没有锁,几乎可以认为是直接读,而RCU的写执行单元在访问它的共享资源前先复制一个复本,然后对副本进行修改,最后使用一个回调机制,在适当的时机指向原来数据的指针重新指向新的被修改的数据,这个时机就是所有引用数据的CPU都退出去共享数据读操作的时候,等适当时机的这一段时间称为宽限期。
RCU 操作
1.读锁定
Rcu_read_lock()
Rcu_read_lock_bh()
2.读解锁
Rcu_read_unlock()
Rcu_read_unlock_bh()
使用RCU进行读的模式下:
3.同步RCU
Synchronize_rcu()
4.挂接回调
Void call_rcu(struct rcu_head *head, void (*func)(struct rcu_head *rcu))
4.释放自旋锁
Spin_unlock(lock)
自旋锁主要针对SMP或者单CPU但内核可抢占的情况,对于单CPU和内核不抢占的系统,自旋锁退化为空操作。尽管自旋锁可以保证临界区不受别的CPU和本CPU内的抢占进程打扰,但是得到锁的代码路径在执行临界区的时候,还可能受到中断和底半部BH的影响。
关中断 :local_irq_disable()
开中断: local_irq_enable()
关底半部 : local_bh_diable()
开底半部: local_bh_enable()
关中断并保存状态字:local_irq_save()
开中断并恢复状态字:local_irq_restore()
在多核中,如果进程和中断访问同一片临界资源 ,我们一般需要在进程上下文中调用spin_lock_irqsave()/spin_unlock_irqrestore(), 在中断上下文调用spin_lock()/spin_unlock()
注意:
1.自旋锁实际上是忙等锁,当锁不可用时,cpu一直执行 测试并设置,直到得到,CPU在等锁时,不做任何有用的工作,因此只在占用锁很短情况下,才可以
2.自旋锁可能导致系统死锁,递归锁
3。在自旋锁锁定期间,不能调用可能引起进程调度的函数,如果在获得自旋锁后再阻塞,如copy_from_user(),copy_to_user(),kmalloc() ,msleep()等
4。在单核情况下,要认为自己的多枋的,因为跨平台,在单CPU下,中断与进程可能访问同一临界区,进程中使用spin_lock_irqsave()是安全的,在中断里其实不调用spin_lock()也没有问题,在为spin_lock_irqsave()可以保证这个CPU的中断服务程序可能 执行,但是cpu如果是多核的,spin_lock_irqsave()不能屏蔽另外一个核的中断,所以另一个核就有可能引起并发。
读写自旋锁
1.定义和初始化读写自旋锁
rwlock_t my_rw_lock
Rwlock_init(&my_rwlock)
2.读锁定
Void read_lock(rwlock_t *lock)
Void read_lock_irqsave(rwlock_t *lock,unsigned long flags)
Void read_lock_irq(rwlock_t *lock)
Void read_lock_bh(rwlock_t *lock)
3.读解锁
Void read_unlock(rwlock_t *lock)
Void read_unlock_irqrestore(rwlock_t *lock,unsigned long flags)
Void read_unlock_irq(rwlock_t *lock)
Void read_unlock_bh(rwlock_t *lock)
4.写锁定
Void write_lock(rwlock_t *lock)
Void write_lock_irqsave(rwlock_t *lock,unsigned long flags)
Void write_lock_irq(rwlock_t *lock)
Void write_lock_bh(rwlock_t *lock)
Int write_tyrlock(rwlock_t *lock)
5.写解锁
Void write_unlock(rwlock_t *lock)
Void write_unlock_irqrestore(rwlock_t *lock,unsigned long flags)
Void write_unlock_irq(rwlock_t *lock)
Void write_unlock_bh(rwlock_t *lock)
五.信号量
信号量,是典型的用于同步与互斥的手段,信号量的值可以是0,1或者n,信号量与操作系统中的经典概念PV操作对应:
P:1. 将信号量S值减 1即S=S-1
2.如果S>=0,则该进程继续执行,否则等待
V:1.将信号量S值加1,即S=S+1
2.如果S>0,唤醒队列中等待信号量的进程。
1.定义信号量:
Struct semaphore sem
2.初始化信号量:
Void sema_init(struct semaphore *sem, int val)
3.获得信号量:
Void down(struct seaphore *sem)
用于获取信号量,它会导致睡眠,因此不能用在中断上下文
Void down_interruptible(struct semaphore *sem)
进入睡眠的信号,可以被打断
Int down_trylock(struct semaphore *sem)可以用在中断上下文
尝试获得信号
4.释放信号量
Void up(struct semaphore *sem)
六.互斥体
1、当锁不能被获取到时,通过开销来衡量
使用互斥的开销:进程上下文的切换时间。如果临界区很大,应该选用互斥锁。
使用自旋锁的开销:忙等待自旋锁,直到锁释放。如果执行临界区时间小,应该选自旋锁。
2、由是否引起阻塞。进程上下文切换容易进入睡眠状态,如果使用自旋锁就会发生死锁。
3、保护共享资源需要在中断或者软终端情况下使用,最好选自旋锁,不会引起睡眠,(当然还可以使用mutex_trylock()互斥体避免阻塞)。
1.初始化
Struct mutex my_mutex;
Mutex_init(&my_mutex)
2.获取互斥体
Void mutex_lock(struct mutex *lock)
Int mutex_lock_interruptible(struct mutex *lock)
Int mutex_trylock(struct mutext *lock)
3.释放互斥体
Void muext_unlock(struct mutex *lock)
七.完成量
1、当锁不能被获取到时,通过开销来衡量
使用互斥的开销:进程上下文的切换时间。如果临界区很大,应该选用互斥锁。
使用自旋锁的开销:忙等待自旋锁,直到锁释放。如果执行临界区时间小,应该选自旋锁。
2、由是否引起阻塞。进程上下文切换容易进入睡眠状态,如果使用自旋锁就会发生死锁。
3、保护共享资源需要在中断或者软终端情况下使用,最好选自旋锁,不会引起睡眠,(当然还可以使用mutex_trylock()互斥体避免阻塞)。
1.初始化
Struct mutex my_mutex;
Mutex_init(&my_mutex)
2.获取互斥体
Void mutex_lock(struct mutex *lock)
Int mutex_lock_interruptible(struct mutex *lock)
Int mutex_trylock(struct mutext *lock)
3.释放互斥体
Void muext_unlock(struct mutex *lock)
八.阻塞与非阻塞
阻塞即需要等待结果返回,当没有结果返回时,进程将进入休眠
非阻塞则不需要等待结果返回,立即返回
常用等待队列来实现阻塞进程的唤醒。
1。定义等待队列头部
——————————————–typedef struct __wait_queue_head wait_queue_head_t;
Wait_queue_head_t my_queue
Wait_queue_head_t 是__wait_queue_head结构体的一个typedef
2。初始化等待队列头部
Init_waitqueue_head(&my_queue)
DECLARE_WAIT_QUEUE_HEAD(name)
3。定义等待队列元素
DECLARE_WAITQUEUE
4.添加移除队列
Void add_wait_queue(wait_queue_head_t *q,wait_queue_t *wait)
Void remove_wait_queue(wait_queue_head_t *q, wait_queue_t *wait)
5.等待事件
Wait_event(queue,condition)
Wait_event_interruptible(queue,condition)
Wait_event_timeout(queue,condition,timeout)
Wait_event_interruptible_timeout(queue,condition,timeout)
6。唤醒队列
Void wake_up(wait_queue_head_t *queue)
7。在等待队列上睡眠
Void wake_up_interruptible(wait_queue_head_t *queue)
打赏作者
详细,有用
不错
good