2012-11-11 39 views
3

我想不出有什麼辦法可以在c中實現流水線操作。這就是爲什麼我決定寫在這裏。我必須說,我知道pipe/fork/mkfifo是如何工作的。我已經看到了很多實施2-3條管道的例子。這很容易。我的問題開始了,當我需要實現shell時,管道數量還未知。在c中實現流水線。什麼是最好的方式來做到這一點? (自己的linux外殼)

我現在得到了什麼: eg。

ls -al | tr a-z A-Z | tr A-Z a-z | tr a-z A-Z 

我的變換,例如行成這樣的事情:

array[0] = {"ls", "-al", NULL"} 
array[1] = {"tr", "a-z", "A-Z", NULL"} 
array[2] = {"tr", "A-Z", "a-z", NULL"} 
array[3] = {"tr", "a-z", "A-Z", NULL"} 

所以我可以在以後使用

execvp(array[0],array) 

現在,我相信一切都好。問題開始,當我試圖將這些函數的輸入/輸出重定向到彼此。

以下是我正在做的是:

mkfifo("queue", 0777); 

    for (i = 0; i<= pipelines_count; i++) // eg. if there's 3 pipelines, there's 4 functions to execvp 
    { 
    int b = fork();    
    if (b == 0) // child 
     {   
     int c = fork(); 

     if (c == 0) 
     // baby (younger than child) 
     // I use c process, to unblock desc_read and desc_writ for b process only 
     // nothing executes in here 
      {  
      if (i == 0) // 1st pipeline 
       { 
       int desc_read = open("queue", O_RDONLY); 
       // dup2 here, so after closing there's still something that can read from 
       // from desc_read 
       dup2(desc_read, 0); 
       close(desc_read);   
       } 

      if (i == pipelines_count) // last pipeline 
       { 
       int desc_write = open("queue", O_WRONLY); 
       dup2(desc_write, 0); 
       close(desc_write);        
       } 

      if (i > 0 && i < pipelines_count) // pipeline somewhere inside 
       { 
       int desc_read = open("queue", O_RDONLY); 
       int desc_write = open("queue", O_WRONLY); 
       dup2(desc_write, 1); 
       dup2(desc_read, 0); 
       close(desc_write); 
       close(desc_read); 
       }    
      exit(0); // closing every connection between process c and pipeline    
      } 
     else 
     // b process here 
     // in b process, i execvp commands 
     {      
     if (i == 0) // 1st pipeline (changing stdout only) 
      { 
      int desc_write = open("queue", O_WRONLY);    
      dup2(desc_write, 1); // changing stdout -> pdesc[1] 
      close(desc_write);     
      } 

     if (i == pipelines_count) // last pipeline (changing stdin only) 
      { 
      int desc_read = open("queue", O_RDONLY);          
      dup2(desc_read, 0); // changing stdin -> pdesc[0] 
      close(desc_read);   
      } 

     if (i > 0 && i < pipelines_count) // pipeline somewhere inside 
      {    
      int desc_write = open("queue", O_WRONLY);  
      dup2(desc_write, 1); // changing stdout -> pdesc[1] 
      int desc_read = open("queue", O_RDONLY);        
      dup2(desc_read, 0); // changing stdin -> pdesc[0] 
      close(desc_write); 
      close(desc_read);        
      } 

     wait(NULL); // it wait's until, process c is death      
     execvp(array[0],array);   
     } 
     } 
    else // parent (waits for 1 sub command to be finished) 
     {  
     wait(NULL); 
     }  
    } 

感謝。

+2

現在每個人都在寫自己的外殼嗎?本週有關「我自己的外殼」的第三個問題。 – 2012-11-11 14:32:28

+1

@ H2CO3這可能與操作系統類別分配有關。至少,在我的第一個操作系統類中,我必須編寫一個shell。 – mah

回答

5

Patryk,你爲什麼要使用fifo,而且每個階段的流水線都使用相同的fifo?

在我看來,你需要在每個階段之間的管道。因此,流動會是這樣的:

Shell    ls    tr    tr 
-----    ----    ----    ---- 
pipe(fds); 
fork(); 
close(fds[0]); close(fds[1]); 
        dup2(fds[0],0); 
        pipe(fds); 
        fork();   
        close(fds[0]); close(fds[1]); 
        dup2(fds[1],1); dup2(fds[0],0); 
        exex(...);  pipe(fds); 
            fork();  
            close(fds[0]);  etc 
            dup2(fds[1],1); 
            exex(...); 

,在每個分叉殼(接近,DUP2,管道等)上運行的程序似乎是一個函數(取姓名和所需的過程的參數)。請注意,直到調用exec爲止,shell的分叉副本正在運行。

編輯:

Patryk:

Also, is my thinking correct? Shall it work like that? (pseudocode): 
start_fork(ls) -> end_fork(ls) -> start_fork(tr) -> end_fork(tr) -> 
start_fork(tr) -> end_fork(tr) 

我不知道你所說的start_fork和end_fork的意思。你是否暗示在tr開始前ls運行完畢?這並不是真正的上圖。在開始tr之前,您的shell不會等待ls完成。它開始的所有過程的按順序配管,爲每一個,以使工藝連接在一起,的lsstdoutstdintr的設置stdinstdout; stdouttrstdin的下一個tr。這就是dup2電話正在做的事情。

進程運行的順序由操作系統(調度程序)決定,但很明顯,如果tr運行並從空的stdin中讀取,它必須等待(阻塞),直到前面的過程將某些內容寫入管道。 ls很有可能在tr甚至從stdin讀取之前運行完畢,但它也可能不會。例如,如果鏈中的第一個命令是連續運行並沿途產生輸出的東西,則管道中的第二個命令將不時調度,以管理第一個沿管道發送的內容。

。希望澄清事情有點:-)

+1

不要管道(fds)覆蓋已經在int fds [2]中的描述符?我會不會因此而失去我的數據?另外,我的想法是否正確?它會像那樣工作嗎? (僞代碼):start_fork(ls) - > end_fork(ls) - > start_fork(tr) - > end_fork(tr) - > start_fork(tr) - > end_fork(tr) – Patryk

+2

不,因爲您已經將dup2描述符 –

+1

它可以這樣工作嗎? (tr) - > start_fork(tr) - > end_fork(tr) – Patryk

1

問題是你正試圖一次做所有事情。將其分解爲更小的步驟。

1)解析您的輸入以得到ls -al |。 1a)從這裏你知道你需要創建一個管道,將其移動到標準輸出,並開始ls -al。然後將管道移至標準輸入。當然還有更多,但你不用擔心代碼問題。

2)解析下一個段得到tr a-z A-Z |。回到步驟1a,只要你的下一個產卵命令的輸出被傳送到某處。

+0

這正是我正在做的。我一次迭代執行1個函數(並將stdin輸入更改爲pdesc [0]和stdout輸入pdesc [1]) – Patryk

+2

這絕對不是你正在做的事情。在你的代碼中,你已經知道有多少管道 - 「for(i = 0; i <= pipelines_count; i ++)'。另外,你只做一個單獨的隊列,並且有一個命名隊列 - 這兩個都不合適。使用'pipe()'創建一個未命名的隊列,併爲每個命令創建一個新隊列。最後 - 在fork()返回非0的情況下,不應該調用wait()。相反,你應該首先讓你的管道的所有進程都運行,只有當你沒有更多的啓動時,你應該呼叫等待。否則,你會打敗管道的目的。 – mah

2

這可能是值得使用libpipeline。它負責您所有的工作,甚至可以在您的管道中包含功能。

+0

如果這只不是作業:P – Patryk

相關問題