linux设备驱动学习(四)——阻塞与非阻塞I/O

1.阻塞与非阻塞I/O介绍

   阻塞和非阻塞I/O是设备访问的两种不同模式。阻塞操作是指在执行设备操作时,若不能获得资源,则挂起进程直到满足可操作的条件后再进行操作。被挂起的进程进入睡眠状态,被从调度器的运行队列移走,直到等待的条件被满足。而非阻塞操作的进程在不能进行设备操作时,并不挂起,它要么放弃,要么不停地查询,直至可以进行操作为止。

    驱动程序通常需要提供这样的能力:当应用程序进行read()、write()等系统调用时,若设备的资源不能获取,而用户又希望以阻塞的方式访问设备,驱动程序应在设备驱动的xxx_read()、xxx_write()等操作中将进程阻塞直到资源可以获取,此后,应用程序的read()、write()等调用才返回,整个过程仍然进行了正确的设备访问,用户并没有感知到;若用户以非阻塞的方式访问设备文件,则当设备资源不可获取时,设备驱动的xxx_read()、xxx_write()等操作应立即返回,read()、write()等系统调用也随即被返回,应用程序收到-EAGAIN返回值。

2.阻塞进程的唤醒

   在Linux驱动程序中,可以使用等待队列(Wait Queue)来实现阻塞进程的唤醒。等待队列很早就作为一个基本的功能单位出现在Linux内核里了,它以队列为基础数据结构,与进程调度机制紧密结合,可以用来同步对系统资源的访问。

wait_queue_head_t my_queue;   //定义“等待队列头部”,wait_queue_head_t是__wait_queue_head结构体的一个typedef

init_waitqueue_head(&my_queue); //初始化“等待队列头部”

DECLARE_WAIT_QUEUE_HEAD (name);   //此宏可以作为定义并初始化等待队列头部的“快捷方式”

DECLARE_WAITQUEUE(name, tsk);  //定义等待队列元素,定义并初始化一个名为name的等待队列元素。

/*添加和移除等待队列*/

void add_wait_queue(wait_queue_head_t *q, wait_queue_t *wait);//将等待队列元素wait添加到等待队列头部q指向的双向链表中

void remove_wait_queue(wait_queue_head_t *q, wait_queue_t *wait);//将等待队列元素wait从由q头部指向的链表中移除

/*等待事件*/

wait_event(queue, condition)
wait_event_interruptible(queue, condition)
wait_event_timeout(queue, condition, timeout)

wait_event_interruptible_timeout(queue, condition, timeout)

    等待第1个参数queue作为等待队列头部的队列被唤醒,而且第2个参数condition必须满足,否则继续阻塞。wait_event()和wait_event_interruptible()的区别在于后者可以被信号打断,而前者不能。加上_timeout后的宏意味着阻塞等待的超时时间,以jiffy为单位,在第3个参数的timeout到达时,不论condition是否满足,均返回。

/*唤醒队列*/

void wake_up(wait_queue_head_t *queue);

void wake_up_interruptible(wait_queue_head_t *queue);

   wake_up()应该与wait_event()或wait_event_timeout()成对使用,而wake_up_interruptible()则应与wait_event_interruptible()或wait_event_interruptible_timeout()成对使用。wake_up()可唤醒处于TASK_INTERRUPTIBLE和TASK_UNINTERRUPTIBLE的进程,而wake_up_interruptible()只能唤醒处于TASK_INTERRUPTIBLE的进程

/*在等待队列上睡眠*/

sleep_on(wait_queue_head_t *q );

interruptible_sleep_on(wait_queue_head_t *q );

sleep_on()函数的作用就是将目前进程的状态置成TASK_UNINTERRUPTIBLE,并定义一个等待队列元素,之后把它挂到等待队列头部q指向的双向链表,直到资源可获得,q队列指向链接的进程被唤醒。interruptible_sleep_on()与sleep_on()函数类似,其作用是将目前进程的状态置成TASK_INTERRUPTIBLE,并定义一个等待队列元素,之后把它附属到q指向的队列,直到资源可获得(q指引的等待队列被唤醒)或者进程收到信号。

sleep_on()函数应该与wake_up()成对使用,interruptible_sleep_on()应该与wake_up_interruptible()成对使用。

/* 示例  */

static int __init globalfifo_init(void)
{
 int ret;
 dev_t devno = MKDEV(globalfifo_major, 0);

 if (globalfifo_major)
 ret = register_chrdev_region(devno, 1, "globalfifo");
 else {
 ret = alloc_chrdev_region(&devno, 0, 1, "globalfifo");
 globalfifo_major = MAJOR(devno);
 }
 if (ret < 0)
 return ret;

 globalfifo_devp = kzalloc(sizeof(struct globalfifo_dev), GFP_KERNEL);
 if (!globalfifo_devp) {
 ret = -ENOMEM;
 goto fail_malloc;
 }

globalfifo_setup_cdev(globalfifo_devp, 0);

mutex_init(&globalfifo_devp->mutex);
init_waitqueue_head(&globalfifo_devp->r_wait);
init_waitqueue_head(&globalfifo_devp->w_wait);

return 0;

fail_malloc:
unregister_chrdev_region(devno, 1);
return ret;
}
module_init(globalfifo_init);



猜你喜欢

转载自blog.csdn.net/kuishao1314aa/article/details/80511928