Linux设备驱动程序学习笔记之信号量和互斥体

来源:互联网 发布:软件视频会议系统方案 编辑:程序博客网 时间:2024/06/07 01:25
一个信号量(semaphore: 旗语,信号灯)本质上是一个整数值,它和一对函数联合使用,这一对函数通常称为P和V。希望进入临届区的进程将在相关信号量上调用P;如果信号量的值大于零,则该值会减小一,而进程可以继续。相反,如果信号量的值为零(或更小),进程必须等待知道其他人释放该信号。对信号量的解锁通过调用V完成;该函数增加信号量的值,并在必要时唤醒等待的进程。
当信号量用于互斥时(即避免多个进程同是在一个临界区运行),信号量的值应初始化为1。这种信号量在任何给定时刻只能由单个进程或线程拥有。在这种使用模式下,一个信号量有事也称为一个“互斥体(mutex)”,它是互斥(mutual exclusion)的简称。Linux内核中几乎所有的信号量均用于互斥
使用信号量,内核代码必须包含<asm/semaphore.h>
 
信号量的创建与初始化
1.void sema_init(struct semaphore *sem,int val);
   其中val是赋予一个信号量的初始值
2.DECLARE_MUTEX(name);
   DECLARE_MUTEX_LOCKED(name);
   上面两个宏的结果是:一个称为name的信号量变量被初始化为1(使用DECLARE_MUTEX)或者0  (DECLARE_MUTEX_LOCKED)。
    void init_MUTEX(struct semaphore *sem);
    void init_MUTEX_LOCKED(struct semaphore *sem);
 
P函数为:

void down(struct semaphore*sem);/*不推荐使用,会建立不可杀进程*/
int down_interruptible(struct semaphore*sem);/*推荐使用,使用down_interruptible需要格外小心,若操作被中断,该函数会返回非零值,而调用这不会拥有该信号量。对down_interruptible的正确使用需要始终检查返回值,并做出相应的响应。*/
int down_trylock(struct semaphore*sem);/*带有“_trylock”的永不休眠,若信号量在调用是不可获得,会返回非零值。*/

 
V函数为:

void up(struct semaphore*sem);/*任何拿到信号量的线程都必须通过一次(只有一次)对up的调用而释放该信号量。在出错时,要特别小心;若在拥有一个信号量时发生错误,必须在将错误状态返回前释放信号量。*/

 
 
在scull中使用信号量
其实在之前的实验中已经用到了信号量的代码,在这里提一下应该注意的地方:
在初始化scull_dev的地方:

/* Initialize each device. */
    for (i = 0; i < scull_nr_devs; i++) {
        scull_devices[i].quantum = scull_quantum;
        scull_devices[i].qset = scull_qset;
        init_MUTEX(&scull_devices[i].sem);/* 注意顺序:先初始化好互斥信号量 ,再使scull_devices可用。*/
        scull_setup_cdev(&scull_devices[i], i);
    }

而且要确保在不拥有信号量的时候不会访问scull_dev结构体。

completion


completion是一种轻量级的机制,它允许一个线程告诉另一个线程某个工作已经完成。代码必须包含<linux/completion.h>。使用的代码如下:

 

DECLARE_COMPLETION(my_completion);/* 创建completion(声明+初始化) */

/////////////////////////////////////////////////////////

struct completion my_completion;/* 动态声明completion 结构体*/
static inline void init_completion(&my_completion);/*动态初始化completion*/

///////////////////////////////////////////////////////

void wait_for_completion(struct completion*c);/* 等待completion */
void complete(struct completion*c);/*唤醒一个等待completion的线程*/
void complete_all(struct completion*c);/*唤醒所有等待completion的线程*/

/*如果未使用completion_all,completion可重复使用;否则必须使用以下函数重新初始化completion*/
INIT_COMPLETION(struct completion c);/*快速重新初始化completion*/


 completion的典型应用是模块退出时的内核线程终止。在这种运行中,某些驱动程序的内部工作有一个内核线程在while(1)循环中完成。当内核准备清除该模块时,exit函数会告诉该线程退出并等待completion。为此内核包含了用于这种线程的一个特殊函数:

void complete_and_exit(struct completion*c,long retval);


原创粉丝点击