c - 两个进程之间的顺序信号

标签 c linux signals ipc sigprocmask

我有一个父进程和两个子进程。父进程只创建两个 child ,一个读者和一个计数器,并等待它的死亡。 children 做了以下事情。

第一个 child (读者):

  1. 打开文件,
  2. 读一行,
  3. 向第二个 child 发送信号 (SIGUSR1),
  4. 等待来自第二个子进程的信号,
  5. 如果我们可以读取另一行,则转到 2,否则杀死第二个 child 。

老二(计数器):

  1. 等待来自阅读器的信号 (SIGUSR1),
  2. 计算行的长度,
  3. 向阅读器发送信号并转到 1。

我无法等待来自另一个进程的信号。在我调用 pause() 函数之前可以接收到信号,即进程可以永远阻塞。我还尝试使用 sigprocmask()sigsuspend()sigwaitinfo(),但它无法正常工作。

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/ipc.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <signal.h>
#include <string.h>

enum { MAX_LEN = 16 };

void handler(int signo)
{
    // do nothing
}

int main(int argc, const char * argv[])
{
    sigset_t ss;
    sigemptyset(&ss);
    sigaddset(&ss, SIGUSR1);

    // handle SIGUSR1
    signal(SIGUSR1, handler);

    // shared memory for file line
    char *data = mmap(NULL, MAX_LEN, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANON, -1, 0);

    pid_t *pid_counter = mmap(NULL, sizeof(*pid_counter), PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANON, -1, 0);

    pid_t pid_reader;
    if (!(pid_reader = fork())) {
        sigprocmask(SIG_BLOCK, &ss, NULL);
        printf("READER: waiting signal from COUNTER\n"); fflush(stdout);
        sigwaitinfo(&ss, NULL);
        sigprocmask(SIG_UNBLOCK, &ss, NULL);
        printf("READER: got signal\n"); fflush(stdout);

        printf("READER: opening file\n"); fflush(stdout);
        FILE *f = fopen(argv[1], "r");
        while (fgets(data, MAX_LEN, f) > 0) {
            printf("READER: reading line and waiting signal from COUNTER\n"); fflush(stdout);

            sigprocmask(SIG_BLOCK, &ss, NULL);
            kill(*pid_counter, SIGUSR1);
            sigwaitinfo(&ss, NULL);
            sigprocmask(SIG_UNBLOCK, &ss, NULL);

            printf("READER: got signal\n"); fflush(stdout);
        }
        printf("READER: closing file and killing COUNTER\n"); fflush(stdout);
        fclose(f);
        kill(*pid_counter, SIGTERM);
        _exit(0);
    }

    if (!(*pid_counter = fork())) {
        sleep(1);
        printf("COUNTER: send signal to READER that COUNTER is ready\n"); fflush(stdout);
        while (1) {
            sigprocmask(SIG_BLOCK, &ss, NULL);
            kill(pid_reader, SIGUSR1);
            printf("COUNTER: waiting signal from READER\n"); fflush(stdout);
            sigwaitinfo(&ss, NULL);
            sigprocmask(SIG_UNBLOCK, &ss, NULL);
            printf("COUNTER: got signal\n"); fflush(stdout);

            printf("%d\n", strlen(data));
            fflush(stdout);
        }
    }

    wait(NULL);
    wait(NULL);

    return 0;
}

对于这段代码,我可以得到以下序列:

$ gcc -o prog prog.c && ./prog input.dat
READER: waiting signal from COUNTER
COUNTER: send signal to READER that COUNTER is ready
COUNTER: waiting signal from READER
READER: got signal
READER: opening file
READER: reading line and waiting signal from COUNTER
READER: got signal
READER: reading line and waiting signal from COUNTER
READER: got signal
READER: reading line and waiting signal from COUNTER
READER: got signal
READER: reading line and waiting signal from COUNTER
READER: got signal
READER: reading line and waiting signal from COUNTER
READER: got signal
READER: reading line and waiting signal from COUNTER
READER: got signal
READER: closing file and killing COUNTER

为什么计数器不写“got signal”?如何同步发送和接收信号? (我知道其他 IPC 方法,但我需要通过信号进行同步。)

最佳答案

这里有几个明显的问题。

真正导致您的问题的是:

if (!(*pid_counter = fork())) {

请记住 (1) pid_counter指向共享内存;和 (2) fork()每次调用时返回两次。当它在 child 中返回时,它将设置 *pid_counter0 ,但是当它在父级返回时,它会设置 *pid_counter到 child 的PID。你无法预测哪个会先发生。你的情况实际发生的是它最终设置为 0 ,所以你所有的kill读取器进程中的调用正在向进程组中的每个进程发送信号,因此读取器和计数器都同时接收到它们。这导致您的同步失败,因为两个进程都从 sigwaitinfo() 返回同时。在阅读器中,您应该发送 SIGUSR1仅到计数器进程。

你需要做的就是把它改成这样:

pid_t temp_pid
if ( !(temp_pid = fork()) ) {
    *pid_counter = getpid();

其他要点:

  1. sigprocmask()保存在 fork() 中调用,所以你应该在 fork() 之前设置一次荷兰国际集团在您的案例中,您永远不需要解锁它,也不应该。

  2. 没有必要(并且可以说最好不要)为 SIGUSR1 建立处理程序如果你调用 sigwaitinfo()在上面。

  3. strlen()返回类型 size_t , 所以你的 printf()格式说明符应为 %zu , 不是 %d .

  4. 所有你打给fflush(stdout)的电话,虽然无害,但在这里是多余的。

  5. 您几乎从不检查系统调用的任何返回值。这不仅适用于生产代码——如果您的程序不工作,首先要验证您是否正在检查所有系统调用是否成功,因为它不工作的原因可能是因为其中一个调用是失败,也许是因为你传递了错误的信息。在测试程序时检查是否成功更为重要,而不是次要。

无论如何,这是你程序的一个工作版本:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/ipc.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <signal.h>
#include <string.h>

enum { MAX_LEN = 16 };

int main(int argc, const char * argv[])
{
    if ( argc != 2 ) {
        fprintf(stderr, "Enter one argument, and one argument only.\n");
        return EXIT_FAILURE;
    }

    // shared memory for file line
    char *data = mmap(NULL, MAX_LEN, PROT_READ | PROT_WRITE, MAP_SHARED |
                      MAP_ANON, -1, 0);
    if ( data == MAP_FAILED ) {
        perror("mmap() failed for data");
        exit(EXIT_FAILURE);
    }

    pid_t *pid_counter = mmap(NULL, sizeof *pid_counter, PROT_READ |
                              PROT_WRITE, MAP_SHARED | MAP_ANON, -1, 0);
    if ( pid_counter == MAP_FAILED ) {
        perror("mmap() failed for pid_counter");
        exit(EXIT_FAILURE);
    }

    pid_t pid_reader, temp_pid;

    sigset_t ss;
    sigemptyset(&ss);
    sigaddset(&ss, SIGUSR1);
    if ( sigprocmask(SIG_BLOCK, &ss, NULL) == -1 ) {
        perror("sigprocmask() failed");
        exit(EXIT_FAILURE);
    }

    if ( (pid_reader = fork()) == -1 ) {
        perror("fork() failed for reader");
        exit(EXIT_FAILURE);
    }
    else if ( !pid_reader ) {
        printf("READER: waiting signal from COUNTER\n");

        if ( sigwaitinfo(&ss, NULL) == -1 ) {
            perror("sigwaitinfo() failed in reader");
            _exit(EXIT_FAILURE);
        }

        printf("READER: got signal\n");

        printf("READER: opening file\n");

        FILE *f = fopen(argv[1], "r");
        if ( !f ) {
            fprintf(stderr, "Couldn't open input file\n");
            _exit(EXIT_FAILURE);
        }

        while ( fgets(data, MAX_LEN, f) ) {
            printf("READER: reading line and waiting signal from COUNTER\n");

            if ( kill(*pid_counter, SIGUSR1) == -1 ) {
                perror("kill() for SIGUSR1 failed in reader");
                _exit(EXIT_FAILURE);
            }

            if ( sigwaitinfo(&ss, NULL) == -1 ) {
                perror("sigwaitinfo() failed in reader");
                _exit(EXIT_FAILURE);
            }

            printf("READER: got signal\n");
        }
        printf("READER: closing file and killing COUNTER\n");
        fclose(f);
        if ( kill(*pid_counter, SIGTERM) == -1 ) {
            perror("kill() for SIGTERM failed in reader");
            _exit(EXIT_FAILURE);
        }

        _exit(EXIT_SUCCESS);
    }

    if ( (temp_pid = fork()) == -1 ) {
        perror("fork() failed for counter");
        exit(EXIT_FAILURE);
    }
    else if ( temp_pid == 0 ) {
        *pid_counter = getpid();

        sleep(1);
        printf("COUNTER: send signal to READER that COUNTER is ready\n");

        while (1) {
            if ( kill(pid_reader, SIGUSR1) == -1 ) {
                perror("kill() failed for SIGUSR1 in counter");
                _exit(EXIT_FAILURE);
            }

            printf("COUNTER: waiting signal from READER\n");

            if ( sigwaitinfo(&ss, NULL) == -1 ) {
                perror("sigwaitinfo() failed in counter");
                _exit(EXIT_FAILURE);
            }

            printf("COUNTER: got signal\n");

            printf("%zu\n", strlen(data));
        }

        _exit(EXIT_SUCCESS);
    }

    if ( wait(NULL) == -1 ) {
        perror("first wait() failed");
        exit(EXIT_FAILURE);
    }

    if ( wait(NULL) == -1 ) {
        perror("second wait() failed");
        exit(EXIT_FAILURE);
    }

    return 0;
}

显示文件的输出如下:

paul@thoth:~/src$ cat file.txt
line one
line two
line three

paul@thoth:~/src$ ./sig file.txt
READER: waiting signal from COUNTER
COUNTER: send signal to READER that COUNTER is ready
COUNTER: waiting signal from READER
READER: got signal
READER: opening file
READER: reading line and waiting signal from COUNTER
COUNTER: got signal
9
COUNTER: waiting signal from READER
READER: got signal
READER: reading line and waiting signal from COUNTER
COUNTER: got signal
9
COUNTER: waiting signal from READER
READER: got signal
READER: reading line and waiting signal from COUNTER
COUNTER: got signal
11
COUNTER: waiting signal from READER
READER: got signal
READER: reading line and waiting signal from COUNTER
COUNTER: got signal
1
COUNTER: waiting signal from READER
READER: got signal
READER: closing file and killing COUNTER
paul@thoth:~/src$ 

关于c - 两个进程之间的顺序信号,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34144685/

相关文章:

c - 如何向标准信号处理程序添加代码?

c - 对 SIGCHLD 使用 sigaction 来了解 child 何时终止但无法判断 child 何时 SIGSEV

c++ - 如何以编程方式在 C/C++ 中导致核心转储

c++ - 为什么结构体的 sizeof 不等于每个成员的 sizeof 之和?

c - SoX 与 OpenAL 性能/开销

linux - 使用 shell 脚本在配置文件中追加行

c++ - 无法处理信号,导致系统调用中断

c - 如何将服务器的日期时间转换为unix时间戳?

json - 读取为 json 时出错

c++ - 如何在Linux中的C++中查找函数属于哪个库