最近阅读了UNP一书中关于select函数的相关部分,虽然队C语言不是那么的熟悉,但是也能从中汲取一些有用的思想
LinuxIO模型
阻塞式I/O
非阻塞式I/O;
I/O复用(select和poll)
信号驱动式I/O(SIGIO) ;
异步I/O(POSIX的aio_系列函数)
select属于I/O复用的模型,select会阻塞与应用程序或者内核上,而不是阻塞与IO,select会接受多个socket,等待这些连接为可读,可写,或者异常时返回,下图为io复用的示意图
int select(int maxfdp1, fd_set *readset, fd_set *writeset, fd_set *exceptset,const struct timeval *timeout);
select的定义如上,maxfdp1参数指定待测试的描述符个数,三个参数readset、 writeset和exceptset指定我们要让内核测试读、 写和异常条件的描述符,timeout, 它告知内核等待所指定描述符中的任何一个就绪可花多长时间,当前几个参数为空指针时,我们就得到了一个精度为10MS的定时器,在tornado中,使用gen.sleep()没有阻塞程序,而time.sleep()会阻塞程序正是因为这个原因,对于文件描述符是否可读可写以及异常,有以下标准
#include "unp.h"
void str_cli(FILE *fp, int sockfd)
{
int maxfdp1;
fd_set rset;
char sendline[MAXLINE], recvline[MAXLINE];
FD_ZERO(&rset);
for ( ; ; ) {
FD_SET(fileno(fp), &rset);
FD_SET(sockfd, &rset);
maxfdp1 = max(fileno(fp), sockfd) + 1;
Select(maxfdp1, &rset, NULL, NULL, NULL);
if (FD_ISSET(sockfd, &rset)) {
if (Readline(sockfd, recvline, MAXLINE) == 0)
err_quit("str_cli: server terminated prematurely");
Fputs(recvline, stdout);
}
if (FD_ISSET(fileno(fp), &rset)) {
if (Fgets(sendline, MAXLINE, fp) == NULL)
return; /* all done */
Writen(sockfd, sendline, strlen(sendline));
}
}
}
代码的基本含义是建立一个tcp连接,然后调用socket,如果soket在返回时socket是可读的,那就读取文本,然后再使用fputs输出,如果socket输入可读,那先用fgets读入一行文本,再用writen写入套接字中
若服务有单个监听描述符,那么描述符fd0,fd1,fd2分别为标准输入,输出,错误,监听套接字的fd3,当不断的有客户端连接时,服务器上的文件描述符数组会变长,本服务器能接受的最大客户端数目为select的连接限制(一般为256)与内核允许进程打开的文件描述符的最小值.