一。之前写过一篇关于并发服务器的处理机制,其内容是这样的:
考虑到有多个客户连接的情况下,服务器在收到每一个新的客户的连接请求时,都会创建一个新的连接套接字,而原来的监听套接字将保留以继续监听后续的连接请求;如果服务器不能立刻接受后来的连接,他们将会被添加到队列中等待被处理。
当服务器调用fork()来为自己创建拷贝时,打开的连接套接字将被新的子进程所继承。新的子进程将和该客户进行连接通信,而父进程服务器将会关闭已有的连接套接字而继续监听后来的连接请求。
值得注意的是,因为我们创建了子进程而不需要等待他的完成,所以必须安排服务器这只SIGCHLD的信号处理函数。该函数将调用wait来清除僵尸进程。
上面这种服务器叫并发服务器,但通过fork()函数处理多客户连接并不是数据库应用中的最好方案,因为有可能会使服务器程序相当庞大或者占用的内存过大,并且在访问数据时还要协调多个进程拷贝数据的问题。
那么,如何更好地让单个服务器进程在不阻塞,不等待客户的连接请求的前提下进行处理多客户呢?select系统调用是一种解决方案。
二。Select系统调用:
1) select系统调用允许程序同时在多个文件描述符中等待输入的到达或者输出的完成;相似的,select也允许服务器通过同时监听多个打开的套接字上等待请求的到达的方法来处理多客户连接问题。
2) select调用用于检测文件描述符集合中,是否有一个描述符处于了可读状态或可写状态或者错误状态;它将阻塞于等待描述符集合中的某一个来完成这个操作。
3) select系统调用模型的局限:其所能监控的文件描述符最大数目由FD_SETSIZE决定,内核默认32*32=1024。
4) select系统调用的原型:
- #include <sys/types.h>
- #include <sys/time.h>
- int select(int nfds, fd_set *readfds, fd_set *writefds,
- fd_set *errorfds, struct timeval *timeout);
select函数会在这些情况下返回:readfds集合中有描述符处于可读,或者writefds集合中有描述符处于可写,或者errorfds集合中有描述符遇到错误情况。如果这三种情况都没有发生,select将在timeval指定的时间后超时返回(如果timeval是一个空指针情切套接字上没有任何活动的话那么select系统调用将一直阻塞下去).
下面是我的一段演示代码(代码参考自《Linux程序设计(第三版)》):
- #include <sys/types.h>
- #include <sys/time.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include <fcntl.h>
- #include <sys/ioctl.h>
- #include <unistd.h>
- int main()
- {
- char buffer[256];
- int result,nread;
- fd_set inputs,testfds;
- struct timeval timeout;
- FD_ZERO(&inputs); //初始化为空集合
- FD_SET(0,&inputs); //在集合中设置由参数fd(此处初始为0,即为标准输入)传递的文件描述符
- while(1)
- {
- testfds = inputs; //暂时不大了解这语句的作用,怎么要设置testfds和inputs,谁可以指导我一下
- timeout.tv_sec=3;
- timeout.tv_usec=500000; //设置超时时限为3.5秒
- //如果3.5秒内没有输入数据,则会再次循环
- result = select(FD_SETSIZE, &testfds, (fd_set*)NULL, (fd_set*)NULL, &timeout);
- switch(result)
- {
- case 0: //超时,select函数返回0
- printf("select called is timeout.\n");
- break;
- case -1: //select系统调用遇到错误,函数返回-1
- perror("select called encounterd errors.\n");
- exit(1);
- default: //文件描述符状态发生变化,系统调用read进行数据读取
- if(FD_ISSET(0, &testfds))
- {
- ioctl(0, FIONREAD, &nread); //设置标准输入stdin
- if(nread==0)
- {
- printf("keyboard done.\n");
- exit(0);
- }
- nread = read(0, buffer, nread); //读取数据
- buffer[nread]='\0';
- printf("read %d from keyboard: %s", nread , buffer);
- }
- break;
- }
- }
- return 0;
- }
(上面代码定义了testfds和inputfds,testfds=inputd这个用处是什么呢,貌似只是别名而已,暂时不理解)