代码开源(2)——UNIX 健壮I/O函数

       摘自《深入理解计算机系统》一书,几个IO读写的封装函数。一共是两类,一是无缓冲的输入输出函数;二是带缓冲的输入函数。这几个函数有一个共同点,就是比较好的处理了“不足值”的情况。所谓“不足值”是指read和write函数传送的字节比应用程序要求的要少。下面几种情况可能会出现不足值:(1)读时遇到EOF;(2)从终端读文本行;(3)读和写网络套接字,内部缓冲约束和较长的网络延迟会引起read和write返回不足值。

       这几个函数自动处理了上述提到的不足值,在像网络程序这样容易出现不足值的应用中,提供了方便、健壮和高效的I/O。

#ifndef RIO_H_
#define RIO_H_

#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#define RIO_BUFSIZE 8192

typedef struct
{
	int rio_fd;
	int rio_cnt;
	char *rio_pbuf;
	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 *pr, int fd);
ssize_t rio_read(rio_t *pr, char *usrbuf, size_t n);
ssize_t rio_readnb(rio_t *pr, void *usrbuf, size_t n);
ssize_t rio_readlineb(rio_t *pr, void *usrbuf, size_t maxlen);

//无缓冲输入函数
//成功返回输入的字节数,若EOF返回0,出错返回-1
ssize_t rio_readn(int fd, void *usrbuf, size_t n)
{
	size_t nleft = n;
	ssize_t nread;
	char *pbuf = usrbuf;
	while(nleft > 0)
	{
		//在某些系统中,当处理程序捕捉到一个信号时,被中断的慢系统调用(read, write, accept)
		//在信号处理程序返回时不再继续,而是立即返回给客户一个错误条件,并将errno设置成为EINTR
		if((nread = read(fd, pbuf, nleft)) < 0)
		{
			if(errno == EINTR)
				nread = 0;    //中断造成的,再次调用read
			else
				return -1;    //出错
		}
		else if(nread == 0)   //到了文件末尾
			break;
		nleft -= nread;
		pbuf += nread;
	}
	return n - nleft;
}
//无缓冲输出函数
//成功返回输出的字节数,出错返回-1
ssize_t rio_writen(int fd, void *usrbuf, size_t n)
{
	size_t nleft = n;
	ssize_t nwritten;
	char *pbuf = usrbuf;
	while(nleft > 0)
	{
		//注意这里是小于等于,磁盘已满或超过一个给定进程的文件长度限制就出错了
		if((nwritten = write(fd, pbuf, nleft)) <= 0)
		{
			if(errno == EINTR)
				nwritten = 0;
			else
				return -1;
		}
		nleft -= nwritten;
		pbuf += nwritten;
	}
	return n; //注意与输入的区别,write不会返回不足值
}

//初始化rio_t结构
void rio_readinitb(rio_t *pr, int fd)
{
	pr->rio_fd = fd;
	pr->rio_cnt = 0;
	pr->rio_pbuf = pr->rio_buf;
}
//带缓存函数 供内部调用
ssize_t rio_read(rio_t *pr, char *usrbuf, size_t n)
{
	int cnt;
	while(pr->rio_cnt <= 0) //内部缓冲空了,进行重填
	{
		pr->rio_cnt = read(pr->rio_fd, pr->rio_buf, sizeof(pr->rio_buf)); //读到内部缓冲中
		if(pr->rio_cnt < 0) 
		{
			if(errno != EINTR) //出错返回
				return -1;
		}
		else if(pr->rio_cnt == 0) //到文件末尾
			return 0;
		else
			pr->rio_pbuf = pr->rio_buf; //重置指针位置
	}
	//从内部缓冲拷贝数据到用户缓冲中
	cnt = (pr->rio_cnt < n)? pr->rio_cnt: n; //取两者之间的小的
	memcpy(usrbuf, pr->rio_pbuf, cnt);
	pr->rio_pbuf += cnt;
	pr->rio_cnt -= cnt;
	return cnt;
}
//带缓冲输入函数
ssize_t rio_readnb(rio_t *pr, void *usrbuf, size_t n)
{
	size_t nleft = n;
	ssize_t nread;
	char *pbuf = usrbuf;
	while(nleft > 0)
	{
		//与无缓冲的区别在于这里调用的是rio_read而不是read
		if((nread = rio_read(pr, pbuf, nleft)) < 0)
		{
			if(errno == EINTR)
				nread = 0;    //中断造成的,再次调用read
			else
				return -1;    //出错
		}
		else if(nread == 0)   //到了文件末尾
			break;
		nleft -= nread;
		pbuf += nread;
	}
	return n - nleft;
}
//带缓冲输入函数,每次输入一行
ssize_t rio_readlineb(rio_t *pr, void *usrbuf, size_t maxlen)
{
	int i, n;
	char c, *pbuf = usrbuf;
	for(i = 1; i < maxlen; i++)
	{
		if((n = rio_read(pr, &c, 1)) == 1) //读一个字符
		{
			*pbuf++ = c;
			if(c == '\n') //读到换行符
				break;
		}
		else if(n == 0)
		{
			if(i == 1) //空文件
				return 0;
			else       //读到了部分数据
				break;
		}
		else  //出错
			return -1;
	}
	*pbuf = 0; //添加字符串结束符
	return i;
}

#endif /* RIO_H_ */


你可能感兴趣的:(struct,IO,网络,unix,终端,磁盘)