【发布时间】:2014-05-22 09:30:55
【问题描述】:
在 Stevens 等人的书Unix Network Programming 的第 5 章中,有一个服务器程序和一个客户端程序,如下所示:
服务器
mysignal(SIGCHLD, sig_child);
for(;;)
{
connfd = accept(listenfd, (struct sockaddr *)&ca, &ca_len);
pid = fork();
if(pid == 0)
{
//sleep(60);
close(listenfd);
str_echo(connfd);
close(connfd);
exit(0);
}
close(connfd);
}
函数 sig_child 用于处理信号 SIGCHLD;代码如下:
void sig_child(int signo)
{
pid_t pid;
int stat;
static i = 1;
i++;
while(1)
{
pid = wait(&stat);
if(pid > 0)
{
printf("ith: %d, child %d terminated\n", i, pid);
}
else
{
break;
}
}
//pid = wait(&stat);
return;
}
客户
for(i = 0 ; i < 5; i++)
{
sockfd[i] = socket(AF_INET, SOCK_STREAM, 0);
if(sockfd[i] < 0)
{
perror("create error");
exit(-1);
}
memset(&sa, 0, sizeof(sa));
sa.sin_family = AF_INET;
sa.sin_port = htons(5900);
if(inet_pton(AF_INET, argv[1], &sa.sin_addr) != 1)
{
perror("inet_pton error");
exit(-1);
}
connect(sockfd[i], (struct sockaddr *)&sa, sizeof(sa));
}
str_cli(sockfd[0], stdin);
exit(0);
在客户端源码中可以看到,程序会与服务器端建立5个连接,但程序中只使用了1个连接; str_cli 完成后,exit(0) 被调用。并且所有连接都应该关闭,然后服务器中的五个子进程将退出,并将SIGCHLD发送给父进程,父进程使用函数sig_child处理SIGCHLD。然后while 循环将确认所有子进程将被父进程正确等待。我对该程序进行了几次测试;效果很好,所有的孩子都会被清理干净。
但在书中,作者写道“wait 不能正常工作,因为函数 wait 可能在所有子进程退出之前就被阻塞了”。那么书上的说法对吗?如果是对的,请您详细解释一下。 (PS:我认为while 语句中的wait 可以正确处理所有子进程的退出。)
【问题讨论】:
-
这到底是哪本书?作者?标题?出版商?年?版?页面?
-
@EJP unpbook.com,第三版,第5章。我看的是中文版的,所以我没有找到英文版的确切页面。
-
那段号呢?章节?最近的前一个标题?你必须提供一些线索。
-
static i = 1;是 C89 或准标准代码; C99 或更高版本需要一个类型(之前推断为int)——初始化器可能应该是0而不是1。请注意,printf()不能在信号处理函数中正式安全地调用。sig_child()中的循环有点洛可可;您可能应该使用while ((pid = wait(&status)) > 0) { … }。这从根本上简化了循环体(其中仅包含printf(),假设您继续与死亡调情),将 12 行减少到 4 行,甚至只有 2 行。 -
如果您在
sig_child()中有一个循环,并且您有(例如)3 个子进程并且其中 1 个终止,则信号处理程序中的循环将不会终止,直到所有 3 个子进程都死了。您需要使用waitpid()和W_NOHANG而不是wait()。