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;
    }

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

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

    来构造满意的读写策略.

    后记 - 让水倒流

  • 相关阅读:
    JavaScript 为字符串添加样式 【每日一段代码80】
    JavaScript replace()方法 【每日一段代码83】
    JavaScript for in 遍历数组 【每日一段代码89】
    JavaScript 创建用于对象的模板【每日一段代码78】
    html5 css3 新元素简单页面布局
    JavaScript Array() 数组 【每日一段代码88】
    JavaScript toUTCString() 方法 【每日一段代码86】
    位运算
    POJ 3259 Wormholes
    POJ 3169 Layout
  • 原文地址:https://www.cnblogs.com/life2refuel/p/10957979.html
Copyright © 2011-2022 走看看