C Linux read write function extension
前言 - 赠送 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的更多相关文章
- [Linux]Linux下signal function传参方式
https://stackoverflow.com/questions/6970224/providing-passing-argument-to-signal-handler This is a r ...
- [PHP] Compile an extension on Windows
https://wiki.php.net/internals/windows/stepbystepbuildhttp://blog.benoitblanchon.fr/build-php-extens ...
- Linux 驱动开发
linux驱动开发总结(一) 基础性总结 1, linux驱动一般分为3大类: * 字符设备 * 块设备 * 网络设备 2, 开发环境构建: * 交叉工具链构建 * NFS和tftp服务器安装 3, ...
- Windows和linux下clock函数
windows: Calculates the wall-clock time used by the calling process. return:The elapsed wall-clock ...
- Linux下一个简单守护进程的实现 (Daemon)
在Linux/UNIX系统引导的时候会开启很多服务,这些服务称为守护进程(也叫Daemon进程).守护进程是脱离于控制终端并且在后台周期性地执行某种任务或等待处理某些事件的进程,脱离终端是为了避免进程 ...
- 使用 ftrace 调试 Linux 内核【转】
转自:http://blog.csdn.net/adaptiver/article/details/7930646 使用 ftrace 调试 Linux 内核,第 1 部分 http://blog.c ...
- SQL Server on Linux: How? Introduction: SQL Server Blog
SQL Server Blog Official News from Microsoft’s Information Platform https://blogs.technet.microsoft. ...
- 使用 ftrace 调试 Linux 内核,第 3 部分
内核头文件 include/linux/kernel.h 中描述了 ftrace 提供的工具函数的原型,这些函数包括 trace_printk.tracing_on/tracing_off 等.本文通 ...
- 使用 ftrace 调试 Linux 内核,第 2 部分
ftrace 操作概述 使用 ftrace 提供的跟踪器来调试或者分析内核时需要如下操作: 切换到目录 /sys/kernel/debug/tracing/ 下 查看 available_tracer ...
随机推荐
- Verification之PSL之use
1 Where can PSL be used? • Documentation – Requirements – RTL Designs • Controllers – Memories, FIFO ...
- 工作中总结的经验之git篇
不要以为你会git,你要知道,git不是只有commit和push 由于系统分析与设计的期末Project需要团队合作开发,因此在这里想谈谈GitHub团队项目合作开发的流程: 项目创建 项目负责人在 ...
- monkey测试环境搭建 及 操作步骤
1.环境搭建 a.下载安卓SDK 链接:https://pan.baidu.com/s/1-OB6UVPvl5-N-vFdykfMmA 提取码:3spx b.配置环境变量(配置完成,重启系统,配置生效 ...
- 怎么从传统的盒子思想转为Flex 布局(css)
前端进化很快,总是有新的技术出来,开始可能有些人用惯了盒子模型的思想 依赖 display属性 + position属性 + float属性.这三大件.它对于那些特殊布局非常不方便 我们就来看看Fle ...
- java中反射讲解及实例
Java反射机制详解 java 反射 定义 功能 示例 概要: Java反射机制详解 | |目录 1反射机制是什么 2反射机制能做什么 3反射机制的相关API ·通过一个对象获得完整的包名和类名 ·实 ...
- hadoop fs 常用命令(1)
Hadoop: https://blog.csdn.net/mulangren1988/article/details/54860924 Hadoop:1. Hadoop fs –fs [local ...
- 数据类型 scanf标准函数 sizeof关键字 二进制(day03)
字符类型的名称是char 字符类型里包含256个不同的整数,每个 整数对应一个字符(例如'a', '^'等) 这些整数和字符完全可以互相替代 ASCII码表列出所有整数和字符的对应关系 ASCII码表 ...
- 共享内存、网络(day13)
一.共享内存 .获取一个键值 ftok() .使用键值获取共享内存的id shmget() #include <sys/ipc.h> #include <sys/shm.h> ...
- String使用方式详细总结
1.用双引号创建 2.用new String方式创建 3.双引号相加创建 4.两个new String相加时 5.两个引用相加时 6.双引号加new String创建或者new String加双引号创 ...
- java中的replaceAll方法注意事项
replaceAll和replace方法参数是不同的,replace的两个参数都是代表字符串,replaceAll的第一个参数是正则表达式 replaceAll中需要注意的特殊字符: \ == \\\ ...