前言 - 赠送 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. 大型工程多个目录下的Makefile写法

    1.前言 目前从事于linux下程序开发,涉及到多个文件,多个目录,这时候编译文件的任务量比较大,需要写Makefile.关于Makefile的详细内容可以参考网上流传非常广泛的<跟我一起写Ma ...

  2. Centos6.6 安装Redis

    一.介绍 redis在做数据库缓存,session存储,消息队列上用的比较多 二.安装 $ yum install -y wget gcc make tcl $ wget http://downloa ...

  3. C# for 遍历 IPagedList

    IPagedList<Doc> ss = Doclist.ToPagedList(page, pageSize); ;i<ss.Count;i++) { var yy = ss[i] ...

  4. Robot Framework(五)使用测试库

    使用测试库 测试库包含那些最低级别的关键字,通常称为 库关键字,实际上与被测系统交互.所有测试用例总是使用某些库中的关键字,通常是通过更高级别的用户关键字.本节介绍如何使用测试库以及如何使用它们提供的 ...

  5. 部署国密SSL证书,如何兼容国际主流浏览器?

    国密算法在主流操作系统.浏览器等客户端中,还没有实现广泛兼容.因此,在面向开放互联网的产品应用中,国密算法无法得到广泛应用.比如,在SSL证书应用领域,由于国际主流浏览器不信任国密算法,如果服务器部署 ...

  6. eas之视图冻结与解冻

    // 冻结视图 table.getViewManager().freeze(verticalIndex, horizonIndex); //冻结视图:该方法在table还没显示的时候使用,也就是该方法 ...

  7. BZOJ1001 BJOI2006 狼抓兔子

    Description 现在小朋友们最喜欢的"喜羊羊与灰太狼",话说灰太狼抓羊不到,但抓兔子还是比较在行的,而且现在的兔子还比较笨,它们只有两个窝,现在你做为狼王,面对下面这样一个 ...

  8. C#的WaitHandle : 管理多线程状态

    有时候,我们创建了多线程,需要知道是否都完成了各自的工作.比如说,开启了多线程的下载,如何终止所有的线程并且在确保所有线程都终止之后才继续执行程序的退出呢? public partial class ...

  9. navicat 为表添加索引

    navicat 为表添加索引 分析常用的查询场景,为字段添加索引,增加查询速度. 可以添加单列索引,可以添加联合索引. 右键,设计表中可以查看和添加修改索引! 索引一定要根据常用的查询场景进行添加! ...

  10. 0208MySQL5.7之Group Replication

    转自http://blog.itpub.net/29510932/viewspace-2055679/ MySQL Group Replication: Hello World! 对测试版(on la ...