Linux内核驱动学习之锁

来源:互联网 发布:com.tw 域名注册 编辑:程序博客网 时间:2024/06/05 15:24

    谈到并发访问共享资源,就会谈到锁的问题,不论是什么系统,什么层次。

     而今天主要是对Linux内核中锁的知识进行了学习,由于知识理论上的学习,知道有这么个东西,具体的深入需要在以后的编码过程中慢慢体会了。

    记得在操作系统课程中学过锁,已讲解了生产者和消费者之间锁的关系,而Linux学习的第一个锁也就是这样的一个所,或者在印象中应该叫做信号量吧,特别之处就是该信号量的值我们总是让它为1,于是就变成了互斥锁了。

一 、旗标

在Linux 3中叫做旗标。

具体使用如下:

包含头文件<asm/semaphore.h>,相关的类型是struct semaphore;可以有几种方法来声明和初始化旗标。

1、直接创建旗标,然后使用sema_init来初始化它。sema_init原形为void sema_init(struct semaphore* sem, int val);这里的val是安排给旗标的初始值,我们把它设置为1就是互斥锁了。

2、在Linux 3书上说,如果旗标要在运行时初始化使用init_MUTEX函数,但是我们在前面编译scull代码时,发现在最近的源码中该函数已经被废弃调用,至于我们直接使用sema_init会不会有问题,暂时不知道,但反正现在我们编译出来的驱动调用是没有问题的,并发没有测试。

3、书上也说可以使用DECLARE_MUTEX(name)和DECLARE_MUTEX_LOCKED(name)宏来声明和初始化,具体情况暂不讨论,我们还要往下讨论实际如何控制并发,而不是对一个旗标的初始化纠结不清。

在Linux世界中P操作称为down,down的意思就是对旗标进行减操作,对于旗标来说,有三个版本的down操作,当然他们使用于不同的情况:

void down(struct semaphore* sem);//一直等待,直到旗标可用,不可中断,可能造成进程是一个不可杀死进程

int down_interruptible(struct semaphore* sem);//可中断,一般是最好的选择,但要注意检查返回值

int down_trylock(struct semaphore* sem);//不等待,如果不能获得锁则直接返回非0值

Linux中的V操作对应的是up.而旗标对应的up操作就只有一个了,声明为这样void up(struct semaphore* sem).不管调用上面三个函数中的那个获得的旗标,在调用up后都表示不在拥有该旗标。在scull使用旗标是把它作为设备结构的一个成员,也就是控制对设备的互斥访问。


读写旗标:允许多个读线程对旗标进行并行访问,而如果写线程则必须和读线程对旗标进行互斥,写线程具有高的优先级。该旗标类型是rwsem.头文件在<linux/rwsem.h>中,

数据类型是 struct rw_semaphore;使用时必须对旗标进行显示初始化void init_rwsema(struct rw_semaphore* sem);对应的PV函数使用方法和前面的使用差不多,不在罗列,使用的时候参看就行了。


Completions机制:是用于两个有依赖关系的线程之间同步的问题,就好像在Windows中的事件机制一样。一个线程在某个地方阻塞了,等待另外线程处理完成后发出通知,"OK,你或者你们可以继续玩乐".要使用completion,需要包含头文件<linux/completion.h>,

可以使用DECLARE_COMPLETION(my_completion);宏来初始化,如果必须动态创建和初始化则如下:

struct completion my_completion;

init_completion(&mycompletion);

使用wait_for_completion(struct completion* c);来等待该completion;要注意的是,使用该函数的进程如果其等待的completion没有发生,则该进程是一个不可杀死的进程。

而发出通知的函数有void complete(struct completion* c)和void complete_all(struct completion* c)。前者表示激活一个等待的线程,后者表示激活所有等待的线程,具体用法不详列了。还有另一个函数需要关注,对编成时很有用void complete_and_exit(struct completion* c, int retval);


二 、自旋锁,再Linux中也是一个重要的锁,其也就是一个互斥所,有上锁和解锁两个状态。自旋锁可用在不能够睡眠的代码中,如中断,其性能比旗标高,但对用法上带来一定的限制。自旋锁相对来说较复杂,在Linux 3中花了大篇幅讲,在新内核中应该有所改动,如SPIN_LOCK_UNLOCKED宏不在被使用,我们在编译scull驱动的时候遇见的问题。这里也不在讨论,本次博客的目的是了解Linux中的所种类及大致使用。


三 、不加锁算法   使用环形缓存区实现不加锁算法也算是一种说。


四、原子变量,在程序处理中,一个整形值作为共享资源的情况很多,Linux提供了一个高效的机制来实现对该整形变量的访问。该原子整数类型为atomic_t,包含在头文件<asm/atomic.h>中。


五、位操作,当需要以原子操作操作单个位时,需要使用位操作锁。


六、seqlock锁,是2.6内核包含的一对新机制打算来提供快速地,无锁地存取一个共享资源。seqlock在这种情况下工作,要保护的资源小,简单,并且常常被访问,但很少被写而起快。其使用要求读者来检查与谢者的冲突而工作,并且当发生这样的冲突时,重试他们的存取。seqlock通常不能够用在保护包含指针的数据结构,因为读者可能跟随着一个无效指针而写者在改变数据结构。其实该锁就是当读者检查到用写操作进行时直接报告失败,将重新读取的操作交给调用者去处理。


七、读取拷贝更新锁,是对经常读极少写的情况做优化。恩,所使用方法多,实际使用中再慢慢学习了。


原创粉丝点击