阻塞式i/o
如果无法立即满足请求,一个驱动程序应该如何响应:
比如一个进程尝试写,但是你的设备没有准备好接受数据,因为你的缓冲区是满的。调用过程通常不关心这些问题;程序员只是希望在必要的工作之后,呼叫读写,并有呼叫返回完成.因此,在这种情况下,你的驱动程序应该(默认)阻止进程,让它睡眠,直到请求可以继续。
睡眠
什么是睡眠:
当一个进程所请求的资源或操作无法立即完成,该进程的状态会被改变,它被放入阻塞队列知道有相应的事件发生,它才会被唤醒。
睡眠的规矩
1、如果线程A持有锁,比如自旋锁,那A一定不能睡眠
2、一定要记得,当线程A被唤醒的时候,A不知道这段时间发生了什么,任何假设都是不成立的。
3、当然还有一点线程A睡眠前,一定要确保有另一个线程B一定会把A叫醒。
如何找到睡眠的进程
当进程睡眠的时候它会到等待队列中注册,负责叫醒的线程只要到该等待队列中把所有睡眠的线程叫醒就行了。
等待队列被定义在<linux/wait.h>中。
一般的等待队列是这样声明和初始化的:
wait_queue_head_t my_queue;
init_waitqueue_head(&my_queue);
Sleeping API
wait_event(queue , condition);
wait_event_interruptible(queue , condition);
wait_event_timeout(queue , condition , timeout);
wait_event_interruptible_timeout(queue , condition , timeout);
void wake_up(wait_queue_head_t *queue);
void wake_up_interruptible(wait_queue_head_t *queue);
如何实现一个阻塞式的i/o如管道:
ssize_t scull_read(struct file *filp , char __user *buf, size_t count,loff_t *f_pos)
{
if (down_interruptible(&mutex))
return -ERESTARTSYS;
ssize_t retval = 0;
while (size == 0) {
up(&mutex);
if (wait_event_interruptible(inq, (size != 0)))
return -ERESTARTSYS; /* signal: tell the fs layer to
handle it */
if (down_interruptible(&mutex))
return -ERESTARTSYS;
}
if (count > size)
count = size;
if (outp + count > MAX_SIZE){
if (copy_to_user(buf, store + outp, MAX_SIZE - outp)) {
retval = -EFAULT;
goto out;
}
if (copy_to_user(buf+ MAX_SIZE - outp, store, count + outp-MAX_SIZE)) {
retval = -EFAULT;
goto out;
}
}else{
if (copy_to_user(buf, store + outp , count)) {
retval = -EFAULT;
goto out;
}}
outp += count;
outp = (outp + count) % MAX_SIZE;
retval = count;
size -= count;
out:
up(&mutex);
wake_up_interruptible(&outq);
return retval;
}
ssize_t scull_write(struct file *filp , char __user *buf, size_t count,loff_t *f_pos)
{
if (down_interruptible(&mutex))
return -ERESTARTSYS;
ssize_t retval = 0;
while (size == MAX_SIZE) {
up(&mutex);
if (wait_event_interruptible(outq, (size != MAX_SIZE)))
return -ERESTARTSYS; /* signal: tell the fs layer tohandle it */
if (down_interruptible(&mutex))
return -ERESTARTSYS;
}
if (count > MAX_SIZE - size)
count =MAX_SIZE - size;
if (outp + count +size > MAX_SIZE){
if (copy_from_user(store + outp+size , buf, MAX_SIZE - outp - size)) {
retval = -EFAULT;
goto out;
}
if (copy_from_user(store, buf+MAX_SIZE - outp - size, count+outp + size-MAX_SIZE)) {
retval = -EFAULT;
goto out;
}
}else{
if (copy_from_user(store + outp +size , buf, MAX_SIZE - outp - size)) {
retval = -EFAULT;
goto out;
}}
size+= count;
retval = count;
out:
up(&mutex);
wake_up_interruptible(&inq);
return retval;
}
其他部分请参考:Linux内核--简单的字符设备scull