linux设备驱动中的阻塞和非阻塞I/O

2012-08-11 16:22

 

    阻塞和非阻塞I/O是设备访问的两种不同模式,驱动程序可以灵活的支持用户空间对设备的这两种访问形式。

       阻塞操作是指在执行设备操作时,若不能获得资源,则挂起进程直到满足可操作的条件后在进行操作。被挂起的进程进入休眠状态,被从调度器的运行队列移走,直到等待的条件被满足。

而非阻塞操作的进程在不能进行设备操作时,并不挂起,他或者放弃,或者不停的查询,直到可以进行操作为止。

     注:阻塞听起来意味着低效率,其实不然,如果设备部阻塞,则用户想获取设备资源时,只能不停的查询,这反而会增加CPU资源的耗费,而阻塞方式是,不能获取资源的进程将进入休眠,他将CPU资源让给其他的进程。

         阻塞会进入休眠,因此必须有一个地方能够唤醒休眠的进程,否则,这个进程就“寿终正寝”了。这个地方最大的可能是在中断,因为硬件的资源的获得往往伴随着中断。下列演示了以阻塞和非阻塞方式读取串口的一个字符。

阻塞方式:

char buf;

fd = open("/dev/ttyS1",O_RDWR);

...

res = read(fd,&buf,1);   //当串口有输入时才返回

if(res==1)

printf("%c\n",buf);

非阻塞方式:

char buf;

fd = open("/dev/ttyS1",O_RDWR | O_NONBLOCK);

...

while (read(fd,&buf,1)!=1)

continue;   //串口无输入时也返回,所以要循环尝试读取串口

printf("%c\n",buf);

 

等待队列

      linux驱动程序中,可以使用等待队列来实现阻塞进程的唤醒。

操作如下:

1 定义等待队列头

wait_queue_head_t  my_queue;

 

2 初始化等待队列头

init_waitqueue_head (& my_queue);

 

3 定义等待队列

DECLARE_WAITQUEUE(name,tsk);

 

4 添加/移除队列

void fastcall add_wait_queue(wait_queue_head_t *q,wait_queue_t,*wait);

void fastcall remove_wait_queue(wait_queue_head_t *q,wait_queue_t,*wait);

 

5 等待事件

wait_event(queue,condition);

wait_event_interruptible(queue,condition);

wait_event_timeout(queue,condition,timeout);

wait_event_interruptible_timeout(queue,condition,timeout);

 

6 唤醒队列

void wake_up(wait_queue_head_t *queue);

void wake_up_interruptible(wait_queue_head_t *queue);

 

7 在等待队列上睡眠

void sleep_on(wait_queue_head_t *q);

void interruptible_sleep_on(wait_queue_head_t *q);

 

轮询操作

        使用非阻塞的应用程序通常会使用select 和poll 系统调用查询是否可以对设备进行无阻塞访问。select 和poll 系统调用最终会引发设备驱动中的poll函数的执行。select 和poll 系统调用本质是一样的。

应用程序的轮询编程:

select()系统调用:原型如下:

int select(int numfds,fd_set *readfds,fd_set *writefds,fd_set *exceptfds,struct timeval *timeout);

         其中:readfds , writefds exceptfds 分别是select 监视的读,写和异常处理的文件描述符集合,numfds 的值是需要检查的号码最高的文件描述符加1 ,timeout 参数是一个指向structtimeval 类型的指针,他可以使select在等待timeout时间后,若没有文件描述符准备好则返回。

 

设备驱动程序中的轮询编程

poll 函数系统调用

unsigned int (*poll )(struct file *filp,struct poll_table * wait);

第一参数为file 结构体的指针,第二个参数是轮询表指针,该函数完成两项工作:

(1)对可能引起设备文件状态变化的等待队列调用poll_wait()函数,将对应的等待队列头添加到poll_table

(2) 返回表示是否能对设备进行无阻塞读,写 访问的掩码。

关键的,用于向poll_table注册等待队列额poll_wait()函数原型如下:

void poll_wait(struct file*filp ,wait_queue_head_t *queue,poll_table *wait);

注:poll_wait()并不会引起阻塞,poll_wait() 函数的工作是把当前的进程添加到wait 参数指定的等待列表(poll_table)中。

     驱动程序poll 函数应该返回设备资源的可获取状态,即:

POLLIN

POLLOUT

POLLPRI

POLLERR

POLLNVAL

等宏的位“或”结果。每个宏的含义都表明设备的一种状态。

设备驱动中的poll 模型如下:

static unsigned int xxx_poll(struct file *filp,poll_table *wait)

{

        unsigned int mask = 0;

        struct xxx_dev *dev = filp->private_data;  //获得设备结构体指针

        ...

        poll_wait(filp,&dev->r_wait,wait);  //加读等待队列头

        poll_wait(filp,&dev->w_wait,wait);//加写等待队列头

 

        if(...)  //可读

                 mask |= POLLIN | POLLRDNORM;   //标识数据可获得

        if(...)//可写

                 mask |= POLLOUT | POLLWRNORM;   //标识数据可写入

         ...

         return mask;

}

 

 

 

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值