2012-02-15 40 views
1

我正在學習如何使用fork創建進程,而我在以下方面感到困惑。這是代碼:這個程序中有兩個&符號是做什麼的?

int main() { 
    int ret = fork(); 
    // printf("%d\n", ret); 
    ret = ret && fork(); /* Here is where I am confused*/ 
    // print("ret: %d\n", ret); 
    if(ret == 0) { 
     fork(); 
    } 
    printf("Hello world\n"); 
    return 1; 
} 

那麼,雙和號的用法到底在做什麼呢? 我用一個「printf」運行程序,知道確切值是什麼,但由於第一個「printf」中的輸出爲0,第二個「printf」爲「1」,所以變得更加混亂。所以我不太確定什麼是雙重&符號。

我很感激幫助!

+0

它是一個布爾操作符... http://en.wikipedia.org/wiki/Boolean_algebra#Basic_operations另外,它非常有趣,爲什麼最後有一個'return 1'。這是無稽之談。 – Gandaro 2012-02-15 18:49:28

+4

你需要的是一個涵蓋了C的基本知識的教程。 – dandan78 2012-02-15 18:49:33

+1

草率代碼確實令人困惑 – 2012-02-15 18:57:03

回答

5

這就是所謂的邏輯與操作。這將評估兩個操作數的邏輯與操作結果。該運算符的屬性如下:

首先評估左側操作數,如果它爲TRUE(非零),則評估右側操作數。如果它也是真的,那麼整個表達式就是真的,否則就是假。另一方面,如果左邊的操作數是FALSE,那麼右邊的操作數是根本沒有評估。這可以這樣做,因爲一個操作數是假的,無論是另一個操作數,表達式都會變成假。這被稱爲short circuiting

在你的代碼,如果左手,如果ret是真實的,那麼只有右側部分進行評估,最終調用fork()系統調用。呼叫的返回值與當前值ret進行與運算,並重新分配到ret

基本上它的工作原理是

if (ret == TRUE) 
{ 
    retval = fork(); 
    ret = ret && retval; 
} 

閱讀:

成功時,子進程的PID在父回來,在孩子則返回0。失敗時,在父項中返回-1,不創建子進程,並且適當地設置errno。

考慮下面的分叉樹。每個樹「節點」顯示每個單獨語句中已執行語句的順序。一行一行。

(p1) 
+--+ret = fork(); 
| printf 1 shows pid 
| && allows   fork(), ret = 1 = pid1 && pid2 
| printf 2 shows 1 + 
| `if' not entered | 
| show hello   | 
|      | (p3) 
|      +--+ ret = 0 = ret && fork() (this is 0 here) 
+-----+      printf 2 shows 0 
     |      `if' is entered 
     |      fork() 
     |      show hello 
     |       + 
     |       | 
     +       | 
    (p2)       | 
    level 1      +-------+ 
    print 0 in 1st printf     | 
    && DOES NOT allow fork()   (p5) 
    print 0 in 2st printf    show hello 
    `if' entered 
    fork() +-----------+ 
    show hello   | 
         | 
         + 
         (p4) 
        show hello 

這裏在每個過程中發生了什麼。

P1 執行fork()一次,並且在RET一個PID(非零)。打印pid 短路允許執行fork()。由於這是父項,它將返回另一個pid,該id與之前的子項pid匹配,評估值爲1.因此,ret現在包含1,該值將打印在第二個printf中。因爲ret爲1,因此if未執行。你好打印。

p2 p1的孩子,所以ret有0.在第一個printf中打印0。短路不允許fork()呼叫。 if正文被輸入,並調用fork(),這使得(p4)。現在(p2)繼續打印Hello。

P3 兒童P1,所以fork()收益爲0,這是相與RET,並使其0分配之後。這是第一次printf後產生的,所以只有第二次printf顯示0. if被輸入,fork()被執行,這使得(p5)。現在p4繼續打印Hello。

P4從if 開始,失控和打印Hello

P5從if 開始,失控和印刷品你好

上面我試圖表達的過程中產卵樹,並且每個進程中的工作順序在樹上的進程「節點」的每一行中表示。邊緣表示產卵,邊緣從相應的叉子開始。

+0

謝謝!這澄清了很多! – Oscarin 2012-02-15 20:13:33

+0

注意在每個級別打印什麼。 – phoxis 2012-02-15 20:19:33

2

這是一個合乎邏輯的AND,這意味着如果ret爲真(非零),以及fork()結果爲真(非零)分配真實ret,否則分配假(零)ret

由於此運算符是short-cirucuited,fork()只有在ret爲真時纔會被調用。

+0

然後解釋爲什麼「第一個printf中的輸出是0,第二個printf是1」 – BlackBear 2012-02-15 18:52:21

+0

@BlackBear - 我將在一個小時內趕上/ – MByD 2012-02-15 18:53:56

+0

重要的是要注意它真正要做的是因爲「成功完成後,fork()應將0返回給子進程,並將子進程的進程ID返回給父進程」。 – BeRecursive 2012-02-15 18:54:11

0

這是一個懶惰的邏輯AND函數。如果您需要更多信息,請搜索AND的真值表。這很懶,因爲如果ret爲假,fork()將不會被評估,因爲任何與false進行的操作總是false

6

在C中,雙與號&&是短路logical AND的操作。如果你有類似於a && b的東西,那麼這將評估如下:如果ab都爲真,則它將返回真,但如果a是假,那麼將永遠不會執行b

作爲一個額外的example

int a = 0; 
if (a && myfunc(b)) { 
    do_something(); 
} 

在本例中,短路評價保證MYFUNC(B)不會被調用。這是因爲評估是錯誤的。該功能允許兩個有用的編程結構。首先,如果第一個子表達式檢查是否需要昂貴的計算並且檢查結果爲假,那麼可以在第二個參數中消除昂貴的計算。其次,它允許一個構造,其中第一個表達式保證條件不成立,第二個表達式可能會導致運行時錯誤。

因此,如果ret爲真,那麼您的代碼只能調用fork()。然後將ret分配爲0(ret爲0)或1(retfork()均爲真)。

+0

謝謝,我感謝幫助!現在它是有道理的。這是一個愚蠢的問題,但是在我展示給你之後我沒有看到答案。再次,謝謝! – Oscarin 2012-02-15 19:35:07

0

這是一個短路邏輯AND。如果ret爲0,則它​​將執行fork()。如果沒有,它不會。我可以爲你閱讀代碼。

//we fork a child process 
int ret = fork(); 

//ret is 0 if we are in the child process, -1 if fork failed 
//otherwise ret is the process id of the child process 

//because of this, the fork below executes only within the child process 
ret = ret && fork(); /* Here is where I am confused*/ 

//at this point, if we did fork a process (in the child), then ret is 0 in the child 
//then we fork again 
if(ret == 0) { 
    fork(); 
} 

所以我們有我們的第一個進程,進程1執行這個代碼。讓我們假設所有的分支都是成功的(但是你應該確保檢查這個......我認爲它應該在現有的代碼中處理,但它不是那麼明顯)。

  • 進程1分叉,創建進程ID爲2的子進程。
  • ret在過程1中爲2,過程2中爲0.
  • 在過程1中,由於ret不爲零,因此不會發生分叉。
  • 在過程2中,ret爲0,因此我們爲過程ID爲3的子過程分叉。
  • 現在ret在過程1,2和3中分別爲2,3和0。
  • 現在流程3將分岔一個新的孩子。
1

我認爲什麼是錯誤的fork()如何工作。如果你是在UNIX上,你需要做的「人叉」,因爲根據一個我讀:

說明 fork()的創建通過複製調用進程的新進程。該 新工藝,被稱爲孩子,是 調用進程完全相同的副本,稱父,

和..

返回值 成功時,PID的子進程返回父進程中,並且在子進程中返回0。失敗時,-1在父代中返回, 未創建子進程,並且適當地設置errno。

我懷疑可能發生了什麼是您可能會看到多個分叉進程的輸出,這些輸出只會成功地混淆你。你的程序的確切完整輸出是什麼?

這不太可能是短路問題,因爲即使第二個失敗,至少第一個分支應該已經成功,因此如果該分支成功,您應該至少從第一個printfs中獲得一個pid。

0

第一叉,RET - 叉(),將導致3分可能的結果:

  • > 0 =>父獲取子
  • = 0 =>子過程
  • < 0的PID =>誤差與叉

第二叉,RET =保留& &叉(),將執行叉()只有當保留非零。這可能發生在父級錯誤情況和父錯誤情況中。此聲明的結果可以是:

  • == 0 => ret爲非零且fork()爲零。
  • != 0 => ret爲非零且fork()爲非零。

第三個叉if(ret == 0){fork()}只會在ret爲零時執行。

那麼這一切意味着什麼呢?第二個分支似乎是可疑的,因爲在父成功或失敗的情況下,第一個分支的返回值可能不爲零!我不知道這是否是預期的結果,但似乎是可疑的。

如果第一個fork返回在孩子中,第二個fork不會被執行,但第三個會發生,第三個fork會發生。如果第一個fork在父上下文中,第二個fork在子上下文中,第三個fork也可以被執行。

有人檢查這個邏輯,因爲我永遠不會寫這種類型的代碼。

0

在C語言中,操作者&&的行爲是如此:

  1. 計算的第一個參數。如果爲零,則不計算第二個參數,結果爲0.
  2. 如果第一個參數不爲零,則計算第二個參數。如果它是零,答案是0。否則,它是1

功能fork返回0給子進程,另一個號碼(孩子的PID)給父親,讓你的第一個fork後,在父進程ret>0,並在子進程中,ret==0。如果您取消註釋第一個printf,您將得到0和另一個數字。

然後,你運行你的線。在孩子中,ret爲0,所以&&的計算在分叉前停止,並且ret保持爲0.在父親ret>0中,所以它運行fork(),並創建另一個孩子。在父進程,fork返回正數,所以ret將是1,而在第二個孩子,fork返回0,所以ret將爲0 所以,如果你取消註釋只有第二printf,你會得到001(也許按不同的順序)。

然後,你做if (ret==0) fork();,所以兩個孩子(的ret爲0)每個創建一個新的過程。現在,您共有5個進程,因此行Hello world\n將被打印5次。

(這是相當危險的使用輸出功能,同時與fork搞亂 - 你有寫同一個文件,而無需任何鎖定5點的過程,這樣你就可以得到的結果一樣HHHHHeeeeellllllllllooooo wwwwwooooorrrrrlllllddddd\n\n\n\n\n

+0

是的。這就是我之後意識到的。感謝您的解釋。別擔心。這個程序的重點是要知道這個程序有多少個進程。我認爲它應該超過五個,當我試圖調試它時,我意識到我在那條線上的想法是錯誤的。這就是爲什麼我要問這個問題。再次感謝您的解釋。 – Oscarin 2012-02-15 19:56:05