Fork(), Exec(), Wait() Sistem Çağrıları İşletim Sistemleri Dersi
FORK() fork(), herhangi bir parametre almayan bir sistem çağrısıdır. Çağrıldığı andan itibaren işletim sistemi tarafından, ana processin bir kopyası oluşturulur. fork() çağrısını yapan process parent, ve bu çağrı ardından oluşan yeni process ise child olarak adlandırılır. fork() çağrısı yapıldığı andan itibaren, oluşan child process, parent’ın kaldığı yerden devam edecek şekilde bir kopya haline gelir.
FORK() fork() çalıştırıldığında aşağıdaki dönüş değerleri ortaya çıkmaktadır: fork() çağrısı başarısız olursa dönüş değeri -1 ’dir çağrı başarılı olursa; parent process için dönüş değeri, yeni oluşan child processin ID’si, child process için dönüş değeri 0 ’dır. fork() çağrısının dönüş değerleri konusunda şu yargıya varabiliriz: fork_donus_degeri > 0 : bu sonuç parent process içinde döndürülür fork_donus_degeri == 0 : bu sonuç child process içinde döndürülür fork_donus_degeri == -1 : çağrı başarısız oldu ve child oluşmadı. parent içinde döndürülür
FORK() Basit bir parent-child process aşağıdaki şekilde oluşturulabilir #include <stdio.h> #include <sys/types.h> #include <unistd.h> int main(void) { printf("Hello, World \n"); int fork_donus_degeri = fork(); printf("Bye Bye, World \n"); printf("fork donus degeri : %d", fork_donus_degeri); return 0; } Bu dönüş değerlerine göre parent ve child processlerimize farklı işlemler yaptırmamız mümkün olabilmektedir
#include <stdio. h> #include <sys/types #include <stdio.h> #include <sys/types.h> #include <unistd.h> #include <errno.h> int main(void) { int fork_donus_degeri = fork(); int sonuc; if (fork_donus_degeri == 0) { // burasi child processte calisir cunku == 0 degerini o saglayabilir sonuc = 0; int i; for(i=0;i<10;i++) { sleep(1); sonuc = sonuc + 1; printf("child_dongu_%d : sonuc : %d \n", i, sonuc); } printf("CHILD SONUCU : %d \n", sonuc); else if (fork_donus_degeri > 0) { // burasi parent processte calisir cunku > 0 degerini o saglayabilir sonuc = 1; sonuc = sonuc * 2; printf("parent_dongu_%d : sonuc : %d \n", i, sonuc); printf("PARENT SONUCU : %d \n", sonuc); else if (fork_donus_degeri == -1) { printf("ERROR : \n %d", errno); return 1; return 0;
SLEEP() Yukarıdaki örnek üzerinde yer alan sleep() komutu, parametre olarak verilen sayı boyunca saniye cinsinden processi uyku moduna alır. sleep(1); Bir process her zaman diğerinden önce sonlanabilir. Eğer bir process bittikten sonra ikincinin bitmesi için biraz süre varsa, bu durumda shell komut satırı sanki tüm program bitmiş gibi açılır. Ardından diğer process işlemini göstererek devam eder.
Yukarıdaki örneğin çıktısı aşağıdaki gibidir. Bir parent process’in, kendi child process’inden önce işleyişini tamamlaması ve kapanması istenmeyen bir durumdur. Bu nedele böyle durumlardan kaçınmak için parent tarafından kullanılabilecek bir wait(&donus_degeri) komutu vardır
Bir parent process’in, kendi child process’inden önce işleyişini tamamlaması ve kapanması istenmeyen bir durumdur. Bu nedenle böyle durumlardan kaçınmak için parent tarafından kullanılabilecek bir wait(&donus_degeri) komutu vardır
wait(&status) Üst proses bir alt prosesin sonlanmasını bekleyebilir. wait fonksiyonunun prototipi şöyledir: #include <sys/wait.h> wait(int *status); Fonksiyon parametre olarak sonlanan prosesin çıkış kodunun yerleştirileceği int türden nesnenin adresini alır. wait fonksiyonu herhangi bir alt proses sonlanana kadar kendisini çağıran thread’i bloke etmektedir. Sonlanan prosesin id değeri, geri dönüş değeri olarak verilir. Ayrıca, parametre NULL adres olarak geçilebilir. Bu durumda fonksiyon bize sonlanan prosesin çıkış kodunu vermez.
wait(&status) Aşağıda alt prosesin çıkış kodununun ekrana yazdırıldığı bir örnek görüyorsunuz: #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/wait.h> #include "general.h" int main(void) { pid_t pidChild; if ((pidChild = fork()) < 0) err_sys("fork"); if (pidChild == 0) { /* Alt proses */ sleep(5); exit(100); } else { /* Ust proses */ int stat; printf("Alt prosesin sonlanmasi bekleniyor...\n"); if (wait(&stat) < 0) err_sys("wait"); printf("Alt prosesin sonlanma kodu: %d\n", WEXITSTATUS(stat)); } return 0; }
EXEC() Çeşitli varyasyonları bulunan bu sistem çağrısı ile bir process içinde yeni bir program çağrısı yapılabilmektedir. (Çağrı yapılan, yeni bir process değil, yeni bir programdır.) #include <stdio.h> #include <sys/types.h> #include <unistd.h> int main(void) { int fork_donus_degeri = fork(); if (fork_donus_degeri == 0) { // burasi child processte calisir execl("/bin/ls", "/bin/ls","-la", NULL); } return 0;
EXEC() fork() ile oluşan yeni bir process, parent process ile aynıdır ve kod, stack ve kullanılan veriler child process içinde yer alır. exec*(..) ile bir child processin sahip olduğu kod, veri ve stack değiştirilebilir. Bunun anlamı, child process parent’ın bir kopyası olarak doğmuştur ancak artık başka bir işlem yürütmektedir ve exec*(..) bitene kadar child beklemededir.
EXEC() fork işlemi sonrasında alt ve üst prosesler aynı program kodunu çalıştırıyor durumda olurlar. Halbuki pek çok uygulamada programcı yaratmış olduğu alt prosesin farklı bir program kodunu çalıştırmasını ister. İşte exec fonksiyonları prosesin başka bir program olarak çalışmaya devam etmesini sağlamaktadır. exec işlemleri sonrasında prosesin id değeri ve kontrol bloğu değişmez. Prosesin kod, data ve bash alanları çalıştırılabilen (executable) dosyadan alınarak yüklenir. Proses artık yaşamını başka bir program olarak sürdürür. Tüm exec fonksiyonlarının prototipleri <unistd.h> dosyası içersindedir.
EXEC() Exec komutu aldığı parametrelere ve gördüğü işlevlere göre 6 farklı şekilde çalışırlar. Bunlar ve aldıkları parametreler aşağıdaki gibidir. int execl(const char *path, const char *arg0, … /*,(char *) 0 */); int execv(const char *path, char *const argv[]); int execle(const char *path, const char *arg0, … /*, (char *) 0, char *const envp[] */); int execve(const char *path, char *const argv[], char *const envp[]); int execlp(const char * file, const char *arg0, … /*, (char *) 0 */); int execvp(const char *file, char *const argv[]);
EXEC() exec fonksiyonlarının l’li (execl, execle, execlp) ve v’li (execv, execve, execvp) biçimleri vardır. Bunların l’li biçimleri komut satırı argümanlarını bir liste olarak, v’li biçimleri ise bir dizi olarak alırlar. Ayrıca bazı exec fonksiyon isimlerinin ‘e’ ya da ‘p’ harfiyle sonlandırıldığını göreceksiniz. Fonksiyonların e’li biçimleri çevre değişkenlerini (environment variables) de programcıdan istemektedir. p’li biçimler çalıştırılabilen dosyanın yerinin belirlenmesinde PATH çevre değişkenlerini dikkate alırlar.
fork() ve exec*() işlemlerinin yürümesi yandaki şekilde ifade edildiği gibidir.
Getpid() Mevcut process için process_ID’yi döndürür. Örnek bir kod aşağıdaki şekildedir. #include <stdio.h> #include <sys/types.h> #include <unistd.h> #include <sys/wait.h> int main(void) { int pid; pid = getpid(); printf("ilk alınan pid : %d\n", pid); int fork_donus_degeri = fork(); if (fork_donus_degeri == 0) { // burasi child processte calisir printf("CHILD pid : %d\n", pid); } else if (fork_donus_degeri > 0) { // burasi parent processte calisir pid = getppid(); printf("PARENT pid : %d\n", pid); else if (fork_donus_degeri == -1) { printf("ERROR : \n"); return 1; pid = getpid(); printf("son alinan pid : %d\n", pid); return 0;
getppid() , getpgrp() getppid(), mevcut bir child process’in parent process ID’sini döndürür. getpgrp(), bir process grubunun (parent ve child içeren) grup ID’sini döndürür. Bir grubun grup ID’si, o child processleri oluşturan parent process’in ID’sidir.
Örnekler fork.c #include <stdio.h> #include <sys/types.h> #include <unistd.h> int main(){ pid_t cocuk_pid; printf(“Ana sürecin pid = %d\n”, (int)getpid() ); cocuk_pid=fork(); if (cocuk_pid!=0){ printf(“burası ana sürectir, süreç id pid=%d\n”,(int)getpid()); printf(“çocuk sürecin idsi pid = %d\n”,(int)cocuk_pid); } else{ printf(“burası çocuk süreçtir, pid=%d\n”, (int)getpid()); } return 0; }
Waitpid() #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/wait.h> void err_sys(const char *msg) { perror(msg); fflush(stdout); exit(EXIT_FAILURE); } int main(void) { pid_t pidChild; if ((pidChild = fork()) < 0) err_sys("fork"); if (pidChild == 0) { /* Alt proses */ sleep(5); exit(100); } else { /* Ust proses */ int stat; printf("Alt prosesin sonlanmasi bekleniyor...\n"); if (waitpid(pidChild, &stat, 0) < 0) err_sys("waitpid"); } return 0; }