一、各IO包的关系
Unix I/O模型是在操作系统内核中实现的,应用程序可以通过诸如open、close、lseek、read、write和stat这样的函数来访问Unix I/O。较高级别的RIO和标准I/O函数都是基于Unix I/O函数来实现的。
RIO函数它们自动处理不足值,并且为读文本行提供了一种高效的带缓冲的方法。标准I/O函数提供了Unix I/O函数的一个更加完整的带缓冲的替代品,包括格式化的I/O例程,如printf和scanf。
二、网络编程不应该使用标准I/O
通过一个例子可以看到在网络编程中使用标准I/O的危险性。
简而言之,问题出在标准I/O的函数自动执行的缓冲上面。
三、RIO包
RIO,全称 Robust I/O,即健壮的IO包。它提供了与系统I/O相似的函数接口,在读取操作时,RIO包添加了读缓冲区,一定程度上添加了程序的读取效率。另外,带缓冲的输入函数是线程安全的。这与Stevens的 UNP 3rd Edition(中文版) P74 中介绍的那个输入函数不同。UNP的那个版本号的带缓冲的输入函数的缓冲区是以静态全局变量存在。所以对于多线程来说是不可重入的。RIO包中有专门的数据结构为每个文件描写叙述符都分配了相应的独立的读缓冲区,这样不同线程对不同文件描写叙述符的读訪问也就不会出现并发问题(然而若多线程同一时候读同一个文件描写叙述符则有可能发生并发访问问题。须要利用锁机制封锁临界区)。
另外,RIO还帮助我们处理了可修复的错误类型:EINTR。考虑read和write在堵塞时被某个信号中断,在中断前它们还未读取/写入不论什么字节,则这两个系统调用便会返回-1表示错误,并将errno置为EINTR。这个错误是能够修复的。而且应该是对用户透明的。用户无需在意read 和 write有没有被中断。他们仅仅须要直到read 和 write成功读取/写入了多少字节,所以在RIO的rio_read()和rio_write()中便对中断进行了处理。
#define RIO_BUFSIZE 4096
typedef struct
{
int rio_fd; //与缓冲区绑定的文件描写叙述符的编号
int rio_cnt; //缓冲区中还未读取的字节数
char *rio_bufptr; //当前下一个未读取字符的地址
char rio_buf[RIO_BUFSIZE];
}rio_t;
这个是rio的数据结构,通过rio_readinitb(rio_t *, int)能够将文件描写叙述符与rio数据结构绑定起来。注意到这里的rio_buf的大小是4096,为linux中文件的块大小。
void rio_readinitb(rio_t *rp, int fd)
/**
* @brief rio_readinitb rio_t 结构体初始化,并绑定文件描写叙述符与缓冲区
*
* @param rp rio_t结构体
* @param fd 文件描写叙述符
*/
{
rp->rio_fd = fd;
rp->rio_cnt = 0;
rp->rio_bufptr = rp->rio_buf;
return;
}