linux 内核同步互斥技术之顺序锁 |
您所在的位置:网站首页 › 内核互斥锁 › linux 内核同步互斥技术之顺序锁 |
顺序锁
顺序锁区分读者和写者,和读写自旋锁相比,它的优点是不会出现写者饿死的情况。读者不会阻塞写者,读者读数据的时候写者可以写数据。顺序锁有序列号,写者把序列号加 1,如果读者检测到序列号有变化,发现写者修改了数据,将会重试,读者的代价比较高。 顺序锁支持两种类型的读者。 (1)顺序读者( sequence readers):不会阻塞写者,但是如果读者检测到序列号有变化,发现写者修改了数据,读者将会重试。 (2)持锁读者( locking readers):如果写者或另一个持锁读者正在访问临界区,持锁读者将会等待。持锁读者也会阻塞写者。这种情况下顺序锁退化为自旋锁。如果使用顺序读者,那么互斥访问的资源不能是指针,因为写者可能使指针失效,读者访问失效的指针会出现致命的错误。 顺序锁比读写自旋锁更加高效,但读写自旋锁适用于所有场合,而顺序锁不能适用于所有场合,所以顺序锁不能完全替代读写自旋锁。 顺序锁有两个版本。 (1)完整版的顺序锁提供自旋锁和序列号。 (2)顺序锁只提供序列号,使用者有自己的自旋锁。 完整版的顺序锁完整版的顺序锁的定义如下: include/linux/seqlock.h typedef struct { struct seqcount seqcount; spinlock_t lock; } seqlock_t; typedef struct seqcount { unsigned sequence; #ifdef CONFIG_DEBUG_LOCK_ALLOC struct lockdep_map dep_map; #endif } seqcount_t; 成员 seqcount 是序列号,成员 lock 是自旋锁。 定义并且初始化静态顺序锁的方法如下:DEFINE_SEQLOCK(x) 运行时动态初始化顺序锁的方法如下:seqlock_init(x) 顺序读者读数据的方法如下: seqlock_t seqlock; unsigned int seq; do { seq = read_seqbegin(&seqlock); //读数据 } while (read_seqretry(&seqlock, seq)); 首先调用函数 read_seqbegin 读取序列号,然后读数据,最后调用函数 read_seqretry 判断序列号是否有变化。如果序列号有变化,说明写者修改了数据,那么读者需要重试。 持锁读者读数据的方法如下: seqlock_t seqlock; read_seqlock_excl(&seqlock); //读数据 read_sequnlock_excl(&seqlock); 函数 read_seqlock_excl 有一些变体。 (1) read_seqlock_excl_bh():申请自旋锁,并且禁止当前处理器的软中断。 (2) read_seqlock_excl_irq():申请自旋锁,并且禁止当前处理器的硬中断。 (3) read_seqlock_excl_irqsave():申请自旋锁,保存当前处理器的硬中断状态,并且禁止当前处理器的硬中断。 读者还可以根据情况灵活选择:如果没有写者在写数据,那么读者成为顺序读者;如果写者正在写数据,那么读者成为持锁读者。方法如下: seqlock_t seqlock; unsigned int seq = 0; do { read_seqbegin_or_lock(&seqlock, &seq); //读数据 } while (need_seqretry(&seqlock, seq)); done_seqretry(&seqlock, seq); 函数 read_seqbegin_or_lock 有一个变体。 read_seqbegin_or_lock_irqsave:如果没有写者在写数据,那么读者成为顺序读者;如果写者正在写数据,那么读者成为持锁读者,申请自旋锁,保存当前处理器的硬中断状态,并且禁止当前处理器的硬中断。 写者写数据的方法如下: write_seqlock(&seqlock); //写数据 write_sequnlock(&seqlock); 函数 write_seqlock 有一些变体。 (1) write_seqlock_bh():申请写锁,并且禁止当前处理器的软中断。 (2) write_seqlock_irq():申请写锁,并且禁止当前处理器的硬中断。 (3) write_seqlock_irqsave():申请写锁,保存当前处理器的硬中断状态,并且禁止当前处理器的硬中断。 函数 write_seqlock 的代码如下: include/linux/seqlock.h static inline void write_seqlock(seqlock_t *sl) { spin_lock(&sl->lock); write_seqcount_begin(&sl->seqcount); } static inline void write_seqcount_begin(seqcount_t *s) { write_seqcount_begin_nested(s, 0); } static inline void write_seqcount_begin_nested(seqcount_t *s, int subclass) { raw_write_seqcount_begin(s); … } static inline void raw_write_seqcount_begin(seqcount_t *s) { s->sequence++; smp_wmb(); } 写者释放顺序锁的执行过程是:首先把序列号加 1,序列号变成偶数,然后释放自旋锁。 只提供序列号的顺序锁只提供序列号的顺序锁的定义如下: include/linux/seqlock.h typedef struct seqcount { unsigned sequence; #ifdef CONFIG_DEBUG_LOCK_ALLOC struct lockdep_map dep_map; #endif } seqcount_t; 定义并且初始化静态顺序锁的方法如下:seqcount_t x = SEQCNT_ZERO(x); 运行时动态初始化顺序锁的方法如下:seqcount_init(s) 读者读数据的方法如下: seqcount_t sc; unsigned int seq; do { seq = read_seqcount_begin(&sc); //读数据 } while (read_seqcount_retry(&sc, seq)); 写者写数据的方法如下: spin_lock(&mylock);/* 假设使用者定义了自旋锁mylock */ write_seqcount_begin(&sc); //写数据 write_seqcount_end(&sc); spin unlock(&mylock); |
今日新闻 |
推荐新闻 |
CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3 |