Linux 内核学习(1) - 同步
不可睡眠锁
1 Spinlock 自旋锁
spinlock
是内核中提供的一种比较常见的锁机制,自旋锁是“原地等待”的方式解决资源冲突的。即,一个线程获取了一个自旋锁后,另外一个线程期望获取该自旋锁,获取不到,只能够原地“打转”(忙等待)。由于自旋锁的这个忙等待的特性,注定了它使用场景上的限制——自旋锁不应该被长时间的持有(消耗 CPU 资源)。
API
初始化:
- 在编译时定义:
DEFINE_SPINLOCK()
- 在运行时初始化:
spin_lock_init()
持续与释放锁:
spin_lock()
/spin_unlock()
线程在include/kthread.h
里面
例子1:编译时初始化自旋锁
#include <linux/module.h>
#include <linux/kthread.h>
#include <linux/delay.h>
MODULE_AUTHOR("OneGhost");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("The module is only used for test");
static DEFINE_SPINLOCK(threads_lock);
struct our_data {
int count1;
int count2;
};
static struct our_data my_data;
static void show_my_data(void) {
printk("count1=%d, count2=%d\n", my_data.count1, my_data.count2);
}
#define MAX_KTHREAD 10
static struct task_struct *threads[MAX_KTHREAD];
static int thread_do(void *data) {
printk("run ...\n");
while (!kthread_should_stop()) {
spin_lock(&threads_lock);
my_data.count1++;
my_data.count2 += 10;
spin_unlock(&threads_lock);
msleep(10);
}
return 0;
}
static int create_thread(void) {
int i;
for (i = 0; i < MAX_KTHREAD; i++) {
struct task_struct *thread;
thread = kthread_run(thread_do, NULL, "thread-%d", i);
if (IS_ERR(thread)) {
return -1;
}
threads[i] = thread;
}
return 0;
}
static void cleanup_threads(void) {
int i;
for (i = 0; i < MAX_KTHREAD; i++) {
if (threads[i]) {
kthread_stop(threads[i]);
}
}
}
static __init int minit(void) // 如果加上__init,则只会调用一次
{
if (create_thread()) {
cleanup_threads();
return -1;
}
return 0;
}
static __exit void mexit(void) // __exit同理
{
printk("call %s.\n", __FUNCTION__);
cleanup_threads();
show_my_data();
}
module_init(minit)
module_exit(mexit)
安装模块sudo insmod tmain.ko
查看输出sudo dmesg
清空之前的输出sudo dmesg -c
卸载模块sudo rmmod tmain
查看加载的模块lsmod
例子2:运行时初始化自旋锁
#include <linux/module.h>
#include <linux/kthread.h>
#include <linux/delay.h>
MODULE_AUTHOR("OneGhost");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("The module is only used for test");
static spinlock_t threads_lock;
static void threads_lock_init(void) {
spin_lock_init(&threads_lock);
}
struct our_data {
int count1;
int count2;
};
static struct our_data my_data;
static void show_my_data(void) {
printk("count1=%d, count2=%d\n", my_data.count1, my_data.count2);
}
#define MAX_KTHREAD 10
static struct task_struct *threads[MAX_KTHREAD];
static int thread_do(void *data) {
printk("run ...\n");
while (!kthread_should_stop()) {
spin_lock(&threads_lock);
my_data.count1++;
my_data.count2 += 10;
spin_unlock(&threads_lock);
msleep(10);
}
return 0;
}
static int create_thread(void) {
int i;
for (i = 0; i < MAX_KTHREAD; i++) {
struct task_struct *thread;
thread = kthread_run(thread_do, NULL, "thread-%d", i);
if (IS_ERR(thread)) {
return -1;
}
threads[i] = thread;
}
return 0;
}
static void cleanup_threads(void) {
int i;
for (i = 0; i < MAX_KTHREAD; i++) {
if (threads[i]) {
kthread_stop(threads[i]);
}
}
}
static __init int minit(void) // 如果加上__init,则只会调用一次
{
printk("call %s.\n", __FUNCTION__);
threads_lock_init();
if (create_thread()) {
cleanup_threads();
return -1;
}
return 0;
}
static __exit void mexit(void) // __exit同理
{
printk("call %s.\n", __FUNCTION__);
cleanup_threads();
show_my_data();
}
module_init(minit)
module_exit(mexit)
2 RCU: read-copy update
RCU (if !CONFIG_PREEMPT_RCU
)
-
RCU: read-copy update
-
在没有启用
CONFIG_PREEMPT_RCU
的时候是不可睡眠锁 -
一种更为轻量的读写锁,即读侧的开销很小(在读的时候没有原子操作),而写侧的延迟很大
-
注意:
- 只用来保护指针,因为指针引用和指针赋值在所有平台都是原子操作(指针声明的时候尽量加上
__rcu
) - 如果写较为频繁,不要使用RCU
CONFIG_PREEMPT_RCU
用于实时性很强的场合,为了提高代码的可移植性,尽可能不要将RCU用于可睡眠的场合
- 只用来保护指针,因为指针引用和指针赋值在所有平台都是原子操作(指针声明的时候尽量加上
-
如果不使用RCU更新指针,则需要自己处理内存屏障的问题,很复杂
-
使用
-
读者
- 使用
rcu_read_lock()
来进入读侧 - 使用
rcu_deference()
来获取指针对应的资源 - 使用
rcu_read_unlock()
来退出读侧
- 使用
-
写者
-
如果有多个写端,需要写端自己做同步,例如使用
spinlock
-
使用
rcu_assign_pointer()
来更新指针 -
使用
call_rcu()
/synchronize_rcu()
来释放旧指针对应的资源call_rcu()
用来将释放的工作交给后台,可以快速返回,不会导致睡眠synchronize_rcu()
会等待所有的读者退出再返回,会导致睡眠
-
-
其它变体
-
Defer Protect
a. synchronize_rcu() rcu_read_lock() / rcu_read_unlock()
call_rcu() rcu_deference()
b. call_rcu_bh() rcu_read_lock_bh() / rcu_read_unlock_bh()
rch_deference_bh()
c. synchronize_sched() rcu_read_lock_shced() / rcu_read_unlock_sched()
preempt_disable() / preempt_enable()
local_irq_save() / local_irq_restore()
hardirq enter / hardirq exit
NMI enter / NMI exit
rcu_dereference_shced()
例子1:synchronize_rcu()
#include <linux/module.h>
#include <linux/kthread.h>
#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/rcupdate.h>
MODULE_AUTHOR("OneGhost");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("The module is only used for test");
#define MAX_KTHREAD 10
static struct task_struct *threads[MAX_KTHREAD];
static ulong reader_bitmap;
static void set_reader_number(int reader) {
WARN_ON((MAX_KTHREAD - reader) != 1);
reader_bitmap = 0;
while (reader--) {
reader_bitmap |= ((ulong) 1 << (ulong) reader);
}
}
struct our_data {
int count1;
int count2;
};
static struct our_data my_data;
static struct our_data __rcu *pmy_data = &my_data; // 声明这个指针被rcu保护
static void show_my_data(void) {
printk("count1=%d, count2=%d\n", pmy_data->count1, pmy_data->count2);
}
static void reader_do(void) {
struct our_data *data;
rcu_read_lock();
data = rcu_dereference(pmy_data);
printk("read count1 %d, count2 %d.\n", data->count1, data->count2);
rcu_read_unlock();
}
static void writer_do(void) {
struct our_data *data, *tmp = pmy_data;
data = kmalloc(sizeof(*data), GFP_KERNEL);
if (!data) return;
// read + copy
memcpy(data, pmy_data, sizeof(*data)); // 把pmy_data指向的值赋给data地址
data->count1 += 1; // 修改拷贝后的值
data->count2 += 10;
rcu_assign_pointer(pmy_data, data); // 用读写锁把新的data覆盖旧的my_data
if (tmp != &my_data) {
synchronize_rcu();
kfree(tmp);
}
}
static int thread_do(void *data) {
long i = (long) data;
int reader = reader_bitmap & ((ulong) 1 << (ulong) i);
printk("run %ld %s...\n", i, reader ? "reader" : "writer");
while (!kthread_should_stop()) {
if (reader) {
reader_do();
} else {
writer_do();
}
msleep(10);
}
return 0;
}
static int create_thread(void) {
int i;
for (i = 0; i < MAX_KTHREAD; i++) {
struct task_struct *thread;
thread = kthread_run(thread_do, (void *) (long) i, "thread-%d", i);
if (IS_ERR(thread)) {
return -1;
}
threads[i] = thread;
}
return 0;
}
static void cleanup_threads(void) {
int i;
for (i = 0; i < MAX_KTHREAD; i++) {
if (threads[i]) {
kthread_stop(threads[i]);
}
}
}
static __init int minit(void) // 如果加上__init,则只会调用一次
{
printk("call %s.\n", __FUNCTION__);
set_reader_number(9);
if (create_thread()) {
cleanup_threads();
return -1;
}
return 0;
}
static __exit void mexit(void) // __exit同理
{
printk("call %s.\n", __FUNCTION__);
cleanup_threads();
show_my_data();
}
module_init(minit)
module_exit(mexit)
例子2:call_rcu()
#include <linux/module.h>
#include <linux/kthread.h>
#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/rcupdate.h>
MODULE_AUTHOR("OneGhost");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("The module is only used for test");
#define MAX_KTHREAD 10
static struct task_struct *threads[MAX_KTHREAD];
static ulong reader_bitmap;
static void set_reader_number(int reader) {
WARN_ON((MAX_KTHREAD - reader) != 1);
reader_bitmap = 0;
while (reader--) {
reader_bitmap |= ((ulong) 1 << (ulong) reader);
}
}
struct our_data {
int count1;
int count2;
struct rcu_head rhead;
};
static struct our_data my_data;
static struct our_data __rcu *pmy_data = &my_data; // 声明这个指针被rcu保护
static void show_my_data(void) {
printk("count1=%d, count2=%d\n", pmy_data->count1, pmy_data->count2);
}
static void reader_do(void) {
struct our_data *data;
rcu_read_lock();
data = rcu_dereference(pmy_data);
printk("read count1 %d, count2 %d.\n", data->count1, data->count2);
rcu_read_unlock();
}
static void rcu_free(struct rcu_head *head){
struct our_data *data;
data = container_of(head, struct our_data, rhead); // 利用struct和成员的地址差,将rhead的位置转换成struct our_data的位置
kfree(data);
}
static void writer_do(void) {
struct our_data *data, *tmp = pmy_data;
data = kmalloc(sizeof(*data), GFP_KERNEL);
if (!data) return;
// read + copy
memcpy(data, pmy_data, sizeof(*data)); // 把pmy_data指向的值赋给data地址
data->count1 += 1; // 修改拷贝后的值
data->count2 += 10;
rcu_assign_pointer(pmy_data, data); // 用读写锁把新的data覆盖旧的my_data
if (tmp != &my_data) {
call_rcu(&tmp->rhead, rcu_free);
}
}
static int thread_do(void *data) {
long i = (long) data;
int reader = reader_bitmap & ((ulong) 1 << (ulong) i);
printk("run %ld %s...\n", i, reader ? "reader" : "writer");
while (!kthread_should_stop()) {
if (reader) {
reader_do();
} else {
writer_do();
}
msleep(10);
}
return 0;
}
static int create_thread(void) {
int i;
for (i = 0; i < MAX_KTHREAD; i++) {
struct task_struct *thread;
thread = kthread_run(thread_do, (void *) (long) i, "thread-%d", i);
if (IS_ERR(thread)) {
return -1;
}
threads[i] = thread;
}
return 0;
}
static void cleanup_threads(void) {
int i;
for (i = 0; i < MAX_KTHREAD; i++) {
if (threads[i]) {
kthread_stop(threads[i]);
}
}
}
static __init int minit(void) // 如果加上__init,则只会调用一次
{
printk("call %s.\n", __FUNCTION__);
set_reader_number(9);
if (create_thread()) {
cleanup_threads();
return -1;
}
return 0;
}
static __exit void mexit(void) // __exit同理
{
printk("call %s.\n", __FUNCTION__);
cleanup_threads();
show_my_data();
}
module_init(minit)
module_exit(mexit)
3 RWlock 读写自旋锁
#include <linux/rwlock.h>
static DEFINE_RWLOCK(myrwspinlock);
可睡眠锁
1 Mutex 互斥量
- 一次只能被一个对象所持有
- 类似于用户空间
glibc
中的pthread_mutex_lock
- 持有mutex后仍然可以睡眠(被调度出去)
- 使用
#include<linux/mutex.h>
- 初始化
- 编译时初始化:
DEFINE_MUTEX()
- 运行时初始化:
mutex_init()
- 编译时初始化:
- 持有锁
mutex_lock()
mutex_lock_interruptible()
收到任何中断都可以相应mutex_lock_killable()
只会相应SIGKILL
信号
- 释放锁
mutex_unlock()
例子:编译时初始化
#include <linux/module.h>
#include <linux/kthread.h>
#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/rcupdate.h>
MODULE_AUTHOR("OneGhost");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("The module is only used for test");
#define MAX_KTHREAD 10
static struct task_struct *threads[MAX_KTHREAD];
static ulong reader_bitmap;
static void set_reader_number(int reader) {
reader_bitmap = 0;
while (reader--) {
reader_bitmap |= ((ulong) 1 << (ulong) reader);
}
}
struct our_data {
int count1;
int count2;
};
static struct our_data my_data;
static struct our_data *pmy_data = &my_data; // 声明这个指针被rcu保护
static void show_my_data(void) {
printk("count1=%d, count2=%d\n", pmy_data->count1, pmy_data->count2);
}
static DEFINE_MUTEX(mylock);
static void writer_do(void) {
mutex_lock(&mylock);
pmy_data->count1++;
pmy_data->count2 += 10;
mutex_unlock(&mylock);
}
static int thread_do(void *data) {
long i = (long) data;
int reader = reader_bitmap & ((ulong) 1 << (ulong) i);
printk("run %ld %s...\n", i, reader ? "reader" : "writer");
while (!kthread_should_stop()) {
writer_do();
msleep(10);
}
return 0;
}
static int create_thread(void) {
int i;
for (i = 0; i < MAX_KTHREAD; i++) {
struct task_struct *thread;
thread = kthread_run(thread_do, (void *) (long) i, "thread-%d", i);
if (IS_ERR(thread)) {
return -1;
}
threads[i] = thread;
}
return 0;
}
static void cleanup_threads(void) {
int i;
for (i = 0; i < MAX_KTHREAD; i++) {
if (threads[i]) {
kthread_stop(threads[i]);
}
}
}
static __init int minit(void) // 如果加上__init,则只会调用一次
{
printk("call %s.\n", __FUNCTION__);
set_reader_number(0); // 所有的worker都是writer
if (create_thread()) {
cleanup_threads();
return -1;
}
return 0;
}
static __exit void mexit(void) // __exit同理
{
printk("call %s.\n", __FUNCTION__);
cleanup_threads();
show_my_data();
}
module_init(minit)
module_exit(mexit)
2 Semaphore 信号量
-
信号量的值可以大于1
-
持有锁时
- 如果当前值为0,则睡眠,等待信号量的值变为非0
- 否则,信号量的值减1
-
释放锁时
- 将信号量的值加1,如果有进程在等待信号量则将其唤醒
-
使用
#include <linux/semaphore.h>
- 初始化
- 编译时初始化
DEFINE_SEMAPHORE(sem, val)
- 运行时初始化
sema_init(sem, val)
- 编译时初始化
- 持有锁
down()
down_interruptible()
down_killable()
只会响应SIGKILL
- 释放锁
up()
例子:
#include <linux/module.h>
#include <linux/kthread.h>
#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/semaphore.h>
MODULE_AUTHOR("OneGhost");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("The module is only used for test");
#define MAX_KTHREAD 10
static struct task_struct *threads[MAX_KTHREAD];
static ulong reader_bitmap;
static void set_reader_number(int reader) {
reader_bitmap = 0;
while (reader--) {
reader_bitmap |= ((ulong) 1 << (ulong) reader);
}
}
struct our_data {
int count1;
int count2;
};
static struct our_data my_data;
static struct our_data *pmy_data = &my_data; // 声明这个指针被rcu保护
static void show_my_data(void) {
printk("count1=%d, count2=%d\n", pmy_data->count1, pmy_data->count2);
}
static DEFINE_SEMAPHORE(mylock);
static void writer_do(void) {
down(&mylock);
pmy_data->count1++;
pmy_data->count2 += 10;
up(&mylock);
}
static int thread_do(void *data) {
long i = (long) data;
int reader = reader_bitmap & ((ulong) 1 << (ulong) i);
printk("run %ld %s...\n", i, reader ? "reader" : "writer");
while (!kthread_should_stop()) {
writer_do();
msleep(10);
}
return 0;
}
static int create_thread(void) {
int i;
for (i = 0; i < MAX_KTHREAD; i++) {
struct task_struct *thread;
thread = kthread_run(thread_do, (void *) (long) i, "thread-%d", i);
if (IS_ERR(thread)) {
return -1;
}
threads[i] = thread;
}
return 0;
}
static void cleanup_threads(void) {
int i;
for (i = 0; i < MAX_KTHREAD; i++) {
if (threads[i]) {
kthread_stop(threads[i]);
}
}
}
static __init int minit(void) // 如果加上__init,则只会调用一次
{
printk("call %s.\n", __FUNCTION__);
set_reader_number(0); // 所有的worker都是writer
if (create_thread()) {
cleanup_threads();
return -1;
}
return 0;
}
static __exit void mexit(void) // __exit同理
{
printk("call %s.\n", __FUNCTION__);
cleanup_threads();
show_my_data();
}
module_init(minit)
module_exit(mexit)
3 Rwsem 读写信号量
-
读定锁
-
只有一个值,空闲时为1,被使用时为0
-
使用
#include <linux/rwsem.h>
- 初始化
- 编译时初始化
DECLARE_RWSEM()
- 运行时初始化
init_rwsem()
- 编译时初始化
- 读者
- 持有锁
down_read()
- 释放锁
up_read()
- 持有锁
- 写者
- 持有锁
down_write()
- 释放锁
up_write()
- 持有锁
-
跟RCU不一样,多个写端可以自己做同步,不需要用额外的锁进行保护
例子
#include <linux/module.h>
#include <linux/kthread.h>
#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/rwsem.h>
#include <linux/semaphore.h>
MODULE_AUTHOR("OneGhost");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("The module is only used for test");
#define MAX_KTHREAD 10
static struct task_struct *threads[MAX_KTHREAD];
static ulong reader_bitmap;
static void set_reader_number(int reader) {
reader_bitmap = 0;
while (reader--) {
reader_bitmap |= ((ulong) 1 << (ulong) reader);
}
}
struct our_data {
int count1;
int count2;
struct rcu_head rhead;
};
static struct our_data my_data;
static struct our_data __rcu *pmy_data = &my_data; // 声明这个指针被rcu保护
static void show_my_data(void) {
printk("count1=%d, count2=%d\n", pmy_data->count1, pmy_data->count2);
}
static DECLARE_RWSEM(myrwlock);
static void reader_do(void) {
down_read(&myrwlock);
printk("read count1 %d, count2 %d.\n", pmy_data->count1, pmy_data->count2);
up_read(&myrwlock);
}
static void writer_do(void) {
down_write(&myrwlock);
pmy_data->count1++;
pmy_data->count2 += 10;
up_write(&myrwlock);
}
static int thread_do(void *data) {
long i = (long) data;
int reader = reader_bitmap & ((ulong) 1 << (ulong) i);
printk("run %ld %s...\n", i, reader ? "reader" : "writer");
while (!kthread_should_stop()) {
if (reader) {
reader_do();
} else {
writer_do();
}
msleep(10);
}
return 0;
}
static int create_thread(void) {
int i;
for (i = 0; i < MAX_KTHREAD; i++) {
struct task_struct *thread;
thread = kthread_run(thread_do, (void *) (long) i, "thread-%d", i);
if (IS_ERR(thread)) {
return -1;
}
threads[i] = thread;
}
return 0;
}
static void cleanup_threads(void) {
int i;
for (i = 0; i < MAX_KTHREAD; i++) {
if (threads[i]) {
kthread_stop(threads[i]);
}
}
}
static __init int minit(void) // 如果加上__init,则只会调用一次
{
printk("call %s.\n", __FUNCTION__);
set_reader_number(5); // 所有的worker都是writer
if (create_thread()) {
cleanup_threads();
return -1;
}
return 0;
}
static __exit void mexit(void) // __exit同理
{
printk("call %s.\n", __FUNCTION__);
cleanup_threads();
show_my_data();
}
module_init(minit)
module_exit(mexit)
4 Completion 完成
- 用于显式地等待一个事件
- 初始化
- 头文件
#include <linux/completion.h>
- 编译时初始化
DECLARE_COMPLETION()
- 运行时初始化
init_completion()
- 头文件
- 等待事件
wait_for_completion()
wait_for_completion_interruptible()
wait_for_completion_killable()
wait_for_completion_iO()
wait_for_completion_xxx_timeout()
- 事情完成
Complete()
只唤醒一个等待的进程complete_all()
唤醒全部等待的进程
例子
#include <linux/module.h>
#include <linux/kthread.h>
#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/completion.h>
MODULE_AUTHOR("OneGhost");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("The module is only used for test");
#define MAX_KTHREAD 10
static struct task_struct *threads[MAX_KTHREAD];
static ulong reader_bitmap;
static void set_reader_number(int reader) {
reader_bitmap = 0;
while (reader--) {
reader_bitmap |= ((ulong) 1 << (ulong) reader);
}
}
struct our_data {
int count1;
int count2;
struct rcu_head rhead;
};
static struct our_data my_data;
static struct our_data __rcu *pmy_data = &my_data; // 声明这个指针被rcu保护
static void show_my_data(void) {
printk("count1=%d, count2=%d\n", pmy_data->count1, pmy_data->count2);
}
static DECLARE_RWSEM(myrwlock);
static DECLARE_COMPLETION(mycomplete);
static void reader_do(void) {
wait_for_completion(&mycomplete);
down_read(&myrwlock);
printk("read count1 %d, count2 %d.\n", pmy_data->count1, pmy_data->count2);
up_read(&myrwlock);
}
static void writer_do(void) {
down_write(&myrwlock);
pmy_data->count1++;
pmy_data->count2 += 10;
up_write(&myrwlock);
complete(&mycomplete);
}
static int thread_do(void *data) {
long i = (long) data;
int reader = reader_bitmap & ((ulong) 1 << (ulong) i);
printk("run %ld %s...\n", i, reader ? "reader" : "writer");
while (!kthread_should_stop()) {
if (reader) {
reader_do();
} else {
writer_do();
}
msleep(10);
}
return 0;
}
static int create_thread(void) {
int i;
for (i = 0; i < MAX_KTHREAD; i++) {
struct task_struct *thread;
thread = kthread_run(thread_do, (void *) (long) i, "thread-%d", i);
if (IS_ERR(thread)) {
return -1;
}
threads[i] = thread;
}
return 0;
}
static void cleanup_threads(void) {
int i;
for (i = 0; i < MAX_KTHREAD; i++) {
if (threads[i]) {
kthread_stop(threads[i]);
}
}
}
static __init int minit(void) // 如果加上__init,则只会调用一次
{
printk("call %s.\n", __FUNCTION__);
set_reader_number(9); // 所有的worker都是writer
if (create_thread()) {
cleanup_threads();
return -1;
}
return 0;
}
static __exit void mexit(void) // __exit同理
{
printk("call %s.\n", __FUNCTION__);
cleanup_threads();
show_my_data();
}
module_init(minit)
module_exit(mexit)
5 SRCU: sleepable read-copy-update
- 使用
- 初始化
#include <linux/srcu.h>
- 编译时初始化
DEFINE_SRCU()
/DEFINE_STATIC_SRCU
- 运行时初始化
init_srcu_struct()
- 读者
- 获得锁
srcu_read_lock()
- 获得保护的指针
srcu_dereference()
- 释放锁
srcu_read_unlock()
- 获得锁
- 写者释放旧指针
synchronize_scru()
/call_srcu()
- 使用完成后清理
cleanup_srcu_struct()
- 初始化
例子
#include <linux/module.h>
#include <linux/kthread.h>
#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/srcu.h>
MODULE_AUTHOR("OneGhost");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("The module is only used for test");
#define MAX_KTHREAD 10
static struct task_struct *threads[MAX_KTHREAD];
static ulong reader_bitmap;
static void set_reader_number(int reader) {
reader_bitmap = 0;
while (reader--) {
reader_bitmap |= ((ulong) 1 << (ulong) reader);
}
}
struct our_data {
int count1;
int count2;
struct rcu_head rhead;
};
static struct our_data my_data;
static struct our_data __rcu *pmy_data = &my_data; // 声明这个指针被rcu保护
static void show_my_data(void) {
printk("count1=%d, count2=%d\n", pmy_data->count1, pmy_data->count2);
}
static void rcu_free(struct rcu_head *head) {
struct our_data *data;
data = container_of(head, struct our_data, rhead); // 利用struct和成员的地址差,将rhead的位置转换成struct our_data的位置
kfree(data);
}
static DECLARE_RWSEM(myrwlock);
DEFINE_STATIC_SRCU(mysrcu);
static void reader_do(void) {
struct our_data *data;
int idx = srcu_read_lock(&mysrcu);
data = srcu_dereference(pmy_data, &mysrcu);
printk("read count1 %d count2 %d.\n", data->count1, data->count2);
srcu_read_unlock(&mysrcu, idx);
}
static void writer_do(void) {
struct our_data *data, *tmp = pmy_data;
data = kmalloc(sizeof(*data), GFP_KERNEL);
if (!data) return;
down_write(&myrwlock);
memcpy(data, pmy_data, sizeof(*data));
data->count1++;
data->count2 += 10;
rcu_assign_pointer(pmy_data, data);
if (tmp != &my_data)
call_srcu(&mysrcu, &tmp->rhead, rcu_free);
up_write(&myrwlock);
}
static int thread_do(void *data) {
long i = (long) data;
int reader = reader_bitmap & ((ulong) 1 << (ulong) i);
printk("run %ld %s...\n", i, reader ? "reader" : "writer");
while (!kthread_should_stop()) {
if (reader) {
reader_do();
} else {
writer_do();
}
msleep(10);
}
return 0;
}
static int create_thread(void) {
int i;
for (i = 0; i < MAX_KTHREAD; i++) {
struct task_struct *thread;
thread = kthread_run(thread_do, (void *) (long) i, "thread-%d", i);
if (IS_ERR(thread)) {
return -1;
}
threads[i] = thread;
}
return 0;
}
static void cleanup_threads(void) {
int i;
for (i = 0; i < MAX_KTHREAD; i++) {
if (threads[i]) {
kthread_stop(threads[i]);
}
}
}
static __init int minit(void) // 如果加上__init,则只会调用一次
{
printk("call %s.\n", __FUNCTION__);
set_reader_number(5); // 所有的worker都是writer
if (create_thread()) {
cleanup_threads();
return -1;
}
return 0;
}
static __exit void mexit(void) // __exit同理
{
printk("call %s.\n", __FUNCTION__);
cleanup_threads();
show_my_data();
}
module_init(minit)
module_exit(mexit)
测试
在rwlock(不可睡眠中)加入msleep(10); 测试:
#include <linux/module.h>
#include <linux/kthread.h>
#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/srcu.h>
#include <linux/rwlock.h>
MODULE_AUTHOR("OneGhost");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("The module is only used for test");
#define MAX_KTHREAD 10
static struct task_struct *threads[MAX_KTHREAD];
static ulong reader_bitmap;
static void set_reader_number(int reader) {
reader_bitmap = 0;
while (reader--) {
reader_bitmap |= ((ulong) 1 << (ulong) reader);
}
}
struct our_data {
int count1;
int count2;
struct rcu_head rhead;
};
static struct our_data my_data;
static struct our_data __rcu *pmy_data = &my_data; // 声明这个指针被rcu保护
static void show_my_data(void) {
printk("count1=%d, count2=%d\n", pmy_data->count1, pmy_data->count2);
}
static DEFINE_RWLOCK(myrwspinlock);
static void reader_do(void) {
read_lock(&myrwspinlock);
msleep(10);
printk("read count1 %d count2 %d.\n", pmy_data->count1, pmy_data->count2);
read_unlock(&myrwspinlock);
}
static void writer_do(void) {
write_lock(&myrwspinlock);
pmy_data->count1++;
pmy_data->count2 += 10;
write_unlock(&myrwspinlock);
}
static int thread_do(void *data) {
long i = (long) data;
int reader = reader_bitmap & ((ulong) 1 << (ulong) i);
printk("run %ld %s...\n", i, reader ? "reader" : "writer");
while (!kthread_should_stop()) {
if (reader) {
reader_do();
} else {
writer_do();
}
msleep(10);
}
return 0;
}
static int create_thread(void) {
int i;
for (i = 0; i < MAX_KTHREAD; i++) {
struct task_struct *thread;
thread = kthread_run(thread_do, (void *) (long) i, "thread-%d", i);
if (IS_ERR(thread)) {
return -1;
}
threads[i] = thread;
}
return 0;
}
static void cleanup_threads(void) {
int i;
for (i = 0; i < MAX_KTHREAD; i++) {
if (threads[i]) {
kthread_stop(threads[i]);
}
}
}
static __init int minit(void) // 如果加上__init,则只会调用一次
{
printk("call %s.\n", __FUNCTION__);
set_reader_number(5); // 所有的worker都是writer
if (create_thread()) {
cleanup_threads();
return -1;
}
return 0;
}
static __exit void mexit(void) // __exit同理
{
printk("call %s.\n", __FUNCTION__);
cleanup_threads();
show_my_data();
}
module_init(minit)
module_exit(mexit)
电脑直接死机了。
但是视频中说显示 BUG: soft lockup - CPU#0 stuck for 22s!
(在/var/log/messages里面)