Linux内核同步机制
作者:网络转载 发布时间:[ 2015/9/23 10:52:19 ] 推荐标签:操作系统 接口 线程
而关于自旋锁的缺点?这里找到ibm一个文章
信号量(semaphore):通用的 和读写的
相对于自旋锁,它大的特点是允许调用它的线程进入睡眠
/* Please don't access any members of this structure directly */
struct semaphore {
raw_spinlock_t lock;
unsigned int count;
struct list_head wait_list;
};
void sema_init(struct semaphore *sem, int val); // val值代表了同时多少个线程可以进入临界区,一般为1 即作为互斥体使用;当然>1 时,并发操作同一资源会引发什么呢?
down_interruptible(struct semaphore *sem); // 获取信号量 ,它是可以中断的。
up(struct semaphore *sem); // 释放信号量,一般配对使用,当然也可以在别的线程里释放它。
读写信号量:rwsem 它和读写自旋锁类似 除了线程可以睡眠
/* the rw-semaphore definition
* - if activity is 0 then there are no active readers or writers
* - if activity is +ve then that is the number of active readers
* - if activity is -1 then there is one active writer
* - if wait_list is not empty, then there are processes waiting for the semaphore
*/
struct rw_semaphore {
__s32 activity;
raw_spinlock_t wait_lock;
struct list_head wait_list;
#ifdef CONFIG_DEBUG_LOCK_ALLOC
struct lockdep_map dep_map;
#endif
};
init_rwsem(sem) ;
// 初始化
down_read(struct rw_semaphore *sem);
// 获取读信号量
up_read(struct rw_semaphore *sem);
//释放读信号量
down_write(struct rw_semaphore *sem);
//获取写信号量
up_write(struct rw_semaphore *sem);
// 释放写信号量
互斥体(mutex):和count=1的信号量几乎没有区别,当然拥有互斥锁的进程总是尽可能的在短时间内释放
struct mutex {
/* 1: unlocked, 0: locked, negative: locked, possible waiters */
atomic_t count;
spinlock_t wait_lock;
struct list_head wait_list;
#if defined(CONFIG_DEBUG_MUTEXES) || defined(CONFIG_SMP)
struct task_struct *owner;
#endif
#ifdef CONFIG_DEBUG_MUTEXES
const char *name;
void *magic;
#endif
#ifdef CONFIG_DEBUG_LOCK_ALLOC
struct lockdep_map dep_map;
#endif
};
mutex_init(mutex);
// init 互斥锁
mutex_lock();
//获取互斥锁,几乎都能获取
mutex_unlock();
//释放互斥锁
原子操作(atomic)(和架构相关,是多条指令相当于一条指令执行,多用于计数)
组要是在smp上有意义,防止多条指令被多cpu执行。也是为了实现互斥。
顺序锁(sequence)
特点:
和读写自旋锁锁类似,但是它的写不会等待。写的时候持有自旋锁。首先读者的代码应该尽可能短且写者不能频繁获得锁,其次被保护的数据结构不包括被写修改的指针或被读间接引用的指针。当要保护的资源很小很简单,会很频繁被访问并且写入操作很少发生且必须快速时,可以用seqlock。
Seqlock:
typedef struct {
unsigned sequence;
spinlock_t lock;
} seqlock_t;
seqlock_init(x) / DEFINE_SEQLOCK(x)
// init
write_seqlock(seqlock_t *sl) ;
// 获取写锁
write_sequnlock(seqlock_t *sl);
read_seqbegin 和 read_seqretry 结合使用
//读时如果有写锁,则循环等待直到锁释放.
应用实例drivers/md/md.c
retry:
seq = read_seqbegin(&bb->lock);
memset(bbp, 0xff, PAGE_SIZE);
for (i = 0 ; i < bb->count ; i++) {
u64 internal_bb = p[i];
u64 store_bb = ((BB_OFFSET(internal_bb) << 10)
| BB_LEN(internal_bb));
bbp[i] = cpu_to_le64(store_bb);
}
bb->changed = 0;
if (read_seqretry(&bb->lock, seq))
goto retry;
RCU:read-copy-update
在linux提供的所有内核互斥设施当中属于一种免锁机制。Rcu无需考虑读和写的互斥问题。
它实际上是rwlock的一种优化。读取者不必关心写入者。所以RCU可以让多个读取者与写入者同时工作。写入者的操作比例在10%以上,需要考虑其他互斥方法。并且必须要以指针的方式来访问被保护资源。
Rcu_read_lock //仅仅是关闭抢占
Rcu_read_unlock //打开抢占
Rcu_assign_pointer(ptr,new_ptr)
//等待队列:它并不是一种互斥机制。它辅助comletion。
//它主要用来实现进程的睡眠等待。
//操作接口:wait/ wake_up
struct __wait_queue_head {
spinlock_t lock;
struct list_head task_list;
};
typedef struct __wait_queue_head wait_queue_head_t;
完成接口(completion) :该机制被用来在多个执行路径间作同步使用,即协调多个执行路径的执行顺序。如果没有完成体,则睡眠在wait_list上。这里usb 在提交urb时会用到。
如果驱动程序要在执行后面操作之前等待某个过程的完成,它可以调用wait_for_completion,以要完成的事件为参数:
Completion机制是线程间通信的一种轻量级机制:允许一个线程告诉另一个线程工作已经完成
struct completion {
unsigned int done;
wait_queue_head_t wait;
};
接口:
DECLARE_COMPLETION(x)
// 静态定义completion
init_completion(struct completion *x);
// 动态init
INIT_COMPLETION(x);
// 初始化一个已经使用过的completion
Wait_for_completion(struct completion *x);
complete(struct completion *);
//done +1,唤醒等待的一个。
Complete_all
// 唤醒所有的,一般不会用。
内存屏障
内存屏障主要有:读屏障、写屏障、通用屏障、优化屏障
内存屏障主要解决了两个问题:单处理器下的乱序问题和多处理器下的内存同步问题
编译器优化以保证程序上下文因果关系为前提。
以 读屏障为例,它用于保证读操作有序。屏障之前的读操作一定会先于屏障之后的读操作完成,写操作不受影响,同属于屏障的某一侧的读操作也不受影响。类似的, 写屏障用于限制写操作。而通用屏障则对读写操作都有作用。而优化屏障则用于限制编译器的指令重排,不区分读写。前三种屏障都隐含了优化屏障的功能。比如:
tmp = ttt; *addr = 5; mb(); val = *data;
有了内存屏障了确保先设置地址端口,再读数据端口。而至于设置地址端口与tmp的赋值孰先孰后,屏障则不做干预。有了内存屏障,可以在隐式因果关系的场景中,保证因果关系逻辑正确。
在Linux中,优化屏障是barrier()宏,它展开为asm volatile(“”:::”memory”)
smp_rmb(); // 读屏障
smp_wmb(); //写屏障
smp_mb(); // 通用屏障
Blk:大内核锁
BKL(大内核锁)是一个全局自旋锁,使用它主要是为了方便实现从Linux初的SMP过度到细粒度加锁机制。它终将退出历史舞台。
BKL的特性:
持有BKL的任务仍然可以睡眠 。因为当任务无法调度时,所加的锁会自动被抛弃;当任务被调度时,锁又会被重新获得。当然,并不是说,当任务持有BKL时,睡眠是安全的,紧急是可以这样做,因为睡眠不会造成任务死锁。
BKL是一种递归锁。一个进程可以多次请求一个锁,并不会像自旋锁那么产生死锁。BKL可以在进程上下文中。
BKL是有害的:
在内核中不鼓励使用BKL。一个执行线程可以递归的请求锁lock_kernel(),但是释放锁时也必须调用同样次数的unlock_kernel()操作,在后一个解锁操作完成之后,锁才会被释放。BKL在被持有时同样会禁止内核抢占。多数情况下,BKL更像是保护代码而不是保护数据.
备注:单核不可抢占内核 的异步事件是硬件中断 ,所以想要同步即关闭中断即可。对于单核可抢占和多核可抢占的 ,除了中断 还有进程调度(即优先级高的进程抢占cpu资源),而上述所有这些机制都是为了防止并发。
本文内容不用于商业目的,如涉及知识产权问题,请权利人联系SPASVO小编(021-61079698-8054),我们将立即处理,马上删除。
相关推荐
Linux下开源的DDR压力测试工具曝Linux恶意软件:让树莓派设备挖掘数字货币linux系统中不同颜色的文件夹及根目录介绍软件测试工程师必知必会Linux命令Linux下DNS服务器配置如何成为不可替代的Linux运维工程师?详解Linux进程(作业)的查看和杀死Linux 日志定时轮询流程详解比特币勒索病毒不只Windows系统有,Linux版的来了Linux日志定时轮询流程详解Linux iommu和vfio概念空间解构Linux系统如何低于TCP洪水攻击Linux无损调整分区大小Linux下防火墙配置实例Linux使用Jexus托管Asp.Net Core应用程序Linux中引号的那些事
更新发布
功能测试和接口测试的区别
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热门文章
常见的移动App Bug??崩溃的测试用例设计如何用Jmeter做压力测试QC使用说明APP压力测试入门教程移动app测试中的主要问题jenkins+testng+ant+webdriver持续集成测试使用JMeter进行HTTP负载测试Selenium 2.0 WebDriver 使用指南