顺序锁

当使用读/写自旋锁时,内核控制路径发出的执行read_lock或write_lock操作的请求具有相同的优先权:读者必须等待,直到写操作完成。同样地,写者也必须等待,直到读操作完成。

Linux 2.6中引入了顺序锁(seqlock),它与读/写自旋锁非常相似,只是它为写者赋予了较高的优先级:事实上,即使在读者正在读的时候也允许写者继续运行。这种策略的好处是写者永远不会等待读(除非另外一个写者正在写),缺点是有些时候读者不得不反复读多次相同的数据直到它获得有效的结果。

每个顺序锁都是包括两个字段的seqlock_t结构:
typedef struct {
    unsigned sequence;
    spinlock_t lock;
} seqlock_t;

一个类型为spinlock_t的lock字段和一个整型的sequence字段,第二个字段sequence是一个顺序计数器。

每个读者都必须在读数据前后两次读顺序计数器,并检查两次读到的值是否相同,如果不相同,说明新的写者已经开始写并增加了顺序计数器,因此暗示读者刚读到的数据是无效的。

通过把SEQLOCK_UNLOCKED赋给变量seqlock_t或执行seqlock_init宏,把seqlock_t变量初始化为“未上锁”,并把sequence设为0:
#define __SEQLOCK_UNLOCKED(lockname) /
         { 0, __SPIN_LOCK_UNLOCKED(lockname) }

#define SEQLOCK_UNLOCKED /
         __SEQLOCK_UNLOCKED(old_style_seqlock_init)

# define __SPIN_LOCK_UNLOCKED(lockname) /
    (spinlock_t)    {    .raw_lock = __RAW_SPIN_LOCK_UNLOCKED,    /
                SPIN_DEP_MAP_INIT(lockname) }
#define __RAW_SPIN_LOCK_UNLOCKED    { 1 }

写者通过调用write_seqlock()和write_sequnlock()获取和释放顺序锁。write_seqlock()函数获取seqlock_t数据结构中的自旋锁,然后使顺序计数器sequence加1;write_sequnlock()函数再次增加顺序计数器sequence,然后释放自旋锁。这样可以保证写者在整个写的过程中,计数器sequence的值是奇数,并且当没有写者在改变数据的时候,计数器的值是偶数。读者进程执行下面的临界区代码:

    unsigned int seq;
    do {
        seq = read_seqbegin(&seqlock);
        /* ... CRITICAL REGION ... */
    } while (read_seqretry(&seqlock, seq));

read_seqbegin()返回顺序锁的当前顺序号;如果局部变量seq的值是奇数(写者在read_seqbegin()函数被调用后,正更新数据结构),或seq的值与顺序锁的顺序计数器的当前值不匹配(当读者正执行临界区代码时,写者开始工作),read_seqretry()就返回1:
static __always_inline int read_seqretry(const seqlock_t *sl, unsigned iv)
{
    smp_rmb();
    return (iv & 1) | (sl->sequence ^ iv);
}


注意在顺序锁机制里,读者可能反复读多次相同的数据直到它获得有效的结果(read_seqretry返回0)。另外,当读者进入临界区时,不必禁用内核抢占;另一方面,由写者获取自旋锁,所以它进入临界区时自动禁用内核抢占。

并不是每一种资源都可以使用顺序锁来保护。一般来说,必须在满足下述条件时才能使用顺序锁:

(1)被保护的数据结构不包括被写者修改和被读者间接引用 的指针(否则,写者可能在读者的眼皮子底下就修改指针)。
(2)读者的临界区代码没有副作用(否则,多个读者的操作会与单独的读操作有不同的结果)。

此外,读者的临界区代码应该简短,而且写者应该不常获取顺序锁,否则,反复的读访问会引起严重的开销。在Linux 2.6中,使用顺序锁主要是保护一些与系统时间处理相关的数据结构。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
读写顺序是在多线程编程中用于同步的两种机制。 读写(Read-Write Lock)是一种特殊的,它允许多个线程同时进行读操作,但只能有一个线程进行写操作。读操作不会互斥,因此多个线程可以同时读取共享数据,这样可以提高并发性能。但当有线程进行写操作时,其他线程无法进行读或写操作,直到写操作完成。 读写的工作方式如下: - 当一个线程希望进行读操作时,它会尝试获取读。如果没有其他线程持有写,则该线程可以获取读,并进行读操作。 - 当一个线程希望进行写操作时,它会尝试获取写。如果没有其他线程持有读或写,则该线程可以获取写,并进行写操作。 - 当一个线程持有写时,其他线程无法获取读或写。当一个线程持有读时,其他线程可以获取读,但无法获取写顺序(Sequence Lock)是一种用于解决并发更新数据的同步机制。它允许多个线程同时进行读操作和写操作,并通过一个序列号来确保操作的顺序。每次更新共享数据时,都会增加序列号。当一个线程进行读操作时,会记录当前的序列号。如果在读操作期间,序列号发生变化,则需要重新读取共享数据。 顺序的工作方式如下: - 当一个线程希望进行读操作时,它会记录当前的序列号,并读取共享数据。 - 当一个线程希望进行写操作时,它会增加序列号,并更新共享数据。 - 在读操作期间,如果序列号发生变化,则需要重新读取共享数据。 顺序适用于读操作频繁而写操作较少的场景,因为它允许多个线程同时进行读操作,避免了读操作的互斥。但需要注意的是,顺序可能会导致写操作的延迟,因为在写操作期间,其他线程无法进行读或写操作。 需要根据具体的需求和场景选择适合的同步机制,读写顺序都是常用的同步机制之一。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值