I/O模型
首先我们将查看UNIX下可用的5种I/O模型的基本区别:
1.阻塞式I/O
2.非阻塞式I/O
3.I/O复用(select和poll)
4.信号驱动式I/O(SIGIO)
5.异步I/O(POSIX的aio_系列函数)
阻塞式I/O模型
最流行的I/O模型是阻塞式I/O模型,下面以数据报套接字作为例子,有如下的情形
非阻塞式I/O模型
进程把一个套接字设置成非阻塞式通知内核:当锁请求的I/O操作非得把本进程投入睡眠才能完成时,不要把本进程投入睡眠,而是返回一个错误
前三次调用recvfrom时没有数据可返回,因此内核转而立即返回一个EWOULDBLOCK错误。
I/O复用模型
我们可以调用select和poll,阻塞在这两个系统调用中的某一个之上,而不是在真正的I/O系统调用上
下面小节将详细讲这两个函数的用法
信号驱动式I/O模型
我们也可以用信号,让内核在描述符就绪时发送SIGIO信号通知我们。
在信号发生之后,在信号处理函数中调用recvfrom读取数据报。
异步I/O模型
信号驱动式I/O是由内核通知我们何时可以启动一个I/O操作,而异步I/O模型是由内核通知我们I/O操作何时完成。
select函数
之前apue的学习笔记也有详细的用法 http://www.cnblogs.com/runnyu/p/4645754.html
#include <sys/select.h> int select(int maxfdp1,fd_set *restrict readfds,fd_set *restrict writefds,fd_set *restrict exceptfds,struct timeval *restrict tvptr);
参数tvptr指定愿意等待的时间,有下面3种情况
1.tvptr==NULL 永远等待,直到所指定的描述符中的一个已经准备好或捕捉到一个信号返回。如果捕捉到一个信号,则select返回-1,errno设置为EINTR
2.tvptr->tv_sec==0 && tvptr->tv_usec==0 不等待,测试所有指定的描述符并立即返回
3.tvptr->tv_sec!=0 || tvptr->tv_usec!=0 等待指定的秒数和微秒数。当指定的描述符之一已准备好,或当指定的时间值已经超过时立即返回
中间3个参数readfds、writefds和exceptfds是指向描述符集的指针。这3个描述符集说明了我们关心的可读、可写和处于异常条件的描述符集合。
对于描述符集fd_set结构,提供了下面4个函数
#include <sys/select.h> int FD_ISSET(int fd,fd_set *fdset); void FD_CLR(int fd,fd_set *fdset); void FD_SET(int fd,fd_set *fdset); void FD_ZERO(fd_set *fdset);
select第一个参数maxfdp1的意思是“最大文件描述符编号值加1”,在上面3个描述符集中找到最大描述符编号值,然后加1就是第一个参数值。
select有3个可能的返回值
1.返回值-1表示出错。如果在所指定的描述符一个都没准备好时捕捉到一个信号,则返回-1
2.返回0表示没有描述符准备好,指定的时间就过了。
3.一个正返回值说明了已经准备好的描述符数,在这种情况下,3个描述符集中依旧打开的位对应于已准备好的描述符。
使用select修订str_cli函数
新版本改为阻塞于select调用,或是等待标准输入可读,或是等待套接字可读。
代码如下
1 #include "unp.h" 2 3 void 4 str_cli(FILE *fp, int sockfd) 5 { 6 int maxfdp1; 7 fd_set rset; 8 char sendline[MAXLINE], recvline[MAXLINE]; 9 10 FD_ZERO(&rset); 11 for ( ; ; ) { 12 FD_SET(fileno(fp), &rset); 13 FD_SET(sockfd, &rset); 14 maxfdp1 = max(fileno(fp), sockfd) + 1; 15 Select(maxfdp1, &rset, NULL, NULL, NULL); 16 17 if (FD_ISSET(sockfd, &rset)) { /* socket is readable */ 18 if (Readline(sockfd, recvline, MAXLINE) == 0) 19 err_quit("str_cli: server terminated prematurely"); 20 Fputs(recvline, stdout); 21 } 22 23 if (FD_ISSET(fileno(fp), &rset)) { /* input is readable */ 24 if (Fgets(sendline, MAXLINE, fp) == NULL) 25 return; /* all done */ 26 Writen(sockfd, sendline, strlen(sendline)); 27 } 28 } 29 }