C、fork和exec进程,并发送信号

C, Fork and exec process, and sending signals

基本上我有两个代码。 其中之一必须在每次收到 SIGUSR1 信号时打印 shell "TIME!"。 我们称它为 exercici.c

#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <signal.h>

char buffer[1000];

void captura_signal(int signum){

    if(signum == SIGUSR1){
        sprintf(buffer,"TIME! %d\n",getpid());
        write(1,buffer,strlen(buffer));
    }
}


int main(int argc, char *argv[]){
    signal(SIGUSR1, captura_signal);
    while(1){
        pause();
    }

}

另一个,必须创建三个 child 进程(只有一个 parent 和三个 child)并在每个 child 上执行 execlp exercici.c。 然后,每一秒,parent 必须向一个 child 发送一个 SIGUSR1,每秒递增。我称之为 exercici2.c

#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <stdbool.h>
#include <signal.h>


bool ready = 1;

void  sigAlarm(int sig){
    if (sig == SIGALRM){
        ready = 1;
    }
}


int main(int argc, char *argv[]){
    int pid[3];
    signal(SIGALRM,sigAlarm);
    for(int i = 0; i<3;i++){
        if((pid[i]=fork()) == 0){
            execlp("./exercici","exercici",(char*)NULL);
        }
    }
    int j =0;
    while(1){
        if(ready){
            ready = 0;
            kill(pid[j],SIGUSR1);
            j = ((j+1)%3);
            alarm(1);
        }
    }
}

问题是,当我执行 exercici2.c 时,它只创建了 2 个进程,而我只收到 TIME!其中 2 个。它跳过 pid[0] 一个。

shell image

理解 fork() 和子进程的创建以及子进程将执行的代码部分有点棘手。在这里,您从每个子进程调用 while(1) 循环,该部分代码不在主进程中执行,而是仅在子进程中执行。因此,‘j’始终为 0,您实际上正在执行 kill(pid[0],SIGUSR1); ( 3次)。

从父进程控制子进程总是更好。请参阅下面适合您的修改后的代码。

int parentPID = 0;

int main(int argc, char *argv[]){
 int pid[3];
 parentPID = getpid();
 signal(SIGALRM,sigAlarm);
 for(int i = 0; i<3;i++){
    if((pid[i]=fork()) == 0){
        printf("child :%d\n",i);
        execlp("./exercici","exercici",(char*)NULL);
    }
 }
 if (parentPID == getpid()){
     sleep(5);/* Let the 3 childs create */
     int j = 0;
     int ready = 1;
     while(ready){
       kill(pid[j],SIGUSR1);
       j = ((j+1)%3);
       ready = j;
       alarm(1);
     }
    }

}

问题的发生是因为-

int j =0;
while(1){
    if(ready){
        ready = 0;
        kill(pid[j],SIGUSR1);
        j = ((j+1)%3);
        alarm(1);
    }
}

在父进程中。

在这里你用 0 初始化 j 并且在 while 循环 kill() 中发送 SIGUSR1 到 pid[0] 在第一次迭代中。

每个信号都有一个 "default disposition" -- 进程在收到该信号时默认执行的操作。

来自 signal(7) 手册页 -

SIGUSR1 和 SIGUSR2 默认配置是 - Term,这意味着终止进程。

Signal Value Action Comment

───────────────────────────────────────────

SIGUSR1 30,10,16 Term User-defined signal 1

SIGUSR2 31,12,17 Term User-defined signal 2

在您的 exercici 子程序中,您正在更改信号 SIGUSR1 的配置并将配置设置为处理函数 captura_signal -

 signal(SIGUSR1, captura_signal)

在你的例子中,发生的事情是当分叉时具有 pid[0] 的子进程在子进程执行并设置 SIGUSR1 信号处理程序之前,父进程将 SIGUSR1 发送到 pid[0]。由于 SIGUSR1 的默认配置是终止进程,因此 pid[0] 子进程被终止并标记为 defunct。

在我的系统上,我可以在 ps 命令的输出中看到 -

ps-eaf | grep 练习

root 29210 19542 86 20:51 pts/5 00:00:04 ./exercici2 -------->父进程

root 29211 29210 0 20:51 pts/5 00:00:00 [exercici2] ------> 收到子进程在执行 execcici

之前发出信号 SIGUSR1

root 29212 29210 0 20:51 pts/5 00:00:00 exercici --------> 子进程成功执行练习并更改 SIGUSR1

的默认配置

root 29213 29210 0 20:51 pts/5 00:00:00 exercici --------> 子进程成功执行练习并更改 SIGUSR1

的默认配置

在这里你可以看到 pid 为 29211 的第一个子进程被标记为 defunct.

您可以通过多种方式解决这个问题。其中之一是通过添加此语句 -

来简单地忽略父进程中的 SIGUSR1
signal(SIGUSR1,SIG_IGN);

在父进程开始分叉子进程的某处。

子进程从父进程继承信号处理程序。因此,在使用新进程 (execcici) 执行当前进程映像之前,如果子进程收到 SIGUSR1,它将忽略它。