两个进程之间的顺序信号
Sequential signals between two processes
我有一个parent进程和两个children。 parent进程只创建了两个children,一个reader和一个计数器,就等着自己死掉。 child人做了以下事情。
第一个child(reader):
- 打开文件,
- 读取一行,
- 向第二个 child、
发送信号 (SIGUSR1)
- 等待来自第二个 child、
的信号
- 如果我们可以读取另一行则转到 2,否则杀死第二行 child。
第二个child(计数器):
- 等待来自 reader、
的信号 (SIGUSR1)
- 计算行长度,
- 向reader发送信号并转到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()
returns 两次 每次调用。当child中的returns时,它会将*pid_counter
设置为0
,但当parent中的returns时,它会设置*pid_counter
到 child 的 PID。你无法预测哪个会先发生。在你的情况下实际发生的是它最终设置为 0
,所以你在 reader 进程中的所有 kill
调用都向进程组中的每个进程发送信号,所以reader 和 counter 同时得到它们。这会导致您的同步失败,因为两个进程同时从 sigwaitinfo()
返回。在 reader 中,您应该只将 SIGUSR1
发送到计数器进程。
你需要做的就是把它改成这样:
pid_t temp_pid
if ( !(temp_pid = fork()) ) {
*pid_counter = getpid();
其他要点:
sigprocmask()
在 fork()
调用中保留,因此您应该在 fork()
ing 之前设置一次。在你的情况下,你永远不需要解锁它,也不应该。
如果您在 sigwaitinfo()
上调用 SIGUSR1
,则没有必要(并且可以说最好不要)为 SIGUSR1
建立处理程序。
strlen()
returns 输入 size_t
,所以你的 printf()
格式说明符应该是 %zu
,而不是 %d
.
你对 fflush(stdout)
的所有调用虽然无害,但在这里都是多余的。
您几乎从不检查系统调用中的任何 returns。这不仅适用于生产代码——如果您的程序不工作,首先要验证您是否正在检查所有系统调用是否成功,因为它不工作的原因可能是因为其中一个调用是失败,也许是因为你传递了错误的信息。在测试程序时检查是否成功更重要,同样重要。
无论如何,这是您程序的工作版本:
#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$
我有一个parent进程和两个children。 parent进程只创建了两个children,一个reader和一个计数器,就等着自己死掉。 child人做了以下事情。
第一个child(reader):
- 打开文件,
- 读取一行,
- 向第二个 child、 发送信号 (SIGUSR1)
- 等待来自第二个 child、 的信号
- 如果我们可以读取另一行则转到 2,否则杀死第二行 child。
第二个child(计数器):
- 等待来自 reader、 的信号 (SIGUSR1)
- 计算行长度,
- 向reader发送信号并转到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()
returns 两次 每次调用。当child中的returns时,它会将*pid_counter
设置为0
,但当parent中的returns时,它会设置*pid_counter
到 child 的 PID。你无法预测哪个会先发生。在你的情况下实际发生的是它最终设置为 0
,所以你在 reader 进程中的所有 kill
调用都向进程组中的每个进程发送信号,所以reader 和 counter 同时得到它们。这会导致您的同步失败,因为两个进程同时从 sigwaitinfo()
返回。在 reader 中,您应该只将 SIGUSR1
发送到计数器进程。
你需要做的就是把它改成这样:
pid_t temp_pid
if ( !(temp_pid = fork()) ) {
*pid_counter = getpid();
其他要点:
sigprocmask()
在fork()
调用中保留,因此您应该在fork()
ing 之前设置一次。在你的情况下,你永远不需要解锁它,也不应该。如果您在
sigwaitinfo()
上调用SIGUSR1
,则没有必要(并且可以说最好不要)为SIGUSR1
建立处理程序。strlen()
returns 输入size_t
,所以你的printf()
格式说明符应该是%zu
,而不是%d
.你对
fflush(stdout)
的所有调用虽然无害,但在这里都是多余的。您几乎从不检查系统调用中的任何 returns。这不仅适用于生产代码——如果您的程序不工作,首先要验证您是否正在检查所有系统调用是否成功,因为它不工作的原因可能是因为其中一个调用是失败,也许是因为你传递了错误的信息。在测试程序时检查是否成功更重要,同样重要。
无论如何,这是您程序的工作版本:
#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$