首页 > 解决方案 > 信号处理程序和程序控制流(事件顺序)

问题描述

我一直在阅读 Richard Stevens 的关于 APUE 中的信号和信号处理程序的信息。关于它们我无法理解的一个概念是它们如何在某些时候保证同步性(即使它们确实如此,我也找不到任何反例)。

我发布的代码做了它应该做的事情:父进程从文件中读取(从循环中的标准输入给定),通过管道将它的内容发送到它的子进程(使用内存映射),它替换了一个单词的所有实例换句话说,通过另一个管道将其返回给父级,然后父级将更改的内容打印到标准输出上。但是我不太明白我们如何确定父进程在打印出来之前安全地取回了内容。

换句话说,我怎样才能确定信号是在正确的时刻被捕获的?我知道一个信号可以在任何时候发生,即在父控制流中的任何地方,但是在这里我没有看到在父到达代码的某个部分之前应用了任何“安全措施”,它不应该能够执行在知道孩子完成之前。

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <sys/mman.h>
#include <sys/wait.h>
#include <string.h>
#include <sys/stat.h>
#include <assert.h>
#include <errno.h>
#include <fcntl.h>

#define LINE_SIZE 1024


void error_fatal (char *message);

static void on_sigchld (int signo);

int
main (int argc, char **argv)
{
  pid_t pid;            
  int filedes[2];       
  int filedes2[2];      
  char line[LINE_SIZE];     
  char *src;            
  char *nl;         
  char *command;        
  int count;            
  char *curr;           
  int left;         
  int fd;           

  struct stat stats;        
  assert (argc == 3);


  if (signal (SIGCHLD, on_sigchld) == SIG_ERR)
    error_fatal ("error signal()");


  while (fgets (line, LINE_SIZE, stdin) != NULL)
    {

      if ((nl = strchr (line, '\n')) != NULL)
    *nl = 0;


      if ((fd = open (line, O_RDONLY)) < 0)
    {
      if (errno == ENOENT)
        {
          fprintf (stdout, "%s\n", strerror (errno));
          continue;
        }
      else
        error_fatal ("error open()");
    }

      if (pipe (filedes) < 0)
    error_fatal ("error pipe()");

      if (pipe (filedes2) < 0)
    error_fatal ("error pipe()");


      if ((pid = fork ()) < 0)
    error_fatal ("error fork()");
      else if (pid == 0)
    {

      if (close (fd) < 0)
        error_fatal ("error close()");

      if (close (filedes[1]) < 0)
        error_fatal ("error close()");

      if (close (filedes2[0]) < 0)
        error_fatal ("error close()");


      if (dup2 (filedes[0], STDIN_FILENO) < 0)
        error_fatal ("error dup2()");


      if (dup2 (filedes2[1], STDOUT_FILENO) < 0)
        error_fatal ("error dup2()");


      if ((command = malloc ((strlen (argv[1]) + strlen (argv[2]) +
                  6) * sizeof (char))) == NULL)
        error_fatal ("error malloc()");
      sprintf (command, "s/%s/%s/g", argv[1], argv[2]);

      execlp ("sed", "sed", command, NULL);

      error_fatal ("error exec()");
    }


      if (close (filedes[0]) < 0)
    error_fatal ("error close()");

      if (close (filedes2[1]) < 0)
    error_fatal ("error close()");


      if (fstat (fd, &stats) < 0)
    error_fatal ("error fstat()");


      if ((src =
       mmap (0, stats.st_size, PROT_READ, MAP_FILE | MAP_PRIVATE, fd,
         0)) == NULL)
    error_fatal ("error mmap()");


      curr = src;
      left = stats.st_size;
      while (left > 0)
    {
      if ((count = write (filedes[1], curr, left)) < 0)
        error_fatal ("error write()");

      if (count > 0)
        {
          left -= count;
          curr += count;
        }
    }


      if (close (filedes[1]) < 0)
    error_fatal ("error close1()");
      if (munmap (src, stats.st_size) < 0)
    error_fatal ("error munmap()");

      // this is the critical line I'm referring to
      while ((count = read (filedes2[0], line, LINE_SIZE)) > 0)
    {
      if (write (STDOUT_FILENO, line, count) != count)
        error_fatal ("error write()");
    }

      if (count < 0)
    error_fatal ("error read()");

      if (close (filedes2[0]) < 0)
    error_fatal ("error close()");
    }

  if (ferror (stdin))
    error_fatal ("error fgets()");


  exit (EXIT_SUCCESS);
}

static void
on_sigchld (int signo)
{
  pid_t pid;            


  for (; (pid = waitpid (-1, NULL, WNOHANG)) > 0;);
  if (pid < 0 && errno != ECHILD)
    error_fatal ("error waitpid()");
}

static void
error_fatal (char *message)
{

  perror (message);


  exit (EXIT_FAILURE);
}

标签: csignalscontrol-flow

解决方案


推荐阅读