阻塞型字符设备驱动

来源:互联网 发布:四会网络推广学校 编辑:程序博客网 时间:2024/04/29 21:15

阻塞型字符设备驱动

[概述]

首先明确一点,不管你是睡眠、休眠还是阻塞,还是挂起,本质上都是把进程放到等待队列上

 

[休眠的实现]

休眠通过等待队列进行处理。等待队列是由等待某些事件发生的进程组成的简单链表。内核中用wake_queue_head_t来代表等待队列。等待队列可以通过DECLARE_WAITQUEUE()静态创建,也可以由Init_waitqueue_head()动态创建。进程把自己放入等待队列中并设置成不可执行状态。当与等待队列相关的事件发生的时候,队列上的进程会被唤醒。为了避免产生竞争条件,休眠和唤醒的实现不能有纰漏。

针对休眠,内核提供了一些简单的接口。但那些接口会带来竞争条件:有可能导致在判断条件为真后,进程却开始了休眠,那样就会使进程无限期地休眠下去,所以,在内核中进行休眠的推荐操作就想复杂了一些:

DEFINE_WAIT(wait); Add_wait_queue(q, &wait);While (!condition) {         Prepare_to_wait(&q,&wait, TASK_INTERRUPTIBLE);         If(signal_pending(current))                   /*处理信号 */         Schedule();}Finish_wait(&q, &wait);

1)      调用宏DEFINE_WAIT()创建一个等待队列的项;

2)      调用add_wait_queue()把自己加入到队列中,该队列会在进程等待的条件满足时唤醒它,当然我们必须在其他地方编写相关代码,在事件发生时,对等待队列执行wake_up()操作;

3)      调用prepare_to_wait()方法将进程的状态变更为TASK_INTERRUPTIBLE或TASK_UNINTERRUPTIBLE。而且该函数如果有必要的话会将进程家回到等待队列,这是在接下来的循环遍历中所需要的;

4)      如果状态被设置为TASK_INTERRUPTIBLE,则信号唤醒进程。这就是所谓的伪唤醒,因此检查并处理信号;

5)      当进程被唤醒的时候,它会再次检查条件是否为真。如果是,它就退出循环;如果不是,它再次调用schedule()并一直重复这步操作;

6)      当条件满足后,进程将自己设置为TASK_RUNNING并调用finish_wait()方法把自己移出等待队列;

如果需要了解这些函数的实现,可以参考http://edsionte.com/techblog/archives/1854

 

[一个阻塞型FIFO实例]

对上面休眠实现理论有了一个了解,看下面的代码应该不难了。

[fifo结构体]

struct simple_chrdev_fifo {         structcdev cdev;         charfifo[MAX_SIZE];         intfifo_size;         intcurrent_pos;       /* read and writeposition in fifo */         structsemaphore sem;   /* semaphore */         wait_queue_head_treadq, writeq;         /* read queue andwrite queue */};

[阻塞型read方法]

static ssize_t simple_read(struct file*filp, char __user *buf, size_t count,                                                                 loff_t*f_pos){         structsimple_chrdev_fifo *dev = filp->private_data;         intret = 0;          DEFINE_WAIT(wait);          if(down_interruptible(&dev->sem))                   return-ERESTARTSYS;          add_wait_queue(&dev->readq,&wait);         while(dev->current_pos == 0) {                   up(&dev->sem);                   if(filp->f_flags & O_NONBLOCK)                            return- EAGAIN;                    prepare_to_wait(&dev->readq,&wait, TASK_INTERRUPTIBLE);                    if(signal_pending(current)) {                            ret  = - ERESTARTSYS;                            gotoout2;                   }                    schedule();                    if(down_interruptible(&dev->sem)) {                            ret= -ERESTARTSYS;                            gotoout2;                   }         }          if(count > dev->current_pos)                   count= dev->current_pos;                 if(copy_to_user(buf, (void *)(dev->fifo), count)) {                   ret= -EFAULT;                   gotoout;         }         dev->current_pos-= count;         ret= count;          wake_up_interruptible(&dev->writeq);out:         up(&dev->sem);out2:         finish_wait(&dev->readq,&wait);         returnret;}

[模块初始化函数]

static int __initsimple_chrdev_fifo_init(void){         ……         /*          * initialze wait queue         */         init_waitqueue_head(&dev->readq);         init_waitqueue_head(&dev->writeq);          init_MUTEX(&dev->sem);         ……}

使用init_waitqueue_head宏对等待读队列头和等待写队列头进行初始化;

这里只贴出了重要的代码,如果需要完整的代码,请发email给我。