Linux内核之读写锁机制

Read-Write Spinlock与Spinlock是极其相似的。自旋锁是一种single-holder lock,如果某个task尝试获取自旋锁,但自旋锁暂时不可得,那么该task便会自旋等待。读写自旋锁与自旋锁是类似的,只是区分了读与写两种操作的自旋锁

  • 那么为何还需要有读写自旋锁?直接使用自旋锁不可以么?

    假如有10个线程,都需要访问同一个全局变量X,可以使用自旋锁进行互斥;但如果这10个线程中,只有线程1是write该变量X,其余的9个线程都是read该变量X,这时候还使用自旋锁,每次只能一个线程进行访问(读或写)该变量X,效率就十分低下了。假如线程2拿到了自旋锁进行读取,这时候线程3至线程10就没法进行读取,而此时变量X是不会改变的,因为线程1没法拿到自旋锁进行写操作。假如是读写锁呢,线程2拿到read-lock,线程3至线程10还可以获取read-lock进行读取,因为此时并没有write操作,所以可以线程2至线程10可以并行执行。

Read Write Spinlock

工作机制
  1. 当临界区(critical section)中没有人时,读者与写者都可以凭借获取对应的读锁或写锁进入,但一次只能一个人进入临界区。
  2. 如果reader进入临界区(拿了read-lock),此时其他reader可以进入临界区,但writer不能进入临界区,必须等所有的reader完成读取操作退出临界区后才能进入。
  3. 如果writer进入了临界区,此时其他reader与writer都不能再进入临界区。
  4. 如果临界区中有一个或者多个reader,此时writer是不能进入临界区的,并且writer不能阻止subsequent的reader进入临界区,这意味这读写自旋锁其实是给了读者更多的机会,并没有给写者机会,如果需要给写者更多的机会,使用seqlock机制
使用场景

如果只需要读取数据,则获取read spinlock。

如果要写数据,则获取write spinlock。

读写自旋锁适用于比较明显区分读者与写者且读者明显多于写者的场景中

initial rwlock
static

include/linux/rwlock_types.h中定义DEFINE_RWLOCK()宏可以静态定义读写自旋锁。

#ifdef CONFIG_DEBUG_SPINLOCK
#define __RW_LOCK_UNLOCKED(lockname)                    \
    (rwlock_t)  {   .raw_lock = __ARCH_RW_LOCK_UNLOCKED,    \
                .magic = RWLOCK_MAGIC,          \
                .owner = SPINLOCK_OWNER_INIT,       \
                .owner_cpu = -1,            \
                RW_DEP_MAP_INIT(lockname) }
#else
#define __RW_LOCK_UNLOCKED(lockname) \
    (rwlock_t)  {   .raw_lock = __ARCH_RW_LOCK_UNLOCKED,    \
                RW_DEP_MAP_INIT(lockname) }
#endif

#define DEFINE_RWLOCK(x)    rwlock_t x = __RW_LOCK_UNLOCKED(x)
dynamic

动态初始化一个读写自旋锁变量,比如:

rwlock_t test_rwlock;
rwlock_init(&test_rwlock);

include/linux/rwlock.h中定义

#ifdef CONFIG_DEBUG_SPINLOCK
  extern void __rwlock_init(rwlock_t *lock, const char *name,
			    struct lock_class_key *key);
# define rwlock_init(lock)					\
do {								\
	static struct lock_class_key __key;			\
								\
	__rwlock_init((lock), #lock, &__key);			\
} while (0)
#else
# define rwlock_init(lock)					\
	do { *(lock) = __RW_LOCK_UNLOCKED(lock); } while (0)
#endif

howto

1. Locking between User context

如果是进user context(内核线程之间)需要获取读写自旋锁,用以下API接口。

read lock

操作API说明
lock*read_lock(rwlock_t lock)获取不到会自选等待
unloc*read_unlock(rwlock_t lock)

write lock

操作API说明
lock*write_lock(rwlock_t lock)获取不到会自选等待
unloc*write_unlock(rwlock_t lock)

代码示例

//Thread 1
int thread_function1(void *pv)
{
    while(!kthread_should_stop()) {  
        write_lock(&etx_rwlock);
        etx_global_variable++;
        write_unlock(&etx_rwlock);
        msleep(1000);
    }
    return 0;
}
//Thread 2
int thread_function2(void *pv)
{
    while(!kthread_should_stop()) {
        read_lock(&etx_rwlock);
        printk(KERN_INFO "In Thread Function2 : Read value %lu\n", etx_global_variable);
        read_unlock(&etx_rwlock);
        msleep(1000);
    }
    return 0;
}
2. Locking between Bottom Halves

如果是在两个不同的下半部之间或者同一个下半部之中,方法同上,read_lock()/write_lock()

3. Locking between User context and Bottom Halves

如果是在bottom half与user context(kernel thread)之间共享数据,读写自旋锁使用API如下。

read lock

操作API说明
lock*read_lock_bh(rwlock_t lock)关闭CPU的软中断
关闭软中断则CPU不响应软中断/tasklet/中断下半部
unloc*read_unlock_bh(rwlock_t lock)打开软中断

write lock

操作API说明
lock*write_lock_bh(rwlock_t lock)关闭CPU的软中断
关闭软中断则CPU不响应软中断/tasklet/中断下半部
unloc*write_unlock_bh(rwlock_t lock)打开软中断

代码示例

//Thread
int thread_function(void *pv)
{
    while(!kthread_should_stop()) {  
        write_lock_bh(&etx_rwlock);
        etx_global_variable++;
        write_unlock_bh(&etx_rwlock);
        msleep(1000);
    }
    return 0;
}
/*Tasklet Function*/
void tasklet_fn(unsigned long arg)
{
        read_lock_bh(&etx_rwlock);
        printk(KERN_INFO "Executing Tasklet Function : %lu\n", etx_global_variable);
        read_unlock_bh(&etx_rwlock);
}
4. Locking between Hard IRQ and Bottom Halves

在中断处理函数interrup ISR与中断下半部之间共享数据

read lock

操作API说明
lock*read_lock_irq(rwlock_t lock)关闭CPU的硬中断
unloc*read_unlock_irq(rwlock_t lock)打开硬中断

write lock

操作API说明
lock*write_lock_irq(rwlock_t lock)关闭CPU的硬中断
unloc*write_unlock_irq(rwlock_t lock)打开硬中断

代码示例

/*Tasklet Function*/
void tasklet_fn(unsigned long arg)
{
        write_lock_irq(&etx_rwlock);
        etx_global_variable++;
        write_unlock_irq(&etx_rwlock);
}
 
//Interrupt handler for IRQ 11. 
static irqreturn_t irq_handler(int irq,void *dev_id) {
        read_lock_irq(&etx_rwlock); 
        printk(KERN_INFO "Executing ISR Function : %lu\n", etx_global_variable);
        read_unlock_irq(&etx_rwlock);
        /*Scheduling Task to Tasklet*/
        tasklet_schedule(tasklet); 
        return IRQ_HANDLED;
}

安全操作

获取锁保留之前的硬中断状态,这样在释放锁的时候还能恢复到之前的硬中断状态。

read lock

操作API说明
lock*read_lock_irqave(rwlock_t lock, unsigned long flags)根据flags保存CPU的硬中断状态,再关闭硬中断
unloc*read_unlock_irq(rwlock_t lock, unsigned long flags)根据flags恢复CPU的硬中断状态

write lock

操作API说明
lock*write_lock_irq(rwlock_t lock, unsigned long flags)根据flags保存CPU的硬中断状态,再关闭硬中断
unloc*write_unlock_irq(rwlock_t lock, unsigned long flags)根据flags恢复CPU的硬中断状态
5. Locking between Hard IRQs

在两个不同的interrupt IRQs之间共享数据,方法同第4点。

示例

#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kdev_t.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include<linux/slab.h>                 //kmalloc()
#include<linux/uaccess.h>              //copy_to/from_user()
#include <linux/kthread.h>             //kernel threads
#include <linux/sched.h>               //task_struct 
#include <linux/delay.h>
 
 
//Static method to initialize the read write spinlock
static DEFINE_RWLOCK(test_rwlock); 
 
//Dynamic method to initialize the read write spinlock
//rwlock_t test_rwlock;
 
unsigned long test_rwlock_global_variable = 0;
 
static struct task_struct *test_rwlock_thread1;
static struct task_struct *test_rwlock_thread2; 
static struct task_struct *test_rwlock_thread3; 
 
/*
** thread function 1 write
*/
int thread_function1(void *pv)
{
    while(!kthread_should_stop()) {  
        write_lock(&test_rwlock);
        test_rwlock_global_variable++;
        pr_info("In Thread Function1 : write once\n");
        write_unlock(&test_rwlock);
        msleep(1000);
    }
    return 0;
}

/*
** thread function 2 - read
*/
int thread_function2(void *pv)
{
    while(!kthread_should_stop()) {
        read_lock(&test_rwlock);
        pr_info("In Thread Function2 : Read value %lu\n", test_rwlock_global_variable);
        read_unlock(&test_rwlock);
        msleep(1000);
    }
    return 0;
}

/*
** thread function 3 - read
*/
int thread_function3(void *pv)
{
    while(!kthread_should_stop()) {
        read_lock(&test_rwlock);
        pr_info("In Thread Function3 : Read value %lu\n", test_rwlock_global_variable);
        read_unlock(&test_rwlock);
        msleep(1000);
    }
    return 0;
}

/*
** Module Init function
*/ 
static int __init test_rwlock_driver_init(void)
{
        /* Creating Thread 1 */
        test_rwlock_thread1 = kthread_run(thread_function1,NULL,"test rwlock Thread1");
        if(test_rwlock_thread1) {
            pr_err("Kthread1 Created Successfully...\n");
        } else {
            pr_err("Cannot create kthread1\n");
      	    return -1;
        }
 
         /* Creating Thread 2 */
        test_rwlock_thread2 = kthread_run(thread_function2,NULL,"test rwlock Thread2");
        if(test_rwlock_thread2) {
            pr_err("Kthread2 Created Successfully...\n");
        } else {
            pr_err("Cannot create kthread2\n");
      	    return -1;
        }

        /* Creating Thread 3 */
        test_rwlock_thread3 = kthread_run(thread_function3,NULL,"test rwlock Thread3");
        if(test_rwlock_thread3) {
            pr_err("Kthread3 Created Successfully...\n");
        } else {
            pr_err("Cannot create kthread3\n");
      	    return -1;
        }
 
        //Dynamic method to initialize the read write spinlock
        //rwlock_init(&test_rwlock);
        
        pr_info("RWspinlock Driver Insert...Done!!!\n");
        return 0;
 
 
}
/*
** Module exit function
*/
static void __exit test_rwlock_driver_exit(void)
{
        kthread_stop(test_rwlock_thread1);
        kthread_stop(test_rwlock_thread2);
        kthread_stop(test_rwlock_thread3);
        pr_info("RWspinlock Driver Remove...Done!!\n");
}
 
module_init(test_rwlock_driver_init);
module_exit(test_rwlock_driver_exit);
 
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("A simple device driver - RW Spinlock");

执行效果

[  527.895219] Kthread1 Created Successfully...
[  527.895267] In Thread Function1 : write once
[  527.895530] Kthread2 Created Successfully...
[  527.895568] In Thread Function2 : Read value 1
[  527.895746] Kthread3 Created Successfully...
[  527.895749] RWspinlock Driver Insert...Done!!!
[  527.895778] In Thread Function3 : Read value 1
[  528.923577] In Thread Function3 : Read value 1
[  528.923577] In Thread Function2 : Read value 1
[  528.923587] In Thread Function1 : write once
[  529.947529] In Thread Function1 : write once
[  529.947569] In Thread Function2 : Read value 3
[  529.947588] In Thread Function3 : Read value 3
[  530.971590] In Thread Function3 : Read value 3
[  530.971607] In Thread Function1 : write once
[  530.971616] In Thread Function2 : Read value 4
[  531.995559] In Thread Function1 : write once
[  531.995566] In Thread Function2 : Read value 5
[  531.995583] In Thread Function3 : Read value 5
[  533.019538] In Thread Function1 : write once
[  533.019583] In Thread Function2 : Read value 6
[  533.019585] In Thread Function3 : Read value 6
[  534.043599] In Thread Function2 : Read value 6
[  534.043601] In Thread Function3 : Read value 6

reference

Linux Device Driver Tutorials – ch23

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值