2017-10-14 106 views
2

我從CMU過去的考試中發現了這個問題,我無法得到輸出是如何可能的。孩子和父母信號之間的併發競爭

基本上,它背後的想法是,有一個父進程阻止用戶定義的信號,然後父母分岔一個孩子。並且基於哪個進程首先運行(又名:贏得比賽),則可能有不同的輸出。 Here is the question that is being asked in the exam(請閱讀)

,這裏是從考試代碼:

int i = 1; 
void handler (int sig) { 
    i++; 
} 
int main() { 
    pid_t pid; 
    sigset_t s; 
    sigemptyset(&s); 
    sigaddset(&s, SIGUSR1); 
    signal(SIGUSR1, handler); 
    sigprocmask(SIG_BLOCK, &s, 0); 
    pid = fork(); 
     <LINE A> 
    if (pid != 0) { 
     i = 2; 
     <LINE B> 
    } else { 
     i = 3; 
     <LINE C> 
    } 
    sigprocmask(SIG_UNBLOCK, &s, 0); 
    pause(); /* pause to allow all signals to arrive */ 
    printf("%d\n", i); 
    exit(0); 
} 

有3種情況需要進行測試,因爲我們需要把功能:

kill(pid,USRSIG1); 

在LINE A或LINE B或LINE C中找到可能的輸出。

現在,這裏是我做的,我把功能LINE A.

比方說,我們運行程序,那麼家長會創建一個空的集合S,信號SIGSUR1添加到它,那麼它將爲SIGUSR1信號分配一個自定義處理程序,並阻止該集合中的信號。這是這些線路

sigset_t s; 
    sigemptyset(&s); 
    sigaddset(&s, SIGUSR1); 
    signal(SIGUSR1, handler); 
    sigprocmask(SIG_BLOCK, &s, 0); 

然後上級將運行線

pid = fork(); 

將從過程創建一個新的孩子。

現在有兩種情況將確定輸出。操作系統安排父母或孩子首先運行。

比方說,父母先跑。然後它將執行LINE A(這是kill函數)

並且由於它是父級,所以pid值將是子級的進程ID。所以它會發送USRSIG1給孩子,但是由於它被阻塞,它什麼都不會做

if語句爲全局變量i分配一個值。如果這個過程是父母的話,我= 2,否則的話,我= 3,所以在我們的父進程中,我們將有I = 2

if (pid != 0) { //if i am a parent then i = 2 
     i = 2; 
     <LINE B> 
    } else { //if i am a child then i = 3 
     i = 3; 
     <LINE C> 
    } 

下一行會在父被執行,它會解鎖在SIGUSR1信號 sigprocmask(SIG_UNBLOCK, &s, 0); 與父進程將暫停,直到它接收到的信號

現在孩子會跑,它會包括自己的過程組中的殺(0,SIGUSR1)信號發送給所有的進程。但是由於它在孩子身上受到阻礙,什麼都不會發生。父母會收到信號,它會使我增加1(所以現在我在父母中= 3)。並且它將從函數暫停中恢復,以打印I(它是3)的值並退出。

孩子現在從kill函數恢復,因爲它是一個孩子,if語句不會是真的(所以我的孩子的值= 3)。孩子解除設置和暫停()的信號。

由於沒有其他進程向小孩發送信號,它將永遠保持暫停狀態,並且僅由父級輸出3。如果我們按照其他方式(孩子在父母之前跑步),那麼輸出將僅爲4。

什麼令我困惑的是,考試的解決方案說每次運行有2個輸出?我不明白這是怎麼可能的,因爲其中一個進程將停留在()。

該解決方案的關鍵認爲,對於A線的可能的輸出是:

3 4, 4 3, 3 5, or 5 3 

這是所有我可以從問題的理解。任何幫助或暗示將不勝感激。

回答

0

如果孩子先跑步,輸出將是5,因爲它會接收來自自身和父母的信號。如果兩個進程在輸入pause()之前完成kill(pid,USRSIG1),則兩個進程都不會終止或打印。

POSIX還允許兩個進程終止並打印由於到延遲信號(例如,如果網絡消息來代替共享存儲器)和爲孩子打印任何int值作爲ivolatile sig_atomic_t類型不是。

從評論中可以看出,考試作者錯誤地認爲pause()會奇蹟般地等待,直到收到所有發送或將發送給過程的信號。

如果sigprocmask(SIG_UNBLOCK, &s, 0); pause();被替換爲sigsuspend的適當調用,它將充當考試作者狀態。父母會收到1個信號,孩子會收到1或2個信號,因爲來自父母的信號可能太遲或與自己的信號結合在一起。