我有一个父进程和两个子进程。父进程只创建两个 child ,一个读者和一个计数器,并等待它的死亡。 children 做了以下事情。
第一个 child (读者):
- 打开文件,
- 读一行,
- 向第二个 child 发送信号 (SIGUSR1),
- 等待来自第二个子进程的信号,
- 如果我们可以读取另一行,则转到 2,否则杀死第二个 child 。
老二(计数器):
- 等待来自阅读器的信号 (SIGUSR1),
- 计算行的长度,
- 向阅读器发送信号并转到 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()
每次调用时返回两次。当它在 child 中返回时,它将设置 *pid_counter
至 0
,但是当它在父级返回时,它会设置 *pid_counter
到 child 的PID。你无法预测哪个会先发生。你的情况实际发生的是它最终设置为 0
,所以你所有的kill
读取器进程中的调用正在向进程组中的每个进程发送信号,因此读取器和计数器都同时接收到它们。这导致您的同步失败,因为两个进程都从 sigwaitinfo()
返回同时。在阅读器中,您应该发送 SIGUSR1
仅到计数器进程。
你需要做的就是把它改成这样:
pid_t temp_pid
if ( !(temp_pid = fork()) ) {
*pid_counter = getpid();
其他要点:
sigprocmask()
保存在fork()
中调用,所以你应该在fork()
之前设置一次荷兰国际集团在您的案例中,您永远不需要解锁它,也不应该。没有必要(并且可以说最好不要)为
SIGUSR1
建立处理程序如果你调用sigwaitinfo()
在上面。strlen()
返回类型size_t
, 所以你的printf()
格式说明符应为%zu
, 不是%d
.所有你打给
fflush(stdout)
的电话,虽然无害,但在这里是多余的。您几乎从不检查系统调用的任何返回值。这不仅适用于生产代码——如果您的程序不工作,首先要验证您是否正在检查所有系统调用是否成功,因为它不工作的原因可能是因为其中一个调用是失败,也许是因为你传递了错误的信息。在测试程序时检查是否成功更为重要,而不是次要。
无论如何,这是你程序的一个工作版本:
#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$
关于c - 两个进程之间的顺序信号,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34144685/