zoukankan      html  css  js  c++  java
  • 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 > 0) {
            ssize_t ret = read(fd, ptr, div);
            if (ret < 0) {
                if (errno == EINTR)
                    continue;
                return -1;
            }
            if (ret == 0) 
                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 > 0) {
            ssize_t ret = write(fd, ptr, div);
            if (ret <= 0) {
                if (errno == EINTR)
                    continue;
                return -1;
            }
            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
    1", 6);
        printf("ret = %ld
    ", ret);
    
        char buf[4];
        ret = readn(STDIN_FILENO, buf, 3);
        buf[3] = '';
        printf("ret = %ld, buf = %s
    ", ret, buf);
    
        return 0;
    }

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

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

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

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

    正文 -  缓冲读

      在了解 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 >= 0);
        r->fd = fd;
        r->cnt = 0;
        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 <= 0) {
            r->cnt = read(r->fd, r->buf, sizeof r->buf);
            if (r->cnt < 0) {
                if (errno == EINTR)
                    continue;
                return -1;
            }
            // EOF 直接返回
            if (r->cnt == 0)
                return 0;
            // 重新设置 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 > 0) {
            ssize_t ret = rio_read(r, ptr, div);
            if (ret < 0)
                return -1;
            if (ret == 0) 
                break;
            ptr += ret;
            div -= ret;
        }
    
        return n - div;    
    }
    
    //
    // rio_readline - 力求读取一行数据, 会吃掉最后一个 
     字符
    // 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 = 1; i < n; ++i) {
            ssize_t ret = rio_read(r, &c, 1);
            if (ret < 0)
                return -1;
            if (c == '
    ' || ret == 0)
                break;
            *ptr++ = c;
        }
    
        *ptr = '';
        return i - 1;
    }

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

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

    来构造满意的读写策略.

    后记 - 让水倒流

  • 相关阅读:
    python之约束、加密及logging模块
    python之反射机制与callattr()、issubclass()、isinstance、type()相关
    python之面向对象初识
    python函数名的应用、闭包和迭代器
    python(动态传参、命名空间、函数嵌套、global和nonlocal关键字)
    python中函数的定义、返回值以及参数的简要介绍
    python文件操作
    python中set(集合),深浅拷贝以及一些补充知识点
    python中is与==的区别,编码和解码
    python数据类型:dict(字典)
  • 原文地址:https://www.cnblogs.com/life2refuel/p/10957979.html
Copyright © 2011-2022 走看看