【问题标题】:SIGSTOP/SIGCONT POSIX behaviorSIGSTOP/SIGCONT POSIX 行为
【发布时间】:2016-10-13 19:13:16
【问题描述】:

我在玩信号:特别是SIGSTOPSIGCONT。 这是我写的一个测试程序。这个想法是创建一个 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

这是程序: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;
    }
}

如果需要,这里有一个 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)

【问题讨论】:

    标签: c signals fork posix


    【解决方案1】:

    当第一个进程终止时,您的一些(全部?)后代进程正在死于系统生成的 SIGHUP。

    在某些情况下这是expected POSIX behavior

    当您从 shell 启动根进程时,它是一个进程组领导,它的后代是该组的成员。当该领导者终止时,进程组将孤立。当系统检测到一个新的孤立进程组,其中任何成员都已停止时,该进程组的每个成员都会收到一个 SIGHUP,然后是一个 SIGCONT。

    因此,当领导者终止时,您的一些后代进程仍然停止,因此每个人都会收到一个 SIGHUP,然后是一个 SIGCONT,这实际上意味着他们死于 SIGHUP。

    究竟哪些后代仍然被阻止(或者甚至只是愉快地向exit()前进)是一场计时赛。在我的系统上,leader 终止的太快了,以至于没有一个后代能够打印任何东西。

    【讨论】:

    • 有趣,谢谢。在这种情况下,处理 SIGHUP 是不够的:SIGCONT 将被传递给每个停止的进程,它们将恢复执行。明智的做法是每个进程都等待它的子进程。谢谢@pilcrow。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2022-06-15
    • 2011-05-24
    • 1970-01-01
    • 2012-04-01
    • 2015-03-24
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多