自旋锁spinlock

参考资料:
《正点原子Linux驱动教程》
《宋宝华 Linux设备驱动开发详解》
 
原子操作只能对整型变量或者bit位进行保护,但是实际使用中,不可能只有整型变量或者bit位等临界区
 
自旋锁spinlock也是一种典型的对临界资源进行互斥访问的手段,其名称来源自它的工作方式。
当一个线程要访问某个共享资源的时候首先要先获取相应的锁,锁只能被一个线程持有,只要此线程不释放持有的锁,那么其他的线程就不能获取此锁。对于自旋锁而言,如果自旋锁正在被线程 A 持有,线程 B 想要获取自旋锁,那么线程 B 就会处于忙循环-旋转-等待状态
 
自旋锁简化定义:
typedef struct {
    arch_spinlock_t raw_lock;
} spinlock_t;
 

自旋锁的相关操作:

spinlock_t lock;            // 定义自旋锁
spin_lock_init(lock)        // 初始化自旋锁
spin_lock(lock)             // 获取自旋锁
spin_unlock(lock)           // 释放自旋锁

 

自旋锁使用demo:

DEFINE_SPINLOCK(lock) /* 定义并初始化一个锁 */

/* 线程 A */
void functionA (){
    unsigned long flags; /* 中断状态 */
    spin_lock_irqsave(&lock, flags) /* 获取锁 */
    /* 临界区 */
    spin_unlock_irqrestore(&lock, flags) /* 释放锁 */
}

/* 中断服务函数 */
void irq() {
    spin_lock(&lock) /* 获取锁 */
    /* 临界区 */
    spin_unlock(&lock) /* 释放锁 */
}
自旋锁主要针对SMP或单CPU但内核可抢占的情况,对于单CPU和内核不支持抢占的系统,自旋锁退化为空操作。自旋锁持有期间内核抢占将被禁止。在多核SMP的情况下,任何一个核拿到了自旋锁,该核上的抢占调度也被暂时禁止了,但是没有禁止另外的核抢占调度
尽管用了自旋锁可以保证临界区不受别的CPU和本CPU内抢占进程的打扰,但是得到锁的代码路径在执行临界区的时候,还可能收到中断的影响,为了防止这种影响,就出现了自旋锁的衍生:
0
本地中断:自旋锁单个核心的中断
 
在SMP编程中,如果进程和中断都有可能访问同一片临界资源时,一般在进程上下文调用spin_lock_irqsave()和spin_lock_irqrestore(),在中断上下文调用spin_lock()和spin_unlock()
编写驱动程序时,需要注意下面的几个问题:
1、自旋锁实际上是忙等待,CPU在等待自旋锁时不做任何有用的工作,仅仅是等待。因此,只有在占用锁的时间极短的情况下,使用自旋锁才是合理的。当临界区很大或共享设备的时候,需要较长时间占用锁,这种情况使用自旋锁会降低系统性能
2、自旋锁可能导致系统死锁,引发这个问题最常见的情况是递归使用一个自旋锁,即如果一个已经拥有某个自旋锁的CPU想第二次获取到这个自旋锁,则该CPU将死锁
3、自旋锁锁定期间不能调度会引起进程调度的函数,如果进程获取自旋锁之后再阻塞,如使用copy_from_user()、copy_to_user()、kmalloc()和msleep()等函数,则会导致系统的崩溃
 

spinlock测试demo:

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/spinlock.h>
#include <linux/kthread.h>
#include <linux/delay.h>

DEFINE_SPINLOCK(my_spinlock);
static int shared_data = 0;

static struct task_struct *my_thread1;
static struct task_struct *my_thread2;

int my_thread_func(void *data)
{
    int i;
    for (i = 0; i < 5; i++) {
        spin_lock(&my_spinlock);
        shared_data++;
        printk(KERN_INFO "Thread %s: Incrementing shared data - %d\n", (char *)data, shared_data);
        spin_unlock(&my_spinlock);
        msleep(1000); // 模拟一些工作
    }
    do_exit(0);
    return 0;
}

static int __init demo_init(void)
{
    printk(KERN_INFO "Demo: Initializing driver\n");

    // 创建两个内核线程
    my_thread1 = kthread_run(my_thread_func, "1", "my_thread1");
    my_thread2 = kthread_run(my_thread_func, "2", "my_thread2");

    return 0;
}

static void __exit demo_exit(void)
{
    if (my_thread1)
        kthread_stop(my_thread1);
    if (my_thread2)
        kthread_stop(my_thread2);

    printk(KERN_INFO "Demo: Exiting driver\n");
}

module_init(demo_init);
module_exit(demo_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("lethe1203");
MODULE_DESCRIPTION("spinlock demo");

 

 
posted @ 2024-03-25 20:32  lethe1203  阅读(11)  评论(0编辑  收藏  举报