3 回答

TA貢獻(xiàn)1804條經(jīng)驗(yàn) 獲得超3個贊
當(dāng)您將某些內(nèi)容打印到“標(biāo)準(zhǔn)輸出”標(biāo)準(zhǔn)輸出(通常是計(jì)算機(jī)監(jiān)視器,盡管您可以將其重定向到文件)時,它最初會存儲在臨時緩沖區(qū)中。
分叉的兩面都繼承未刷新的緩沖區(qū),因此,當(dāng)分叉的每一面觸及return語句并結(jié)束時,都會刷新兩次。
在分叉之前,應(yīng)該fflush(stdout);
先刷新緩沖區(qū),以使子代不會繼承該緩沖區(qū)。
屏幕上的stdout(而不是將其重定向到文件時)實(shí)際上是由行尾緩沖的,因此,如果您這樣做了printf("Hi\n");
,則不會有此問題,因?yàn)樗鼤⑿戮彌_區(qū)本身。

TA貢獻(xiàn)1820條經(jīng)驗(yàn) 獲得超10個贊
printf("Hi");并不會立即在屏幕上顯示“ Hi”一詞。它所做的是在stdout緩沖區(qū)中填充單詞“ Hi”,一旦緩沖區(qū)“被刷新”,該字詞就會顯示出來。在這種情況下,stdout指向您的顯示器(假定)。在這種情況下,緩沖區(qū)將在緩沖區(qū)已滿,強(qiáng)制您刷新或(最常見)打印換行符(“ \ n”)時刷新。由于在fork()調(diào)用時緩沖區(qū)仍然充滿,因此父進(jìn)程和子進(jìn)程都繼承該緩沖區(qū),因此在刷新緩沖區(qū)時,它們都將打印出“ Hi”。如果fflush(stout);在調(diào)用fork之前先調(diào)用,它應(yīng)該可以工作:
int main() {
printf("Hi");
fflush(stdout);
fork();
return 0;
}
另外,正如我所說,如果您在其中包含換行符,printf那么它也應(yīng)該可以正常工作:
int main() {
printf("Hi\n");
fork();
return 0;
}

TA貢獻(xiàn)1752條經(jīng)驗(yàn) 獲得超4個贊
通常,在fork()兩側(cè)的庫中使用開放的句柄/對象是非常不安全的。
這包括C標(biāo)準(zhǔn)庫。
fork()使兩個進(jìn)程合二為一,沒有庫可以檢測到它的發(fā)生。因此,如果兩個進(jìn)程繼續(xù)使用相同的文件描述符/套接字等運(yùn)行,它們現(xiàn)在將具有不同的狀態(tài),但共享相同的文件句柄(從技術(shù)上講,它們具有副本,但具有相同的基礎(chǔ)文件)。這使壞事發(fā)生。
fork()導(dǎo)致此問題的情況的示例
stdio,例如tty輸入/輸出,管道,光盤文件
數(shù)據(jù)庫客戶端庫使用的套接字
服務(wù)器進(jìn)程使用的套接字-當(dāng)為一個套接字提供服務(wù)的孩子碰巧繼承了文件的句柄時,可能會產(chǎn)生奇怪的效果-正確地進(jìn)行這種編程是很棘手的,請參閱Apache的源代碼示例。
在一般情況下如何解決此問題:
要么
a)在fork()之后,可能立即在同一二進(jìn)制文件上調(diào)用exec()(帶有必要的參數(shù)以完成您打算做的任何工作)。這很容易。
b)分叉之后,請勿使用任何依賴于它們的現(xiàn)有打開的句柄或庫對象(可以打開新的句柄);盡快完成工作,然后調(diào)用_exit()(而不是exit())。不要從調(diào)用fork的子例程中返回,因?yàn)槟菢涌赡軙?dǎo)致調(diào)用C ++析構(gòu)函數(shù)等,這可能會對父進(jìn)程的文件描述符造成不良影響。這相當(dāng)容易。
c)分叉之后,在讓孩子繼續(xù)之前,以某種方式清理所有物體并使它們都處于健全狀態(tài)。例如,關(guān)閉基礎(chǔ)文件描述符,而不刷新父緩沖區(qū)中重復(fù)的緩沖區(qū)中的數(shù)據(jù)。這很棘手。
c)大約是Apache所做的。
添加回答
舉報