【问题标题】:Concurrency race between child and parent signals子信号和父信号之间的并发竞争
【发布时间】:2018-03-26 11:14:07
【问题描述】:

我在 CMU 过去的考试中发现了这个问题,但我不知道输出是如何可能的。

基本上,它背后的想法是有一个父进程阻止用户定义的信号,然后父进程分叉一个子进程。并且基于哪个进程首先运行(又名:赢得比赛),可能会有不同的输出。 Here is the question that is being asked in the exam(请阅读)

这是考试的代码:

int i = 1;
void handler (int sig) {
    i++;
}
int main() {
    pid_t pid;
    sigset_t s;
    sigemptyset(&s);
    sigaddset(&s, SIGUSR1);
    signal(SIGUSR1, handler);
    sigprocmask(SIG_BLOCK, &s, 0);
    pid = fork();
        <LINE A>
    if (pid != 0) {
        i = 2;
        <LINE B>
    } else {
        i = 3;
        <LINE C>
    }
    sigprocmask(SIG_UNBLOCK, &s, 0);
    pause(); /* pause to allow all signals to arrive */
    printf("%d\n", i);
    exit(0);
}

由于我们需要放函数,所以需要测试3种情况:

kill(pid,USRSIG1);

在 LINE A 或 LINE B 或 LINE C 中找到可能的输出。

现在这就是我所做的,我将函数放在 LINE A 中。

假设我们运行程序,然后父级将创建一个空集 s,向其添加信号 SIGSUR1,然后它将为 SIGUSR1 信号分配一个自定义处理程序,并阻塞该集 s 中的信号。这是哪几行

    sigset_t s;
    sigemptyset(&s);
    sigaddset(&s, SIGUSR1);
    signal(SIGUSR1, handler);
    sigprocmask(SIG_BLOCK, &s, 0);

然后父级将运行该行

    pid = fork();

这将从进程中创建一个新的子进程。

现在有两种情况将决定输出。操作系统安排父或子先运行。

假设父母先运行。然后它会执行LINE A(也就是kill函数)

因为它是父进程,所以 pid 值将是子进程的进程 ID。所以它会将 USRSIG1 发送给孩子,但由于它被阻止它不会做任何事情

if 语句为全局变量 i 赋值。如果进程是父进程,则 i = 2,否则 i = 3。所以在我们的父进程中,我们将有 i = 2。

    if (pid != 0) { //if i am a parent then i = 2
        i = 2;
        <LINE B>
    } else { //if i am a child then i = 3
        i = 3;
        <LINE C>
    }

下一行将在父进程中执行,它将解除对 SIGUSR1 信号的阻塞 sigprocmask(SIG_UNBLOCK, &s, 0); 并且父进程会暂停直到收到信号

现在子进程将运行,它将向进程组中的所有进程发送一个 kill(0,SIGUSR1) 信号,包括它自己。但是由于它被阻止在孩子身上,所以什么都不会发生。父级将收到信号,它会将我增加 1(所以现在 i = 3 在父级中)。它(父级)将从函数 pause 中恢复以打印 I 的值(即 3)并退出。

孩子现在从 kill 函数中恢复,因为它是一个孩子,所以 if 语句将不正确(所以孩子中 i 的值 = 3)。子进程解除对 set 和 pause() 信号的阻塞。

由于没有其他进程向子进程发送信号,因此它将永远暂停,并且仅父进程的输出为 3。如果我们走另一条路(孩子在父母之前跑),那么输出将只有 4。

什么让我感到困惑的是,考试的解决方案说每次运行有 2 个输出?我不明白这怎么可能,因为其中一个进程将停留在 pause() 中。

解决方案键表示 LINE A 的可能输出是:

3 4, 4 3, 3 5, or 5 3

这就是我从问题中所能理解的全部内容。任何帮助或提示将不胜感激。

【问题讨论】:

    标签: c linux concurrency signals fork


    【解决方案1】:

    如果子进程先运行,则输出将为 5,因为它将同时接收来自自身和父进程的信号。如果两个进程在进入pause() 之前完成kill(pid,USRSIG1),则两者都不会终止或打印。

    POSIX 还允许由于延迟信号(例如,如果使用网络消息而不是共享内存)而终止和打印两个进程,并且允许子进程打印任何 int 值,因为 i 不是 @987654325 类型@。

    从评论中可以看出,考试作者错误地认为pause()会神奇地等待,直到收到所有发送或将发送到进程的信号。

    如果sigprocmask(SIG_UNBLOCK, &amp;s, 0); pause(); 被替换为对sigsuspend 的适当调用,它将充当考试作者的状态。父级将收到 1 个信号,子级将收到 1 个或 2 个信号,因为来自父级的信号可能为时已晚,也可能与来自自身的信号相结合。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2019-04-29
      • 2021-09-27
      • 1970-01-01
      • 1970-01-01
      • 2021-09-03
      • 1970-01-01
      • 2021-03-05
      相关资源
      最近更新 更多