fri*_*ich 4 c posix fork signals
我正在玩弄信号:SIGSTOP特别SIGCONT是。这是我写的一个测试程序。这个想法是创建一个 N + 1 个进程链(包括主进程)。每个人都必须等待其孩子停止,然后自己停止。当子进程停止时,主进程必须唤醒它的子进程。
为此,该f函数递归地创建流程链。SIGCHLD除了最后一个直接停止自身的子进程之外,每个进程都对信号使用 sigsuspend 。当它的子进程停止时,进程将收到信号SIGCHLD,然后它可以轮流停止。当主进程收到SIGCHLD信号时,意味着所有进程都处于停止状态,因此它会将信号发送SIGCONT给其子进程。每个进程都会发送SIGCONT给自己的子进程,然后退出,除了最后一个刚刚退出的子进程。
我试图说清楚:删除了返回代码测试并写了一些注释。
执行程序时,除了链条之外,一切似乎都很好SIGCONT
。一些进程被唤醒,但不是全部。查看正在运行的程序(例如 ps),一切似乎都很好:没有阻塞的进程。我真的不明白这个程序可能有什么问题。欢迎任何帮助或提示。
这是一个示例跟踪。正如您所看到的,“分叉链”进展顺利,进程在 上挂起SIGCHLD。然后最后一个子进程生成并停止。这会SIGCHLD在父进程上创建一条“链”,因为每个进程都会自行停止。当主进程收到通知时,SIGCHLD它会发送SIGCONT给它的子进程,子进程会被唤醒,然后发送SIGCONT给它自己的子进程,等等。您可以注意到这个链并不完整:
$ ./bin/trycont
n pid log
0 6257 "suspending on SIGCHLD"
1 6258 "suspending on SIGCHLD"
2 6259 "suspending on SIGCHLD"
3 6260 "suspending on SIGCHLD"
4 6261 "suspending on SIGCHLD"
5 6262 "last child - stopping"
4 6261 "got SIGCHLD"
4 6261 "stopping"
3 6260 "got SIGCHLD"
3 6260 "stopping"
2 6259 "got SIGCHLD"
2 6259 "stopping"
1 6258 "got SIGCHLD"
1 6258 "stopping"
0 6257 "got SIGCHLD"
0 6257 "sending SIGCONT to 6258"
1 6258 "awakened - sending SIGCONT to 6259"
2 6259 "awakened - sending SIGCONT to 6260"
# <- not the expected trace
Run Code Online (Sandbox Code Playgroud)
这是程序:src/trycont.c
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
/* number of created processes with fork
*/
#define N 5
#define printHeader() printf("n\tpid\tlog\n");
#define printMsg(i, p, str, ...) printf("%d\t%d\t" #str "\n", i, p, ##__VA_ARGS__)
void f(int n);
void handler(int sig);
sigset_t set;
struct sigaction action;
int main(int argc, char *argv[])
{
/* mask SIGCHLD
*/
sigemptyset(&set);
sigaddset(&set, SIGCHLD);
sigprocmask(SIG_SETMASK, &set, NULL);
/* handler will be called when SIGCHLD is sent to the process
* during the handler, SIGCHLD will be masked (sa_mask)
*/
action.sa_mask = set;
action.sa_handler = handler;
action.sa_flags = 0;
/* SIGCHLD will trigger action
*/
sigaction(SIGCHLD, &action, NULL);
/* start
*/
printHeader();
f(N);
exit(EXIT_SUCCESS);
}
void f(int n)
{
pid_t p, pc;
int myIndex;
myIndex = N - n;
p = getpid();
if (n == 0)
{
/* last child
*/
printMsg(myIndex, p, "last child - stopping");
kill(p, SIGSTOP);
printMsg(myIndex, p, "END REACHED");
exit(EXIT_SUCCESS);
}
pc = fork();
if (pc == 0)
{
/* recursion
*/
f(n - 1);
/* never reached
* because of exit
*/
}
/* father
*/
/* suspending on SIGCHLD
* need to unmask the signal
* and suspend
*/
printMsg(myIndex, p, "suspending on SIGCHLD");
sigfillset(&set);
sigdelset(&set, SIGCHLD);
sigsuspend(&set);
printMsg(myIndex, p, "got SIGCHLD");
if (n < N)
{
/* child process
* but not last
*/
printMsg(myIndex, p, "stopping");
kill(p, SIGSTOP);
printMsg(myIndex, p, "awakened - sending SIGCONT to %d", pc);
kill(pc, SIGCONT);
}
else
{
/* root process
*/
printMsg(myIndex, p, "sending SIGCONT to %d", pc);
kill(pc, SIGCONT);
}
exit(EXIT_SUCCESS);
}
void handler(int sig)
{
switch (sig)
{
case SIGCHLD:
/* when the process received SIGCHLD
* we can ignore upcoming SIGCHLD
*/
action.sa_handler = SIG_IGN;
sigaction(SIGCHLD, &action, NULL);
break;
default:
break;
}
}
Run Code Online (Sandbox Code Playgroud)
如果您需要,这里有一个 Makefile:
CC=gcc
DEFINES=-D_POSIX_C_SOURCE
STD=-std=c11 -Wall -Werror
OPTS=-O2
CFLAGS=$(STD) $(DEFINES) $(OPTS) -g
LDFLAGS=
SRC=src
OBJ=obj
BIN=bin
DIRS=$(BIN) $(OBJ)
.PHONY: mkdirs clean distclean
all: mkdirs $(BIN)/trycont
$(BIN)/%: $(OBJ)/%.o
$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $<
$(OBJ)/%.o: $(SRC)/%.c
$(CC) $(CFLAGS) -c -o $@ $<
mkdirs:
- mkdir $(DIRS)
clean:
rm -vf -- $(OBJ)/*.o
distclean: clean
rm -vfr -- $(DIRS)
Run Code Online (Sandbox Code Playgroud)
当第一个进程终止时,您的一些(全部?)后代进程将因系统生成的 SIGHUP 而死亡。
这是某些情况下预期的 POSIX 行为。
当您从 shell 启动根进程时,它是进程组领导者,其后代是该组的成员。当该领导者终止时,该进程组将成为孤立进程。当系统检测到新孤立的进程组中的任何成员已停止时,会向该进程组的每个成员发送一个 SIGHUP,然后发送一个 SIGCONT。
因此,当领导者终止时,你的一些后代进程仍然停止,因此每个人都会收到一个 SIGHUP 信号,然后是一个 SIGCONT 信号,这实际上意味着他们死于 SIGHUP 信号。
到底哪些后代仍然被阻止(或者甚至只是愉快地向 前进exit())是一场计时竞赛。在我的系统上,领导者终止得如此之快,以至于没有一个后代能够打印任何内容。