linux进程一个阻塞 I/O 的例子
最后, 我们看一个实现了阻塞 I/O 的真实驱动方法的例子. 这个例子来自 scullpipe 驱 动; 它是 scull 的一个特殊形式, 实现了一个象管道的设备.
在驱动中, 一个阻塞在读调用上的进程被唤醒, 当数据到达时; 常常地硬件发出一个中断 来指示这样一个事件, 并且驱动唤醒等待的进程作为处理这个中断的一部分. scullpipe 驱动不同, 以至于它可运行而不需要任何特殊的硬件或者一个中断处理. 我们选择来使用
另一个进程来产生数据并唤醒读进程; 类似地, 读进程被用来唤醒正在等待缓冲空间可用 的写者进程.
这个设备驱动使用一个设备结构, 它包含 2 个等待队列和一个缓冲. 缓冲大小是以常用
的方法可配置的(在编译时间, 加载时间, 或者运行时间).
struct scull_pipe
{
wait_queue_head_t inq, outq; /* read and write queues
*/ char *buffer, *end; /* begin of buf, end of buf */
int buffersize; /* used in pointer arithmetic */ char
*rp, *wp; /* where to read, where to write */
int nreaders, nwriters; /* number of openings for r/w
*/
struct fasync_struct *async_queue; /* asynchronous readers */ struct
semaphore sem; /* mutual exclusion
semaphore */ struct cdev cdev; /*
Char device structure */
};
读实现既管理阻塞也管理非阻塞输入, 看来如此:
static ssize_t scull_p_read (struct file *filp, char
user *buf, size_t count, loff_t *f_pos)
{
struct scull_pipe *dev = filp->private_data; if
(down_interruptible(&dev->sem))
return -ERESTARTSYS;
while (dev->rp == dev->wp)
{ /* nothing to read */
up(&dev->sem); /* release the lock */ if
(filp->f_flags & O_NONBLOCK)
return -EAGAIN;
PDEBUG("\"%s\" reading: going to
sleep\n", current->comm);
if
(wait_event_interruptible(dev->inq, (dev->rp != dev->wp))) return
-ERESTARTSYS; /* signal: tell the fs layer to
handle it */ /* otherwise loop, but first reacquire
the lock */
if
(down_interruptible(&dev->sem)) return -ERESTARTSYS;
}
/* ok, data is there, return something */
if (dev->wp > dev->rp)
count = min(count, (size_t)(dev->wp -
dev->rp));
else /* the write pointer
has wrapped, return data up to dev->end */ count = min(count,
(size_t)(dev->end - dev->rp));
if (copy_to_user(buf, dev->rp, count))
{
up (&dev->sem); return -EFAULT;
}
dev->rp += count;
if (dev->rp == dev->end)
dev->rp =
dev->buffer; /* wrapped */ up (&dev->sem);
/* finally, awake any writers and return */
wake_up_interruptible(&dev->outq);
PDEBUG("\"%s\" did read %li
bytes\n",current->comm, (long)count);
return count;
}
如同你可见的, 我们在代码中留有一些 PDEBUG 语句. 当你编译这个驱动,
你可使能消息 机制来易于跟随不同进程间的交互.
让我们仔细看看 scull_p_read 如何处理对数据的等待. 这个 while 循环在持有设备旗 标下测试这个缓冲. 如果有数据在那里, 我们知道我们可立刻返回给用户, 不必睡眠, 因 此整个循环被跳过. 相反, 如果这个缓冲是空的, 我们必须睡眠.
但是在我们可做这个之 前, 我们必须丢掉设备旗标; 如果我们要持有它而睡眠, 就不会有写者有机会唤醒我们. 一旦这个确保被丢掉, 我们做一个快速检查来看是否用户已请求非阻塞 I/O, 并且如果是 这样就返回. 否则, 是时间调用
wait_event_interruptible.
一旦我们过了这个调用, 某些东东已经唤醒了我们, 但是我们不知道是什么. 一个可能是 进程接收到了一个信号. 包含 wait_event_interruptible 调用的这个 if 语句检查这种 情况. 这个语句保证了正确的和被期望的对信号的反应, 它可能负责唤醒这个进程(因为 我们处于一个可中断的睡眠). 如果一个信号已经到达并且它没有被这个进程阻塞, 正确 的做法是让内核的上层处理这个事件. 到此, 这个驱动返回
-ERESTARTSYS 到调用者; 这 个值被虚拟文件系统(VFS)在内部使用, 它或者重启系统调用或者返回 -EINTR 给用户空 间. 我们使用相同类型的检查来处理信号, 给每个读和写实现.
但是, 即便没有一个信号, 我们还是不确切知道有数据在那里为获取. 其他人也可能已经 在等待数据, 并且它们可能赢得竞争并且首先得到数据. 因此我们必须再次获取设备旗标; 只有这时我们才可以测试读缓冲(在
while 循环中)并且真正知道我们可以返回缓冲中的 数据给用户. 全部这个代码的最终结果是, 当我们从
while 循环中退出时, 我们知道旗 标被获得并且缓冲中有数据我们可以用.
仅仅为了完整, 我们要注意, scull_p_read 可以在另一个地方睡眠, 在我们获得设备旗 标之后: 对
copy_to_user 的调用. 如果 scull 当在内核和用户空间之间拷贝数据时睡
眠, 它在持有设备旗标中睡眠. 在这种情况下持有旗标是合理的因为它不能死锁系统(我 们知道内核将进行拷贝到用户空间并且在不加锁进程中的同一个旗标下唤醒我们), 并且
因为重要的是设备内存数组在驱动睡眠时不改变.
linux进程一个阻塞 I/O 的例子的更多相关文章
- linux进程 阻塞和非阻塞操作
在我们看全功能的 read 和 write 方法的实现之前, 我们触及的最后一点是决定何时使 进程睡眠. 有时实现正确的 unix 语义要求一个操作不阻塞, 即便它不能完全地进行下去. 有时还有调用进 ...
- 【Linux】一个简单的线程创建和同步的例子
最近很多精力在Linux上,今天简单看了一下Linux上的线程和同步,其实不管windows还是Linux,OS层面的很多原理和概念都是相同的,很多windows之上的经验和概念完全可以移植到Linu ...
- linux select 与 阻塞( blocking ) 及非阻塞 (non blocking)实现io多路复用的示例
除了自己实现之外,还有个c语言写的基于事件的开源网络库:libevent http://www.cnblogs.com/Anker/p/3265058.html 最简单的select示例: #incl ...
- linux进程解析--进程的创建
通常我们在代码中调用fork()来创建一个进程或者调用pthread_create()来创建一个线程,创建一个进程需要为其分配内存资源,文件资源,时间片资源等,在这里来描述一下linux进程的创建过程 ...
- linux select 与 阻塞( blocking ) 及非阻塞 (non blocking)实现io多路复用的示例【转】
转自:https://www.cnblogs.com/welhzh/p/4950341.html 除了自己实现之外,还有个c语言写的基于事件的开源网络库:libevent http://www.cnb ...
- Linux进程调度器的设计--Linux进程的管理与调度(十七)
1 前景回顾 1.1 进程调度 内存中保存了对每个进程的唯一描述, 并通过若干结构与其他进程连接起来. 调度器面对的情形就是这样, 其任务是在程序之间共享CPU时间, 创造并行执行的错觉, 该任务分为 ...
- Linux进程调度器概述--Linux进程的管理与调度(十五)
调度器面对的情形就是这样, 其任务是在程序之间共享CPU时间, 创造并行执行的错觉, 该任务分为两个不同的部分, 其中一个涉及调度策略, 另外一个涉及上下文切换. 1 背景知识 1.1 什么是调度器 ...
- linux 进程创建clone、fork与vfork
目录: 1.clone.fork与vfork介绍 2.fork说明 3.vfork说明 4.clone说明5.fork,vfork,clone的区别 内容: 1.clone.fork与vfork介绍 ...
- Linux进程间的通信
一.管道 管道是Linux支持的最初Unix IPC形式之一,具有以下特点: A. 管道是半双工的,数据只能向一个方向流动: B. 需要双工通信时,需要建立起两个管道: C. 只能用于父子进程或者兄弟 ...
随机推荐
- NSArray 查询数组中的对象
1.NSString 对象 NSArray *array =@["123", @"234" , @"345"]; NSPredicate ...
- NodeJS基础之Express路由和中间件
路由 路由是指如何定义应用的端点(URIs)以及如何响应客户端的请求. 路由是由一个 URI.HTTP 请求(GET.POST等)和若干个句柄组成,它的结构如下: app.method(path, [ ...
- mysql通过日志恢复数据库
案例:http://www.linuxidc.com/Linux/2012-11/74005.htm http://blog.csdn.net/ssrc0604hx/article/details/1 ...
- 一维数组的初始化及遍历 Day06
package com.sxt.arraytest1; import java.util.Arrays; /* * 一维数组 */ public class ArrayTest2 { public s ...
- Java练习 SDUT-1211_英文金曲大赛
英文金曲大赛 Time Limit: 1000 ms Memory Limit: 65536 KiB Problem Description 我们在"渊子数"的题目中已经了解了渊子 ...
- Python基础:18类和实例之二
1:绑定和非绑定 当存在一个实例时,方法才被认为是绑定到那个实例了.没有实例时方法就是未绑定的.在很多情况下,调用的都是一个绑定的方法. 调用非绑定方法并不经常用到,其中一个主要的场景是:派生一个子类 ...
- @CSP模拟2019.10.16 - T3@ 垃圾分类
目录 @description@ @solution@ @accepted code@ @details@ @description@ 为了保护环境,p6pou建设了一个垃圾分类器. 垃圾分类器是一个 ...
- 12-2 js基础
一 数据类型 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UT ...
- oracle函数 sys_guid()
[功能]生产32位的随机数,不过中间包括一些大写的英文字母. [返回]长度为32位的字符串,包括0-9和大写A-F [示例] select sys_guid() from dual
- 模板—点分治A(容斥)(洛谷P2634 [国家集训队]聪聪可可)
洛谷P2634 [国家集训队]聪聪可可 静态点分治 一开始还以为要把分治树建出来……• 树的结构不发生改变,点权边权都不变,那么我们利用刚刚的思路,有两种具体的分治方法.• A:朴素做法,直接找重心, ...