FD_SET之后, select不到的问题
现在有一个问题,想了半天也没想明白。想请教一下大家:
大体程序功能是实现一个进程池, 主进程fork出N多子进程,然后开始select 监听的sockid, 当收到连接时,将连接的描述符传递给子进程。 然后将子进程的返回管道描述符FD_SET到select中。 就可以收到子进程处理完毕的回应了。
简易的代码如下,删除了进程管理相关的控制:
FD_ZERO(&oset);
FD_ZERO(&rset);
FD_SET(listenfd, &oset);
for(;;)
{
FD_SET(listenfd, &oset);
rset = oset;
nselect = select(fdmax+1, &rset, NULL, NULL, NULL);
if( FD_ISSET(listenfd, &rset) ) { /*收到客户端连接请求*/
connfd = accept(listenfd, (SA *)&clientaddr, (socklen_t *)&len);
FD_SET(mypid[i].fd, &oset); /*加入描述集,等待子进程*/
fdmax = max(fdmax ,mypid[i].fd);
/*把连接套接字传送给子进程*/
close(connfd); /*因为有一个connfd在飞,所以这个可以关闭*/
if(--nselect ==0)
continue;
}
else
{
for(i = 0; i < nchild; i++ )
{
if(FD_ISSET(mypid[i].fd , &rset))
{
read(mypid[i].fd, &c, 1);
FD_CLR(mypid[i].fd, &oset);
nselect--;
}
if(nselect == 0)
break;
}
}
}
}
我的问题是这样的:
1.我之前FD_SET和 FD_CLR,都是直接对 &rset进行操作。没用上oset 。 这样的结果是,有多笔请求同时上来的时候,只有最后一笔请求的管道能触发select。 我想了半天,最后在网上找了个例子程序,发现别人用了oset作为临时变量。
我按照上面代码修改之后,就好用了。但是不明白为什么。
2.fd_max的问题,FD_CLR之后,fd_max已经不能缩小了。这个只能这样呗,比如说我FD_SET的时候,一个管道描述符是20, 然后这个管道描述符被FD_CLR,但是fd_max也只能大于20了,尽管其他描述符都很小。 FD_SET select FD_CLR
[解决办法]
1.第一个你存在的问题是因为,select中fd_set值-结果参数,并且你所关心的那一位是相同的。多个请求到达的时候,由于值-结果的缘故,只有最后一个请求保存下来了。其他覆盖掉了。所以通常我们在再次调用select函数的时候,都得再次把所有关心的字集置1.
2.第二个问题,我不太懂你的意思。FD_CLR 只是关闭那个位,并不代表那个位的空间就不存在了。另外,这也许是设计上的问题。我不太懂这个。
个人见解。
[解决办法]
int select(int nfds, fd_set *restrict readfds,
fd_set *restrict writefds, fd_set *restrict errorfds,
struct timeval *restrict timeout);
注意几个fds都是(fd_set *)类型,没有const哦,会被修改该的。
If the readfds argument is not a null pointer, it points to an object of type fd_set that on input specifies the file descriptors to be checked for being ready to read, and on output indicates which file descriptors are ready to read.
说的明白一点就是说:
调用select时,你用 readfds 告诉系统哪些描述字需要监测,
select返回时,系统用 readfds 告诉你哪些描述字已经可读!
[解决办法]
是的,还要注意的是select的最后一个参数timeout
int select(int nfds, fd_set *restrict readfds,
fd_set *restrict writefds, fd_set *restrict errorfds,
struct timeval *restrict timeout);
Upon successful completion, the select() function may modify the object pointed to by the timeout argument
所以要用这个参数的话,在循环里每次都得重新赋值