最后, 我们看一个实现了阻塞 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 的例子的更多相关文章

  1. linux进程 阻塞和非阻塞操作

    在我们看全功能的 read 和 write 方法的实现之前, 我们触及的最后一点是决定何时使 进程睡眠. 有时实现正确的 unix 语义要求一个操作不阻塞, 即便它不能完全地进行下去. 有时还有调用进 ...

  2. 【Linux】一个简单的线程创建和同步的例子

    最近很多精力在Linux上,今天简单看了一下Linux上的线程和同步,其实不管windows还是Linux,OS层面的很多原理和概念都是相同的,很多windows之上的经验和概念完全可以移植到Linu ...

  3. linux select 与 阻塞( blocking ) 及非阻塞 (non blocking)实现io多路复用的示例

    除了自己实现之外,还有个c语言写的基于事件的开源网络库:libevent http://www.cnblogs.com/Anker/p/3265058.html 最简单的select示例: #incl ...

  4. linux进程解析--进程的创建

    通常我们在代码中调用fork()来创建一个进程或者调用pthread_create()来创建一个线程,创建一个进程需要为其分配内存资源,文件资源,时间片资源等,在这里来描述一下linux进程的创建过程 ...

  5. linux select 与 阻塞( blocking ) 及非阻塞 (non blocking)实现io多路复用的示例【转】

    转自:https://www.cnblogs.com/welhzh/p/4950341.html 除了自己实现之外,还有个c语言写的基于事件的开源网络库:libevent http://www.cnb ...

  6. Linux进程调度器的设计--Linux进程的管理与调度(十七)

    1 前景回顾 1.1 进程调度 内存中保存了对每个进程的唯一描述, 并通过若干结构与其他进程连接起来. 调度器面对的情形就是这样, 其任务是在程序之间共享CPU时间, 创造并行执行的错觉, 该任务分为 ...

  7. Linux进程调度器概述--Linux进程的管理与调度(十五)

    调度器面对的情形就是这样, 其任务是在程序之间共享CPU时间, 创造并行执行的错觉, 该任务分为两个不同的部分, 其中一个涉及调度策略, 另外一个涉及上下文切换. 1 背景知识 1.1 什么是调度器 ...

  8. linux 进程创建clone、fork与vfork

    目录: 1.clone.fork与vfork介绍 2.fork说明 3.vfork说明 4.clone说明5.fork,vfork,clone的区别 内容: 1.clone.fork与vfork介绍 ...

  9. Linux进程间的通信

    一.管道 管道是Linux支持的最初Unix IPC形式之一,具有以下特点: A. 管道是半双工的,数据只能向一个方向流动: B. 需要双工通信时,需要建立起两个管道: C. 只能用于父子进程或者兄弟 ...

随机推荐

  1. 删除指定节点Remove Nth Node From End of List

    Given a linked list, remove the nth node from the end of list and return its head. For example, Give ...

  2. objectarx之判断三点是否在一条直线上

    bool CCommonFuntion::IsOnLine(AcGePoint2d& pt1, AcGePoint2d& pt2, AcGePoint2d& pt3){ AcG ...

  3. iOS 获取 APP 的 Launch Image

    http://www.cocoachina.com/ios/20151027/13780.html 作者:里脊串 授权本站转载. 启动图(LaunchImage)的管理其实在iOS开始中算比较简单的了 ...

  4. day39-Spring 15-Spring的JDBC模板:C3P0连接池配置

    <!-- 配置C3P0连接池 --> <bean id="dataSource2" class="com.mchange.v2.c3p0.ComboPo ...

  5. Nginx 缓存代理

    访问ArcGIS官网的地图瓦片太慢.想到可用NIGIX代理. Nginx是Linux下http代理软件,Windows下也有. 以下为配置方法,注意红色部分. 1.需要在本地proxy_cache_p ...

  6. Android 高仿微信支付键盘

    现在很多app的支付.输入密码功能,都已经开始使用自定义数字键盘,不仅更加方便.其效果着实精致. 下面带着大家学习下,如何高仿微信的数字键盘,可以拿来直接用在自身的项目中. 先看下效果图: 1. 自定 ...

  7. SGU 103 Traffic Lights【最短路】

    题目链接: http://acm.hust.edu.cn/vjudge/problem/visitOriginUrl.action?id=16530 题意: 给定每个点最初的颜色,最初颜色持续时间,以 ...

  8. OpenKruise - 云原生应用自动化引擎正式开源

    2019 年 6 月 24 日至 26 日, 由 Cloud Native Computing Foundation (CNCF) 主办的云原生技术大会 KubeCon + CloudNativeCo ...

  9. 解决ArcMap绘图错误

    这几天在用ArcMap对shapefile做矢量化的过程中,遇到一件特别蛋疼的事,ArcMap竟然会出现绘图错误.如下所示: 纠结了许久之后,终于在Esri社区找到了解决办法:帮助文档中说 “检查属性 ...

  10. Android 错误:IllegalStateException: Can not perform this action after onSaveInstanceState

    今天做Fragment切换.状态保存功能的时候,出现了这个错误: E/AndroidRuntime(12747): Caused by: java.lang.IllegalStateException ...