首页 文章

儿童和父母信号之间的并发竞争

提问于
浏览
2

我在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();

这将从过程中创建一个新的孩子 .

现在有2个案例将确定输出 . 操作系统会安排父或子项首先运行 .

让我们说父母先跑 . 然后它将执行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(所以现在我父母的身份是3) . 并且它(父)将从函数暂停中恢复以打印I的值(即3)并退出 .

现在,孩子从kill函数恢复,因为它是一个孩子,if语句不会为真(所以child中的i值= 3) . 孩子解锁来自set和pause()的信号 .

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

令我困惑的是,考试的解决方案是每次运行有2个输出?我不知道这是怎么回事,因为其中一个进程将保持暂停() .

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

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

这就是我从这个问题中可以理解的一切 . 任何帮助或提示将不胜感激 .

1 回答

  • 0

    如果孩子先跑,输出将是5,因为它将从他们自己和父母接收信号 . 如果两个进程在输入_2651187之前完成 kill(pid,USRSIG1) ,则不会终止或打印 .

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

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

    如果 sigprocmask(SIG_UNBLOCK, &s, 0); pause(); 被适当调用 sigsuspend 替换,它将作为考试作者状态 . 父母将收到1个信号,孩子将收到1或2个信号,因为来自父母的信号可能太晚或与来自其自身的信号结合 .

相关问题