最后, 我们看一个实现了阻塞 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. centOS7 安装vsftp服务器

    一.目的:有许多时候我们需要从自己机器上,上传文件到Linux服务器上,想要上传文件就必须要通过FTP 协议(File Transfer Protocol(文件传输协议)).所以要在服务器上配置FTP ...

  2. java视频长度读取 方案参照文件

    ffmpeg库 必须http://ffmpeg.org/download.html#build-windows 第三方jar sauronsoftwarehttp://www.sauronsoftwa ...

  3. Directx11教程38 纹理映射(8)

    原文:Directx11教程38 纹理映射(8)      上篇日志中,我们用纹理和光照颜色调制的方式得到最终颜色,本章我们尝试用纹理采样的颜色,直接做为材质的漫反射系数Kd,并用它来做光照计算,最后 ...

  4. 1-1.go开发工具安装

    (1) (2) bin:go的可执行文件 src:go的源代码 (3)安装开发工具 安装:goland-2018.1.1.exe 将.jar文件复制到工具的bin目录下   用记事本打开“goland ...

  5. ES6对象的super关键字

    super是es6新出的关键字,它既可以当作函数使用,也可以当作对象使用,两种使用方法不尽相同 1.super用作函数使用的时候,代表父类的构造函数,es6规定在子类中使用this之前必须先执行一次s ...

  6. Calendar类实现当前日期的日历

    package com.sxt.home1; import java.text.DateFormat; import java.text.ParseException; import java.tex ...

  7. [软考]之软件过程模型I 标签: 总结软考 2015-10-24 11:58 863人阅读 评论(35) 收藏

    做软考题的时候经常碰到软件工程的题,因为这些题有的很相近,容易混淆,所以在这里总结归纳一下. 软件过程模型: 瀑布模型: 瀑布模型是将软件生存周期中的各个活动规定为依线性顺序连接的若干阶段的模型,包括 ...

  8. oracle函数 current_timestamp

    [功能]:以timestamp with time zone数据类型返回当前会话时区中的当前日期 [参数]:没有参数,没有括号 [返回]:日期 [示例]select current_timestamp ...

  9. 5、nginx配置

    1.安装 sudo apt-get install nginx 2.启用 sudo service nginx start 3.若要将 Nginx 配置为转发请求向 ASP.NET Core 应用程序 ...

  10. getopt、getopt_long和getopt_long_only解析命令行参数

    一:posix约定: 下面是POSIX标准中关于程序名.参数的约定: 程序名不宜少于2个字符且不多于9个字符: 程序名应只包含小写字母和阿拉伯数字: 选项名应该是单字符或单数字,且以短横 '-' 为前 ...