
以下 C 程式應該說明子行程和父行程之間的競爭條件:
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
int main()
{
fork();
printf("\n 1234567890 \n");
return 0;
}
當我的朋友執行它時(在烏班圖),他們得到預期的輸出,這是混亂的 1234567890s
一個例子:12312345645678907890
但是當我在我的電腦上嘗試相同的程式時架構Linux,它永遠不會給出這樣的輸出。總是一個接著一個。
1234567890
1234567890
我喜歡拱門Linux是一些如何避免競爭條件的方法,但我想停用任何這樣的功能,並希望得到我朋友的輸出。
答案1
此printf
調用將運行一個或多個write(2)
系統調用,它們的處理順序將是輸出的實際順序。一個或多個,因為這取決於 C 庫內部的緩衝。使用行緩衝輸出(轉到終端),您可能會收到兩個write
調用,一次用於初始換行符,另一個用於其餘部分。
write(1, "\n", 1);
write(1, " 1234567890 \n", 13);
可以在呼叫之間安排另一個進程,首先給出兩個空行,然後給出帶有數字的行,但由於沒有進行太多處理,因此在卸載的系統上不太可能。
請注意,由於兩個進程列印完全相同的內容,因此只要一個進程不中斷另一個進程,哪個進程先執行並不重要。
如果輸出進入檔案或管道,預設情況下它是完全緩衝的,因此您可能只會得到一次write
呼叫(每個進程),並且沒有機會混合輸出。
如果數字是透過單獨的系統呼叫一一輸出的,則混合數字的範例是可能的。很難理解為什麼明智的庫實現在列印長度已知的靜態字串時會這樣做。隨著循環中的寫入次數增多,混合輸出更有可能出現:
像這樣的東西:
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
int i;
setbuf(stdout, NULL); /* explicitly unbuffered */
int x = fork();
for (i = 0 ; i < 500 ; i++) {
printf("%d", !!x);
}
if (x) {
wait(NULL);
printf("\n");
}
return 0;
}
給我如下的輸出。大多數時候是這樣,但並非總是如此。由系統決定如何安排進程。不可預測性是我們通常試圖避免競爭條件的原因。
111111111111111111111111111111111111111111111111111111111111111111111111111
111111111111111111111111111111111111111111111111111111111111111111111111111
111100000000000000000000000000000000000000000000000000000000000000000000000
000000000000000000000000000000000000000000000000000000000000000000000000000
000000000000000000000000000000000000000000000000000000000000000000000000000
000000000000000000000000000000000000000000000000000000000000010000001001100
110000000011001100110011000000010011001100110000000100110011001100000001001
100110011000000010011001100110000000100110011001100000001001100110011000000
...
答案2
我懷疑fork()
系統呼叫使父進程或子進程等待足夠長的時間,以允許另一個進程完成對 的呼叫printf()
並使字串出現在輸出中,甚至在到達其自己的 之前printf()
。
如果父進程和子進程都有時間同時執行循環,則在循環中輸出大量字串可能會顯示您所描述的混合輸出。
「修復」這個問題可能涉及重寫fork()
系統呼叫或涉及其中的核心元件。