0

I have a parent process and two children. The parent process only creates two children, a reader and a counter, and waits for its death. The children do the following things.

The first child (reader):

  1. opens file,
  2. reads a line,
  3. sends a signal (SIGUSR1) to the second child,
  4. waits for a signal from the second child,
  5. go to 2 if we can read another line, else kill the second child.

The second child (counter):

  1. waits for a signal (SIGUSR1) from reader,
  2. counts line length,
  3. sends a signal to reader and go to 1.

I have trouble waiting for a signal from another process. The signal can be received before I call pause() function, i.e., process can be blocked forever. I also tried to use sigprocmask(), sigsuspend() and sigwaitinfo(), but it doesn't work correctly.

#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;
}

For this code I can get the following sequence:

$ 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

Why counter doesn't write "got signal"? How I can send and receive signals synchronously? (I know about other IPC methods, but I need synchronization through signals.)

4

1 回答 1

6

这里有几个明显的问题。

实际上导致您的问题的一个是:

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

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

您需要做的就是将其更改为:

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

其他要点:

  1. sigprocmask()在调用中保留,因此您应该在ingfork()之前设置一次。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$ 
于 2015-12-10T01:16:36.830 回答