健壮I/O读写函数——RIO包

RIO(Robust I/O,健壮的I/O)

头文件说明

#include <unistd.h>     //unistd.h 中所定义的接口通常都是大量针对系统调用的封装,如 fork、pipe 以及各种 I/O 原语(read、write、close 等)
#include <fcntl.h>      //fcntl.h定义了很多宏和open、fcntl函数原型
#include <stdio.h>             
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>   //dev_t 、pid_t、size_t等类型
#include <sys/stat.h>    //文件的stat
#include <sys/socket.h>  //涉及套接字通讯
#include <errno.h>       //定义了通过错误码来回报错误资讯的宏


以下程序跟CSAPP源程序有一定的不同,CSAPP中的代码为阻塞读被我注释掉了。因为我在调试网络通讯阻塞读的时候,会由于通讯问题超时还是什么原因,具体是什么忘了,返回错误值。所以所有的读操作我改为非阻塞读。

需要注意的是程序接收到一些errno错误码后会退出,比如说阻塞读发生错误时,出现了程序退出的问题。


#define RIO_BUFSIZE    ( 4096 )

typedef struct {
    int rio_fd;                //与内部缓冲区关联的描述符
    int rio_cnt;               //缓冲区中剩下的字节数
    char *rio_bufptr;          //指向缓冲区中下一个未读的字节
    char rio_buf[RIO_BUFSIZE]; 
} rio_t;

ssize_t rio_readn(int fd, void *usrbuf, size_t n);
ssize_t rio_writen(int fd, void *usrbuf, size_t n);
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); 

无缓冲的输入输出函数

这些函数直接在存储器和文件之间传送数据,没有应用级缓冲。对将二进制数据读写到网络和从网络读写二进制数据尤其有用

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 sig handler return */
                nread = 0;        /* and call read() again */
            } else {
                return -1;        /* errno set by read() */
            }
        } 
        else if (nread == 0) {
            break;     
        }
        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 ((nwritten = send(fd, bufp, nleft, 0)) <= MSG_DONTWAIT) { //非阻塞
            if (errno == EINTR) { /* Interrupted by sig handler return */
                nwritten = 0;     /* and call write() again */
            } else {
                return -1;        /* errno set by write() */
            }
        }
        nleft -= nwritten;
        bufp += nwritten;
    }
    return n;
}


带缓冲的输入输出函数

这些函数高效地从文件中读取文本行和二进制数据,这些文件的内容缓存在应用级缓冲区中,类似printf这种标准I/O提供的缓冲区。

这些函数是线程安全的。


这里引入线程安全和可重入概念的区别。

线程安全:如果一个函数在同一时刻可以被多个线程安全地调用,线程安全函数解决多个线程调用时访问共享资源的冲突问题。

使用全局变量的函数是非线程安全的,使用静态数据或其他共享数据必须加锁。

可重入:函数可以由多于一个线程并发使用。可重入函数可以在任意时刻被中断,稍后再继续运行,不会丢失数据。

可重入函数不能在内部使用,返回全局变量和静态数据,使用全局变量要用局部变量保护全局变量,不调用不可重入函数。

如果一个函数是可重入的,那么这个函数线程安全。如果一个函数线程安全不一定可重入。


void rio_readinitb(rio_t *rp, int fd) 
{
    rp->rio_fd = fd;  
    rp->rio_cnt = 0;  
    rp->rio_bufptr = rp->rio_buf;
}
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)); //阻塞
        rp->rio_cnt = recv(rp->rio_fd, rp->rio_buf, sizeof(rp->rio_buf), MSG_DONTWAIT); //非阻塞
//        rp->rio_cnt = recv(rp->rio_fd, rp->rio_buf, sizeof(rp->rio_buf), 0); //非阻塞
        if (rp->rio_cnt < 0) {
//            if (errno != EINTR) {    /* Interrupted by sig handler return */
//                return -1;
//            }
           return 0;
        }
        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;
}
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;
}

摘自《深入理解计算机系统》

猜你喜欢

转载自blog.csdn.net/mwj327720862/article/details/80106613