• 健壮的I/O(RIO)


    在上篇Unix系统级I/O中,我们介绍了有关在Unix环境下读取和写入文件的函数readwrite,也提到了标准I/O在进行网络I/O时的局限性。但是在某些地方,直接使用readwrite往往会出现不足值,比如在复杂的网络环境中读取socket。如果想让我们的程序更加的可靠,就需要反复的调用readwrite去处理,知道传送完所需要的字节。在csapp一书中,给我们提供了一个健壮可靠的I/O包来自动处理这种不足值的情况,称为RIO(Robust I/O)。本文主要整理RIO提供的函数备忘。

    详细代码及用法示例可以在这里找到。

    无缓冲区的rio

    rio_readnrio_writenreadwrite用法基本一致,只是rio_readn会不断尝试读出,直到读取出n个字节或遇到EOF或出错;rio_writen函数绝不会返回一个不足值,它会不断尝试写入直到写入全部字节或者出错。由于没有缓冲区的存在,rio_readnrio_writen可以任意交替使用。

    #include "rio.h"
    
    ssize_t  rio_readn(int fd, void *buf, size_t n);
    /* 返回:若成功则为传送的字节数,若为EOF则为0,若出错则为-1 */
    
    ssize_t  rio_writen(int fd, void *buf, size_t n);
    /* 返回:若成功则为传送的字节数,若出错则为-1 */
    

    函数定义如下:

    ssize_t rio_readn(int fd, void *usrbuf, size_t n)
    {
        size_t nleft = n;
        ssize_t nread;
        char *bufp = usrbuf;
    
        while (nleft > 0) {
            if ((nread = read(fd, bufp, nleft)) < 0) {
                if(errno == EINTR)      /* Interrupted by big handler return */
                    nread = 0;          /* and call read() again */
                else
                    return -1;          /* errno set by read() */
            }
            else if (nread == 0)
                break;                  /* EOF */
            nleft -= nread;
            bufp += nread;
        }
        return (n - nleft);             /* return >= 0 */
    }
    
    ssize_t rio_writen(int fd, void *usrbuf, size_t n)
    {
        size_t nleft = n;
        ssize_t nwritten;
        char *bufp = usrbuf;
    
        while (nleft > 0) {
            if ((nwritten = write(fd, bufp, nleft)) <= 0) {
                if(errno == EINTR)      /* Interrupted by big handler return */
                    nwritten = 0;       /* and call write() again */
                else
                    return -1;          /* errno set by write() */
            }
            nleft -= nwritten;
            bufp += nwritten;
        }
        return n;
    }
    

    带缓冲区的rio

    带缓冲区的rio所包含的函数如下:

    #include "rio.h"
    
    void rio_readinitb(rio_t *rp, int fd);
    /* 返回:无 */
    
    ssize_t rio_readlineb(rio_t *rp, void *usrbuf, size_t maxlen);
    ssize_t rio_readnb(rio_t *rp, void *usrbuf, size_t n);
    /* 返回:若成功则为读的字节数,若为EOF则为0,若出错则为-1 */
    

    带缓冲区的rio由一个rio_t的结构体管理,其形式如下:

    #define RIO_BUFSIZE 8192
    
    typedef struct {
        int     rio_fd;                 /* 描述符 */
        int     rio_cnt;                /* 缓冲区中还未读的字节数 */
        char    *rio_bufptr;            /* 缓冲区中下一个未读的字节 */
        char    rio_buf[RIO_BUFSIZE];   /* 缓冲区 */
    } rio_t;
    

    在使用带缓冲区的rio时,每打开一个描述符,都需要使用rio_readinitb来对rio_t进行初始化:

    void rio_readinitb(rio_t *rp, int fd) {
        rp->rio_fd = fd;
        rp->rio_cnt = 0;
        rp->rio_bufptr = rp->rio_buf;
    }
    

    对缓冲区的控制主要由rio_read来完成:

    static ssize_t rio_read(rio_t *rp, char *usrbuf, size_t n) {
        int cnt;
    
        while (rp->rio_cnt <= 0) {  /* Refill if buf is empty */
            rp->rio_cnt = read(rp->rio_fd, rp->rio_buf, sizeof(rp->rio_buf));
            if (rp->rio_cnt < 0) {
                if (errno != EINTR) /* Interrupted by sig handler return */
                    return -1;
            } else if (rp->rio_cnt == 0)  /* EOF */
                return 0;
            else
                rp->rio_bufptr = rp->rio_buf; /* Reset buffer ptr */
        }
    
        /* Copy min(n, rp->rio_cnt) bytes from internal buf to user buf */
        cnt = n;
        if (rp->rio_cnt < n)
            cnt = rp->rio_cnt;
        memcpy(usrbuf, rp->rio_bufptr, cnt);
        rp->rio_bufptr += cnt;
        rp->rio_cnt -= cnt;
        return cnt;
    }
    

    rio_read函数由static关键字修饰成静态函数,这对与一个函数来说,说明这个函数只对声明它的文件可见,且不同的文件可以声明相同名的静态函数。

    rio_readlinebrio_readnb的具体实现如下:

    ssize_t rio_readnb(rio_t *rp, void *usrbuf, size_t n) {
        size_t nleft = n;
        ssize_t nread;
        char *bufp = usrbuf;
    
        while (nleft > 0) {
            if ((nread = rio_read(rp, bufp, nleft)) < 0)
                return -1;          /* errno set by read() */
            else if (nread == 0)
                break;              /* EOF */
            nleft -= nread;
            bufp += nread;
        }
        return (n - nleft);         /* return >= 0 */
    }
    
    ssize_t rio_readlineb(rio_t *rp, void *usrbuf, size_t maxlen) {
        int n, rc;
        char c, *bufp = usrbuf;
    
        for (n = 1; n < maxlen; n++) {
            if ((rc = rio_read(rp, &c, 1)) == 1) {
                *bufp++ = c;
                if (c == '
    ') {
                    n++;
                    break;
                }
            } else if (rc == 0) {
                if (n == 1)
                    return 0;   /* EOF, no data read */
                else
                    break;      /* EOF, some data was read */
            } else
                return -1;      /* Error */
        }
        *bufp = 0;
        return n - 1;
    }
    
  • 相关阅读:
    管理员必备的Linux系统监控工具
    kafka入门:简介、使用场景、设计原理、主要配置及集群搭建(转)
    RedHat linux配置yum本地资源
    RedHat Linux RHEL6配置本地YUM源
    c语言中的fgets函数
    sprintf()函数的用法
    spring boot整合JWT例子
    spring boot 自定义过滤器链
    (转)ArrayList和LinkedList的几种循环遍历方式及性能对比分析
    (转)Springboot 中filter 注入对象
  • 原文地址:https://www.cnblogs.com/tcctw/p/11707809.html
Copyright © 2020-2023  润新知