什么是阻塞操作?
这个好理解,其实就是对设备的操作没成功,进程进入等待状态,等待系统唤醒。
在代码的层面理解: 运行到某代码(如某read()操作), 进程挂在那了,不再往下运行。
下面是我对等待队列的理解:
如上图,一个FIFO设备的读写,需要两个wait_queue_head,分别对应读、写。
等待读、写的进程分别挂在这两个wait_queue_head后面。
注:其实FIFO的读(或写)就是某种资源,等待队列的作用就是把等待该资源的进程都挂在上面,然后进程本身进入等待状态。
|
|
2,实例
如果不关心具体实现,只需要了解怎么使用等待队列的话,所需要做的工作还是很少的。(原理后续再研究)
假设一个my_fifo设备,实现: 如果没有进程写,则试图读的进程会被阻塞
1) 该设备的结构体
-
structmyfifo_dev
-
{
-
/*其他成员这里就不列出了*/
-
-
wait_queue_head_t r_write; // 阻塞写的等待队列头
-
wait_queue_head_t r_read; // 阻塞读的等待队列头
-
-
};
2) 等待队列的初始化
从上文我们知道,这个就是等待队列3类操作中的第1类了 :)
问1:在哪里初始化呢?
答:当然是在内核模块加载函数中。
问2: 怎么初始化?
答:调用init_waitqueue_head( r_wait )。 (这个r_wait是怎么来的?,呵呵,设备结构体中)
问3:关于初始化还有别的东西吗?
答:好像没有了,就这么简单。
3) 将一个进程加入等待队列(阻塞)
根据前面的功能描述,因为是read()操作被阻塞,所以需要在read()函数中做修改,如下:
-
static ssize_t mydev_read(struct file*filp, char* buf, size_t len,loff_t* off)
-
{
-
if(wait_event_interruptible(r_read,flag!=0) ) // 就是这里
-
return-ERESTARTSYS;
-
-
if(down_interruptible(&sem) )
-
return-ERESTARTSYS;
-
-
flag= 0;
-
if(copy_to_user(buf,data,len) )
-
{
-
up(&sem);
-
return-EFAULT;
-
}
-
up(&sem);
-
returnlen;
-
}
根据前面的功能,只要有进程执行write()操作,则就会唤醒被read()阻塞的进程。所以,需要在write()函数中做修改:
-
ssize_t mydev_write(struct file *file,const char __user *buf, size_t count, loff_t *f_pos)
-
{
-
ssize_t ret = 0;
-
pr_info("mydev_write!\n");
-
pr_info("writing %d bytes\n", count);
-
-
if( down_interruptible(&sem) )
-
return-ERESTARTSYS;
-
-
if (count > 127)
-
return -ENOMEM;
-
-
if (count < 0)
-
return -EINVAL;
-
-
if (copy_from_user(data, buf, count))
-
{
-
up(&sem);
-
return -EFAULT;
-
}
-
else {
-
data[127] = '\0';
-
pr_info("kernel received: %s\n", data);
-
ret = count;
-
-
}
-
-
up(&sem);
-
-
flag = 1;
-
-
wake_up_interruptible(&r_read); //就是这里
-
-
return ret;
-
-
}