linux驱动程序中的并发控制-4(顺序自旋锁)-46
Posted 杨斌并
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了linux驱动程序中的并发控制-4(顺序自旋锁)-46相关的知识,希望对你有一定的参考价值。
顺序自旋锁
- 顺序锁与读写自旋锁类似,只是为写锁赋予了更高的权限。在读写自旋锁中,读锁和写锁的优先级是相同的。
- 当读锁获取读自旋锁时,写锁必须等待,直到临界区的代码执行完成,并释放读自旋锁为止,反之亦然。
- 顺序锁在获取读锁的时候,仍然可以获取写锁,并继续执行写临界区中的
代码。也就是说,写锁永远不会被读锁阻塞(当然,写锁仍然可以被写锁阻塞)。 - 顺序锁需要定义seqlock_ t 变量。seqlock t结构体的代码如下(从seqlock_ _t 结构体的代码可以看出,顺序锁是通过自旋锁扩展而来的):
#include <linux/seqlock.h>
typedef struct {
struct seqcount seqcount;
spinlock_t lock;
} seqlock_t;
- 当获取顺序锁(write_seqlock函数)时,seqlock_t.sequence 变量会被加1。释放写顺序锁(write_sequnlock函数)时,seqlock_t.sequence 变量仍然会被加1。因此,在获取顺序锁,但并未释放顺序锁的情况下(正在执行写临界区的代码),seqlock_t.sequence 变量的值是奇数。释放写顺序后,seqlock_t.sequence 变量的值是偶数。
- 读取共享资源可以通过read_seqbegin 和read_seqretry 函数配合来完成。标准的执行读临界区的代码如下:
unsigned seq;
do {
//获取顺序号,如果顺序号未被释放,read_seqbegin 函数会被阻塞
seq = read_seqbegin(&seqlock);
//如果单前的顺序号和seq一致则退出循环,否则继续共享资源
} while (read_seqretry(&seqlock, seq));
- read_seqbegin 函数用于获取顺序锁的顺序号,也就是seqlock_ t.sequence 变量的值。成功获取顺序号后,就会执行读临界区代码。
- read_seqretry 函数用于判断在执行读临界区的过程中顺序号是否发生变化。从read_seqretry 函数的源代码也可以看出这一点。
static inline int read_seqcount_retry(const seqcount_t *s, unsigned start)
{
smp_rmb();
return __read_seqcount_retry(s, start);
}
static inline int __read_seqcount_retry(const seqcount_t *s, unsigned start)
{
//比较通过read_seqbegin 获取的顺序号和当前的顺序号是否一致
return unlikely(s->sequence != start);
}
- 如果在执行临界区代码的过程中顺序号并未发生改变(也就是说在读过程中并未发生写操作),read_seqretry 函数返回0,否则返回非0。
- 在执行读临界区的代码时,如果发生写操作,read_seqretry 函数会返回非0值, do…while循环将继续执行。当再次执行到read_seqbegin函数时,会检测顺序锁是否执行完,如果顺序锁未执行完, read_seqbegin 函数将被阻塞,直到顺序
锁释放。也就是说,read_seqbegin 函数返回的顺序号一定是一个偶数(顺序锁已被释放)。
相关的api
函数 | 描述 |
---|---|
DEFINE_SEQLOCK(lock) | 定义和初始化顺序锁变量 |
void seqlock_init(seqlock_t *lock) | 初始化顺序锁变量 |
void write_seqlock(seqlock_t *lock) | 获取顺序锁 |
int write_tryseqlock(seqlock_t *lock) | 获取顺序锁。如果成功获取顺序锁,函数立刻返回非0值,否则立刻返回0 |
write_seqlock_irq_save(seqlock_t *lock, flags) | = local_irq_save + write_seqlock |
write_seqlock_irq(seqlock_t *lock) | local_irq_disable + write_seqlock |
write_seqlock_bh(seqlock_t *lock) | local_bh_disable + write_seqlock |
void write_sequnlock(seqlock_t *lock) | 释放顺序锁 |
write_sequnlock_irqrestore(seqlock_t *lock, flags) | write_sequnlock + local_irq_restore |
write_sequnlock_irq(seqlock_t * lock) | write_sequnlock + local_irq_enable |
write_sequnlock_bh(seqlock_t * lock) | write_sequnlock + local_bh_enable |
unsigned read_seqbegin(const seqlock_t * lock) | 获取顺序锁的顺序号。如果lock指定的顺序锁未被释放,该函数会等待顺序锁释放后才能获取顺序锁的顺序号 |
read_seqbegin_irqsave(const seqlock_t *lock, flags) | local_irq_save + read_seqbegin |
int read_seqretry(const seqlock_t *lock, unsigned iv) | 检测read _seqbegin 函数获取的顺序号是否与执行完读临界区的顺序号-致。如果不一致,返回非0值,否则返回0 |
read_seqretry_irqrestore(const seqlock_t *lock, iv, flags) | read_seqretry + local_irq_restore |
实例
- seq_lock.c
//
// seq_lock_test.c
// seqlock_demo
//
// Created by lianfei on 2021/7/12.
//
#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/miscdevice.h>
#include <linux/uaccess.h>
#include <linux/seqlock.h>
#include <linux/delay.h>
#define DEVICE_NAME "seq_lock"
static DEFINE_SEQLOCK(seqlock);
static char *data = "data";
static ssize_t demo_read(struct file *file, char __user * buf, size_t count, loff_t *ppos){
unsigned long data_size = strlen(data);
unsigned seq;
do {
//获取顺序号,如果顺序号未被释放,read_seqbegin 函数会被阻塞
seq = read_seqbegin(&seqlock);
if (copy_to_user(buf, (void *)data, data_size))
{
return -EINVAL;
}
mdelay(1000);
//如果单前的顺序号和seq一致则退出循环,否则继续共享资源
} while (read_seqretry(&seqlock, seq));
printk("顺序号不一致退出循环\\n");
return data_size;
}
static ssize_t demo_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos){
struct timeval tv;
do_gettimeofday(&tv);
printk("write: start sec:%ld\\n", tv.tv_sec);
write_seqlock(&seqlock);
do_gettimeofday(&tv);
mdelay(10000);
printk("write: end sec:%ld\\n", tv.tv_sec);
write_sequnlock(&seqlock);
return count;
}
static int demo_release (struct inode *node, struct file *file){
return 0;
}
static int demo_open (struct inode *node, struct file *file){
return 0;
}
static struct file_operations dev_fops={
.owner = THIS_MODULE,
.open = demo_open,
.release = demo_release,
.read = demo_read,
.write = demo_write
};
static struct miscdevice misc={
.minor=MISC_DYNAMIC_MINOR,
.name=DEVICE_NAME,
.fops=&dev_fops
};
static int demo_init(void){
int ret=misc_register(&misc);
if(ret < 0 ){
printk("atomic_init is error\\n");
return -1;
}
printk("demo_init_success\\n");
return ret;
}
static void demo_exit(void){
printk("ademo_exit_success\\n");
misc_deregister(&misc);
}
module_init(demo_init);
module_exit(demo_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("binbing.Yang");
- 测试脚本
- seq_test.sh
#!/system/bin/sh
cat /dev/seq_lock &
echo data > /dev/seq_lock
- 测试seq lock 驱动时首先读取/dev/seq lock 文件的内容(这时会调用dermo_ read 函数来延迟10秒),然后再向/dev/seq lock 文件写入数据。如果在日志中输出的两个毫秒数相等,说明write_ seqlock函数并未被阻塞。为了方便测试,本例提供了一个用于测试的脚本文件(seq_test.sh),
以上是关于linux驱动程序中的并发控制-4(顺序自旋锁)-46的主要内容,如果未能解决你的问题,请参考以下文章