互斥机制之自旋锁(spinlock)

本文深入介绍了自旋锁、读写自旋锁、顺序锁及RCU等并发控制技术的原理与应用。通过具体宏指令与函数说明了各种锁的定义、初始化、获取与释放过程,并提供了典型使用场景。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、(基础)自旋锁

如果测试结果表明锁仍被占用,程序将在一个小的循环内重复这个“测试并设置”操作,即进行所谓的“自旋”。

1.定义自旋锁

spinlock_t spin;

2.初始化自旋锁

spin_lock_init(lock);

//该宏用于动态初始化自旋锁lock。

3.获得自旋锁

spin_lock(lock);

//该宏用于获得自旋锁lock。

//如果能立即获得锁,就马上返回;否则将自旋在那里,直到该自旋锁的保持者释放;

spin_trylock(lock);

//该宏尝试获得自旋锁lock。

//如果能立即获得锁,它获得锁并返回真;否则立即返回假,实际上不再“在原地打转”。

4.释放自旋锁

spin_unlock(lock);

//该宏释放自旋锁lock。

//它与spin_trylock或spin_lock配对使用。

###自旋锁典型用法###

spinlock_t lock; //定义一个自旋锁lock

spin_lock_init(&lock); //初始化自旋锁lock

spin_lock (&lock) ; //获取自旋锁,保护临界区

… //临界区

spin_unlock (&lock) ; //解锁

###自旋锁的衍生###

spin_lock_irq() = spin_lock() + local_irq_disable()

spin_unlock_irq() =spin_unlock() + local_irq_enable()

spin_lock_irqsave() = spin_lock() + local_irq_save()

spin_unlock_irqrestore() = spin_unlock() + local_irq_restore()

spin_lock_bh() =spin_lock() + local_bh_disable()

spin_unlock_bh() =spin_unlock() + local_bh_enable()

二、读写自旋锁(rwlock)

//写操作:只能最多有一个写进程;读操作:同时可以有多个读执行单元;读和写也不能同时进行。

1. 定义和初始化读写自旋锁

rwlock_t my_rwlock;

rwlock_t my_rwlock = RW_LOCK_UNLOCKED; /* 静态初始化*/

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); int write_trylock(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);

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);

//对共享资源进行读取之前,先调用写锁定函数,完成之后调用写解锁函数。

##读写自旋锁典型用法 ##

rwlock_t lock; //定义rwlock

rwlock_init(&lock); //初始化rwlock

read_lock(&lock); //读时获取锁

… //临界资源

read_unlock(&lock); //写时获取锁

write_lock_irqsave(&lock, flags);

… //临界资源

write_unlock_irqrestore(&lock, flags);

三、顺序锁(seqlock)

//该锁是对读写锁的一种优化,允许读写同时进行;

//写执行单元与写执行单元之间仍然是互斥的;

//读执行单元在读操作期间,写执行单元已经发生了写操作,则读执行单元必须重新读取数据,以便确保得到的数据是完整的;

//限制:要求被保护的共享资源不含有指针!(写执行单元可能使得指针失效)

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() = loal_irq_save() + write_seqlock()

write_seqlock_irq() = local_irq_disable() + write_seqlock()

write_seqlock_bh() = local_bh_disable() + write_seqlock()

2.释放顺序锁

void write_sequnlock(seqlock_t *sl);

write_sequnlock_irqrestore(lock, flags);

write_sequnlock_irq(lock);

write_sequnlock_bh(lock);

其中:

write_sequnlock_irqrestore() = write_sequnlock() + local_irq_restore()

write_sequnlock_irq() = write_sequnlock() +local_irq_enable()

write_sequnlock_bh() = write_sequnlock() + local_bh_enable()

##写执行单元使用顺序锁的模式##

write_seqlock(&seqlock_a);

… //写操作代码块

write_sequnlock(&seqlock_a);

##读执行单元使用顺序锁的模式##

do {

seqnum = read_seqbegin(&seqlock_a);

… //读操作代码块

} while (read_seqretry(&seqlock_a, seqnum));

其中:

unsigned read_seqbegin(const seqlock_t *sl); 读开始

int read_seqretry(const seqlock_t *sl, unsigned iv); 重读

另外:

read_seqbegin_irqsave(lock, flags); 读开始

== local_irq_save() + read_seqbegin()

read_seqretry_irqrestore(lock, iv, flags); 重读

== read_seqretry() + local_irq_restore()

四、读-拷贝-更新(RCU)

//可以看做读写锁的高性能版本。既允许多个读执行单元同时访问被保护的数据,又允许多个读执行单元和多个写执行单元同时访问被保护的数据。

//BUT,RCU不能替代读写锁。(因为如果写比较多时,对读执行单元的性能提高不能弥补写执行单元导致的损失)

1.读锁定

rcu_read_lock();

rcu_read_lock_bh();

2.读解锁

rcu_read_unlock();

rcu_read_unlock_bh();

//rcu_read_lock()和rcu_read_unlock()实质: 禁止和使能内核的抢占调度

3.同步RCU

synchronize_rcu();

//由RCU写执行单元调用,将阻塞写执行单元,直到所有的读执行单元已经完成读执行单元临界区,写执行单元才可以继续下一步操作。

//保证所有CPU都处理完正在运行的读执行单元临界区。

4.挂接回调

void
fastcall call_rcu(struct rcu_head *head, void (*func)(struct rcu_head *rcu));

void fastcall call_rcu_bh(struct rcu_head *head, void (*func)(struct rcu_head *rcu));

//由RCU写执行单元调用,不会使写执行单元阻塞,故可以在中断上下文或软中断中使用。

//函数功能:把函数func挂接到RCU回调函数链上,然后立即返回。

5.链表操作函数的RCU版本

//把链表元素new插入到RCU保护的链表head 的开头。

static inline void list_add_rcu(struct list_head *new, struct list_head *head);

//把新的链表元素new添加到被RCU保护的链表的末尾。

static inline void list_add_tail_rcu(struct list_head *new, struct list_head *head);

//从RCU保护的链表中删除指定的链表元素entry。

static inline void list_del_rcu(struct list_head *entry);

//使用新的链表元素new取代旧的链表元素old。

static inline void list_replace_rcu(struct list_head *old, struct list_head *new);

list_for_each_rcu(pos,
head) //该宏用于遍历由RCU保护的链表head。

list_for_each_safe_rcu(pos, n, head) //该宏类似于list_for_each_rcu,不同之处在于它允许安全地删除当前链表元素pos。

list_for_each_entry_rcu(pos, head,member) //该宏类似于list_for_each_rcu,不同之处在于它用于遍历指定类型的数据结构链表;当前链表元素pos为一个包含struct list_head结构的特定的数据结构。

//从由RCU保护的哈希链表中移走链表元素n

static inline void hlist_del_rcu(struct hlist_node *n);

//用于把链表元素n插入到被RCU保护的哈希链表的开头,但同时允许读执行单元对该哈希链表的遍历。

static inline void hlist_add_head_rcu(struct hlist_node *n, struct hlist_head *h);

hlist_for_each_rcu(pos, head) //该宏用于遍历由RCU 保护的哈希链表head。

hlist_for_each_entry_rcu(tpos, pos, head, member) //类似hlist_for_each_rcu(),不同之处在于它用于遍历指定类型的数据结构哈希链表;当前链表元素pos为一个包含struct list_head结构的特定的数据结构。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值