2017-05-06 125 views
0

我目前正在學習C語言的多線程,但有一點我不太明白,我們的命名管道excersize。
我們期望做一個文件搜索系統的實現,該文件搜索系統可以找到文件並用一個進程添加到緩衝區,第二個進程應該從第一個進程的線程獲取文件名,在該文件中找到搜索查詢並返回第一個位置通過管道加工。我做了幾乎所有的事情,但我很困惑如何做兩個進程之間的溝通。C fifo保持阻塞狀態

這裏是我的代碼,做通信:
的main.c

void *controller_thread(void *arg) { 
    pthread_mutex_lock(&index_mutex); 
    int index = t_index++; /*Get an index to thread*/ 
    pthread_mutex_unlock(&index_mutex); 
    char sendPipe[10]; 
    char recvPipe[10]; 
    int fdsend, fdrecv; 
    sprintf(sendPipe, "contrl%d", (index+1)); 
    sprintf(recvPipe, "minion%d", (index+1)); 
    mkfifo(sendPipe, 0666); 
    execlp("minion", "minion", sendPipe, recvPipe, (char*) NULL); 
    if((fdsend = open(sendPipe, O_WRONLY|O_CREAT)) < 0) 
     perror("Error opening pipe"); 
    if((fdrecv = open(recvPipe, O_RDONLY)) < 0) 
     perror("Error opening pipe"); 
    while(1) { 
     char *fileName = pop(); /*Counting semaphore from buffer*/ 
     if(notFile(fileName)) 
      break; 
     write(fdsend, fileName, strlen(fileName)); 
     write(fdsend, search, strlen(search)); 
     char place[10]; 
     while(1) { 
      read(fdrecv, place, 10); 
      if(notPlace(place)) /*Only checks if all numeric*/ 
       break; 
      printf("Minion %d searching %s in %s, found at %s\n", index, 
        search, fileName, place); 
     } 
    } 
} 

從我發現網上的資源,我認爲這是處理的主要內部FIFO中的方式。我試着寫一個測試奴才只是爲了確保它的工作原理,所以這裏是

minion.c

int main(int argc, char **argv) { 
    char *recvPipe = argv[1]; 
    char *sendPipe = argv[2]; 
    char fileName[100]; 
    int fdsend, fdrecv; 
    return 0; 
    fdrecv = open(recvPipe, O_RDONLY); 
    mkfifo(sendPipe, 0666); 
    fdsend = open(sendPipe, O_WRONLY|O_CREAT); 
    while(1) { 
     read(fdrecv, fileName, 100); 
     write(fdsend, "12345", 6); 
     write(fds, "xxx", 4); 
    } 
    return 0; 
} 

當我以這種方式運行,線程被阻塞和印刷品沒有任何反應,如果我改爲O_NONBLOCK爲打開模式。然後打印「錯誤打開管道沒有這樣的設備或地址」的錯誤,所以我知道,不知怎的,我無法打開裏面僕從的recvPipe,但我不知道什麼是錯誤

回答

1

您的代碼存在的問題之一是對execlp()的使用的明顯誤解。一旦成功,這個函數不會返回,所以它後面的代碼將永遠不會被執行。通常先執行fork(),然後在子進程中執行execlp(),如果execlp()失敗,肯定會使孩子終止。父進程可能還需要最終等待分叉子進程。

此外,每個進程在嘗試打開FIFO的寫入結束時都會通過O_CREAT標誌,這很奇怪,而且可能不合需要。這應該是沒有必要的,因爲每個人都已經創建了與mkfifo()的FIFO。即使在mkfifo()失敗或其他進程在可以打開之前將其刪除的情況下,也不希望以O_CREAT打開,因爲這會爲您提供常規文件而不是FIFO。

修復execlp()問題後,您也會遇到競爭狀況。父進程依賴於孩子創建一個FIFO,但不等待該進程這樣做。如果父母在孩子完成其mkfifo()之前達到其打開的嘗試,您將無法獲得所需的行爲。

我建議讓父母創建這兩個 FIFO,然後創建子進程。孩子和父母必須合作打開一個FIFO的兩端,然後再打開另一端的兩端。開放閱讀將阻止直到另一個打開相同的FIFO用於書寫。

或者您可以使用普通(匿名)管道(請參閱pipe())而不是FIFO。這些創建在兩端都是開放的,並且它們更適合於通過繼承相關的進程之間的通信。

無論如何,一定要檢查函數調用的返回值。幾乎所有這些函數都可能失敗,並且檢測並處理該前沿比清理當您錯誤地假設每次調用成功時可能形成的糾結要好得多。

+0

感謝您的詳細解答,讓我明白exec的問題部分。所以對於匿名管道部分,我可以通過exec參數傳遞一個管道。我不認爲這是可能的,因爲所有參數都轉換爲char * – BrokenFrog

0

的FIFO需要在打開時有些同步。默認情況下,open(s)是阻塞的,所以讀取的開放會被阻塞,直到其他讀者打開相同的fifo進行讀取,並且相反(這使對等體進行同步以進行通信)。您可以使用O_NONBLOCK打開閱讀,而沒有實際的開放式對等體,但反過來是錯誤的,因爲在沒有閱讀對等體的情況下打開以進行書寫會導致錯誤(在沒有閱讀器的情況下允許進程嘗試寫入如無意義)。

例如,您可以閱讀Linux Fifo manual entry

+0

我不這麼認爲,我正確理解你的答案,所以你建議我做'fdrecv = open(recvPipe,O_RDONLY | O_NONBLOCK);'minion.c? – BrokenFrog