一、(基础)自旋锁

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

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结构的特定的数据结构。