Chinaunix首页 | 论坛 | 博客
  • 博客访问: 17725
  • 博文数量: 14
  • 博客积分: 520
  • 博客等级: 中士
  • 技术积分: 150
  • 用 户 组: 普通用户
  • 注册时间: 2008-05-06 23:21
文章分类
文章存档

2011年(1)

2008年(13)

我的朋友
最近访客

分类: LINUX

2008-05-08 21:06:54

2_2_0___设计

    因为 pipe 模块实现的机能很简单,因此作为字符设备驱动程序,我实现了 open / release / write / read ,另外 skyeye 使用了 select 方法监视设备,因此又添加了 poll 实现。

    当用户空间读取 pipe 的时候,可能还没有数据,或者写入的时候没有空间了,这应该是最经典的情况,我按照 linux设备驱动程序(LDD)第3版 第6章 阻塞和非阻塞 的方法进行了对应:

    如果是非阻塞读取:
    1 有数据,立刻返回真实的数据。
    2 没有数据,立刻返回 0。

    如果是阻塞读取:
    1 有数据,立刻返回真实读取数据的大小。
    2 没有数据,睡眠等待数据到来。
    3 在睡眠的过程中,如果被信号啥的唤醒了,则立刻返回 -ERESTARTSYS。
    4 如果被其他什么东西唤醒了,继续检查有无数据,没有的话,继续睡觉。

    如果是非阻塞写入:
    1 有空间,立刻返回真实写入的大小。
    2 没有空间,立刻返回 0。

    如果是阻塞写入:
    1 有空间,立刻返回真实写入的大小。
    2 没有空间,水面等待数据到来。
    3 在睡眠的过程中,如果被信号啥的唤醒了,则立刻返回 -ERESTARTSYS。
    4 如果被其他什么东西唤醒了,继续检查有无空间,没有的话,继续睡觉。

    
    在编码形式上,因为需要创建两个字符设备,一个是 pipe0 一个是 pipe1 ,而这两个设备对应的 能力(read/write啥的)是完全一样,因此他们公用同一套处理函数,只不过在处理函数中通过判断 字符设备的 minor 号判断当前应用程序正在处理哪一个设备文件。

    另外,如果存在多人读取或者多人写入的情况,那么读取到的数据就是不确定的,为了避免这种情况我之允许 pipe0 和 pipe1 在同一时刻只能被打开一次,多次打开返回错误 -EBUSY。

    按照上面的想法,对于 linux  内核方法的使用,主要用了 自旋锁(spinlock) ,等待队列(waitqueue) 的方法。


    数据结构的设计:

    pipe0 和 pipe1 各自私有一个数据结构,这个数据结构主要是描述 设备本身是谁(minor号),以及数据缓冲,当然还有辅助的数据结构。
    因此从逻辑上可以分成 2 个数据结构,一个用于描述 设备,一个用于描述 缓冲。

    在 设备结构 中,
    需要一个 锁 和 变量 来标识 设备被打开的次数。(本来想用atmoc_t实现的,但是没编译通过)
    需要一个 真正的 数据缓冲,考虑到交互的数据不大,我觉得 4000 字节应该够了。
    为了提高代码的通用性,又定义了 wb 指针指向对方的 数据缓冲, rb 指向自己的缓冲。

    在 缓冲结构 中,
    需要一个 锁 防止对关键数据的重入。(写入位置 和 读取位置)
    需要一个 针对读取的 等待队列。
    需要一个 针对写入的 等待队列。
    需要缓冲区的描述 : 写入位置/读取位置/缓冲区


    在设计 写入位置/读取位置 的时候,我习惯性的使用了指针,但是发现判断 没有数据 和 初始化状态的时候,会很困难,因为都会指向缓冲区的头,翻阅 LDD 的时候,发现内核已有实现比较巧妙,因此借鉴了它的方法。用相对位置,然后再转化为绝对位置。 ( 内核目录 include/linux/kfifo.h  kernel/kfifo.c )

    下面是数据结构图:

    2_2_1___pipe_struct.dia
    <网站不支持上传这种文件类型>

    2_2_1___pipe_struct.png

 

返回目录
阅读(409) | 评论(0) | 转发(0) |
0

上一篇:2_3_0___代码

下一篇:2_1_0___框架

给主人留下些什么吧!~~