高级字符驱动学习--阻塞型I/0

提出问题:若驱动程序无法立即满足请求,该如何响应? 比如:当数据不可用时调用read,或是在缓冲区已满时,调用write

解决问题:驱动程序应该(默认)该阻塞进程,将其置入休眠状态直到请求可继续。

 

休眠:

当一个进程被置入休眠时,它会被标记为一种特殊状态并从调度器运行队列中移走,直到某些情况下修改了这个状态,才能运行该进程。

安全进入休眠两原则:

1.永远不要在原子上下文中进入休眠。(原子上下文:在执行多个步骤时,不能有任何的并发访问。这意味着,驱动程序不能再拥有自旋锁,seqlock,或是RCU锁时,休眠)

2.对唤醒之后的状态不能做任何假定,因此必须检查以确保我们等待的条件真正为真

 

临界区  vs  原子上下文

原子上下本:一般说来,具体指在中断,软中断,或是拥有自旋锁的时候。

临界区:每次只允许一个进程进入临界区,进入后不允许其它进程访问。

 

other question:

要休眠进程,必须有一个前提:有人能唤醒进程,而起这个人必须知道在哪儿能唤醒进程,这里,就引入了“等待队列”这个概念。

等待队列:就是一个进程链表(我的理解:是一个休眠进程链表),其中包含了等待某个特定事件的所有进程。

等待队列头:wait_queue_head_t,定义在<linux/wait.h>

定义方法:静态  DECLARE_QUEUE_HEAD(name)

                      动态  wait_queue_head_t  my_queue;

                                 init_waitqueue_head(&my_queue);

 

struct __wait_queue_head {
    spinlock_t lock;
    struct list_head task_list;
};
typedef struct __wait_queue_head wait_queue_head_t;

 

简单休眠

linux最简单的休眠方式是wait_event(queue,condition)及其变种,在实现休眠的同时,它也检查进程等待的条件。四种wait_event形式如下:

wait_event(queue,condition);/*不可中断休眠,不推荐*/

wait_event_interruptible(queue,condition);/*推荐,返回非零值意味着休眠被中断,且驱动应返回-ERESTARTSYS*/

wait_event_timeout(queue,condition,timeout);

wait_event_interruptible_timeout(queue,conditon,timeout);/*有限的时间的休眠,若超时,则不管条件为何值返回0*/

 

唤醒休眠进程的函数:wake_up

void wake_up(wait_queue_head_t  *queue);

void wake_up_interruptible(wait_queue_head  *queue);

惯例:用wake_up唤醒wait_event,用wake_up_interruptible唤醒wait_event_interruptible

 

休眠与唤醒 实例分析:

本例实现效果为:任何从该设备上读取的进程均被置于休眠。只要某个进程向给设备写入,所有休眠的进程就会被唤醒。

static DECLARE_WAIT_QUEUE_HEAD(wq);

static int flag =0;

ssize_t sleepy_read(struct file *filp,char __user *buf,size_t count,loff_t *pos)

{

pirntk(KERN_DEBUG "process %i (%s) going to sleep\n",current->pid,current->comm);

wait_event_interruptible(wq,flag!=0);

flag=0;

printk(KERN_DEBUG "awoken %i (%s) \n",current->pid,current->comm);

return 0;

}

 

ssize_t sleepy_write(struct file *filp,const char __user *buf,size_t count,loff_t *pos)

{

printk(KERN_DEBUG "process %i (%s) awakening the readers ...\n",current->pid,current->comm);

flag=1;

wake_up_interruptible(&wq);

return count;  /*成功并避免重试*/

}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章