转载地址:http://bdxnote.blog.163.com/blog/static/844423520124270173127/
定义在头文件linux/seqlock.h中;
顺序锁(seqlock)是对读写锁的一种优化,若使用顺序锁,读执行单元绝对不会被写执行单元所阻塞,也就是说,读执行单元可以在写执行单元对被顺序锁保护的共享资源进行写操作的同时仍然可以继续读,而不必等待写执行单元完成之后再去读,同样,写执行单元也不必等待所有的读执行单元读完之后才去进行写操作;
但是写执行单元与写执行单元之间仍然是互斥的,即:如果有写执行单元正在进行写操作,那么其它的写执行单元必须自旋在那里,直到写执行单元释放顺序锁为止;
如果读执行单元在读操作期间,写执行单元已经发生了写操作,那么,读执行单元必须重新去读数据,以便确保读到的数据是完整的;这种锁在读写操作同时进行的概率比较小,性能是非常好的,而且它允许读写操作同时进行,因而更大地提高了并发性;
顺序锁有一个限制:它必须要求被保护的共享资源中不能含有指针;因为写执行单元可能会使指针失效,当读执行单元如果正要访问该指针时,系统就会崩溃;
比较:顺序锁允许读操作和写操作之间的并发,也允许读操作与读操作之间的并发,但不允许写操作与写操作之间的并发,写操作与写操作之间只能是互斥的、串行的;读写自旋锁只允许读操作与读操作之间的并发,而读操作与写操作之间只能是互斥的、串行的,写操作与写操作之间也只能是互斥的、串行的;自旋锁则是不允许任何操作之间并发,即:读操作与读操作之间、读操作与写操作之间、写操作与写操作之间都是互斥的、串行的;
1).顺序锁的初始化:
seqlock_init(x); //动态初始化
DEFINE_SEQLOCK(x); //静态初始化
2).顺序锁的写锁定:
void write_seqlock(seqlock_t* sl); //写加锁
int write_tryseqlock(seqlock_t* sl); //尝试写加锁
write_seqlock_irqsave(lock, flags); //<=>local_irq_save() + write_seqlock()
write_seqlock_irq(lock); //<=>local_irq_disable() + write_seqlock()
write_seqlock_bh(lock); //local_bh_disable() + write_seqlock()
3).顺序锁的写解锁:
void write_sequnlock(seqlock_t* sl); //写解锁
write_sequnlock_irqrestore(lock, flags); //<=>write_sequnlock() + local_irq_restore()
write_sequnlock_irq(lock); //<=>write_sequnlock() + local_irq_enable()
write_sequnlock_bh(lock); //<=>write_sequnlock() + local_bh_enable()
4).顺序锁的读锁:
A.读操作:
unsigned int read_seqbegin(const seqlock_t* sl);
read_seqbegin_irqsave(lock, flags); //<=>local_irq_save() + read_seqbegin()
读执行单元在访问共享资源时要调用顺序锁的读函数,返回顺序锁s1的顺序号;该函数没有任何获得锁和释放锁的开销,只是简单地返回顺序锁当前的序号;
B.重读操作:
int read_seqretry(const seqlock_t* sl, unsigned start);
read_seqretry_irqrestore(lock, iv, flags);
在顺序锁的一次读操作结束之后,调用顺序锁的重读函数,用于检查是否有写执行单元对共享资源进行过写操作;如果有,则需要重新读取共享资源;iv为顺序锁的顺序号;
5).顺序锁的写执行单元模式:
write_seqlock(&lock);
...... //写操作代码
write_sequnlock(&lock);
6).顺序锁的读执行单元模式:
unsigned int seq_num = 0;
do
{
seq_num = read_seqbegin(&seqlock);
//读操作代码
......
} while(read_seqretry(&seqlock, seq_num));
7).如果写执行单元在操作被顺序锁保护的共享资源时已经保持了互斥锁保护对共享资源的写操作,即:写执行单元与写执行单元之间已经是互斥的,但是读执行单元仍然可以与写执行单元同时访问,那么这种情况下仅需要使用顺序计数(struct seqcount)即可,而不必使用spinlock;
A.顺序计数的初始化:
seqcount_init(sc);
B.顺序计数读锁定:
unsigned int read_seqcount_begin(const seqcount_t* sc);
在读执行单元对被顺序计数sc保护的共享资源进行读操作之前调用该函数来获得顺序计数sc的当前序号;
int read_seqcount_retry(const seqcount_t* sc, unsigned int start);
在读执行单元对被顺序计数sc保护的共享资源执行完一次读操作之后调用该函数,来检查在读操作期间是否有写执行单元访问过该共享资源;如果是,则读执行单元就需要重新读取共享资源,否则,就算是成功完成了读操作;
C.顺序计数写锁定:
void write_seqcount_begin(seqcount_t* sc);
在写执行单元对被顺序计数sc保护的共享资源进行写操作之前调用该函数,来对顺序计数sc的顺序号加1,以便于读执行单元能够检查出是否在读操作期间有写执行单元访问过;
void write_seqcount_end(seqcount_t* sc);
在写执行单元对被顺序计数sc保护的共享资源进行写操作之后调用该函数,来对顺序计数sc的顺序号加1,以便于读执行单元能够检查出是否在读操作期间有写执行单元访问过;
D.顺序计数读锁定模式:
unsigned int seq_num = 0;
do
{
seq_num = read_seqcount_begin(&sc);
//读操作代码
......
} while(read_seqcount_retry($sc, seq_num));
E:顺序计数的写锁定模式:
write_seqcount_begin(&sc);
//写操作代码
......
write_seqcount_end(&sc);
特别提醒:使用顺序计数必须非常小心,只有确定在访问共享资源时已经保持了互斥锁才可以使用;即:只有写操作与写操作之间已经是互斥的、串行的时,才可以使用顺序计数;
例子:
#include <linux/module.h>
#include <linux/version.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/jiffies.h>
#include <linux/delay.h>
//这三个头文件与内核线程的使用有关;
#include <linux/sched.h>
#include <linux/kthread.h>
#include <linux/err.h>
//顺序锁相关
#include <linux/seqlock.h>
MODULE_LICENSE("GPL");
MODULE_AUTHOR("*************");
MODULE_VERSION("2.6.35.000");
static int sleep_time = (1*10*HZ);
static int shared_res = 0;
//STEP1:定义顺序锁
seqlock_t my_seq_lock;
//STEP5:实现线程函数
static int thread_process1(void* param)
{
//int val = 0, ret = 0;
while(1)
{
set_current_state(TASK_UNINTERRUPTIBLE);
if(kthread_should_stop())
{
printk("kernel thread '%s' should stop;file:%s;line:%d\n", __FUNCTION__, __FILE__, __LINE__);
break;
}
//STEP3:对临界区加锁
write_seqlock(&my_seq_lock);
shared_res++;
//STEP4:对临界区解锁
write_sequnlock(&my_seq_lock);
mdelay(sleep_time);
}
return 12;
};
static int thread_process2(void* param)
{
//int val = 0, ret = 0;
while(1)
{
set_current_state(TASK_UNINTERRUPTIBLE);
if(kthread_should_stop())
{
printk("kernel thread '%s' should stop;file:%s;line:%d\n", __FUNCTION__, __FILE__, __LINE__);
break;
}
//STEP3:对临界区加锁
write_seqlock(&my_seq_lock);
shared_res++;
//STEP4:对临界区解锁
write_sequnlock(&my_seq_lock);
msleep(sleep_time);
}
return 34;
};
static int thread_process3(void* param)
{
int val = 0;//, ret = 0;
unsigned int seq_num = 0;
while(1)
{
set_current_state(TASK_UNINTERRUPTIBLE);
if(kthread_should_stop())
{
printk("kernel thread '%s' should stop;file:%s;line:%d\n", __FUNCTION__, __FILE__, __LINE__);
break;
}
//seq lock read --- begin
do
{
seq_num = read_seqbegin(&my_seq_lock);
val = shared_res;
printk("%s: shared resource = %d;\n%s", __FUNCTION__, val, ((val % 3) ? "" : "\n"));
} while(read_seqretry(&my_seq_lock, seq_num));
//seq lock read --- end
msleep(sleep_time);
}
return 56;
};
static int thread_process4(void* param)
{
int val = 0;//, ret = 0;
unsigned int seq_num = 0;
while(1)
{
set_current_state(TASK_UNINTERRUPTIBLE);
if(kthread_should_stop())
{
printk("kernel thread '%s' should stop;file:%s;line:%d\n", __FUNCTION__, __FILE__, __LINE__);
break;
}
//seq lock read --- begin
do
{
seq_num = read_seqbegin(&my_seq_lock);
val = shared_res;
printk("%s: shared resource = %d;\n%s", __FUNCTION__, val, ((val % 3) ? "" : "\n"));
} while(read_seqretry(&my_seq_lock, seq_num));
//seq lock read --- end
msleep(sleep_time);
}
return 78;
};
static struct task_struct* my_thread1 = NULL;
static struct task_struct* my_thread2 = NULL;
static struct task_struct* my_thread3 = NULL;
static struct task_struct* my_thread4 = NULL;
static int __init study_init(void)
{
int err = 0;
printk("%s\n", __PRETTY_FUNCTION__);
//STEP2:初始化顺序锁
seqlock_init(&my_seq_lock);
printk("init seq lock ok\n");
my_thread1 = kthread_create(thread_process1, NULL, "my_thread1");
if(IS_ERR(my_thread1))
{
err = PTR_ERR(my_thread1);
my_thread1 = NULL;
printk(KERN_ERR "unable to start kernel thread1:%d\n", err);
return err;
}
my_thread2 = kthread_create(thread_process2, NULL, "my_thread2");
if(IS_ERR(my_thread2))
{
err = PTR_ERR(my_thread2);
my_thread2 = NULL;
printk(KERN_ERR "unable to start kernel thread2:%d\n", err);
return err;
}
my_thread3 = kthread_create(thread_process3, NULL, "my_thread3");
if(IS_ERR(my_thread3))
{
err = PTR_ERR(my_thread3);
my_thread3 = NULL;
printk(KERN_ERR "unable to start kernel thread3:%d\n", err);
return err;
}
my_thread4 = kthread_create(thread_process4, NULL, "my_thread4");
if(IS_ERR(my_thread4))
{
err = PTR_ERR(my_thread4);
my_thread4 = NULL;
printk(KERN_ERR "unable to start kernel thread4:%d\n", err);
return err;
}
wake_up_process(my_thread1);
wake_up_process(my_thread2);
wake_up_process(my_thread3);
wake_up_process(my_thread4);
printk("%s:all kernel thread start;\n", __FUNCTION__);
return 0;
}
static void __exit study_exit(void)
{
int ret = -1;
printk("%s\n",__PRETTY_FUNCTION__);
if(my_thread1)
{
ret = kthread_stop(my_thread1);
my_thread1 = NULL;
printk("kernel thread1 stop,exit code is %d;\n",ret);
}
if(my_thread2)
{
ret = kthread_stop(my_thread2);
my_thread2 = NULL;
printk("kernel thread2 stop,exit code is %d;\n",ret);
}
if(my_thread3)
{
ret = kthread_stop(my_thread3);
my_thread3 = NULL;
printk("kernel thread3 stop,exit code is %d;\n",ret);
}
if(my_thread4)
{
ret = kthread_stop(my_thread4);
my_thread4 = NULL;
printk("kernel thread4 stop,exit code is %d;\n",ret);
}
printk("%s:all kernel thread stop;\n", __FUNCTION__);
}
module_init(study_init);
module_exit(study_exit);