2010-04-29 112 views
1

所以我有一個程序創建一個子進程並執行一個命令(例如ls)。父母然後將使用管道發送和從孩子獲得。當我從命令行自己輸入命令時,這工作正常。C IPC等待孩子

但是,當輸入來自文件時,看起來孩子沒有足夠的時間運行,並且從管道讀取時我得到NULL - 即使會有信息來自它。

使用睡眠()的缺點,有沒有更好的方法來確保孩子在嘗試讀取它之前已經運行?

非常感謝!

回答

2

如果沒有設置你管文件描述符非阻塞的,那麼就應該沒有問題。任何對管道的讀取都會阻塞,直到孩子產生輸出;如果需要響應多個文件描述符(例如,來自用戶的標準輸入和來自子進程的管道),請使用select()poll()

我認爲它是fgets()返回NULL。這表示文件結束(意味着孩子已經關閉了管道的末端),或者是錯誤。您可以使用feof()ferror()來檢查其中哪些爲真。在錯誤情況下使用perror()來查看錯誤實際是什麼。

+0

應該提到文件描述符是非阻塞的。我這樣做是因爲當沒有新數據時,從fgets()中讀取數據被阻塞,導致程序永遠等待。 – Gary 2010-04-30 00:15:26

+0

@Gary:在這種情況下,使用'select()'將文件描述符添加到您正在監視的文件描述符集中 - 例如,如果您想等待來自用戶或來自管道的輸入,請使用'select )'來監視stdin和管道。 – caf 2010-04-30 00:20:39

+0

在這種情況下,我只想從一個文件描述符輸入而不是標準輸入,所以我不認爲我需要選擇?基本上我只是想給孩子足夠的時間來運行它的命令,如果它仍然是NULL,就把它當作錯誤來處理。 – Gary 2010-04-30 01:47:56

1

問題是你對孩子有多少控制?如果你自己寫孩子,那麼你可以讓孩子在共享內存中設置一個標誌,它已經開始(或觸摸一個文件或任何你喜歡的癢癢),然後只有當你知道孩子在線運行它。

如果您對孩子沒有控制權,仍然可以通過用自己的腳本包裝孩子來實現上述解決方案,該腳本首先啓動孩子,然後設置共享內存,這可以採用一個shell腳本如下

#!/bin/sh 

$CHILD 
my_app_that_set_the_flag 

最後另一種選擇是繼續等待aslong當你從管空,顯然這會導致一個無限循環,但如果你不能garuntee,你總是會得到的東西管道

1

您的父線程需要等到孩子準備好再嘗試重新廣告來自管道。有幾種方法可以做到這一點。

首先,您可以使用條件變量。聲明兩個線程都可以訪問的變量並將其設置爲0。準備父母閱讀時,您的子線程會將其設置爲1。父母將等待,直到變量變爲1(使用諸如while(!condvar) sleep(1);之類的東西),然後它將從管道讀取並將變量重置爲0,以便孩子知道父母完成。

另一種選擇是使用進程間通信的一種形式,如signals。與條件變量方法類似,子線程將執行其工作,然後在完成時向父線程發送信號。父線程將等待,直到它從管道讀取信號之前接收到信號,然後它可以發回一個信號給孩子,表明它已完成。

編輯:既然你產卵子進程與fork,而不是與線程,您不能使用條件變量在這裏(父母和孩子都會有它獨立的副本)。

相反,您可以使用signal()kill()函數在進程之間發送信號。在分叉之前,使用getpid來存儲父級PID的副本(用於子進程)。同時存儲返回值fork,因爲它將包含孩子的pid。

發送信號到其它進程,使用類似:

kill(parent_pid, SIGUSR1); 

接收過程需要建立一個信號處理程序。例如:

int signal_received = 0; 
void signal_handler(int signal_num) { 
    if (signal_num == SIGUSR1) 
     signal_received = 1; 
} 

signal(SIGUSR1, signal_handler); 

功能signal_handler現在將被自動調用每當過程接收信號編號SIGUSR1。你的線程會等待在一個循環中,看着這個變量使用類似的改變:

while (1) { // Signal processing loop 
    // Wait here for a signal to come in 
    while (!signal_received) { sleep(1); } 

    // Wake up and do something 
    read_from_pipe(); 
    ... 
    signal_received = 0; 
} 
+0

您可以簡單地使用'select()'等待管道可讀。 – caf 2010-04-29 23:53:06

+0

@caf:是的,但只是告訴你有什麼需要閱讀的東西。 select()'告訴你,你可以並不意味着你所有的數據都在那裏。許多時候,等待所有內容都準備好被讀取比讀取內容時更容易,然後嘗試將它重新組合在一起(特別是如果數據本身不能告訴你在哪裏結束是)。 – bta 2010-04-29 23:59:30

+0

這似乎是最簡單的解決方案,所以我嘗試了它 - 但無法讓它工作。這個變量是在我做了fork之後被聲明的,並且在孩子中發生了變化,但是父母似乎根本沒有看到變化。這可能與我在兒童中使用exec有關嗎? – Gary 2010-04-30 01:51:51