Mengapa vfork () dimaksudkan untuk digunakan ketika proses anak memanggil exec () atau keluar () segera setelah pembuatan?

11

Konsep Sistem Operasi dan APUE mengatakan

Dengan vfork (), proses induk ditangguhkan, dan proses anak menggunakan ruang alamat induk. Karena vfork () tidak menggunakan copy-on-write, jika proses anak mengubah halaman apa pun dari ruang alamat orang tua, halaman yang diubah akan terlihat oleh orang tua setelah resume. Oleh karena itu, vfork () harus digunakan dengan hati-hati untuk memastikan bahwa proses anak tidak mengubah ruang alamat orang tua.

vfork () dimaksudkan untuk digunakan ketika proses anak memanggil exec () atau keluar () segera setelah pembuatan.

Bagaimana saya bisa mengerti kalimat terakhir?

Ketika proses anak dibuat oleh vfork()panggilan exec(), tidak exec()mengubah ruang alamat dari proses induk, dengan memuat program baru?

Ketika proses anak dibuat oleh vfork()panggilan exit(), exit()tidak mengubah ruang alamat dari proses induk saat mengakhiri anak?

Saya lebih suka Linux.

Terima kasih.

Tim
sumber

Jawaban:

15

Ketika proses anak dibuat oleh vfork()panggilan exec(), tidak exec()mengubah ruang alamat dari proses induk, dengan memuat program baru?

Tidak, exec()menyediakan ruang alamat baru untuk program baru; itu tidak mengubah ruang alamat induk. Lihat misalnya diskusi tentang execfungsi - fungsi dalam POSIX , dan manual Linuxexecve() .

Ketika proses anak yang dibuat oleh vfork () memanggil keluar (), apakah keluar () tidak mengubah ruang alamat dari proses induk saat mengakhiri anak?

Plain exit()mungkin - menjalankan kait keluar yang diinstal oleh program yang sedang berjalan (termasuk pustaka-pustaka nya). vfork()lebih membatasi; dengan demikian, di Linux, itu mengamanatkan penggunaan _exit()yang tidak memanggil fungsi pembersihan C library.

vfork()ternyata cukup sulit untuk diperbaiki; itu telah dihapus dalam versi standar POSIX saat ini, dan posix_spawn()sebagai gantinya harus digunakan.

Namun, kecuali Anda benar - benar tahu apa yang Anda lakukan, Anda tidak boleh menggunakan salah satu vfork()atau posix_spawn(); menempel tua yang baik fork()dan exec().

Linux manpage yang ditautkan di atas menyediakan lebih banyak konteks:

Namun, di masa lalu yang buruk fork(2) akan membutuhkan membuat salinan lengkap dari ruang data pemanggil, sering kali tidak perlu, karena biasanya segera setelah exec(3)itu dilakukan. Jadi, untuk efisiensi yang lebih besar, BSD memperkenalkan vfork() pemanggilan sistem, yang tidak sepenuhnya menyalin ruang alamat dari proses induk, tetapi meminjam memori induk dan untaian kontrol hingga panggilan ke execve(2)atau keluar terjadi. Proses induk ditangguhkan sementara anak menggunakan sumber dayanya. Penggunaannya vfork()rumit: misalnya, tidak memodifikasi data dalam proses induk tergantung pada mengetahui variabel mana yang disimpan dalam register.

Stephen Kitt
sumber
Terima kasih. "exec () menyediakan ruang alamat baru untuk program baru;" Apakah perilaku normal exec () untuk memuat program ke dalam ruang alamat proses? Saya tidak menemukan di dua tautan di mana ia menciptakan ruang alamat baru baik secara normal atau khususnya untuk vfork ().
Tim
1
Yang lucu adalah vfork () menang melawan hampir semua hal lain sekarang. Ini jauh lebih cepat daripada garpu () ketika Anda memiliki memori yang bisa ditulis hingga satu gigabyte.
Joshua
2
Tolong jangan bilang orang untuk menggunakan posix_spawn. Secara signifikan lebih sulit untuk menulis kode yang benar menggunakan posix_spawndaripada dengan yang lama fork, dan jika Anda mencoba, Anda mungkin mengalami tembok bata karena tidak ada file action atau atribut yang melakukan hal yang perlu Anda lakukan di antaranya forkdan exec. Dan tidak dijamin memiliki efisiensi seperti vfork, sehingga tidak menyelesaikan masalah yang orang ingin atasi.
zwol
1
@ zwol: Itu saran yang sangat buruk. Meskipun Anda posix_spawnmungkin kekurangan fungsionalitas yang Anda inginkan (Anda dapat menyelesaikannya melalui program pembantu perantara, ditulis dalam C atau skrip shell inline-on-cmdline), segala upaya untuk mencapai apa yang Anda inginkan dengan vforkmemanggil perilaku berbahaya yang tidak terdefinisi. Spesifikasi untuk vforktidak memungkinkan memanggil fungsi acak untuk mengatur negara untuk anak untuk diwarisi sebelumnya execve, dan upaya untuk melakukannya dapat merusak keadaan orang tua.
R .. GitHub BERHENTI MEMBANTU ICE
1
@ Yosua: Implementasi modern dari posix_spawnkinerja kira-kira sama dengan vforkdi sebagian besar kondisi. Yang mana ada perbedaan cenderung menjadi kasus-kasus di mana vforksangat tidak aman: di mana ada penangan sinyal diinstal yang posix_spawnharus menekan agar tidak berjalan pada anak sebelum eksekutif.
R .. GitHub BERHENTI MEMBANTU ICE
4

Saat Anda menelepon vfork(), proses baru dibuat dan proses baru itu meminjam gambar proses dari proses induk dengan pengecualian tumpukan. Proses anak diberi bintang stack baru sendiri namun tidak diizinkan returndari fungsi yang dipanggil vfork().

Saat anak berjalan, proses induk diblokir, karena anak meminjam ruang alamat dari induk.

Terlepas dari apa yang Anda lakukan, semua yang hanya mengakses tumpukan memodifikasi hanya tumpukan pribadi anak. Namun, jika Anda memodifikasi data global, ini memodifikasi data umum dan karenanya juga memengaruhi induknya.

Hal-hal yang mengubah data global adalah:

  • menelepon malloc () atau gratis ()

  • menggunakan stdio

  • memodifikasi pengaturan sinyal

  • memodifikasi variabel yang tidak lokal ke fungsi yang dipanggil vfork().

  • ...

Setelah Anda menelepon _exit()(penting, tidak pernah menelepon exit()), anak tersebut dihentikan dan kontrol diberikan kembali kepada orang tua.

Jika Anda memanggil fungsi apa pun dari exec*()keluarga, ruang alamat baru dibuat dengan kode program baru, data baru, dan bagian dari tumpukan dari induknya (lihat di bawah). Setelah ini siap, anak tidak lagi meminjam ruang alamat dari anak, tetapi menggunakan ruang alamat sendiri.

Kontrol diberikan kembali ke induk, karena ruang alamat itu tidak lagi digunakan oleh proses lain.

Penting: Di Linux, tidak ada vfork()implementasi nyata . Linux lebih mengimplementasikan vfork()berdasarkan fork()konsep Copy on Write yang diperkenalkan oleh SunOS-4.0 pada tahun 1988. Untuk membuat pengguna percaya bahwa mereka menggunakan vfork(), Linux hanya mengatur data bersama dan menangguhkan orang tua sementara anak tidak memanggil _exit()atau salah satu exec*()fungsi.

Oleh karena itu Linux tidak mendapat manfaat dari kenyataan bahwa nyata vfork()tidak perlu mengatur deskripsi ruang alamat untuk anak di kernel. Ini menghasilkan vfork()yang tidak lebih cepat dari fork(). Pada sistem yang mengimplementasikan nyata vfork(), biasanya 3x lebih cepat dari fork()dan mempengaruhi kinerja shell yang menggunakan vfork()- ksh93, yang terbaru Bourne Shelldan csh.

Alasan mengapa Anda tidak boleh menelepon exit()dari vfork()anak ed adalah bahwa exit()flushes stdio kalau-kalau ada data tidak rata dari waktu sebelum menelepon vfork(). Ini dapat menyebabkan hasil yang aneh.

BTW: posix_spawn()diimplementasikan di atas vfork(), jadi vfork()tidak akan dihapus dari OS. Telah disebutkan bahwa Linux tidak digunakan vfork()untuk posix_spawn().

Untuk tumpukan, ada beberapa dokumentasi, inilah yang dikatakan halaman manual Solaris:

 The vfork() and vforkx() functions can normally be used  the
 same  way  as  fork() and forkx(), respectively. The calling
 procedure, however, should not return while running  in  the
 child's  context,  since the eventual return from vfork() or
 vforkx() in the parent would be to a  stack  frame  that  no
 longer  exists. 

Jadi implementasinya dapat melakukan apa pun yang disukainya. Implementasi Solaris menggunakan memori bersama untuk bingkai stack dari pemanggilan fungsi vfork(). Tidak ada implementasi yang memberikan akses ke bagian stack yang lebih lama dari induknya.

schily
sumber
4
Baik pustaka GNU C maupun pustaka musl C mengimplementasikan posix_spawn()di atas Linux vfork(). Mereka berdua mengimplementasikannya di atas __clone().
JdeBP
1
@ JdeBP: Anda tahu vfork()panggilan saja, clone()bukan? Secara harfiah ini adalah one-liner di kernel.
Joshua
1
"Penting: Di Linux, tidak ada implementasi vfork () nyata." <- Ini tidak benar dan belum benar setidaknya selama satu dekade. Jika tolok ukur shell Anda tidak mengamati perbedaan kinerja antara vforkdan forkdi Linux, itu melakukan sesuatu yang salah.
zwol
1
Bagian kedua dari jawaban ini dimulai dengan "Penting: Di Linux, tidak ada implementasi vfork ()" sebagian besar atau seluruhnya salah.
R .. GitHub BERHENTI MEMBANTU ICE
1
Tolong jangan membuat klaim tanpa memverifikasi. Bourne Shell saat ini dapat dikompilasi dengan dan tanpa dukungan vfork, jadi bahkan jika Anda yakin bahwa fitur debug dari Linux tidak dapat memberikan hasil yang dapat diandalkan, Anda dapat membandingkan waktu eksekusi untuk panggilan konfigurasi dengan dan dengan vfork di dalam shell. Saya menggunakan skrip configure dengan 800 tes. Pada Solaris, Bourne Shell yang menggunakan vfork membutuhkan waktu cpu sistem 30% lebih sedikit dibandingkan dengan case fork. Di Linux, pengujian yang sama menghasilkan waktu cpu sistem kurang dari 10% lebih sedikit. Pada Solaris itu tidak 3x karena ada banyak panggilan kompiler yang disertakan.
schily