阻塞型字符设备驱动

阻塞型字符设备驱动

[概述]

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

 

[休眠的实现]

休眠通过等待队列进行处理。等待队列是由等待某些事件发生的进程组成的简单链表。内核中用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给我。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值