linux 一个写缓存例子
我们已经几次提及 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 一个写缓存例子的更多相关文章
- Linux页快速缓存与回写机制分析
參考 <Linux内核设计与实现> ******************************************* 页快速缓存是linux内核实现的一种主要磁盘缓存,它主要用来降低 ...
- Linux内核中的信号机制--一个简单的例子【转】
本文转载自:http://blog.csdn.net/ce123_zhouwei/article/details/8562958 Linux内核中的信号机制--一个简单的例子 Author:ce123 ...
- ? 原创: 铲子哥 搜狗测试 今天 shell编程的时候,往往不会把所有功能都写在一个脚本中,这样不太好维护,需要多个脚本文件协同工作。那么问题来了,在一个脚本中怎么调用其他的脚本呢?有三种方式,分别是fork、source和exec。 1. fork 即通过sh 脚本名进行执行脚本的方式。下面通过一个简单的例子来讲解下它的特性。 创建father.sh,内容如下: #!/bin/bas
? 原创: 铲子哥 搜狗测试 今天 shell编程的时候,往往不会把所有功能都写在一个脚本中,这样不太好维护,需要多个脚本文件协同工作.那么问题来了,在一个脚本中怎么调用其他的脚本呢?有三种方式,分别 ...
- linux下的缓存机制及清理buffer/cache/swap的方法梳理 (转)
一.缓存机制介绍 在Linux系统中,为了提高文件系统性能,内核利用一部分物理内存分配出缓冲区,用于缓存系统操作和数据文件,当内核收到读写的请求时,内核先去缓存区找是否有请求的数据,有就直接返回,如果 ...
- Linux 下 Memcached 缓存服务器安装配置
Linux 下 Memcached 缓存服务器安装配置 [日期:2011-08-06] 来源:Linux社区 作者:Linux [字体:大 中 小] [安装Memcache服务器端]我目前的平台 ...
- linux下的缓存机制buffer、cache、swap - 运维总结 ["Cannot allocate memory"问题]
一.缓存机制介绍 在Linux系统中,为了提高文件系统性能,内核利用一部分物理内存分配出缓冲区,用于缓存系统操作和数据文件,当内核收到读写的请求时,内核先去缓存区找是否有请求的数据,有就直接返回,如果 ...
- linux下的缓存机制buffer、cache、swap
一.缓存机制介绍 在Linux系统中,为了提高文件系统性能,内核利用一部分物理内存分配出缓冲区,用于缓存系统操作和数据文件,当内核收到读写的请求时,内核先去缓存区找是否有请求的数据,有就直接返回,如果 ...
- Linux手动释放缓存的方法
Linux释放内存的命令:syncecho 1 > /proc/sys/vm/drop_caches drop_caches的值可以是0-3之间的数字,代表不同的含义:0:不释放(系统默认值)1 ...
- MySql 缓冲池(buffer pool) 和 写缓存(change buffer) 转
应用系统分层架构,为了加速数据访问,会把最常访问的数据,放在缓存(cache)里,避免每次都去访问数据库. 操作系统,会有缓冲池(buffer pool)机制,避免每次访问磁盘,以加速数据的访问. M ...
随机推荐
- 使用curl指令实现restful接口操作
curl 是很方便的Rest客戶端,可以很方便的完成許多Rest API測試的需求,甚至,如果是需要先登入或認證的rest api,也可以進行測試,利用curl指令,可以送出HTTP GET, POS ...
- 自定义连接池DataSourse
自定义连接池DataSourse 连接池概述: 管理数据库的连接, 作用: 提高项目的性能.就是在连接池初始化的时候存入一定数量的连接,用的时候通过方法获取,不用的时候归还连接即可.所有的连接池必须实 ...
- bzoj1834 网络扩容
Description 给定一张有向图,每条边都有一个容量C和一个扩容费用W.这里扩容费用是指将容量扩大1所需的费用.求: 1. 在不扩容的情况下,1到N的最大流: 2. 将1到N的最大流增加K所需的 ...
- 关于element-ui的弹框问题
el-dialog获取数据. el-dialog加载到页面中的时候,其实已经加载好了.只是默认隐藏了. 第一次点击的时候弹出,为何拿不到数据?之后再次操作就一点问题都没有了.
- 【JZOJ4884】【NOIP2016提高A组集训第12场11.10】图的半径
题目描述 mhy12345学习了树的直径,于是开始研究图的半径,具体来说,我们需要在图中选定一个地方作为中心,其中这个中心有可能在路径上. 而这个中心的选址需要能够使得所有节点达到这个中心的最短路里面 ...
- nodeJs学习-15 mysql中间件下载与使用、基本用法
下载mysql中间件(客户端):cnpm install mysql 链接数据库.查询示例: const mysql=require('mysql'); //1.连接 //createConnecti ...
- hdu5441 并查集 长春网赛
对于每次询问的大的值,都是从小的值开始的,那就从小到大处理,省去很多时间,并且秩序遍历一遍m; 这题cin容易超时,scanf明显快很多很多.G++又比C++快; //这代码scanf400+,cin ...
- MUI - 解决动态列表页图片懒加载再次加载不成功的bug
首先描述一下功能 实现列表页动态加载 通过官方提供的"下拉刷新和上拉刷新"及"图片懒加载"示例实现. http://www.cnblogs.com/philly ...
- 微信服务号获得openid 跟用户信息
https://open.weixin.qq.com/connect/oauth2/authorize?appid=xxxxxxxxxxxxx&redirect_uri=http://www. ...
- @atcoder - AGC035E@ Develop
目录 @description@ @solution@ @accepted code@ @details@ @description@ 给定初始集合为 1 ~ N 的全集,并给定一个 K. 每次对于当 ...