前言 - 赠送 readn / writen

  Linux 上默认的 read 和 write 函数会被信号软中断. 且 read 和 write 函数中第三个参数 count

#include <unistd.h>

extern ssize_t read(int fd, void * buf, size_t count);
extern ssize_t write(int fd, const void * buf, size_t count);

也会因内部缓冲机制, 不一定保证读取或写入到指定 count 大小数据.

这里将 read 和 write 拓展成 readn 和 writen

//
// readn - 力求读取 n 个字节
// fd : 文件描述符
// buf : 缓冲区
// n : 读取长度
// return : 返回读取的长度, -1 标识错误, < n 标识关闭, 默认 n
//
ssize_t
readn(int fd, void * buf, size_t n) {
size_t div = n;
char * ptr = buf; while (div > ) {
ssize_t ret = read(fd, ptr, div);
if (ret < ) {
if (errno == EINTR)
continue;
return -;
}
if (ret == )
break;
ptr += ret;
div -= ret;
} return n - div;
} //
// writen - 力求写入 n 个字节
// fd : 文件描述符
// buf : 缓冲区
// n : 读取长度
// return : 返回写入长度, -1 标识错误, 默认 n
//
ssize_t
writen(int fd, const void * buf, size_t n) {
size_t div = n;
const char * ptr = buf; while (div > ) {
ssize_t ret = write(fd, ptr, div);
if (ret <= ) {
if (errno == EINTR)
continue;
return -;
}
ptr += ret;
div -= ret;
} return n;
}

有了这些收获, 不妨写个小测试

#include <stdio.h>
#include <errno.h>
#include <assert.h>
#include <unistd.h> //
// readn - 力求读取 n 个字节
// fd : 文件描述符
// buf : 缓冲区
// n : 读取长度
// return : 返回读取的长度, -1 标识错误, < n 标识关闭, 默认 n
//
extern ssize_t readn(int fd, void * buf, size_t n); //
// writen - 力求写入 n 个字节
// fd : 文件描述符
// buf : 缓冲区
// n : 读取长度
// return : 返回写入长度, -1 标识错误, 默认 n
//
extern ssize_t writen(int fd, const void * buf, size_t n); /*
_oo0oo_
o8888888o
88" . "88
(| -_- |)
0\ = /0
___/`---'\___
.' \\| |// '.
/ \\||| : |||// \
/ _||||| -:- |||||- \
| | \\\ - /// | |
| \_| ''\---/'' |_/ |
\ .-\__ '-' ___/-. /
___'. .' /--.--\ `. .'___
."" '< `.___\_<|>_/___.' >' "".
| | : `- \`.;`\ _ /`;.`/ - ` : | |
\ \ `_. \_ __\ /__ _/ .-` / /
=====`-.____`.___ \_____/___.-`___.-'=====
`=---='
*/
int main(int argc, char * argv[]) {
ssize_t ret = writen(STDOUT_FILENO, "12345\n1", );
printf("ret = %ld\n", ret); char buf[];
ret = readn(STDIN_FILENO, buf, );
buf[] = '\0';
printf("ret = %ld, buf = %s\n", ret, buf); return ;
}

一忧一喜皆心火,一荣一枯皆眼尘,静心看透炎凉事,千古不做梦里人。

聪明人,一味向前看;智慧人,事事向后看;聪明人,是战胜别人的人;智慧人,是战胜自己的人。

修心当以净心为要,修道当以无我为基。

过去事,过去心,不可记得;现在事,现在心,随缘即可;未来事,未来心,不必劳心。

正文 -  缓冲读

  在了解 readn 套路基础上, 你是否有所想过那缓冲读写的实现思路呢. 这里不妨借用深入理解计算机系统

书中的思路实现一番.

struct rio {
int fd; // 文件描述符
char * ptr; // 下一次读取缓冲池 buf 起点
ssize_t cnt; // 缓冲池 buf 字符数量
char buf[BUFSIZ]; // 缓冲池
}; // rio_init - rio 初始化
inline void rio_init(struct rio * r, int fd) {
assert(r && fd >= );
r->fd = fd;
r->cnt = ;
r->ptr = r->buf;
} //
// readn - 力求读取 n 个字节
// r : 缓冲读取对象
// buf : 缓冲区
// n : 读取长度
// return : 返回读取的长度, -1 标识错误, < n 标识关闭, 默认 n
//
extern ssize_t rio_readn(struct rio * r, void * buf, size_t n); //
// rio_readline - 力求读取一行数据
// r : 缓冲读取对象
// buf : 缓冲区
// n : 读取长度
// return : 返回读取的长度, -1 标识错误, < n 标识关闭, 默认 n
//
extern ssize_t rio_readline(struct rio * r, void * buf, size_t n);

实现了缓冲读固定字符和缓冲读一行. 额外的缓冲写也是相似的思路, 简单点写不了会进入写缓冲区,

可以当课外作业自行实现.

// rio_read - 带缓冲版本的 read
static ssize_t rio_read(struct rio * r, void * buf, size_t n) {
// 当缓冲区中没有数据, 我们重新填充缓冲区
while (r->cnt <= ) {
r->cnt = read(r->fd, r->buf, sizeof r->buf);
if (r->cnt < ) {
if (errno == EINTR)
continue;
return -;
}
// EOF 直接返回
if (r->cnt == )
return ;
// 重新设置 buffer ptr
r->ptr = r->buf;
}
// 尝试读取数据并返回
ssize_t cnt = r->cnt < n ? r->cnt : n;
memcpy(buf, r->ptr, cnt);
r->cnt -= cnt;
r->ptr += cnt;
return cnt;
} //
// readn - 力求读取 n 个字节
// r : 缓冲读取对象
// buf : 缓冲区
// n : 读取长度
// return : 返回读取的长度, -1 标识错误, < n 标识关闭, 默认 n
//
ssize_t
rio_readn(struct rio * r, void * buf, size_t n) {
size_t div = n;
char * ptr = buf; while (div > ) {
ssize_t ret = rio_read(r, ptr, div);
if (ret < )
return -;
if (ret == )
break;
ptr += ret;
div -= ret;
} return n - div;
} //
// rio_readline - 力求读取一行数据, 会吃掉最后一个 \n 字符
// r : 缓冲读取对象
// buf : 缓冲区
// n : 读取长度
// return : 返回读取的长度, -1 标识错误, < n 标识关闭, 默认 n
//
ssize_t
rio_readline(struct rio * r, void * buf, size_t n) {
size_t i;
char * ptr = buf, c; for (i = ; i < n; ++i) {
ssize_t ret = rio_read(r, &c, );
if (ret < )
return -;
if (c == '\n' || ret == )
break;
*ptr++ = c;
} *ptr = '\0';
return i - ;
}

缓冲写实战包装要复杂一点. 和业务绑定重(或者实现多策略的). 例如缓冲区满了这时候的策略就由业务

决定, 是缓冲区扩容, 还是等待下次写事件触发. 等等, 真实战场的缓冲读写需要具体场景和机器打配合,

来构造满意的读写策略.

后记 - 让水倒流

C Linux read write function extension的更多相关文章

  1. [Linux]Linux下signal function传参方式

    https://stackoverflow.com/questions/6970224/providing-passing-argument-to-signal-handler This is a r ...

  2. [PHP] Compile an extension on Windows

    https://wiki.php.net/internals/windows/stepbystepbuildhttp://blog.benoitblanchon.fr/build-php-extens ...

  3. Linux 驱动开发

    linux驱动开发总结(一) 基础性总结 1, linux驱动一般分为3大类: * 字符设备 * 块设备 * 网络设备 2, 开发环境构建: * 交叉工具链构建 * NFS和tftp服务器安装 3, ...

  4. Windows和linux下clock函数

    windows:  Calculates the wall-clock time used by the calling process. return:The elapsed wall-clock ...

  5. Linux下一个简单守护进程的实现 (Daemon)

    在Linux/UNIX系统引导的时候会开启很多服务,这些服务称为守护进程(也叫Daemon进程).守护进程是脱离于控制终端并且在后台周期性地执行某种任务或等待处理某些事件的进程,脱离终端是为了避免进程 ...

  6. 使用 ftrace 调试 Linux 内核【转】

    转自:http://blog.csdn.net/adaptiver/article/details/7930646 使用 ftrace 调试 Linux 内核,第 1 部分 http://blog.c ...

  7. SQL Server on Linux: How? Introduction: SQL Server Blog

    SQL Server Blog Official News from Microsoft’s Information Platform https://blogs.technet.microsoft. ...

  8. 使用 ftrace 调试 Linux 内核,第 3 部分

    内核头文件 include/linux/kernel.h 中描述了 ftrace 提供的工具函数的原型,这些函数包括 trace_printk.tracing_on/tracing_off 等.本文通 ...

  9. 使用 ftrace 调试 Linux 内核,第 2 部分

    ftrace 操作概述 使用 ftrace 提供的跟踪器来调试或者分析内核时需要如下操作: 切换到目录 /sys/kernel/debug/tracing/ 下 查看 available_tracer ...

随机推荐

  1. Android App 开机启动画面和开机自动启动APP程序设置

    1.当前比较成熟一点的应用基本上都会在进入应用之显示一个启动界面 如腾讯微博 2.准备元素  需要开机启动的图片一张 3.新建Activity AlphaAnimation动画:控制对象alpha水平 ...

  2. Table is specified twice, both as a target for 'UPDATE' and as a separate source

    UPDATE Bins b SET b.ShopSn =’111201611111168706’ WHERE b.Id IN (SELECT b.Id FROM Bins b JOIN BinInve ...

  3. handyJson的技术内核

    1.swift对象内存模型: 2.指针操作: 3.协议.泛型.扩展: 4.kvc: 1是所有实现的基础,没有内存对象(类)模型,后面的一切都我从谈起. 在1的基础上使用2进行对象模型信息的提取和转换. ...

  4. MySQL创建临时表

    drop TEMPORARY table if EXISTS temp_table; create TEMPORARY table temp_table( id int not null, usern ...

  5. eas之网络互斥功能示手工控制

    public void doMutexService()    {        IMutexServiceControl mutex = MutexServiceControlFactory.get ...

  6. eas之缓存清理

    apusic缓存清理,安装web框架补丁后,先清空apusic缓存,然后再重启服务apusic-domains-server1-deploy-easweb-tmpfiles

  7. 【剑指Offer】62、二叉搜索树的第k个结点

      题目描述:   给定一棵二叉搜索树,请找出其中的第k小的结点.例如(5,3,7,2,4,6,8) 中,按结点数值大小顺序第三小结点的值为4.   解题思路:   本题实际上比较简单,主要还是考察对 ...

  8. linux基础随记

    1.cd 切换路径 cd ~vbird 进入这个用户的主目录cd ~ 进入root这个目录下cd .. 进入root上层目录cd - 进入root这个目录下cd /var/spool/mail 直接访 ...

  9. Shell入门基础

    Shell的Helloworld #!/bin/bash echo "helloworld taosir" 执行方式 方式一:用 bash 或 sh 的相对或绝对路径(不用赋予脚本 ...

  10. Spring MVC学习总结(3)——Spring3 MVC详解

    DispatcherServlet 前置控制器 使用Spring MVC,配置DispatcherServlet是第一步.DispatcherServlet是一个Servlet,所以可以配置多个Dis ...