使用带有waitpid pselect fork sigaction的处理程序清理子进程
我有一个服务器,它将消息接收到套接字中,对于接收到的每条消息,执行fork exec。这部分似乎工作正常 但我需要在非阻塞模式下执行此操作,因此我创建了一个处理程序,用waitpid正确清理所有终止的子进程,正如论坛中许多主题所解释的那样 问题是,此处理程序生成对我的pselect命令的中断系统调用,并使用以下消息停止程序: 选择:中断系统调用 我在论坛上找到了一些关于这个问题的解释,比如防止种族状况等等,所以我尝试使用sigprocmask来阻止几个信号,但没有成功 我相信这是一个很小的问题,但这是我第一次使用这种程序 我需要一些帮助。 提前谢谢 节目如下:使用带有waitpid pselect fork sigaction的处理程序清理子进程,c,select,fork,zombie-process,waitpid,C,Select,Fork,Zombie Process,Waitpid,我有一个服务器,它将消息接收到套接字中,对于接收到的每条消息,执行fork exec。这部分似乎工作正常 但我需要在非阻塞模式下执行此操作,因此我创建了一个处理程序,用waitpid正确清理所有终止的子进程,正如论坛中许多主题所解释的那样 问题是,此处理程序生成对我的pselect命令的中断系统调用,并使用以下消息停止程序: 选择:中断系统调用 我在论坛上找到了一些关于这个问题的解释,比如防止种族状况等等,所以我尝试使用sigprocmask来阻止几个信号,但没有成功 我相信这是一个很小的问题,
void
clean_up_child_process (int signal_number)
{
pid_t p;
int status;
while (1)
{
p = waitpid (-1, &status, WNOHANG);
if (p == -1)
{
if (errno == EINTR)
{
continue;
}
break;
}
else if (p == 0)
{
break;
}
}
}
static void
app (void)
{
SOCKET sock;
char commande[BUF_SIZE];
char res_cmd[BUF_SIZE];
int max;
int n;
sock = init_connection ();
max = sock;
fd_set rdfs;
sigemptyset (&sigmask);
sigaddset (&sigmask, SIGCHLD);
sigaddset (&sigmask, SIGINT);
sigaddset (&sigmask, SIGTSTP);
sigaddset (&sigmask, SIGTERM);
sigprocmask (SIG_BLOCK, &sigmask, NULL);
struct sigaction sigchld_action;
memset (&sigchld_action, 0, sizeof (sigchld_action));
sigchld_action.sa_handler = &clean_up_child_process;
sigaction (SIGCHLD, &sigchld_action, NULL);
while (1)
{
int i = 0;
FD_ZERO (&rdfs);
/* add STDIN_FILENO */
FD_SET (STDIN_FILENO, &rdfs);
/* add the connection socket */
FD_SET (sock, &rdfs);
sigemptyset (&empty_mask);
if (pselect (max + 1, &rdfs, NULL, NULL, NULL, &empty_mask) == -1)
if (errno != EINTR)
{
perror ("select()");
exit (errno);
}
if (FD_ISSET (STDIN_FILENO, &rdfs))
{
/* stop process when type on keyboard */
// break; must be disable to avoid bad exits
}
else if (FD_ISSET (sock, &rdfs))
{
/* new client */
SOCKADDR_IN csin = { 0 };
size_t sinsize = sizeof csin;
int csock = accept (sock, (SOCKADDR *) & csin, &sinsize);
if (csock == SOCKET_ERROR)
{
perror ("accept()");
continue;
}
if ((n = recv (csock, commande, BUF_SIZE - 1, 0)) < 0)
{
perror ("recv(commande)");
n = 0;
continue;
}
commande[n] = 0;
if ((n = fork ()) == -1)
perror ("fork()");
else if (n == 0)
{
close (STDOUT_FILENO);
dup (csock);
close (STDERR_FILENO);
dup (csock);
execlp (commande, commande, 0);
}
else
{
closesocket (csock);
}
}
}
end_connection (sock);
}
您需要进一步了解POSIX信号处理 在本例pselect中,当在可中断系统调用期间接收到信号时,信号调用将退出到userspace并调用信号处理程序。信号处理程序完成后,正常行为是信号调用返回EINTR。在某些系统上,可以通过使信号动作sau RESTART来避免这种情况,在这种情况下,内核将自动重新启动系统调用。这听起来是一个不错的选择,直到你意识到你经常想要捕捉信号,比如SIGINT,并让它们设置一个全局变量,例如退出程序并进行测试。因此,以下适用于您的程序的结构是常见的:
volatile sig_atomic_t rxsig_quit = 0;
void
handlesignal (int sig)
{
/* Only do signal safe things here; remember mutexes may be held */
switch (sig)
{
case SIGINT:
case SIGTERM:
rxsig_quit++;
break;
case SIGCHLD:
/* do all our waiting here */
while (1)
{
int status;
waitpid (WAIT_ANY, &status, WNOHANG);
}
break;
}
}
static void
app (void)
{
/* ... */
while (!rxsig_quit)
{
/* ... */
do
{
int ret;
ret = pselect (max + 1, &rdfs, NULL, NULL, NULL, &empty_mask);
}
while ((ret < 0) && (errno == EINTR) && !rxsig_quit);
/* ... */
}
/* ... */
}
您可以使用man-s7信号获取更多信息。这还列出了异步安全函数,即可以在信号处理程序中安全调用的函数
然而,您假设您需要等待。在现代POSIX系统上,情况并非如此。您可以将SIGCHLD设置为SIG_IGN,在这种情况下,操作系统将按照wait2手册中的这一段进行工作:
POSIX.1-2001规定,如果SIGCHLD的处置设置为SIG_IGN或SIGCHLD的SAU NOCLDWAIT标志设置为SIGCHLD see sigaction2,则终止的子项不会变成僵尸,对wait或waitpid的调用将阻塞,直到所有子项终止,然后在errno设置为ECHILD时失败。原始POSIX标准未指定将SIGCHLD设置为SIG_IGN的行为。请注意,即使SIGCHLD的默认处置是ignore,显式地将处置设置为SIG_IGN也会导致对僵尸进程子进程的不同处理。Linux 2.6符合此规范。但是,Linux2.4和更早版本没有这样做:如果在忽略SIGCHLD时进行wait或waitpid调用,则调用的行为就像没有忽略SIGCHLD一样,也就是说,调用将一直阻塞,直到下一个子进程终止,然后返回该子进程的进程ID和状态
显然,这不太便于携带。信号号似乎没有用于您的清理子处理方法?我的理解是:信号由sigaction给出:sigaction SIGCHLD,&SIGCHLD\U action,NULL;在这里设置句柄函数:sigchld\u action.sa\u handler=&clean\u child\u process;当pselect返回-1时,只要在errno==EINTR-loop-back时不要退出,而是重新启动pselect调用。这是很正常的行为。嗨,保罗,非常感谢你的建议,它现在正常工作了。我只需将直接更新的处理函数更改为post。我不知道为什么第一次连接后旧的会挂起来。现在没事了。我不知道为什么如果我用FD_ISSETSTDIN_FILENO和rdfs管理键盘,如果我不按键盘,程序会在第一次连接事件后直接退出。这并不重要,因为我不会在真正的服务器上使用它。这对我来说很奇怪:-嗨,艾布莱,非常感谢你的建议。我花了很长时间分析了所有的解释和代码,现在一切都正常工作并在控制之下。有了这个解释,我节省了很多工作时间,我的代码也安全多了。@thorgal99:没问题,你提醒我,我错过了上一段刚刚补充的东西,这可能会让你的生活更轻松。