两个进程之间的顺序信号

时间:2015-12-07 22:40:05

标签: c linux signals ipc sigprocmask

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

第一个孩子(读者):

  1. 打开文件,
  2. 读一行,
  3. 向第二个孩子发送信号(SIGUSR1),
  4. 等待第二个孩子的信号,
  5. 如果我们可以读另一行,请转到2,否则杀死第二个孩子。
  6. 第二个孩子(柜台):

    1. 等待读者发出信号(SIGUSR1),
    2. 计算行长度,
    3. 向读者发送信号并转到1。
    4. 我无法等待来自其他进程的信号。在我调用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
      

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

1 个答案:

答案 0 :(得分:6)

There are a few obvious problems here.

The one that is actually causing your issue is this:

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

Remember that (1) pid_counter points to shared memory; and (2) fork() returns twice every time it is called. When it returns in the child, it will set *pid_counter to 0, but when it returns in the parent, it'll set *pid_counter to the PID of the child. You cannot predict which will happen first. What's actually going on in your case is that it ends up set to 0, and so all your kill calls in the reader process are sending signals to every process in your process group, so both reader and counter are getting them at the same time. This is causing your synchronization to fail, because both processes are returning from sigwaitinfo() at the same time. In the reader, you should send SIGUSR1 to the counter process only.

What you need to do is change it to this:

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

Other points:

  1. sigprocmask() is preserved across fork() calls, so you should just set it once before fork()ing. You never need to unblock it in your case, and should not.

  2. There is no need to (and arguably better not to) establish a handler for SIGUSR1 if you're calling sigwaitinfo() on it.

  3. strlen() returns type size_t, so your printf() format specifier should be %zu, not %d.

  4. All your calls to fflush(stdout), while harmless, are here superfluous.

  5. You hardly ever check any of the returns from your system calls. This is not just for production code - if your program is not working, the very first thing to do it verify that you're checking all your system calls for success, because the reason it may not be working is because one of those calls is failing, perhaps because you passed it bad information. It's more important to check for success when you are testing your program, not less important.

Anyway, here's a working version of your program:

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

with the following output for the shown file:

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$