我一直在从Richard Stevens阅读关于APUE中的信号和信号处理程序的信息。我无法理解它们的一个概念是它们如何在特定时间保证同步性(如果他们什至完全做到这一点,我还找不到任何反例)。
我发布的代码完成了预期的工作:父进程从文件中读取(从循环中的stdin提供),通过管道将其内容发送到其子进程(使用内存映射),该子进程将替换所有实例将一个单词与另一个单词放在一起,通过另一个管道将其返回给父对象,然后由其将更改后的内容打印到标准输出中。但是我不太清楚如何确定父进程在打印出来之前可以安全地将内容取回。
换句话说,如何确定信号在正确的时刻被捕获?我知道信号可以随时发生,即在父级控制流中的任何位置,但是在父级到达它不应执行的代码的特定部分之前,这里没有看到任何“安全措施”在知道孩子完蛋之前。
#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);
}