Merhaba, bu yazıda linux sistemlerindeki fork, exec ve wait işlemlerine değineceğiz.

Fork

Linux sistemlerinde yeni bir proses oluşturmak için fork() fonksiyonu kullanılır. Bu fonksiyon çağrıldığı prosesin bir kopyasını oluşturur ve geriye proses id döndürür. Genel olarak oluşturulan prosese child process, oluşturana ise parent process denir.

Fork işlemi başarısız olursa -1 döndürür. Başarılı olduğunda ise parent process tarafında child process’in proses id’sini, child process tarafında ise 0 döndürür.

Proses id’ler pid_t tipinde tutulur.

Örnek

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int main()
{
    pid_t processId;

    //Fork fonksiyonu ile yeni bir proses oluşturuluyor.
    processId = fork();

    if(processId < 0){ //Fork işlemi başarısız
      exit(EXIT_FAILURE);
    } else if(processId > 0){ 
      //Parent proseste yapılacak işlemler
    } else if (processId == 0) {
      //Child proseste yapılacak işlemler
    }
    
    return 0;
}

Yukarıdaki kodu kısaca açıklamak gerekirse; fork fonksiyonu çağrıldığı anda program çatallanır yani parent ve child olmak üzere iki kola ayrılır. Parent kolunda fork fonksiyonu oluşan child prosesin id’sini döndürür. Child kolunda ise fork geriye 0 döndürür. Bu sayede if else koşulları ile geriye dönen proses id’sini kontrol ederek şu an hangi prosesin içinde olduğumuzu anlayabilir ve ona göre işlemlerimizi yapabiliriz.

Exec

Bir prosesin içerisinde ayrı bir program çalıştırmak için kullanılır.

Aynı işi yapan fakat aralarında çok küçük farklılıklar bulunan 6 adet exec fonksiyonu vardır. İlk parametre olarak çalıştırılabilir dosyanın yol bilgisini alırlar.

  • 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 fonksiyon isimlerinin sonundaki harflere açıklık getirelim.

  • l harfi varsa, exec fonksiyonu çalıştıracağımız programa gönderilecek olan parametreleri liste şeklinde alır.
  • v harfi varsa, exec fonksiyonu çalıştıracağımız programa gönderilecek olan parametreleri dizi şeklinde alır.
  • e harfi varsa, exec fonksiyonu ek olarak çevre değişkeni(envp) parametresi alır.
  • p harfi varsa, exec fonksiyonu çalıştırılabilir dosyanın yerinin belirlenmesinde PATH çevre değişkenlerine bakar.

Not: Parametreleri dizi ya da liste şeklinde gönderirken, dizinin ya da listenin son elemanı NULL olmalı. Yalnız liste şeklinde gönderirken birkaç sebepten dolayı NULL yerine NULL ile aynı anlama gelen (char *) 0 değerini kullanıyoruz.

execv Kullanımı

main.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int main(){

  //execv parametreleri dizi olarak aldığından, parametre dizimizi oluşturuyoruz.
  char *args[] = {"another", "Onur", NULL};

  //Burada exec fonksiyonu 0'dan küçük değer döndürürse başarısız demektir.
  if(execv("another", args) < 0){
    exit(EXIT_FAILURE);
  }

  return EXIT_SUCCESS;
}

another.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <stdio.h>
#include <stdlib.h>

int main(int argc, char *argv[]){

  int i;

  //Gönderdiğimiz parametreler argv dizisine gelecek. Burada tüm parametreleri yazdırıyoruz.
  for(i=0; i<argc; i++)
  {
      printf("%s\n",argv[i]);
  }

  return EXIT_SUCCESS;
}

Yukarıda main.c dosyasında execv kullanarak aynı dizinde bulunan another isimli programı çalıştırdık ve parametre olarak “another” ve “Onur” değerlerini gönderdik.(Dizi şeklinde gönderim)

main.c ve another.c derlenip main programı çalıştırıldığında çıktı şu şekilde olacaktır.

another
Onur

execl Kullanımı

main.c

1
2
3
4
5
6
7
8
9
10
11
12
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int main(){

  if(execl("another", "İlkay", "Günel", (char *) 0) < 0){
    exit(EXIT_FAILURE);
  }

  return EXIT_SUCCESS;
}

another.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <stdio.h>
#include <stdlib.h>

int main(int argc, char *argv[]){

  int i;

  //Gönderdiğimiz parametreler argv dizisine gelecek. Burada tüm parametreleri yazdırıyoruz.
  for(i=0; i<argc; i++)
  {
      printf("%s\n",argv[i]);
  }

  return EXIT_SUCCESS;
}

Yukarıda main.c dosyasında execl kullanarak aynı dizinde bulunan another isimli programı çalıştırdık ve parametre olarak “İlkay” ve “Günel” değerlerini gönderdik.(Liste şeklinde gönderim)

main.c ve another.c derlenip main programı çalıştırıldığında çıktı şu şekilde olacaktır.

İlkay
Günel

execlp Kullanımı

main.c

1
2
3
4
5
6
7
8
9
10
11
12
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int main(){

  if(execlp("ls", "-l", (char *) 0) < 0){
    exit(EXIT_FAILURE);
  }

  return EXIT_SUCCESS;
}

Burada sonunda p harfi olan exec fonksiyonu arkaplanda ne yapıyor bir bakalım; execlp fonksiyonu ilk parametre olarak girilen programı sistemin PATH değişkeninde yazılı olan yerlerde arar. ls programı sistemde /bin konumunda bulunuyor. PATH değişkeni içerisinde de bin klasörü tanımlı olduğundan programımızı aynı dizinde olmamasına rağmen çalıştırabildik.

main.c dosyası derlenip çalıştırıldığında Linux sistemlerinde dahili olarak bulunan ls programı -l parametresiyle çalıştırılacaktır ve çıktı şu şekilde olacaktır.

total 36
-rwxrwxr-x 1 onur onur 8645 Oct 24 11:53 another
-rw-rw-r-- 1 onur onur  203 Oct 24 11:48 another.c
-rwxrwxr-x 1 onur onur 8602 Oct 24 11:53 main
-rw-rw-r-- 1 onur onur  255 Oct 24 11:50 main.c
-rw-rw-r-- 1 onur onur   61 Oct 23 14:45 makefile

execvp Kullanımı

main.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int main(){

    char *args[] = {"ls", "-l", NULL};

    if (execvp("ls", args) < 0) {
        perror("execvp");
        exit(EXIT_FAILURE);
    }

  return EXIT_SUCCESS;
}

Bir önceki örnekte yapılan işlemler burada da yapıldı. Tek fark şu: sonunda v olan exec fonksiyonu kullandığımız için parametreleri dizi olarak verdik.

main.c derlenip çalıştırıldığında çıktı şu şekilde olacaktır.

total 36
-rwxrwxr-x 1 onur onur 8645 Oct 24 11:53 another
-rw-rw-r-- 1 onur onur  203 Oct 24 11:48 another.c
-rwxrwxr-x 1 onur onur 8602 Oct 24 11:53 main
-rw-rw-r-- 1 onur onur  255 Oct 24 11:50 main.c
-rw-rw-r-- 1 onur onur   61 Oct 23 14:45 makefile

Not: Sonunda p olan exec fonksiyonları ilk parametre olarak verilen programı aynı dizinde aramazlar. Fakat eğer biz program ismimizin başına ./ koyarak parametre olarak verirsek aynı dizinde aramasını sağlayabiliriz. Kendi yazdığımız bir programı komut satırından çalıştırmak istediğimizde başına ./ koymamızın sebebi de budur. Çünkü Linux sistemi yazdığımız program ismini arka planda sonu p ile biten exec fonksiyonlarıyla çağırır.

execve Kullanımı

Not: Aşağıdaki örneklerde gönderdiğimiz çevre değişkenleri another.c isimli dosyadaki extern char **environ; değişkenine atanmaktadır.

main.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int main(){

  char *args[] = {"another", NULL};
  char *env[] = {"name=Mustafa Demir", NULL};

  if (execve("another", args, env) < 0)  {
      exit(EXIT_FAILURE);
  }

  return EXIT_SUCCESS;
}

another.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <stdio.h>
#include <stdlib.h>

extern char **environ;

int main(int argc, char *argv[]){

  int i;

    for (i = 0; environ[i] != NULL; ++i)
        puts(environ[i]);


  return EXIT_SUCCESS;
}

Yukarıda main.c dosyasının içinde execve kullanarak aynı dizinde bulunan another isimli programı çağırdık. Parametre olarak “another” değerini, çevre değişkeni olarak da “name=Mustafa Demir” değerini yolladık. Sonunda e olan exec fonksiyonu kullanmamız bize çalıştıracağımız programa çevre değişkeni gönderebilmemizi sağladı.

main.c ve another.c derlenip main programı çalıştırıldığında çıktı şu şekilde olacaktır.

name=Mustafa Demir

execle Kullanımı

main.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int main(){

  char *env[] = {"name=Alican Akkuş", NULL};

  if (execle("another", "another", (char *) 0, env) < 0) {
      exit(EXIT_FAILURE);
  }

  return EXIT_SUCCESS;
}

another.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <stdio.h>
#include <stdlib.h>

extern char **environ;

int main(int argc, char *argv[]){

  int i;

    for (i = 0; environ[i] != NULL; ++i)
        puts(environ[i]);


  return EXIT_SUCCESS;
}

Bir önceki örnekten farklı bir şey yapmadık. Sadece sonunda l olan bir exec fonksiyonu kullandığımız için parametreleri liste şeklinde yolladık.

main.c ve another.c derlenip main programı çalıştırıldığında çıktı şu şekilde olacaktır.

name=Alican Akkuş

Wait

Bir prosesin başka bir prosesi beklemesi için kullanılır. Wait fonksiyonu alt proseslerden herhangi birisi sonlanıncaya kadar bekler. Yani birden fazla alt proses varsa hepsini beklemez.

Fonksiyonunun tanımı şu şekildedir;

  • pid_t wait(int *status);

Wait fonksiyonu geriye sonlanan prosesin proses id‘sini döndürür. Parametre olarak int tipinde bir değişkenin adresini alır. Bu değişkene sonlanan prosesin çıkış kodu yazılır. Not: Parametre olarak NULL verilirse sonlanan prosesin çıkış kodunu vermez.

Örnek

main.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <stdio.h>
#include <stdlib.h>

int main(){

  pid_t processId;
  int status;

  processId = fork();

  if(processId < 0){
    exit(EXIT_FAILURE);
  } else if(processId > 0){ //Parent process
    wait(NULL);
    //wait(&status); şeklinde çağırsaydım çıkış kodu status değişkenine yazılacaktı
    printf("Child Process işini tamamlayıncaya kadar beklendi\n");
  } else if(processId == 0){ //Child process
    sleep(5);
  }

  return EXIT_SUCCESS;
}

Yukarıdaki kodda önce fork() fonksiyonu ile child process oluşturduk. Parent process içerisinde wait() fonksiyonu ile alt proses sonlanıncaya kadar beklemesini söyledik. Child process içerisinde ise programı 5 saniye duraklattık.

main.c derlenip çalıştırıldığında 5 saniye boyunca program bekleyecek ve bu sürenin sonunda Child Process işini tamamlayıncaya kadar beklendi yazacaktır. Yani parent process, child processi beklemiş olacaktır.

Kaynaklar:

www.kaanaslan.com - Exec

www.kaanaslan.com - Fork