前言 - 赠送 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. 关于MYSQL 存储过程的文章摘录

    1.      存储过程简介   我们常用的操作数据库语言SQL语句在执行的时候需要要先编译,然后执行,而存储过程(Stored Procedure)是一组为了完成特定功能的SQL语句集,经编译后存储 ...

  2. 使用Ajax验证用户名

    Ajax是一项很重要的技术,下面简要举个例子,来解释如何使用Ajax.步骤如下:使用Ajax验证用户名使用文本框的onBlur事件 使用Ajax技术实现异步交互创建XMLHttpRequest对象通过 ...

  3. windows server 2012 r2 安装无法找到install.wim 错误代码0x80070026,以及制作U启动盘决解ISO文件超过5G大小限制的解决方案(转)

    戴尔服务器r530 windows server 2012 r2 安装无法找到install.wim 错误代码0x80070026,以及制作U启动盘决解ISO文件超过5G大小限制的解决方案 关于在服务 ...

  4. PCL: 根据几何规则的曲面剖分-贪婪法表面重建三角网格

    点云场景中进行物体识别,使用全局特征的方法严重依赖于点云分割,难以适应杂乱场景.使用局部特征,即对点云进行提取类似于3D SURF.ROPS之类的局部特征,需要寻找离散点云块的局部显著性. 点云的基本 ...

  5. jsTree checkbox plugin使用笔记

    引入css文件 <link rel="stylesheet" type="text/css" href="js/assets/global/pl ...

  6. spotlight on mysql 监控

    . 安装 下载地址:https://pan.baidu.com/s/1qYi3lec 官网地址——https://www.quest.com/common/registration.aspx?requ ...

  7. vue,基础整理,夯实基础,为进阶打基础

    把基础部分,再次系统的了解一遍,整理成文档.

  8. 2019-05-14 Python SSL

    解决SSL报错问题 -- 导库 import ssl import urllib.request context = ssl._create_unverified_context() --用urlli ...

  9. 5、Linux的常用命令

    ls 查看当面目录结构 ls -l 列表查看当前目录 cd:切换目录 pwd:显示目前的目录 mkdir:创建一个新的目录 rmdir:删除一个空的目录 cp: 复制文件或目录 rm: 移除文件或目录 ...

  10. POJ 3762 The Bonus Salary!

    The Bonus Salary! Time Limit: 2000ms Memory Limit: 65536KB This problem will be judged on PKU. Origi ...