2017-10-11 89 views
1

基本上我有兩個代碼。 其中之一必須在shell中打印「TIME!」每次它收到一個SIGUSR1信號。 我們稱之爲exercici.cC,fork和exec進程,併發送信號

#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(); 
    } 

} 

另一種,必須創建三個子進程(只有一個父和三個孩子的)和execlp exercici.c上的每個孩子。然後,父母每秒都必須向一個孩子發送一個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個進程,我只接收時間!其中2個。它跳過一個pid [0]。

shell image

+0

你可以在'fork'(測試fork()== -1')中測試錯誤,並使用'perror'打印出任何錯誤嗎? (小貼士:考慮一個switch語句,其中'default'是父進程)...哦。並測試'execlp',而你在...;-) – Myst

+0

另外,在某些時候用'SIGINT'殺死孩子不是一個好主意嗎? – Myst

回答

0

問題是由於發生 -

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

在這裏,您初始化爲j 0,並在while循環kill()中發送SIGUSR1到第一次迭代中的pid [0]。

每個信號都有一個「默認處置」 - 當它接收到該信號時默認處理默認處理。

signal(7)手冊頁 -

SIGUSR1和SIGUSR2的默認配置是 - 期限,這就意味着終止進程。

信號值動作註釋

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

SIGUSR1 30,10,16術語用戶定義信號1個

SIGUSR2 31,12,17術語用戶定義信號2

在你exercici兒童節目,你正在改變的信號SIGUSR1的配置和配置設置來處理函數captura_signal -

signal(SIGUSR1, captura_signal) 

在你的情況,什麼情況是,與子進程PID [0]分叉時和在子進程exec執行並設置SIGUSR1信號處理程序之前,父進程將SIGUSR1發送給pid [0]。由於SIGUSR1的默認處置是終止進程,因此pid [0]子進程將終止並標記爲已禁用。

在我的系統,我可以在ps命令的輸出中看到 -

PS -eaf | grep的exercici

根29210 19542 86 20點51分/ 5零點00分04秒./exercici2 -------->父進程

根29211 29210 0 20 :51點/ 5 00:00:00 [exercici2] <被禁>------>子進程exec'ing exercici之前接收到的信號SIGUSR1

根29212 29210 0 20點51分/ 5 00:00:00練習----- --->子過程成功exec'ed exercici和改變SIGUSR1的默認配置

根29213 29210 0 20點51分/ 10 00:00:00 exercici -------->子進程成功exec'ed exercici和SIGUSR1

在這裏你可以看到PID 29211的第一個孩子的過程被標記爲解散的更改的默認配置。

你可以通過各種方式解決這個問題。其中之一是簡單地通過增加這種說法忽略SIGUSR1在父進程 - 在分叉子進程的父進程的開始

signal(SIGUSR1,SIG_IGN); 

地方。

子進程從父進程繼承信號處理程序。因此,在使用新進程(exercici)執行當前進程映像之前,如果子進程接收到SIGUSR1,它將忽略它。

+0

我見過的最詳細的答案。非常感謝。 –

0

這是有點棘手理解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); 
    } 
    } 

}