失眠网,内容丰富有趣,生活中的好帮手!
失眠网 > 内核并发控制---自旋锁(来自网易)

内核并发控制---自旋锁(来自网易)

时间:2020-10-27 07:31:23

相关推荐

内核并发控制---自旋锁(来自网易)

定义在头文件linux/spinlock.h中;

自旋锁(spin lock)是一种对临界资源进行互斥访问的典型手段;为了获得一个自旋锁,在某CPU上运行的代码需要首先执行一个原子操作,该操作测试并设置某个内存变量,由于该操作是原子操作,所以在该操作完成之前,其它执行单元对该内存变量的访问被禁止;

如果测试结果表明该自旋锁已经空闲,则程序获得这个自旋锁并继续运行;如果测试结果表明该自旋锁仍然被占用,那么,程序将在一个小的循环内重复执行这个"测试并设置"操作,即,进行所谓的"自旋",通俗地讲就是"在原地打转".当该自旋锁的持有者通过重置该变量释放这个自旋锁之后,某个等待的"测试并设置"操作向其它调用者报告该所已经释放;

理解自旋锁最简单的方法就是把它看成一个变量,该变量把一个临界区或者标记为"我当前正在运行,请稍等一会",或者标记为"我当前不在运行,可以被使用";如果A执行单元首先进入临界区,那么,它将获得并持有该自旋锁;当B执行单元试图进入同一个临界区的时候,将获知该自旋锁已经被持有,需要等到A执行单元释放该自旋锁之后才能进入该临界区;

自旋锁使得设备最多只被一个进程打开;

自旋锁的相关操作:

1).定义自旋锁:

spinlock_t spin;

2).初始化自旋锁:

spin_lock_init(lock);

该函数用于动态初始化自旋锁lock

3).获得自旋锁:

spin_lock(lock);

该函数用于获得自旋锁lock,如果能够立即获得自旋锁,它将马上返回,否则,它将自旋在那里,直到该自旋锁被持有者释放为止;

spin_trylock(lock);

该函数尝试获得自旋锁,如果能够立即获得自旋锁,那么它将得到并持有自旋锁并马上返回真,否则立即返回假,实际上不再"在原地打转"了;

4).释放自旋锁

spin_unlock(lock);

该函数用于释放自旋锁lock;它与spin_lock()或spin_trylock()配对使用;

自旋锁的使用套路:

spinlock_t lock; //定义自旋锁

spin_lock_init(&lock); //初始化自旋锁

spin_lock(&lock); //获得自旋锁,保护临界区;

......

//临界区代码

......

spin_unlock(&lock); //释放自旋锁

自旋锁主要是针对SMP和单CPU但内核支持可抢占式调度的系统;对于单CPU和内核不支持可抢占式调度的系统来说,自旋锁就退化为空操作了;在单CPU和内核可抢占的系统中,自旋锁被持有期间,内核的抢占被禁止;由于内核可抢占的单CPU系统的行为实际很类似于SMP系统,因此,在这样的单CPU系统中使用自旋锁仍十分必要;

尽管使用了自旋锁可以保证临界区不会受到其它CPU和本CPU内抢占进程的打扰,但是得到自旋锁的代码路径在执行临界区代码的时候仍然可能会被中断和低半部所影响;为了防止这种影响,就需要用到自旋锁的衍生;spin_lock()和spin_unlock()是自旋锁机制的基础;把自旋锁与中断屏蔽、原子操作等手段联合使用,就可以形成一整套完整的自旋锁机制;

关系如下:

spin_lock_irq() = spin_lock() + local_irq_disable();

spin_unlock_irq() = spin_unlock() + local_irq_enable();

spin_lock_irqsave() = spin_lock() + local_irq_save();

spin_unlock_irqrestore() = spin_unlock() + local_irq_restore();

spin_lock_bh() = spin_lock() + local_bh_disable();

spin_unlock_bh() = spin_unlock() + local_bh_enable();

5).驱动程序工程师应该谨慎使用自旋锁,而且在使用中还要注意以下几个问题:

A.自旋锁实际上是忙等待;当自旋锁不可用的时候,CPU就一直重复循环执行"测试并设置"该锁,直到可用并获得锁为止;CPU在等待自旋锁的时候不做任何有用的事情,仅仅是忙等待而已;因此,只有在占用锁的时间极短的情况下使用自旋锁才是合理的;当临界区范围很大或者是有共享资源的时候,需要较长时间占用自旋锁,这个时候使用自旋锁会降低系统性能;

B.自旋锁可能导致系统死锁;引发这种问题的一个最常见的情况就是递归地使用一个自旋锁,比如:如果一个已经拥有某个自旋锁的CPU想再次获得这个自旋锁,则该CPU就会陷入死锁状态;此外,如果进程获得自旋锁之后进入阻塞状态,也有可能会导致该进程陷入死锁状态;copy_from_user()、copy_to_user()和kmalloc()等函数都有可能会引起阻塞而导致陷入死锁状态,因此在自旋锁被占用期间不能调用这些函数;

6).其它函数/宏:

spin_is_locked(spinlock):

该宏用于判断自旋锁spinlock是否已经被某执行单元所持有(锁定);如果是,则返回真,否则返回假;

spin_unlock_wait(spinlock):

该宏用于等待自旋锁spinlock变得没有被任何执行单元所持有,也即:等待自旋锁spinlock被所有持有该锁的执行单元释放;如果所有持有自旋锁spinlock的执行单元都已经释放该锁(即:自旋锁spinlock不再被任何执行单元所持有),则该宏立即返回,否则就循环在那里,直到该锁被所有持有该锁的执行单元释放为止;

例子:

#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/spinlock.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:定义自旋锁

spinlock_t my_spin_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:对临界区加锁

spin_lock(&my_spin_lock);

shared_res++;

//STEP4:对临界区解锁

spin_unlock(&my_spin_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:对临界区加锁

spin_lock(&my_spin_lock);

shared_res++;

//STEP4:对临界区解锁

spin_unlock(&my_spin_lock);

msleep(sleep_time);

}

return 34;

};

static int thread_process3(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:对临界区加锁

spin_lock(&my_spin_lock);

val = shared_res;

printk("%s: shared resource = %d;\n%s", __FUNCTION__, val, ((val % 3) ? "" : "\n"));

//STEP4:对临界区解锁

spin_unlock(&my_spin_lock);

msleep(sleep_time);

}

return 56;

};

static int thread_process4(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:对临界区加锁

spin_lock(&my_spin_lock);

val = shared_res;

printk("%s: shared resource = %d;\n%s", __FUNCTION__, val, ((val % 3) ? "" : "\n"));

//STEP4:对临界区解锁

spin_unlock(&my_spin_lock);

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:初始化自旋锁

spin_lock_init(&my_spin_lock);

printk("init spin 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);

如果觉得《内核并发控制---自旋锁(来自网易)》对你有帮助,请点赞、收藏,并留下你的观点哦!

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。