【发布时间】:2018-08-06 12:26:10
【问题描述】:
我正在尝试按照问题Make children process wait until receiving parent's signal的答案开发一个简单的铁路模拟。
我的任务:我正好有 5 个进程代表火车。我需要通过fork() 创建这 5 个进程(T1、T2、T3、T4 和 T5),并暂停每个进程,直到所有进程都创建完毕。之后,父进程将向子进程发送信号,每个子进程将使用execl(即execl(execl_path_name, CHILDETCONE, i, NULL);)。发出信号后,父母等待所有孩子完成他们的任务。
handler函数我挺了解的,但是这几点我不是很清楚:
我需要在处理函数中插入我的
execl吗?-
从上一个问题的答案看不懂这最后一个循环的意义:
for (int i = 0; i < NUMBER_TRAINS; i++) { wait(NULL); }
这是我的代码:
#include <stdio.h>
#include <signal.h>
#include <sys/wait.h>
#include "accessory.h"
#define NUMBER_TRACKS 16
#define NUMBER_STATIONS 8
#define NUMBER_TRAINS 5
#define TRACKS_INITIALS "MA"
#define STATION_INITIALS "S"
#define SIZE 256
#define CHILDETCONE "childETCone"
void handler(int sig);
int main(int argc , char *argv[]) {
pid_t pid;
pid_t pid_array[NUMBER_TRAINS];
char track_name[2];
char track_number[2];
int execl_return;
char str[2];
char * execl_path_name;
memset(pid_array, 0, sizeof(pid_array));
/* create the MAx file initialized to zero */
for (int i = 1; i < (NUMBER_TRACKS + 1); i++) {
memset(track_name, '\0', sizeof(track_name));
memset(track_number, '\0', sizeof(track_number));
strcpy(track_name, TRACKS_INITIALS);
sprintf(track_number, "%d", i);
strcat(track_name, track_number);
create_track_file(track_name, "", SIZE);
}
execl_path_name = get_file_name(CHILDETCONE, "", SIZE);
printf("path %p\n", execl_path_name);
for(int i = 0; i < NUMBER_TRAINS; i++) {
pid = fork();
if (pid < 0) {
perror("fork");
exit(1);
}
if (pid == 0) { //child
//sprintf(str, "%d", i+1);
//execl_return = execl(execl_path_name, CHILDETCONE, i, NULL);
signal(SIGUSR1, handler);
pause();
exit(0);
}
//parent
pid_array[i] = pid;
}
for (int j = 0; j < NUMBER_TRAINS; j++) {
kill(pid_array[j], SIGUSR1);
sleep(1);
}
for (int i = 0; i < NUMBER_TRAINS; i++) {
wait(NULL);
}
return 0;
}
void handler(int sig) {
printf("printed from child [%d]\n", getpid());
printf("signal [%d]\n", sig);
}
【问题讨论】:
-
代码中存在竞争条件,因为父进程可以在子进程设置信号处理程序之前发送 SIGUSR1 信号。这可能导致没有、部分或所有子进程被信号终止而不是捕获它。
-
谢谢@IanAbbott,我需要睡觉来腾出时间吗?
-
睡眠可能会起作用,但不能保证足够。
-
您可以简单地在
fork()循环之前调用signal()处理程序将被继承。并回答最初的问题:在pause()之后调用execl(),而不是从信号处理程序内部 -
@IngoLeonhardt 信号传递和
pause()之间是否仍然存在竞争条件?它可能需要在fork()之前用sigprocmask阻止信号,并且孩子用sigsuspend()替换pause()调用。不过我还没有真正深入了解细节。