最后, 我们看一个实现了阻塞 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. Spring_Aop_(二)

    切面的优先级 @Order(1)注解 指定切面的优先级,值越小优先级越高 @Order(1) @Aspect @Component public class VlidationAspect { @Be ...

  2. Hibernate的DetachedCriteria使用(含Criteria)转载

    https://www.cnblogs.com/deng-cc/p/6428599.html 1.背景了解:Hibernate的三种查询方式 Hibernate总的来说共有三种查询方式:HQL.QBC ...

  3. 24种编程语言的Hello World程序

    24种编程语言的Hello World程序 这篇文章主要介绍了 24 种编程语言的 Hello World 程序,包括熟知的 Java.C 语言.C++.C#.Ruby.Python.PHP 等编程语 ...

  4. C#中App.config文件配置获取

    最新的framework使用如下方法: using System.Configuration; ConfigurationManager.AppSettings["key"]; A ...

  5. JavaScript —— 给函数参数设置默认值

    一.ES5 function fn(x, y){ y = y || 20; console.log(x, y); } fn(); // undefined 20 fn(5); // 5 20 fn(5 ...

  6. ActiveX控件的消息处理函数

    首先切换到类视图 然后鼠标单击选中类(如果你要给ClockCtrl类添加事件,你就选中ClockCtrl类) PS:顺便多说一句,如果不用这种方法,而是手动添加,即使你的代码跟MFC添加的一模一样,那 ...

  7. POI解决内存溢出问题

    在POI3.8中SXSSF仅仅支持excel2007格式是对XSSF的一种流的扩展.目的在生成excel时候,需要生成大量的数据的时候,通过刷新的方式将excel内存信息刷新到硬盘的方式,提供写入数据 ...

  8. @codeforces - 444A@ DZY Loves Physics

    目录 @description@ @solution@ @accepted code@ @details@ @description@ 给定一个 n 点 m 边的图,边有边权,点有点权. 找到一个连通 ...

  9. HZOJ 连连看

    考场几乎想到了正解,然而我也不知道当时在想啥,在没有证伪的情况下只是觉得无法实现就否了…… 最后打的好象是达哥说的O(4*15*n*m),复杂度不是很会证反正T成了暴力…… 题解: 对于测试点8,9, ...

  10. python if elif else 区别

    if data_ori=='医疗': # 医疗 df = pd.read_excel(path_apply + 'apply/YS_ZY_HZSQ_样例.xls', encoding='gbk', e ...