Di banyak program dan halaman manual Linux, saya telah melihat penggunaan kode fork()
. Mengapa kita perlu menggunakan fork()
dan apa tujuannya?
Jawaban:
fork()
adalah cara Anda membuat proses baru di Unix. Saat Anda menelepon fork
, Anda membuat salinan proses Anda sendiri yang memiliki ruang alamatnya sendiri . Hal ini memungkinkan banyak tugas untuk berjalan secara independen satu sama lain seolah-olah mereka masing-masing memiliki memori penuh dari mesin itu sendiri.
Berikut adalah beberapa contoh penggunaan fork
:
fork
untuk menjalankan program yang Anda panggil dari baris perintah.fork
untuk membuat beberapa proses server, yang masing-masing menangani permintaan di ruang alamatnya sendiri. Jika satu mati atau kebocoran memori, yang lain tidak terpengaruh, sehingga berfungsi sebagai mekanisme toleransi kesalahan.fork
untuk menangani setiap halaman dalam proses terpisah. Ini akan mencegah kode sisi klien pada satu halaman menurunkan seluruh browser Anda.fork
digunakan untuk menelurkan proses di beberapa program paralel (seperti yang ditulis menggunakan MPI ). Perhatikan bahwa ini berbeda dengan menggunakan utas , yang tidak memiliki ruang alamat sendiri dan ada dalam suatu proses.fork
secara tidak langsung untuk memulai proses anak. Misalnya, setiap kali Anda menggunakan perintah seperti subprocess.Popen
di Python, Anda fork
merupakan proses anak dan membaca keluarannya. Ini memungkinkan program untuk bekerja sama.Penggunaan umum fork
di shell mungkin terlihat seperti ini:
int child_process_id = fork();
if (child_process_id) {
// Fork returns a valid pid in the parent process. Parent executes this.
// wait for the child process to complete
waitpid(child_process_id, ...); // omitted extra args for brevity
// child process finished!
} else {
// Fork returns 0 in the child process. Child executes this.
// new argv array for the child process
const char *argv[] = {"arg1", "arg2", "arg3", NULL};
// now start executing some other program
exec("/path/to/a/program", argv);
}
Shell memunculkan proses anak menggunakan exec
dan menunggu sampai selesai, lalu melanjutkan dengan eksekusinya sendiri. Perhatikan bahwa Anda tidak harus menggunakan garpu dengan cara ini. Anda selalu dapat mengeluarkan banyak proses anak, seperti yang mungkin dilakukan oleh program paralel, dan masing-masing dapat menjalankan program secara bersamaan. Pada dasarnya, setiap kali Anda membuat proses baru dalam sistem Unix, Anda menggunakan fork()
. Untuk Windows yang setara, lihat CreateProcess
.
Jika Anda menginginkan lebih banyak contoh dan penjelasan yang lebih panjang, Wikipedia memiliki ringkasan yang layak. Dan berikut adalah beberapa slide di sini tentang cara kerja proses, utas, dan konkurensi dalam sistem operasi modern.
fork()
adalah cara untuk membuat proses baru di UNIX, tetapi untuk menjadi bertele-tele, ada setidaknya satu lainnya: posix_spawn()
.
fork () adalah cara Unix membuat proses baru. Pada titik yang Anda panggil fork (), proses Anda dikloning, dan dua proses berbeda melanjutkan eksekusi dari sana. Salah satunya, anak, akan memiliki fork () return 0. Yang lainnya, orang tua, akan memiliki fork () mengembalikan PID (ID proses) anak.
Misalnya, jika Anda mengetik berikut ini di shell, program shell akan memanggil fork (), dan kemudian menjalankan perintah yang Anda berikan (telnetd, dalam hal ini) pada anak, sementara orang tua akan menampilkan prompt lagi, juga sebagai pesan yang menunjukkan PID dari proses latar belakang.
$ telnetd &
Adapun alasan Anda membuat proses baru, begitulah cara sistem operasi Anda dapat melakukan banyak hal pada saat yang bersamaan. Itulah mengapa Anda dapat menjalankan program dan, saat sedang berjalan, beralihlah ke jendela lain dan lakukan sesuatu yang lain.
fork () digunakan untuk membuat proses anak. Ketika fungsi fork () dipanggil, proses baru akan muncul dan pemanggilan fungsi fork () akan mengembalikan nilai yang berbeda untuk anak dan induk.
Jika nilai yang dikembalikan adalah 0, Anda tahu bahwa Anda adalah proses anak dan jika nilai yang dikembalikan adalah angka (yang kebetulan merupakan id proses anak), Anda tahu bahwa Anda adalah orang tuanya. (dan jika itu adalah angka negatif, garpu gagal dan tidak ada proses turunan yang dibuat)
fork () pada dasarnya digunakan untuk membuat proses anak untuk proses di mana Anda memanggil fungsi ini. Setiap kali Anda memanggil fork (), itu mengembalikan nol untuk id anak.
pid=fork()
if pid==0
//this is the child process
else if pid!=0
//this is the parent process
dengan ini, Anda dapat memberikan tindakan yang berbeda untuk induk dan anak dan memanfaatkan fitur multithreading.
fork () akan membuat proses anak baru yang identik dengan induknya. Jadi semua yang Anda jalankan dalam kode setelah itu akan dijalankan oleh kedua proses - sangat berguna jika Anda memiliki misalnya server, dan Anda ingin menangani banyak permintaan.
Anda mungkin tidak perlu menggunakan garpu dalam pemrograman sehari-hari jika Anda menulis aplikasi.
Bahkan jika Anda ingin program Anda memulai program lain untuk melakukan beberapa tugas, ada antarmuka lain yang lebih sederhana yang menggunakan garpu di belakang layar, seperti "sistem" di C dan perl.
Misalnya, jika Anda ingin aplikasi Anda meluncurkan program lain seperti bc untuk melakukan beberapa kalkulasi untuk Anda, Anda dapat menggunakan 'sistem' untuk menjalankannya. Sistem melakukan 'garpu' untuk membuat proses baru, lalu 'exec' untuk mengubah proses itu menjadi bc. Setelah bc selesai, sistem mengembalikan kontrol ke program Anda.
Anda juga dapat menjalankan program lain secara asinkron, tetapi saya tidak ingat caranya.
Jika Anda menulis server, shell, virus atau sistem operasi, kemungkinan besar Anda ingin menggunakan fork.
system()
. Saya membaca tentang fork()
karena saya ingin kode C saya menjalankan skrip python.
Sistem call fork () digunakan untuk membuat proses. Tidak membutuhkan argumen dan mengembalikan ID proses. Tujuan dari fork () adalah untuk membuat proses baru, yang menjadi proses anak pemanggil. Setelah proses anak baru dibuat, kedua proses akan menjalankan instruksi selanjutnya mengikuti panggilan sistem fork (). Oleh karena itu, kita harus membedakan orang tua dengan anak. Ini bisa dilakukan dengan menguji nilai yang dikembalikan dari fork ():
Jika fork () mengembalikan nilai negatif, pembuatan proses anak tidak berhasil. fork () mengembalikan nol ke proses anak yang baru dibuat. fork () mengembalikan nilai positif, ID proses dari proses anak, ke induk. ID proses yang dikembalikan adalah tipe pid_t yang didefinisikan di sys / types.h. Biasanya, ID proses adalah bilangan bulat. Selain itu, sebuah proses dapat menggunakan fungsi getpid () untuk mengambil ID proses yang ditetapkan untuk proses ini. Oleh karena itu, setelah panggilan sistem ke fork (), pengujian sederhana dapat mengetahui proses mana yang merupakan anak. Harap dicatat bahwa Unix akan membuat salinan persis dari ruang alamat orang tua dan memberikannya kepada anak tersebut. Oleh karena itu, proses induk dan anak memiliki ruang alamat yang terpisah.
Mari kita pahami dengan contoh untuk memperjelas poin di atas. Contoh ini tidak membedakan proses induk dan anak.
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#define MAX_COUNT 200
#define BUF_SIZE 100
void main(void)
{
pid_t pid;
int i;
char buf[BUF_SIZE];
fork();
pid = getpid();
for (i = 1; i <= MAX_COUNT; i++) {
sprintf(buf, "This line is from pid %d, value = %d\n", pid, i);
write(1, buf, strlen(buf));
}
}
Misalkan program di atas mengeksekusi hingga titik panggilan ke fork ().
Jika panggilan ke fork () berhasil dijalankan, Unix akan membuat dua salinan identik dari ruang alamat, satu untuk induk dan yang lainnya untuk anak. Kedua proses akan memulai eksekusinya pada pernyataan berikutnya setelah panggilan fork (). Dalam hal ini, kedua proses akan memulai pelaksanaannya di penugasan
pid = .....;
Kedua proses memulai eksekusinya tepat setelah system call fork (). Karena kedua proses memiliki ruang alamat yang identik tetapi terpisah, variabel-variabel yang diinisialisasi sebelum panggilan fork () memiliki nilai yang sama di kedua ruang alamat. Karena setiap proses memiliki ruang alamatnya sendiri, modifikasi apa pun akan independen dari yang lain. Dengan kata lain, jika induk mengubah nilai variabelnya, modifikasi hanya akan mempengaruhi variabel dalam ruang alamat proses induk. Ruang alamat lain yang dibuat oleh panggilan fork () tidak akan terpengaruh meskipun mereka memiliki nama variabel yang identik.
Apa alasan menggunakan tulis daripada printf? Itu karena printf () adalah "buffered", yang berarti printf () akan mengelompokkan output dari suatu proses bersama-sama. Saat melakukan buffering output untuk proses induk, anak juga dapat menggunakan printf untuk mencetak beberapa informasi, yang juga akan di-buffer. Akibatnya, karena keluaran tidak akan langsung dikirim ke layar, Anda mungkin tidak mendapatkan urutan yang benar dari hasil yang diharapkan. Lebih buruk lagi, keluaran dari kedua proses tersebut mungkin tercampur dengan cara yang aneh. Untuk mengatasi masalah ini, Anda dapat mempertimbangkan untuk menggunakan tulisan "unbuffered".
Jika Anda menjalankan program ini, Anda mungkin melihat yang berikut ini di layar:
................
This line is from pid 3456, value 13
This line is from pid 3456, value 14
................
This line is from pid 3456, value 20
This line is from pid 4617, value 100
This line is from pid 4617, value 101
................
This line is from pid 3456, value 21
This line is from pid 3456, value 22
................
ID Proses 3456 mungkin yang ditetapkan ke orang tua atau anak. Karena fakta bahwa proses ini dijalankan secara bersamaan, jalur keluarannya bercampur dengan cara yang agak tidak terduga. Selain itu, urutan baris ini ditentukan oleh penjadwal CPU. Karenanya, jika Anda menjalankan program ini lagi, Anda mungkin mendapatkan hasil yang sama sekali berbeda.
Multiprocessing adalah pusat komputasi. Misalnya, IE atau Firefox Anda dapat membuat proses untuk mengunduh file untuk Anda saat Anda masih menjelajah internet. Atau, saat Anda mencetak dokumen dalam pengolah kata, Anda masih dapat melihat halaman yang berbeda dan masih melakukan beberapa pengeditan dengannya.
Fork () digunakan untuk membuat proses baru seperti yang telah ditulis oleh setiap tubuh.
Berikut adalah kode saya yang membuat proses dalam bentuk pohon biner ....... Ini akan meminta untuk memindai jumlah level yang Anda inginkan untuk membuat proses dalam pohon biner.
#include<unistd.h>
#include<fcntl.h>
#include<stdlib.h>
int main()
{
int t1,t2,p,i,n,ab;
p=getpid();
printf("enter the number of levels\n");fflush(stdout);
scanf("%d",&n);
printf("root %d\n",p);fflush(stdout);
for(i=1;i<n;i++)
{
t1=fork();
if(t1!=0)
t2=fork();
if(t1!=0 && t2!=0)
break;
printf("child pid %d parent pid %d\n",getpid(),getppid());fflush(stdout);
}
waitpid(t1,&ab,0);
waitpid(t2,&ab,0);
return 0;
}
KELUARAN
enter the number of levels
3
root 20665
child pid 20670 parent pid 20665
child pid 20669 parent pid 20665
child pid 20672 parent pid 20670
child pid 20671 parent pid 20670
child pid 20674 parent pid 20669
child pid 20673 parent pid 20669
Pertama, orang perlu memahami apa itu pemanggilan sistem fork (). Biar saya jelaskan
panggilan sistem fork () membuat duplikat yang tepat dari proses induk, itu membuat duplikat tumpukan induk, heap, data yang diinisialisasi, data yang tidak diinisialisasi dan berbagi kode dalam mode hanya-baca dengan proses induk.
Panggilan sistem garpu menyalin memori pada basis copy-on-write, berarti anak membuat di halaman memori virtual ketika ada persyaratan penyalinan.
Sekarang Tujuan dari fork ():
fork()
digunakan untuk menelurkan proses anak. Biasanya ini digunakan dalam situasi yang sama seperti threading, tetapi ada perbedaan. Tidak seperti utas, fork()
menciptakan seluruh proses terpisah, yang berarti bahwa anak dan orang tua sementara mereka adalah salinan langsung satu sama lain pada titik yang fork()
dipanggil, mereka benar-benar terpisah, tidak ada yang dapat mengakses ruang memori orang lain (tanpa pergi ke masalah normal Anda pergi untuk mengakses memori program lain).
fork()
masih digunakan oleh beberapa aplikasi server, kebanyakan yang berjalan sebagai root pada mesin * NIX yang melepaskan izin sebelum memproses permintaan pengguna. Masih ada beberapa kasus penggunaan lain, tetapi kebanyakan orang telah pindah ke multithreading sekarang.
Alasan di balik fork () versus hanya memiliki fungsi exec () untuk memulai proses baru dijelaskan dalam jawaban untuk pertanyaan serupa pada pertukaran tumpukan unix .
Pada dasarnya, karena fork menyalin proses saat ini, semua opsi yang memungkinkan untuk suatu proses ditetapkan secara default, sehingga programmer tidak menyediakannya.
Dalam sistem operasi Windows, sebaliknya, programmer harus menggunakan fungsi CreateProcess yang JAUH lebih rumit dan membutuhkan struktur beraneka ragam untuk menentukan parameter dari proses baru.
Jadi, singkatnya, alasan forking (versus exec'ing) adalah kesederhanaan dalam membuat proses baru.
Pemanggilan sistem Fork () digunakan untuk membuat proses anak. Ini adalah duplikat persis dari proses induk. Bagian tumpukan salinan garpu, bagian heap, bagian data, variabel lingkungan, argumen baris perintah dari induk.
Fungsi fork () digunakan untuk membuat proses baru dengan menduplikasi proses yang ada dari mana ia dipanggil. Proses yang ada di mana fungsi ini dipanggil menjadi proses induk dan proses yang baru dibuat menjadi proses anak. Seperti yang telah dinyatakan bahwa anak adalah salinan duplikat dari induknya tetapi ada beberapa pengecualian untuk itu.
Anak tersebut memiliki PID unik seperti proses lain yang berjalan di sistem operasi.
Anak tersebut memiliki ID proses induk yang sama dengan PID dari
proses yang membuatnya.
Pemanfaatan sumber daya dan penghitung waktu CPU disetel ulang ke nol dalam proses anak.
Set sinyal tertunda pada anak kosong.
Anak tidak mewarisi timer apa pun dari orang tuanya
Contoh:
#include <unistd.h>
#include <sys/types.h>
#include <errno.h>
#include <stdio.h>
#include <sys/wait.h>
#include <stdlib.h>
int var_glb; /* A global variable*/
int main(void)
{
pid_t childPID;
int var_lcl = 0;
childPID = fork();
if(childPID >= 0) // fork was successful
{
if(childPID == 0) // child process
{
var_lcl++;
var_glb++;
printf("\n Child Process :: var_lcl = [%d], var_glb[%d]\n", var_lcl, var_glb);
}
else //Parent process
{
var_lcl = 10;
var_glb = 20;
printf("\n Parent process :: var_lcl = [%d], var_glb[%d]\n", var_lcl, var_glb);
}
}
else // fork failed
{
printf("\n Fork failed, quitting!!!!!!\n");
return 1;
}
return 0;
}
Sekarang, ketika kode di atas dikompilasi dan dijalankan:
$ ./fork
Parent process :: var_lcl = [10], var_glb[20]
Child Process :: var_lcl = [1], var_glb[1]