两个进程之间的顺序信号

Sequential signals between two processes

我有一个parent进程和两个children。 parent进程只创建了两个children,一个reader和一个计数器,就等着自己死掉。 child人做了以下事情。

第一个child(reader):

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

第二个child(计数器):

  1. 等待来自 reader、
  2. 的信号 (SIGUSR1)
  3. 计算行长度,
  4. 向reader发送信号并转到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() returns 两次 每次调用。当child中的returns时,它会将*pid_counter设置为0,但当parent中的returns时,它会设置*pid_counter 到 child 的 PID。你无法预测哪个会先发生。在你的情况下实际发生的是它最终设置为 0,所以你在 reader 进程中的所有 kill 调用都向进程组中的每个进程发送信号,所以reader 和 counter 同时得到它们。这会导致您的同步失败,因为两个进程同时从 sigwaitinfo() 返回。在 reader 中,您应该只将 SIGUSR1 发送到计数器进程。

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

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

其他要点:

  1. sigprocmask()fork() 调用中保留,因此您应该在 fork()ing 之前设置一次。在你的情况下,你永远不需要解锁它,也不应该。

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

  3. strlen() returns 输入 size_t,所以你的 printf() 格式说明符应该是 %zu,而不是 %d .

  4. 你对 fflush(stdout) 的所有调用虽然无害,但在这里都是多余的。

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

无论如何,这是您程序的工作版本:

#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$