健壮的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') {
                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;
}
posted @ 2019-10-20 15:47  一川official  阅读(595)  评论(0编辑  收藏  举报