【问题标题】:Make children process in pause until receiving parent's signal to execute an execl task让子进程暂停,直到收到父进程的信号以执行 execl 任务
【发布时间】:2018-08-06 12:26:10
【问题描述】:

我正在尝试按照问题Make children process wait until receiving parent's signal的答案开发一个简单的铁路模拟。

我的任务:我正好有 5 个进程代表火车。我需要通过fork() 创建这 5 个进程(T1、T2、T3、T4 和 T5),并暂停每个进程,直到所有进程都创建完毕。之后,父进程将向子进程发送信号,每个子进程将使用execlexecl(execl_path_name, CHILDETCONE, i, NULL);)。发出信号后,父母等待所有孩子完成他们的任务。

handler函数我挺了解的,但是这几点我不是很清楚:

  1. 我需要在处理函数中插入我的execl 吗?

  2. 从上一个问题的答案看不懂这最后一个循环的意义:

    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() 调用。不过我还没有真正深入了解细节。

标签: c signals fork execl


【解决方案1】:

我需要在处理函数中插入我的execl 吗?

没有。 pause() 仅在调用它的进程捕获导致信号处理函数运行的信​​号后才会返回。因此,execl 调用可以在pause 调用之后进行。我认为你的情况会更清楚。

还要注意,POSIX 标准化了“异步信号安全”函数列表,这些函数对于信号处理程序调用是安全的,并且对于处理程序调用其他处理程序是安全的. execl 在列表中,但 printf 和其他流 I/O 函数没有。信号处理程序不应调用printf。您的特定信号处理程序根本不需要做任何事情。

此外,考虑使用sigsuspend() 代替pause(),因为前者可以让您更好地控制哪些信号会导致您的火车启动。

我不明白答案中最后一个循环的重要性 上一个问题:

for (int i = 0; i < NUMBER_TRAINS; i++)
       {
           wait(NULL);
       }

wait() 函数指示调用进程阻塞,直到其子进程之一终止。循环发出与子级一样多的wait() 调用,因此,在没有错误的情况下,主程序不会继续,直到其所有子级都终止。

看起来您可能已经尝试通过在循环中调用sleep() 来实现类似的目标kill 调用,但这种策略显然是错误的。首先, 在每个kill 之后等待意味着孩子们的execl 呼叫将至少间隔sleep 时间,这不是我理解你想要的。其次,您无法提前知道孩子们需要多长时间才能完成,因此在某些情况下,您允许的一秒钟可能还不够。第三,既然你似乎期望孩子们跑得很快,那么一秒钟可能比你大部分时间需要的多得多。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-12-29
    • 2015-01-13
    • 1970-01-01
    • 2011-10-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多