unix 向子进程发送信号,SIGCONT,SIGSTOP

mrphzbgm  于 2022-11-04  发布在  Unix
关注(0)|答案(2)|浏览(170)

我的代码有问题,
我希望所有的子进程在程序启动时停止,之后我只希望索引为i的子进程继续执行,而其他的子进程停止。

我希望按照p0、p1、p2、p3、p4、p0、p1 ....的顺序执行它们。

# include <stdio.h>

# include <stdlib.h>

# include <unistd.h>

# include <signal.h>

# include <sys/wait.h>

# define N 5

void handler(int i)
{
    if (i == SIGCONT)
    {
        printf("signal cont\n");
    }
}
int main()
{
    int pid[N];
    for (int i = 0; i < N; i++)
    {
        if ((pid[i] = fork()) == 0)
        {
            /* code */
            while (1)
            {
                printf("ici fils %d\n", i);
                usleep(50000);
            }
        }
        else
        {
            kill(pid[i], SIGSTOP);
            // kill(pid[i], SIGSTOP);
            if (i == N - 1)
            {
                kill(pid[i], SIGCONT);
                sleep(2);
                kill(pid[i], SIGSTOP);
                kill(pid[0], SIGCONT);
            }
            else
            {

                kill(pid[i], SIGCONT);
                sleep(2);
                kill(pid[i], SIGSTOP);
                kill(pid[i + 1], SIGCONT);
            }
            // kill(pid[i], SIGKILL);
            waitpid(pid[i], NULL, 0);
        }
    signal(SIGCONT, &handler);
    }
}
z31licg0

z31licg01#

您的代码存在以下几个问题:
1.任何通过SIGSTOP停止的进程都不能有为该信号注册的处理程序。注册处理程序会导致处理程序的行为 * 替换 * 停止进程的默认行为。
1.为SIGCONT注册一个处理程序通常不是一个好主意,这样做不会阻止SIGCONT继续进程,这是SIGCONT的一个特殊特性,可能会让人感到惊讶,而且每当SIGCONT被传递时,即使进程没有停止,处理程序也会触发,这通常是一种不同的惊讶。
1.您只在第一次分支之后的父级中注册信号处理程序。随后分支的子级将继承这些处理程序,但第一个子级不会。除此之外,这将防止第一个子级的pause()被父级发送给它的信号解除阻塞。您可以让每个子级为自己注册任何所需的处理程序,也可以在第一次分支之前在父级中注册它们。
1.在每个子级的pause()和父级的第一个kill()之间存在竞争。子级有可能在调用pause()之前接收到SIGCONT,在这种情况下,它将等待 next 信号。您可以通过在派生之前在父级中阻塞SIGCONT,并在子级中使用sigsuspend()来防止这种情况。在这种情况下,您可能希望在从初始sigsuspend()返回后解除对SIGCONT的阻塞。
1.父进程尝试向它尚未派生的进程发送信号(kill(pid[i + 1], SIGCONT);)。
目前还不清楚您要实现的完整行为是什么,但您可能希望首先派生所有子级,然后才开始发送信号。

更新

关于问题的最新情况,
1.显然,您希望在子进程中重复循环,但您的代码只运行一次。这是实现我在上面建议的内容的一个很好的理由:先把所有的孩子分开,然后,分别做所有的信号。

gopyfrb3

gopyfrb32#

child 进程中,不使用pause(2),而是使用raise(3)向调用进程发出停止SIGSTOP的信号。真实的上不需要注册信号处理程序。
parent 进程中,创建 child 进程后,使用设置了WUNTRACED标志的waitpid(2)等待其停止(或终止)。WIFSTOPPED(...)宏可用于明确确定子进程的状态。WCONTINUE标志可用于等待 child 进程继续,就像之前的WIFCONTINUED(...)宏一样。
这里是一个粗略的例子,没有任何有意义的错误处理。请注意,并发睡眠虽然简单,但从技术上讲并不是一种一致的调度方式。此程序的输出 * 可能 * 在执行之间略有不同。


# define _POSIX_C_SOURCE 200809L

# include <signal.h>

# include <stdio.h>

# include <stdlib.h>

# include <sys/wait.h>

# include <unistd.h>

# define CHILDCOUNT 5

sig_atomic_t looping = 1;

void handler(int sig) {
    (void) sig;
    looping = 0;
}

pid_t create_child(void) {
    pid_t pid = fork();

    if (!pid) {
        /* child */
        raise(SIGSTOP);
        pid_t self = getpid();
        printf("SIGCONT in %d\n", self);

        while (1) {
            printf("RUNNING in %d\n", self);
            sleep(1);
        }

        /* bug net */
        exit(EXIT_SUCCESS);
    }

    return pid;
}

void killwait(pid_t pid, int sig) {
    kill(pid, sig);

    int status;
    waitpid(pid, &status, WUNTRACED | WCONTINUED);

    if (WIFSTOPPED(status))
        printf("P: C(%d) STOPPED!\n", pid);
    if (WIFCONTINUED(status))
        printf("P: C(%d) CONTINUED!\n", pid);
    if (WIFSIGNALED(status) && SIGKILL == WTERMSIG(status))
        printf("P: C(%d) SUCCESSFULLY KILLED!\n", pid);
}

int main(void) {
    pid_t pids[CHILDCOUNT];

    /* tentative: catch this in all processes so the parent may reap manually */
    signal(SIGINT, handler);

    for (size_t i = 0; i < CHILDCOUNT; i++) {
        pid_t current = pids[i] = create_child();

        printf("Parent now has child (%d) [#%zu].\n", current, i);
        killwait(current, 0);
    }

    for (size_t i = 0; looping; i = (i + 1) % CHILDCOUNT)  {
        pid_t current = pids[i];

        printf("P: C(%d) STARTING [#%zu].\n", current, i);

        killwait(current, SIGCONT);
        sleep(2);
        killwait(current, SIGSTOP);
    }

    for (size_t i = 0; i < CHILDCOUNT; i++)
        killwait(pids[i], SIGKILL);
}

相关问题