我们已经几次提及 shortprint 驱动; 现在是时候真正看看. 这个模块为并口实现一个非 常简单, 面向输出的驱动; 它是足够的, 但是, 来使能文件打印. 如果你选择来测试这个 驱动, 但是, 记住你必须传递给打印机一个文件以它理解的格式; 不是所有的打印机在给 一个任意数据的流时很好响应.

shortprint 驱动维护一个一页的环形输出缓存. 当一个用户空间进程写数据到这个设备, 数据被填入缓存, 但是写方法实际没有进行任何 I/O. 相反, shortp_write 的核心看来 如此:

while (written < count)

{

/* Hang out until some buffer space is available. */ space = shortp_out_space();

if (space <= 0) {

if (wait_event_interruptible(shortp_out_queue,

(space = shortp_out_space()) > 0))

goto out;

}

/* Move data into the buffer. */ if ((space + written) > count)

space = count - written;

if (copy_from_user((char *) shortp_out_head, buf, space)) { up(&shortp_out_sem);

return -EFAULT;

}

shortp_incr_out_bp(&shortp_out_head, space); buf += space;

written += space;

}

out:

/*
If no output is active, make it active. */
spin_lock_irqsave(&shortp_out_lock, flags); if (! shortp_output_active)

shortp_start_output();
spin_unlock_irqrestore(&shortp_out_lock, flags);

*f_pos += written;

一个旗标 ( shortp_out_sem ) 控制对这个环形缓存的存取; shortp_write 就在上面的 代码片段之前获得这个旗标. 当持有这个旗标, 它试图输入数据到这个环形缓存. 函数
shortp_out_space 返回可用的连续空间的数量(因此, 没有必要担心缓存回绕); 如果这 个量是 0, 驱动等到释放一些空间. 它接着拷贝它能够的数量的数据到缓存中.

一旦有数据输出,
shortp_write 必须确保数据被写到设备. 数据的写是通过一个工作队 列函数完成的; shortp_write 必须启动这个函数如果它还未在运行. 在获取了一个单独

的, 控制存取输出缓存的消费者一侧(包括 shortp_output_active)的数据的自旋锁后, 它调用
shortp_start_output 如果需要. 接着只是注意多少数据被写到缓存并且返回.

启动输出进程的函数看来如下:

static
void shortp_start_output(void)

{

if (shortp_output_active) /* Should never happen */
return;

/* Set up
our 'missed interrupt' timer */ shortp_output_active = 1; shortp_timer.expires
= jiffies + TIMEOUT; add_timer(&shortp_timer);

/*
And get the process going. */ queue_work(shortp_workqueue, &shortp_work);

}

处理硬件的事实是, 你可以, 偶尔, 丢失来自设备的中断. 当发生这个, 你确实不想你的 驱动一直停止直到系统重启; 这不是一个用户友好的做事方式. 最好是认识到一个中断已 经丢失, 收拾残局, 继续. 为此, shortprint
甚至一个内核定时器无论何时它输出数据 给设备. 如果时钟超时,
我们可能丢失一个中断. 我们很快会看到定时器函数, 但是, 暂 时, 让我们坚持在主输出功能上.
那是在我们的工作队列函数里实现的, 它, 如同你上面
看到的, 在这里被调度. 那个函数的核心看来如下:

spin_lock_irqsave(&shortp_out_lock,
flags);

/*
Have we written everything? */

if
(shortp_out_head == shortp_out_tail)

{
/* empty */

shortp_output_active
= 0; wake_up_interruptible(&shortp_empty_queue);
del_timer(&shortp_timer);

}

/*
Nope, write another byte */ else

shortp_do_write();

/*
If somebody's waiting, maybe wake them up. */

if
(((PAGE_SIZE + shortp_out_tail -shortp_out_head) % PAGE_SIZE) >
SP_MIN_SPACE)

{

wake_up_interruptible(&shortp_out_queue);

}

spin_unlock_irqrestore(&shortp_out_lock,
flags);

因为我们在使用共享变量的输出一侧, 我们必须获得自旋锁. 接着我们看是否有更多的数 据要发送; 如果无, 我们注意输出不再激活, 删除定时器,
并且唤醒任何在等待队列全空 的进程(这种等待当设备被关闭时结束).
如果, 相反, 有数据要写, 我们调用 shortp_do_write 来实际发送一个字节到硬件.

接着, 因为我们可能在输出缓存中有空闲空间, 我们考虑唤醒任何等待增加更多数据给那 个缓存的进程. 但是我们不是无条件进行唤醒; 相反, 我们等到有一个最低数量的空间.

每次我们从缓存拿出一个字节就唤醒一个写者是无意义的; 唤醒进程的代价, 调度它运行, 并且使它重回睡眠, 太高了. 相反, 我们应当等到进程能够立刻移动相当数量的数据到缓 存. 这个技术在缓存的, 中断驱动的驱动中是普通的.

为完整起见, 这是实际写数据到端口的代码:

static
void shortp_do_write(void)

{

unsigned
char cr = inb(shortp_base + SP_CONTROL);

/*
Something happened; reset the timer */ mod_timer(&shortp_timer, jiffies +
TIMEOUT);

/*
Strobe a byte out to the device */ outb_p(*shortp_out_tail,
shortp_base+SP_DATA); shortp_incr_out_bp(&shortp_out_tail, 1);

if
(shortp_delay)

udelay(shortp_delay);

outb_p(cr
| SP_CR_STROBE, shortp_base+SP_CONTROL); if (shortp_delay)

udelay(shortp_delay);

outb_p(cr
& ~SP_CR_STROBE, shortp_base+SP_CONTROL);

}

这里, 我们复位定时器来反映一个事实, 我们已经作了一些处理, 输送字节到设备, 并且 更新了环形缓存指针.

工作队列函数没有直接重新提交它自己, 因此只有一个单个字节会被写入设备. 在某一处, 打印机将, 以它的缓慢方式, 消耗这个字节并且准备好下一个; 它将接着中断处理器. shortprint 中使用的中断处理是简短的:

static
irqreturn_t shortp_interrupt(int irq, void *dev_id, struct pt_regs *regs)

{

if (! shortp_output_active) return IRQ_NONE;

/*
Remember the time, and farm off the rest to the workqueue function */
do_gettimeofday(&shortp_tv);

queue_work(shortp_workqueue,
&shortp_work); return IRQ_HANDLED;

}

因为并口不要求一个明显的中断确认, 中断处理所有真正需要做的是告知内核来再次运行 工作队列函数.

如果中断永远不来如何? 至此我们已见到的驱动代码将简单地停止. 为避免发生这个, 我 们设置了一个定时器在几页前. 当定时器超时运行的函数是:

static
void shortp_timeout(unsigned long unused)

{

unsigned
long flags; unsigned char status;

if (! shortp_output_active) return;

spin_lock_irqsave(&shortp_out_lock, flags);
status = inb(shortp_base + SP_STATUS);

/*
If the printer is still busy we just reset the timer */ if ((status &
SP_SR_BUSY) == 0 || (status & SP_SR_ACK)) {

shortp_timer.expires
= jiffies + TIMEOUT; add_timer(&shortp_timer);
spin_unlock_irqrestore(&shortp_out_lock, flags); return;

}

/*
Otherwise we must have dropped an interrupt. */
spin_unlock_irqrestore(&shortp_out_lock, flags);
shortp_interrupt(shortp_irq, NULL, NULL);

}

如果没有输出要被激活, 定时器函数简单地返回. 这避免了定时器重新提交自己, 当事情 在被关闭时. 接着, 在获得了锁之后, 我们查询端口的状态; 如果它声称忙,
它完全还没 有时间来中断我们, 因此我们复位定时器并且返回. 打印机能够, 有时, 花很长时间来使 自己准备; 考虑一下缺纸的打印机, 而每个人在一个长周末都不在. 在这种情况下, 只有 耐心等待直到事情改变.

但是, 如果打印机声称准备好了, 我们一定丢失了它的中断. 这个情况下,
我们简单地手 动调用我们的中断处理来使输出处理再动起来.

shortpirnt 驱动不支持从端口读数据; 相反, 它象 shortint 并且返回中断时间信息. 但是一个中断驱动的读方法的实现可能非常类似我们已经见到的. 从设备来的数据可能被 读入驱动缓存; 它可能被拷贝到用户空间只在缓存中已经累积了相当数量的数据, 完整的 读请求已被满足, 或者某种超时发生.

linux 一个写缓存例子的更多相关文章

  1. Linux页快速缓存与回写机制分析

    參考 <Linux内核设计与实现> ******************************************* 页快速缓存是linux内核实现的一种主要磁盘缓存,它主要用来降低 ...

  2. Linux内核中的信号机制--一个简单的例子【转】

    本文转载自:http://blog.csdn.net/ce123_zhouwei/article/details/8562958 Linux内核中的信号机制--一个简单的例子 Author:ce123 ...

  3. ? 原创: 铲子哥 搜狗测试 今天 shell编程的时候,往往不会把所有功能都写在一个脚本中,这样不太好维护,需要多个脚本文件协同工作。那么问题来了,在一个脚本中怎么调用其他的脚本呢?有三种方式,分别是fork、source和exec。 1. fork 即通过sh 脚本名进行执行脚本的方式。下面通过一个简单的例子来讲解下它的特性。 创建father.sh,内容如下: #!/bin/bas

    ? 原创: 铲子哥 搜狗测试 今天 shell编程的时候,往往不会把所有功能都写在一个脚本中,这样不太好维护,需要多个脚本文件协同工作.那么问题来了,在一个脚本中怎么调用其他的脚本呢?有三种方式,分别 ...

  4. linux下的缓存机制及清理buffer/cache/swap的方法梳理 (转)

    一.缓存机制介绍 在Linux系统中,为了提高文件系统性能,内核利用一部分物理内存分配出缓冲区,用于缓存系统操作和数据文件,当内核收到读写的请求时,内核先去缓存区找是否有请求的数据,有就直接返回,如果 ...

  5. Linux 下 Memcached 缓存服务器安装配置

    Linux 下 Memcached 缓存服务器安装配置 [日期:2011-08-06] 来源:Linux社区  作者:Linux [字体:大 中 小]   [安装Memcache服务器端]我目前的平台 ...

  6. linux下的缓存机制buffer、cache、swap - 运维总结 ["Cannot allocate memory"问题]

    一.缓存机制介绍 在Linux系统中,为了提高文件系统性能,内核利用一部分物理内存分配出缓冲区,用于缓存系统操作和数据文件,当内核收到读写的请求时,内核先去缓存区找是否有请求的数据,有就直接返回,如果 ...

  7. linux下的缓存机制buffer、cache、swap

    一.缓存机制介绍 在Linux系统中,为了提高文件系统性能,内核利用一部分物理内存分配出缓冲区,用于缓存系统操作和数据文件,当内核收到读写的请求时,内核先去缓存区找是否有请求的数据,有就直接返回,如果 ...

  8. Linux手动释放缓存的方法

    Linux释放内存的命令:syncecho 1 > /proc/sys/vm/drop_caches drop_caches的值可以是0-3之间的数字,代表不同的含义:0:不释放(系统默认值)1 ...

  9. MySql 缓冲池(buffer pool) 和 写缓存(change buffer) 转

    应用系统分层架构,为了加速数据访问,会把最常访问的数据,放在缓存(cache)里,避免每次都去访问数据库. 操作系统,会有缓冲池(buffer pool)机制,避免每次访问磁盘,以加速数据的访问. M ...

随机推荐

  1. Rol租车网项目总结

    现在自行车的租聘如此火爆,我们是否需要加入这个浩浩荡荡的行列? 相比起现在ofo共享单车,摩拜单车.而我们的竞争力在何处? 如何能让我们的项目脱颖而出? 而我们的Rol租车网为什么要叫Rol呢? Ri ...

  2. Introduction to 3D Game Programming with DirectX 12 学习笔记之 --- Direct12优化

    原文:Introduction to 3D Game Programming with DirectX 12 学习笔记之 --- Direct12优化 第一章:向量代数 1.向量计算的时候,使用XMV ...

  3. Maximum Depth of Binary Tree 树的最大深度

    Given a binary tree, find its maximum depth. The maximum depth is the number of nodes along the long ...

  4. LeetCode153 Find Minimum in Rotated Sorted Array. LeetCode162 Find Peak Element

    二分法相关 153. Find Minimum in Rotated Sorted Array Suppose a sorted array is rotated at some pivot unkn ...

  5. 在Debug模式下,如何给.lib和.dll添加一个d标记(*d.lib,*d.dll)

    选中工程->右键->属性->配置属性->常规,可以看到项目默认值的配置类型有好几种类型,选择静态库类型生成lib文件,选择动态库类型生成dll文件,选择应用程序生成exe文件, ...

  6. Python学习之路3☞编程风格

    语句和语法 #   表示注释掉的内容 \    续行 print("yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy\ yyyyyyyyyyyyyyyyyyyyyyy& ...

  7. hdu1730 尼姆博弈

    抽象一下把距离当做石子个数.虽然在这里石子个数可以增加,但是不管怎么增加,不会影响结果,因为你增加了,必须会有减少的. 所以类似取石子,观察平衡状态,如果(x2-x1-1)^...==0,必输. wa ...

  8. 【Leetcode链表】环形链表(141)

    题目 给定一个链表,判断链表中是否有环. 为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始). 如果 pos 是 -1,则在该链表中没有环. 示例 1: ...

  9. python 列表对象的增减

  10. 模板—Hash_map

    struct Hash_map { ],nx[]; ];]; inline double &operator [] (int x) { ,i=fi[k]; for(;i&&st ...