Dalam Program 1 Hello world
akan dicetak hanya sekali, tetapi ketika saya menghapus \n
dan menjalankannya (Program 2), hasilnya akan dicetak 8 kali. Bisakah seseorang tolong jelaskan saya pentingnya di \n
sini dan bagaimana pengaruhnya terhadap fork()
?
Program 1
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
int main()
{
printf("hello world...\n");
fork();
fork();
fork();
}
Output 1:
hello world...
Program 2
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
int main()
{
printf("hello world...");
fork();
fork();
fork();
}
Output 2:
hello world... hello world...hello world...hello world...hello world...hello world...hello world...hello world...
./prog1 > prog1.out
) atau pipa (./prog1 | cat
). Bersiaplah untuk memiliki pikiran Anda meledak. :-) fork()
agak unix-spesifik juga, sehingga tampaknya ini cukup pada topik untuk unix.SE.Jawaban:
Saat mengeluarkan ke keluaran standar menggunakan fungsi pustaka C
printf()
, output biasanya buffered. Buffer tidak memerah sampai Anda menampilkan baris baru, memanggilfflush(stdout)
atau keluar dari program (tidak melalui panggilan_exit()
). Aliran keluaran standar secara default dilindungi buffer dengan cara ini ketika terhubung ke TTY.Saat Anda memotong proses di "Program 2", proses anak mewarisi setiap bagian dari proses induk, termasuk buffer output yang tidak rata. Ini secara efektif menyalin buffer tanpa flush ke setiap proses anak.
Saat proses berakhir, buffer dibilas. Anda memulai total delapan proses besar (termasuk proses asli), dan buffer yang tidak dibilas akan memerah pada penghentian setiap proses individu.
Itu delapan karena pada masing-masing
fork()
Anda mendapatkan dua kali jumlah proses yang Anda miliki sebelumfork()
(karena mereka tanpa syarat), dan Anda memiliki tiga dari ini (2 3 = 8).sumber
main
dengan_exit(0)
hanya membuat panggilan sistem keluar tanpa buffer flushing, dan kemudian akan dicetak nol kali tanpa baris baru. ( Implementasi keluar Syscall () dan Bagaimana bisa _exit (0) (keluar oleh syscall) mencegah saya menerima konten stdout? ). Atau Anda dapat mem-pipe Program1 kecat
atau mengarahkannya ke file dan melihatnya dicetak 8 kali. (stdout buffer penuh secara default ketika itu bukan TTY). Atau tambahkanfflush(stdout)
ke case no-newline sebelum tanggal 2fork()
...Itu tidak mempengaruhi garpu dengan cara apa pun.
Dalam kasus pertama, Anda berakhir dengan 8 proses tanpa menulis, karena buffer output sudah dikosongkan (karena
\n
).Dalam kasus kedua Anda masih memiliki 8 proses, masing-masing dengan buffer yang berisi "Hello world ..." dan buffer ditulis pada akhir proses.
sumber
@ Kusalananda menjelaskan mengapa output diulang . Jika Anda penasaran mengapa output diulang 8 kali dan tidak hanya 4 kali (program dasar + 3 garpu):
sumber
Latar belakang yang penting di sini adalah bahwa garis
stdout
harus disangga oleh standar sebagai pengaturan default.Ini menyebabkan a
\n
untuk menyiram output.Karena contoh kedua tidak mengandung baris baru, output tidak memerah dan sebagai
fork()
salinan seluruh proses, itu juga menyalin keadaanstdout
buffer.Sekarang,
fork()
panggilan ini dalam contoh Anda membuat total 8 proses - semuanya dengan salinan statusstdout
buffer.Menurut definisi, semua proses ini memanggil
exit()
saat kembali darimain()
danexit()
panggilanfflush()
diikuti olehfclose()
semua aliran stdio aktif . Ini termasukstdout
dan sebagai hasilnya, Anda melihat konten yang sama delapan kali.Merupakan praktik yang baik untuk memanggil
fflush()
semua aliran dengan output yang tertunda sebelum memanggilfork()
atau membiarkan anak bercabang memanggil secara eksplisit_exit()
yang hanya keluar dari proses tanpa menyiram aliran stdio.Perhatikan bahwa panggilan
exec()
tidak menghilangkan buffer stdio, jadi tidak apa-apa untuk tidak peduli tentang buffer stdio jika Anda (setelah meneleponfork()
) memanggilexec()
dan (jika itu gagal) menelepon_exit()
.BTW: Untuk memahami bahwa buffering yang salah dapat menyebabkan, berikut adalah bug di Linux yang baru-baru ini diperbaiki:
Standar ini
stderr
harus di-unbuffered secara default, tetapi Linux mengabaikannya dan membuatstderr
line buffered dan (bahkan lebih buruk) sepenuhnya buffered jika stderr dialihkan melalui pipa. Jadi program yang ditulis untuk UNIX melakukan output barang tanpa baris baru terlambat di Linux.Lihat komentar di bawah, sepertinya sudah diperbaiki sekarang.
Inilah yang saya lakukan untuk mengatasi masalah Linux ini:
Kode ini tidak membahayakan platform lain karena memanggil
fflush()
stream yang baru saja dibilas adalah noop.sumber
setbuf()
, di Debian ( yang ada di man7.org terlihat serupa ), menyatakan bahwa "Standar aliran stream kesalahan selalu tidak terganggu secara default." dan tes sederhana tampaknya bertindak seperti itu, terlepas dari apakah output masuk ke file, pipa atau terminal. Apakah Anda memiliki referensi untuk versi C library apa yang akan dilakukan sebaliknya?