使用信号的进程同步是如何工作的? [英] How does processes synchronization using signals work?

查看:16
本文介绍了使用信号的进程同步是如何工作的?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我最近读完了"Unix环境中的高级编程"(第3版)的第10部分(信号),我看到了一段我不完全理解的代码:

#include "apue.h"

static volatile sig_atomic_t sigflag; /* set nonzero by sig handler */
static sigset_t newmask, oldmask, zeromask;

static void
sig_usr(int signo)  /* one signal handler for SIGUSR1 and SIGUSR2 */
{
    sigflag = 1;
}

void
TELL_WAIT(void)
{
    if (signal(SIGUSR1, sig_usr) == SIG_ERR)
        err_sys("signal(SIGUSR1) error");
    if (signal(SIGUSR2, sig_usr) == SIG_ERR)
        err_sys("signal(SIGUSR2) error");
    sigemptyset(&zeromask);
    sigemptyset(&newmask);
    sigaddset(&newmask, SIGUSR1);
    sigaddset(&newmask, SIGUSR2);

    /* Block SIGUSR1 and SIGUSR2, and save current signal mask */
    if (sigprocmask(SIG_BLOCK, &newmask, &oldmask) < 0)
        err_sys("SIG_BLOCK error");
}

void
TELL_PARENT(pid_t pid)
{
    kill(pid, SIGUSR2);     /* tell parent we're done */
}

void
WAIT_PARENT(void)
{
    while (sigflag == 0)
        sigsuspend(&zeromask);  /* and wait for parent */
    sigflag = 0;

    /* Reset signal mask to original value */
    if (sigprocmask(SIG_SETMASK, &oldmask, NULL) < 0)
        err_sys("SIG_SETMASK error");
}

void
TELL_CHILD(pid_t pid)
{
    kill(pid, SIGUSR1);         /* tell child we're done */
}

void
WAIT_CHILD(void)
{
    while (sigflag == 0)
        sigsuspend(&zeromask);  /* and wait for child */
    sigflag = 0;

    /* Reset signal mask to original value */
    if (sigprocmask(SIG_SETMASK, &oldmask, NULL) < 0)
        err_sys("SIG_SETMASK error");
}

上面的例程用于(您当然知道)使用信号的同步进程。虽然我能听懂每一句台词,但我看不到大局。代码本身在以下场景中使用:为了避免程序中的争用条件,在fork()之后,我们使子进程Tell_Parent和Wait_Parent,然后对具有Tell_Child和Wait_Child的父进程执行相同的操作。我的问题是:

1.)当子级和父级都使用自己的变量集(副本)时,子级如何通过变量与父级通信?是否因为子进程不直接修改sigflag,而是通过信号处理程序修改(父进程也是如此)? 2.)为什么我们需要挡路SIGUSR1和SIGUSR2,然后用SIGPROMASK解除屏蔽?

使用其中三个例程的程序可以是(摘自本书):

#include "apue.h"

static void charatatime(char *);

int
main(void)
{
    pid_t   pid;

    TELL_WAIT();

    if ((pid = fork()) < 0) {
        err_sys("fork error");
    } else if (pid == 0) {
        WAIT_PARENT();      /* parent goes first */
        charatatime("output from child
");
    } else {
        charatatime("output from parent
");
        TELL_CHILD(pid);
    }
    exit(0);
}

static void
charatatime(char *str)
{
    char    *ptr;
    int     c;

    setbuf(stdout, NULL);           /* set unbuffered */
    for (ptr = str; (c = *ptr++) != 0; )
        putc(c, stdout);
}

干杯,

推荐答案

<1)它们不是通过"变量"进行通信-此处使用的唯一通信工具是kill函数。我们通过调用kill来"告诉"事情,我们"等待"被sigsuspend告知。sig_flag不是共享的,它是每个进程的本地状态,它说明此特定进程是否已被其他进程"告知"。

2)如果信号在fork之前未被阻止,则父进程可以在子进程开始等待信号之前将其发送给子进程。也就是说,时间线可能如下所示:

  • 叉子
  • 父级获取时间片,用kill向子级发送信号
  • 子级获取时间片,等待信号

但此信号已发送,因此无限期等待。因此,我们必须确保在子进程开始等待循环之前不会将信号传递给子进程。为此,我们在fork之前挡路,原子解封开始等待。原子性是关键;此操作作为两个独立的步骤执行时无法实现所需的不变量,因为信号可以在这两个步骤之间传递。

这篇关于使用信号的进程同步是如何工作的?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆