tcp服务器端fork子进程没有exit引起的问题及原因
点击(此处)折叠或打开
-
for( ; ; )
-
{
-
FD_SET(listenfd,&rset);
-
n=select(listenfd+1,&rset,NULL,NULL,NULL);
-
if(FD_ISSET(listenfd,&rset))
-
{
-
clilen=sizeof(cliaddr);
-
connfd=accept(listenfd,(struct sockaddr*)&cliaddr,&clilen);
-
if( (childpid=fork())==0 )
-
{
-
close(listenfd);
-
printf("a child is forked\n");
-
//exit(0);
-
}
-
close(connfd);
-
}
- }
子进程忘记了退出,那么当有客户发起链接时,服务端出现了以下情况:
当时觉得很奇怪,按理说就是子进程没有退出也应该和父进程一样阻塞在select上啊,为什么会出现死循环。又来检查了select的返回值,如下:
点击(此处)折叠或打开
-
n=select(listenfd+1,&rset,NULL,NULL,NULL);
-
if(n==-1)
- perror("select error");
从新启动服务端,当有客户链接时,服务端显示如下:
这下我们知道其中的原因了,因为监听套接字描述符在子进程中已经close,所以对一个close的描述符select会直接返回错误,从而子进程不会阻塞在select,造成死循环。
我们也可以如下验证,在子进程中不close监听套接字:
点击(此处)折叠或打开
-
for( ; ; )
-
{
-
FD_SET(listenfd,&rset);
-
n=select(listenfd+1,&rset,NULL,NULL,NULL);
-
if(n==-1)
-
perror("select error");
-
if(FD_ISSET(listenfd,&rset))
-
{
-
clilen=sizeof(cliaddr);
-
connfd=accept(listenfd,(struct sockaddr*)&cliaddr,&clilen);
-
if( (childpid=fork())==0 )
-
{
-
//close(listenfd);
-
printf("a child is forked\n");
-
//exit(0);
-
}
-
close(connfd);
-
}
- }
再次运行服务端,并从客户端发起链接,服务度显示如下:
我们发现这次子进程阻塞在了select,因为被select的套接字没有close。
但是这里面有个问题,有人会问父进程也打开了监听套接字,fork后监听套接字的打开引用计数会变为2,子进程close只会使监听套接字的打开引用计数减1,并不会真正关闭套接字,那为什么子进程就不能select呢?
这个问题要从VFS角度说明,即父子进程描述符、打开文件表、FILE(socket)对象之间的关系。
可以看到,父子进程有各自的打开文件表(files_struct),但是共享底层的FILE对象,而close的效果就是使FILE对象的引用计数减一,也就是解除了一个描述符和FILE对象的关联。对于子进程,当调用close后,子进程中的listenfd对应的指针下标已经不再和对应监听套接字对象相关联了,所以从子进程角度来说这个listenfd已经是个无效的描述符,不能再对其进行操作了。
补充:以上select的例子同样适用于accept,当子进程close(listenfd)后没有exet退出,再次执行accept同样会造成Bad file descriptor。例如如下tcp服务端程序:
点击(此处)折叠或打开
-
for(;;)
-
{
-
socklen_t addrlen = sizeof(client_addr);
-
//accept返回客户端套接字描述符
-
connfd = accept(listenfd,(struct sockaddr *)&client_addr,&addrlen);
-
if(connfd<0)
-
perror("accept error");
-
printf("receive connection\n");
-
if((pid = fork()) == 0) //子进程,与客户端通信
-
{
-
close(listenfd);
-
//exit(0);
-
}
-
else
-
{
-
close(connfd);
-
}
- }
当客户端连接后效果如下图所示。
郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。