[解決済み] fork()後のprintfの不具合
質問
OS:Linux Linux、言語:ピュアC
私は一般的なCプログラミング、そして特殊なケースとしてUNIXでのCプログラミングを学ぶために前進しているのです。
私は、(私にとって)奇妙な振る舞いを検出しました。
printf()
を使用した後に
fork()
を呼び出します。
コード
#include <stdio.h>
#include <system.h>
int main()
{
int pid;
printf( "Hello, my pid is %d", getpid() );
pid = fork();
if( pid == 0 )
{
printf( "\nI was forked! :D" );
sleep( 3 );
}
else
{
waitpid( pid, NULL, 0 );
printf( "\n%d was forked!", pid );
}
return 0;
}
出力
Hello, my pid is 1111
I was forked! :DHello, my pid is 1111
2222 was forked!
なぜ2つ目の "Hello" の文字列が子供の出力に現れたのでしょうか?
はい、それはまさに親が起動時に出力したものであり、親の
pid
.
しかし!もし
\n
文字を各文字列の末尾に置くと、期待通りの出力が得られます。
#include <stdio.h>
#include <system.h>
int main()
{
int pid;
printf( "Hello, my pid is %d\n", getpid() ); // SIC!!
pid = fork();
if( pid == 0 )
{
printf( "I was forked! :D" ); // removed the '\n', no matter
sleep( 3 );
}
else
{
waitpid( pid, NULL, 0 );
printf( "\n%d was forked!", pid );
}
return 0;
}
出力 :
Hello, my pid is 1111
I was forked! :D
2222 was forked!
なぜこのようなことが起こるのでしょうか?正しい動作なのか、それともバグなのでしょうか?
どのように解決するのですか?
私は、以下のことに注目しています。
<system.h>
は非標準のヘッダであることに注意してください。
<unistd.h>
に置き換えたら、きれいにコンパイルされました。
プログラムの出力がターミナル(画面)に向かうときは、ラインバッファリングされます。 プログラムの出力がパイプに向かう場合は、フルバッファリングされます。 バッファリングモードを制御するには、標準C関数
setvbuf()
と
_IOFBF
(フルバッファリング) を使用します。
_IOLBF
(ラインバッファリング) および
_IONBF
(バッファリングなし) モードがあります。
修正したプログラムでこれを実証するには、プログラムの出力をパイプで、例えば
cat
. の末尾に改行があっても
printf()
の文字列の最後に改行があっても、二重の情報を見ることになる。 ターミナルに直接送信した場合は、1つのロットの情報だけが表示されます。
この話の教訓は、注意深く
fflush(0);
を呼び出して、フォークする前に全てのI/Oバッファを空にすることに注意することです。
要求された行ごとの分析(中括弧などを削除し、マークアップ エディターによって先頭の空白を削除)。
-
printf( "Hello, my pid is %d", getpid() );
-
pid = fork();
-
if( pid == 0 )
-
printf( "\nI was forked! :D" );
-
sleep( 3 );
-
else
-
waitpid( pid, NULL, 0 );
-
printf( "\n%d was forked!", pid );
分析が
- "こんにちは、私の pid は 1234 です" を標準出力用のバッファにコピーしています。 最後に改行がなく、出力はライン バッファ モード (またはフル バッファ モード) で実行されているため、ターミナルには何も表示されません。
- stdout バッファに全く同じ内容を持つ、2 つの別々のプロセスを与えます。
-
子プロセスには
pid == 0
を持ち、4行目と5行目を実行します。親はゼロ以外の値のpid
の戻り値がゼロでないことです (この 2 つの処理の数少ない違いの 1 つはgetpid()
とgetppid()
はさらに2つ)。 - 子の出力バッファに改行と "私はフォークされました!:D" を追加します。 出力の最初の行は端末に表示されます。出力はラインバッファリングされているため、残りはバッファに保持されます。
- すべてが 3 秒間停止します。 この後、子プロセスは main の最後にある return を通して通常通り終了します。 このとき、stdout バッファに残っているデータはフラッシュされます。 これは、改行がないため、出力位置が行末のままである。
- 親はここに来る。
- 親は子供が死に終わるのを待ちます。
- 親は出力バッファに改行と "1345 was forked!" を追加します。 改行は、子プロセスによって生成された不完全な行の後に、 'Hello' メッセージを出力にフラッシュします。
最後にまだ改行がないため、カーソル位置は感嘆符の後になり、シェルプロンプトが同じ行に表示されます。
私が見たものは
Osiris-2 JL: ./xx
Hello, my pid is 37290
I was forked! :DHello, my pid is 37290
37291 was forked!Osiris-2 JL:
Osiris-2 JL:
PID番号は異なりますが、全体的な外観は明らかです。 の末尾に改行を追加する。
printf()
文の最後に改行を加える (これはすぐに標準的な習慣になります) と、出力が大きく変わります。
#include <stdio.h>
#include <unistd.h>
int main()
{
int pid;
printf( "Hello, my pid is %d\n", getpid() );
pid = fork();
if( pid == 0 )
printf( "I was forked! :D %d\n", getpid() );
else
{
waitpid( pid, NULL, 0 );
printf( "%d was forked!\n", pid );
}
return 0;
}
今、私は手に入れた。
Osiris-2 JL: ./xx
Hello, my pid is 37589
I was forked! :D 37590
37590 was forked!
Osiris-2 JL: ./xx | cat
Hello, my pid is 37594
I was forked! :D 37596
Hello, my pid is 37594
37596 was forked!
Osiris-2 JL:
出力がターミナルに送られるとき、ラインバッファリングされることに注意してください。
fork()
の前に表示され、1つだけコピーされていることに注意してください。 出力がパイプで
cat
にパイプされたとき、それは完全にバッファリングされているので、何も
fork()
の前に何も表示されず、両方のプロセスで 'Hello' 行がフラッシュされるバッファにあります。
関連
-
[解決済み] printf ファミリーを使用して、size_t 変数をポータブルに印刷するにはどうすればよいですか?
-
[解決済み] Windows用Cコンパイラ?[クローズド]
-
[解決済み] ⑭と⑯は何のためにあるのですか?
-
[解決済み] printf/String.Formatに相当するJavaScriptの機能
-
[解決済み] printfは、フォーマット文字列の中に改行がないと、呼び出し後にフラッシュしないのはなぜですか?
-
[解決済み] longをフォーマットするprintfの引数は何ですか?
-
[解決済み] printfにおけるdoubleの正しい書式指定子
-
[解決済み] LD_PRELOADのトリックとは何ですか?
-
[解決済み】boolのprintfフォーマット指定子とは?
-
[解決済み】fork()、vfork()、exec()、clone()の違いについて)
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
[C] レポートエラー 代入の左オペランドとしてlvalueが必要
-
Cエラー [エラー] 代入_Ashesの左オペランドにlvalueが必要です-プログラマーズ・シークレット
-
[解決済み] "static const" vs "#define" vs "enum"
-
[解決済み] C言語では「?」演算子は何をするのですか?
-
[解決済み] 配列のすべてのメンバーを同じ値で初期化するには?
-
[解決済み] C言語でのブーリアン値の使用
-
[解決済み] 演算子 *, /, +, -, % を使わずに 3 で割る。
-
[解決済み] while ( !feof (file) ) 」は、なぜいつも間違っているのですか?
-
[解決済み] ストラクチャーとユニオンの違い
-
[解決済み] 2次元配列の反復処理において、ループの順序がパフォーマンスに影響するのはなぜですか?