Linux 内核学习(1) - 同步

Linux 内核学习(1) - 同步

不可睡眠锁

1 Spinlock 自旋锁

spinlock是内核中提供的一种比较常见的锁机制,自旋锁是“原地等待”的方式解决资源冲突的。即,一个线程获取了一个自旋锁后,另外一个线程期望获取该自旋锁,获取不到,只能够原地“打转”(忙等待)。由于自旋锁的这个忙等待的特性,注定了它使用场景上的限制——自旋锁不应该被长时间的持有(消耗 CPU 资源)。

Linux 内核同步(二):自旋锁(Spinlock)

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更新指针,则需要自己处理内存屏障的问题,很复杂

  • 使用

    • 读者

      1. 使用rcu_read_lock()来进入读侧
      2. 使用rcu_deference()来获取指针对应的资源
      3. 使用rcu_read_unlock()来退出读侧
    • 写者

      1. 如果有多个写端,需要写端自己做同步,例如使用spinlock

      2. 使用rcu_assign_pointer()来更新指针

      3. 使用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里面)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值