Yazan : Şadi Evren ŞEKER

Bu yazının amacı, bilgisayar bilimlerinin bir çalışma alanı olan işletim sistemlerinde sıklıkla kullanılan ve yeni bir işlem (process) oluşturmaya yarayan fork() ve exec() fonksiyonlarını açıklamaktır. Bu fonksiyonlar C programlama dilleri tarafından desteklenmekte ve unistd.h dosyasının içinde bulunmaktadır.

Örnek bir kod verip çalışmasını açıklayarak konuyu anlatmaya başlayalım:

#include <unistd.h> /* fork fonksiyonu için */
#include <sys/types.h> /* pid yapısı için */ 
#include <stdio.h> /* klasik girdi çıktı */
#include <stdlib.h> /* standart lib fonksiyonları */
 
int main()
{
 pid_t cocukpid; /* çocuğun pid değeri için */
 int cocukokunan; /* çocuğun döndürdüğü değer */
 int durum; /* çocuğun çıkış durumu */

 /* yeni bir işlem oluşturuyoruz */
 cocukpid = fork();
 
 if (cocukpid >= 0) /* şayet if sağlanırsa çocuk doğdu */
 {
 if (cocukpid == 0) /* çocuk için pid 0 olur*/
 {
 printf("COCUK: Ben cocuk islemim!n");
 printf("COCUK: PID: %dn", getpid());
 printf("COCUK: Ata PID: %dn", getppid());
 printf("COCUK: cocukpid: %dn", cocukpid);
 printf("COCUK: Bir deger girin (0 ile 255 arasi): ");
 scanf(" %d", &cocukokunan);
 printf("COCUK: Cocuk oluyor!n"); 
 exit(cocukokunan); /* cocuk degeri dondurup öldü */
 }
 else /* fork() ata için pid döndürdü demektir */
 {
 printf("ATA: Ata işlem!n");
 printf("ATA: Ata PID: %dn", getpid());
 printf("ATA: cocugun pid kopyasi %dn", childpid);
 printf("ATA: cocugun bitmesi icin bekleniyor.n");
 wait(&durum); /*cocugun durumunu bekle */
 printf("ATA: Cocugun cikis kodu: %dn", WEXITSTATUS(durum)); 
 exit(0); /* parent exits */ 
 }
 }
 else /* Fork hatasi olduysa -1 döner */
 {
 perror("fork"); /* hata durumu mesaji */
 exit(0); 
 }
}

Yukarıdaki kodda, fork() fonksiyonu çağrılmış ve döndürdüğü değer, cocukpid isimli bir yapıya (struct) döndürülmüştür. Bu yapı (struct) daha önceden tanımlı olan ve sys/types.h dosyasından alınan pid_t tipindedir. Aslında yapı incelendiğinde basit bir int yapısı olduğu görülür. Ancak işletim sistemi ile programlama sırasında bu şekilde yapılar kullanılması gerekmektedir. Bunun sebebi hem standartlaşma hem de güvenlik olarak düşünülebilir.

Sonuçta fork() fonksiyonu çağrıldıktan sonra dönen değer için iki ihtimal bulunmaktadır. Ya 0’dan büyük ve eşit ya da eksi bir değer olacaktır. Eksi değer dönmesi sadece fonksiyonun hatalı olması anlamındadır. Yani -1 hata kodudur ve bu durumda en altta bulunan else bloğu çalışacak ve hata mesajı ekrana basılacaktır.

Diğer taraftan 0’dan büyük eşit olması durumunda da iki ihtimal bulunmaktadır, ancak burada bir fark vardır. Fonksiyon çağrıldıktan sonra hem 0 hem de 0’dan büyük bir değer döner. Bunu aynı anda iki işlemin (process) çalışması ile açıklamak gerekir:

Yukarıdaki şekilde görüldüğü üzere fork() fonksiyonu çağrıldıktan sonra, iki ayrı işlem olduğu söylenebilir. Bunlardan ilki pid==0, if bloğuna girerken ikincisi pid>0 if bloğuna girer ve çalıştırır.

Yukarıdaki kodda, ayrıca iki if bloğu içerisinde de temel bazı bilgiler ekrana basılmıştır.

Bu basılan değerleri anlamak için örnek bir çalışma aşağıda verilmiştir:

SADIs-MacBook-Air:Downloads sadievrenseker$ ./a.out
ATA: Ata işlem!
ATA: Ata PID: 23256
ATA: cocugun pid kopyasi 23257
ATA: cocugun bitmesi icin bekleniyor.
COCUK: Ben cocuk islemim!
COCUK: PID: 23257
COCUK: Ata PID: 23256
COCUK: cocukpid: 0
COCUK: Bir deger girin (0 ile 255 arasi): 44
COCUK: Cocuk oluyor!
ATA: Cocugun cikis kodu: 44
SADIs-MacBook-Air:Downloads sadievrenseker$

Yukarıda görüldüğ üzere, öncelikle ATA işlem çalışır. Bu işlem o anda işletim sistemi tarafından kendisine atanan işlem numarasını (process id , PID) ekrana basmıştır. Ardından çocuk işlemin PID değerine ulaşarak ekrana basar. Bu basma işlemleri için önce getpid() fonksiyonu çağrılarak mevcut işlemin pid değeri alınır ardından childpid değişkeninin değeri alınarak ekrana basılır. Bu iki değeri bastıktan sonra çocuktan gelecek değer beklenmeye başlanır ( wait fonksiyonu ile).

Bu esnada çocuk tarafında, sırasıyla getpid ve getppid fonksiyonları ile, önce çocuğun PID değeri ardıdan da çocuğun atasının PID değeri (getppid = get parent pid) alınarak ekrana basılır. Çocuk tarafından cocukpid değişken değeri ekrana basıldıktan sonra kullanıcıdan scanf fonksiyonu ile bir girdi okunur. Okunan değer, exit fonksiyonuna parametre verilerek ata işleme geri döndürülür. Bu sırada bekleyen ata işlem gelen kodu alarak ekrana basar. Yukarıdaki örnek çalışmada bu girdi kullanıcı tarafından 44 olarak verilmiştir.

Yorumlar

  1. taha

    Hocam çok teşekkür ederim bende kaç günden beri uğraşıyorum mantık olarak anlayamamıştım. child procesin önceliği daha düşük oluyor olduğu için ilk önce parent process çalışıyor diye anladım vermiş oldupunuz resim anlamama çok yardımcı oldu tekrar teşekkür ederim

  2. Furkan

    Hocam merhaba, kodda childpid ve cocukpidin değişmesi gerekiyor, sanırım dikkatinizden kaçmış. Bir de sizde önce ata işlem çalışmış, derste hocamızın bize dediğine göre child process oluşturulduğunda ata process ready state durumuna gider ve child process çalışır demişti, hatta kodunuzu Ubuntu’da gcc ile derleyip çalıştırdığımda önce child process çalıştı, daha sonra ata process çalıştı. Biraz açıklama yapabilir misiniz?

Bir cevap yazın

E-posta hesabınız yayımlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir