> Proseslerin "exit code" Bilgileri: Bir proses sonlandığında işletim sistemine "exit kodu" ya da "exit durumu" denilen bir kod iletmektedir. Proseslerin exit kodları tamsayı bir değerdir. İşletim sistemleri bu exit kodunu alır. Ancak onun hangi değerde olduğuna bakmaz. Bu exit kod prosesi yaratan proses (yani üst proses) isterse ona verilmektedir. Böylece biz bir programı çalıştırdıktan sonra onun işini başarılı bir biçimde yapıp yapmadığını exit koduna bakarak anlayabiliriz. Proseslerin exkit kodları fonksiyonların geri dönüş değerlerine benzetilebilir. Geleneksel olarak işletim sistemlerinde başarılı sonlanmalar için sıfır değeri başarısız sonlanmalar için sıfır dışı değerler kullanılmaktadır. Bir C programında prosesin exit odu standart exit fonksiyonunun argümanı ile belirlenmektedir. exit fonksiyonunun prototipini anımsayınız: void exit(int status); Ayrıca C'de okunabilirliği artırmak için içerisinde EXIT_SUCCESS ve EXIT_FAILURE sembolik sabitleri de bulundurulmuştur. C standartlarında bu sembolik sabitlerin değerleri belirtilmemiş olsa da tipik olarak 0 ve 1 biçimindedir: #define EXIT_SUCCESS 0 #define EXIT_FAILURE 1 Pekiyi biz hiç exit fonksiyonunu çağırmazsak prosesin exit kodu ne olacaktır? İşte bu durumda main fonksiyonun geri dönüş değeri prosesin exit kodu olmaktadır. C standartları main fonksiyonunun exit(main(...)) biçiminde çağrıldığını belirtmektedir. Pekiyi bir program normal dışı sonlanamaz mı? Örneğin standart abort fonksiyonunun bir exit kod parametresi yoktur: void abort(void); Bir program abort fonksiyonuyla sonlanırsa ya da çökerse exit kodu ne olacaktır? İşte bu tür durumlarda prosesin exit kodları belirsiz durumda olur. Dolayısıyla programcının eğer program normal olarak sonlanmışsa exit koduna bakması uygun olur. UNIX/Linux kabuk programlarında son çalıştırılan programın exit kodu $? kabuk değişkeni ile elde edilebilmektedir. Örneğin: $ ./sample $ echo $? 123 Aynı şey Windows kabuklarında errorlevel değişkeni ile yapılmaktadır. Örneğin: C:\>sample C:\>echo %errorlevel% 123 C standartlarında main fonksiyonuna özgü şöyle bir durum belirtilmiştir: Eğer main fonksiyonunda hiç return kullanılmamışsa akışi ana bloğu bitirerek main sonlanmışsa sanki ana bloğun sonunda "return 0" deyim varmış gibi bir etki oluşmaktadır. Yani örneğin: int main(void) { ... return 0; } ile aşağıdakinin arasında hiçbir farklılık yoktur: int main(void) { ... } Tipik olarak bir prosesin sonlandırılması exit standart C fonksiyonuyla yapılmaktadır. (Anımsanacağı gibi eğer bu fonksiyon hiç çağrılmazsa zaten main bittiğinde çağrılmaktadır.) exit fonksiyonu aslında sonlandırmayı işletim sisteminin prosesi sonlandıran sistem fonksiyonunu çağırarak yapmaktadır. Bu sistem fonksiyonu Windows sistemlerinde ExitProcess, UNIX/Linux ve macOS sistemlerinde _exit isimli fonksiyondur. Ancak exit standart C fonksiyonu bundan önce sırasıyla şu işlemleri de yapmaktadır: -> atexit fonksiyonuyla kaydettirilmiş olan fonksiyonları ters sırada çağırır. -> Açık olan bütün dosyaların stdio tamponlarını flush eder ve bu dosyaları kapatır. Şimdi de sırasıyla bu Windows ve POSIX fonksiyonlarını inceleyelim: >> Windows Sistemlerinde: >>> "ExitProcess" : Windows sistemlerinde prosesi sonlandıran API fonksiyonu ExitProcess isimli fonksiyondur. Zaten Windows sistemlerinde C'nin standart exit fonksiyonu bu fonksiyonu çağırmaktadır. Fonksiyonun prototipi şöyledir: void ExitProcess(UINT uExitCode); Fonksiyon parametre olarak prosesin exit kodunu almaktadır. Bu fonksiyon başarısız olamamaktadır. Bu fonksiyon normal olarak standart C fonksiyonları için herhangi bir sonlandırma işlemi yapmaz. (Microsoft standart C kütüphanesinde bir süredir ExitProcess tarafından çağrılan DLL_THREAD_DETACH bildirimi yoluyla stdio tamponlarını flush etmeye başlamıştır. Bu nedenle ExitProcess uygulandığında stdio tamponları flush edilmektedir. Ancak atexit fonksiyonu ile kaydettirilen fonksiyonlar çağrılmamaktadır.) >>> "GetExitCodeProcess" : Windows sistemlerinde bir proses bir program çalıştırdığında çalıştırdığı programın exit kodunu GetExitCodeProcess isimli API fonksiyonuyla elde edebilmektedir. Bu işlemi yapabilen herhangi bir standart C fonksiyonu yoktur. Fonksiyonun prototipi şöyşedir: BOOL GetExitCodeProcess( HANDLE hProcess, LPDWORD lpExitCode ); Fonksiyonun, >>>> Birinci parametresi exit kodun elde edileceği prosesin handle değerini belirtmektedir. >>>> İkinci parametresi exit kodunun yerleştirileceği DWORD türünden nesnenin adresini almaktadır. Fonksiyon başarı durumunda sıfır dışı bir değere başarısızlık durumunda sıfır değerine geri dönmektedir. Aşağıdaki örnekte "prog1" programı "prog2" programını CreateProcess ile çalıştırıp onun exit kodunu GetExitCodeProcess API fonksiyonu ile elde etmiştir. Programlar UNICODE/ASCII uyumlu biçimde yazılmıştır. Burada "prog1" programı çalıştırdığı "prog2" programının bitmesini WaitForSingleObject fonksiyonuyla beklemektedir. Bu fonksiyonu thread'ler konusunda ele alacağız. * Örnek 1, /* prog1.c */ #include #include #include #include void ExitSys(LPCTSTR lpszMsg); int main(void) { TCHAR szPath[] = _TEXT("prog2.exe"); STARTUPINFO si = {sizeof(STARTUPINFO)}; PROCESS_INFORMATION pi; DWORD dwExitCode; if (!CreateProcess(NULL, szPath, NULL, NULL, TRUE, CREATE_NEW_CONSOLE, NULL, NULL, &si, &pi)) ExitSys(_TEXT("CreateProcess")); _tprintf(_TEXT("waiting for process to exit...\n")); if (WaitForSingleObject(pi.hProcess, INFINITE) == WAIT_FAILED) ExitSys(_TEXT("WaitForSingleObject")); if (!GetExitCodeProcess(pi.hProcess, &dwExitCode)) ExitSys(_TEXT("GetExitCodeProcess")); if (dwExitCode == STILL_ACTIVE) { _tprintf(_TEXT("Proces still active...\n")); exit(EXIT_FAILURE); } _tprintf(_TEXT("Exit Code: %lu\n"), dwExitCode); CloseHandle(pi.hThread); CloseHandle(pi.hProcess); return 0; } void ExitSys(LPCTSTR lpszMsg) { DWORD dwLastError = GetLastError(); LPTSTR lpszErr; if (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, NULL, dwLastError, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)&lpszErr, 0, NULL)) { _ftprintf(stderr, _TEXT("%s: %s"), lpszMsg, lpszErr); LocalFree(lpszErr); } exit(EXIT_FAILURE); } /* prog2.c */ #include #include int _tmain(void) { _tprintf(_TEXT("press ENTER to exit...\n")); getchar(); return 123; } >>> "TerminateProcess" : exit standart C fonksiyonu ve ExitProcess API fonksiyonu kendi prosesini sonlandırmaktadır. Ancak bazen bir prosesi zorla sonlandırmak da isteyebiliriz. Bunun için Windows sistemlerinde TerminateProcess isimli API fonksiyonu kullanılmaktadır. Fonksiyonun prototipi şöyledir: BOOL TerminateProcess( HANDLE hProcess, UINT uExitCode ); Fonksiyonun, >>>> Birinci parametresi sonlandırılacak prosesin handle değerini, >>>> İkinci parametresi sonlandırılacak prosesin exit kodunu belirtmektedir. Fonksiyon başarı durumunda sıfır dışı bir değere başarıızlık durumunda sıfır değerine geri dönmektedir. TerminateProcess fonksiyonu geri döndüğünde proses sonlanmış olmak zorunda değildir. Ancak sonlanma süreci başlatılmıştır. Sonlanmanın yine WaitForSingleObject gibi bir fonksiyonla beklenmesi uygun olur. Öte yandan her proses handle değerini bildiği her prosesi sonlandıramaz. Ancak üst proses genel olarak kendi alt proseslerini sonlandırabilmektedir. Windows'taki yetkisel özellikler karmaşık bir konusudur. Bu kursta ele alınmamaktadır. ("Windows Sistem Programalama" kursunda bu konu ele alınmaktadır.) TerminateProcess fonksiyonu son çare olarak uygulanmalıdır. Çünkü programın ansızın sonlandırılması o programın çalışması sırasında olumsuzluklar oluşturabilir. Proses kritik birtakım işlemleri yaparken sonlandırılırsa sorunlar oluşabilmektedir. * Örnek 1, Aşağıdaki örnekte "prog1" programı ENTER tuşuna basıldığında "prog2" programını zorla TerminateProcess API fonksiyonuyla sonlandırmaktadır. Kod yine UNICODE/ASCII uyumlu yazılmıştır. /* prog1.c */ #include #include #include #include void ExitSys(LPCTSTR lpszMsg); int main(void) { TCHAR szPath[] = _TEXT("prog2.exe"); STARTUPINFO si = {sizeof(STARTUPINFO)}; PROCESS_INFORMATION pi; DWORD dwExitCode; if (!CreateProcess(NULL, szPath, NULL, NULL, TRUE, CREATE_NEW_CONSOLE, NULL, NULL, &si, &pi)) ExitSys(_TEXT("CreateProcess")); _tprintf(_TEXT("Press ENTER to to terminate child process...\n")); getchar(); if (!TerminateProcess(pi.hProcess, 999)) ExitSys(_TEXT("TerminateProcess")); if (WaitForSingleObject(pi.hProcess, INFINITE) == WAIT_FAILED) ExitSys(_TEXT("WaitForSingleObject")); if (!GetExitCodeProcess(pi.hProcess, &dwExitCode)) ExitSys(_TEXT("GetExitCodeProcess")); if (dwExitCode == STILL_ACTIVE) { _tprintf(_TEXT("Proces still active...\n")); exit(EXIT_FAILURE); } _tprintf(_TEXT("Exit Code: %lu\n"), dwExitCode); CloseHandle(pi.hThread); CloseHandle(pi.hProcess); return 0; } void ExitSys(LPCTSTR lpszMsg) { DWORD dwLastError = GetLastError(); LPTSTR lpszErr; if (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, NULL, dwLastError, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)&lpszErr, 0, NULL)) { _ftprintf(stderr, _TEXT("%s: %s"), lpszMsg, lpszErr); LocalFree(lpszErr); } exit(EXIT_FAILURE); } /* prog2.c */ #include #include #include int _tmain(void) { for (int i = 0; i < 100; ++i) { _tprintf(_TEXT("%d\n"), i); Sleep(1000); } return 0; } >> UNIX/Linux Sistemleri: Biz Windows sistemlerinde prosesin exit kodunu GetExitCodeProcess isimli API fonksiyonuyla elde etmiştik. WaitForSingleObject fonksiyonunu ise Proses sonlanana kadar beklemek için kullanmıştık. Şimdi de UNIX/Linux sistemlerinde benzer işlemlerin nasıl yapılacağını göreceğiz. UNIX/Linux sistemlerinde alt proses sonlanana kadar bekleme işlemi ve alt prosesin exit kodunu alma işlemi wait, waitpid ve waitid isimli üç POSIX fonksiyonu kullanılmaktadır. Linux sistemlerinde wait3 ve wait4 isimli wait fonksiyonları da vardır. wait fonksiyonlarının iki önemli işlevi vardır: Alt proses bitene kadar üst prosesi blokede bekletmek ve alt prosesin exit kodunu elde etmek. Çok eskidne yalnızca wait fonksiyonu vardı. Sonra bu fonksiyonun yetersizlikleri nedeniyle waitpid ve sonra da waitid fonksiyonları tasarlandı. Bu fonksiyonunların hepsinin prototipleri dosyası içerisindedir. Şimdi de sırasıyla bu wait fonksiyonları inceleyelim: >>> "wait" : wait fonksiyonunun prototipi şöyleid:r #include pid_t wait(int *status); Fonksiyon parametre olarak alt prosesin exit kodunun ve sonlanma biçiminin (buna "status" de denilmektedir) yerleştirileceği int nesnenin adresini almaktadır. Fonksiyon başarı durumunda beklenen alt prosesin id değeri ile başarısızlık durumunda -1 değeri ile geri dönmektedir. Fonksiyon çağrıldığında bir ya da birden fazla ilt prosesin hiçbiri henüz sonlanmamış olabilir. Bu durumda fonksiyon ilk sonlanacal alt prosese kadar bekleme yapar. Eğer fonksiyon çağrıldığında zaten bir ya da birden fazla alt proses sonlanmış durumdaysa fonksiyon bunlardan herhangi birinin durumunu (status) elde ederek hemen geri döner. Fonksiyonun ilk sonlanmış olan prosesin durumuyla geri dönmesi garanti edilmemiştir. Fonksiyonun parametresi NULL adres de geçilebilir. Bu durumda fonksiyon yine bekleme işlevini yerine getirir ancak alt prosesin durum bilgisini programcıya iletmez. (Yani programcı yalnızca alt prosesi beklemek istiyorsa, onun durum bilgisini elde etmek istemiyorsa parametre olarak NULL adres geçebilir.) Fonksiyonun bize verdiği durumsal bilgide hem prosesin nasıl sonlandığı hem de exit kodu bulunmaktadır. Bu bilgilerin int nesnenin neresinde depolandığı standart olarak belirlenmemiştir. Bunun için WIFEXITED ve WIFSIGNALED makroları kullanılmaktadır. Bu iki makro da wait fonksiyonun parametresine geçirilen int nesneyi parametre olarak almaktadır. WIFEXITED normal sonlanma durumunu, WIFSIGNALED sinyal dolayısıyla normal olmayan sonlanma durumunu test etmektedir. Bu makrolar sıfır ya da sıfır dışı değere geri dönmektedir. int nesne içerisindeki prosesin exit kodu WEXITSTATUS makrosuyla elde edilmektedir. Tabii programcı ancak proses normal sonlanmışsa exit kodunu bu makroyla almaya çalışmalıdır. Örneğin: int status; ... if (wait(&status) == -1) exit_sys("wait"); if (WIFEXITED(status)) printf("Exit code: %d\n", WEXITSTATUS(status)); else /* if (WIFSIGNALED(status)) */ printf("child terminates via signal!..\n"); Aşağıdaki örnekte "sample" programı komut satırı argümanlarıyla aldığı programı fork/exec ile çalıştırıp onun sonlanmasını beklemektedir. "sample" programını aşağıdaki gibi çalıştırabilirsiniz: $ ./sample mample Buradaki "mample" programı birer saniye aralıklarla ekrana sayıları basıp 123 exit koduyla geri dönmektedir. Sonra da "sample" programını standart komutlar için çalıştırarak durumu gözlemleyiniz: $ ./sample /bin/ls Pek çok UNIX/Linux sisteminde prosesin exit kodu 1 byte uzunlukta işaretsiz tamsayı türündendir. * Örnek 1, /* sample.c */ #include #include #include #include void exit_sys(const char* msg); int main(int argc, char* argv[]) { pid_t pid; int status; if (argc == 1) { fprintf(stderr, "wrong number of arguments!..\n"); exit(EXIT_FAILURE); } if ((pid = fork()) == -1) exit_sys("fork"); if (pid == 0 && execv(argv[1], &argv[1]) == -1) exit_sys("execv"); printf("parent process waiting for the child to exit...\n"); if (wait(&status) == -1) exit_sys("wait"); if (WIFEXITED(status)) printf("child exited with %d\n", WEXITSTATUS(status)); else /* if (WIFSIGNALED(status)) */ printf("child terminates via signal!..\n"); return 0; } void exit_sys(const char* msg) { perror(msg); exit(EXIT_FAILURE); } /* mample.c */ #include #include #include int main(int argc, char* argv[]) { for (int i = 0; i < 10; ++i) { printf("%d\n", i); sleep(1); } exit(123); return 0; } * Örnek 2, Aşağıdaki örnekte üst proses üç farklı alt proses yaratmıştır ve hepsini wait fonksiyonu ile beklemiştir. Ekranda aşağıdaki gibi bir çıktı göreceksiniz: child created: 3471 child running... child created: 3472 child running... child created: 3473 child (3472) terminated with 20 child running... child (3471) terminated with 10 child (3473) terminated with 30 Tabii buradaki alt proseslerin sonlanma sıraları birbirlerinden farklı olabilecektir. #include #include #include #include #include void exit_sys(const char* msg); int main(void) { pid_t pid1, pid2, pid3, pid; int status; if ((pid1 = fork()) == -1) exit_sys("fork"); if (pid1 == 0) { printf("child running...\n"); exit(10); } else printf("child created: %jd\n", (intmax_t)pid1); if ((pid2 = fork()) == -1) exit_sys("fork"); if (pid2 == 0) { printf("child running...\n"); exit(20); } else printf("child created: %jd\n", (intmax_t)pid2); if ((pid3 = fork()) == -1) exit_sys("fork"); if (pid3 == 0) { printf("child running...\n"); exit(30); } else printf("child created: %jd\n", (intmax_t)pid3); for (int i = 0; i < 3; ++i) { if ((pid = wait(&status)) == -1) exit_sys("wait"); if (WIFEXITED(status)) printf("child (%jd) terminated with %d\n", (intmax_t)pid, WEXITSTATUS(status)); else /* if (WIFSIGNALED(status)) */ printf("child terminates via signal!..\n"); } return 0; } void exit_sys(const char* msg) { perror(msg); exit(EXIT_FAILURE); } >>> "waitpid" : waitpid fonksiyonu wait fonksiyonunun daha gelişmiş bir biçimidir. Fonksiyonun prototipi şöyledir: #include pid_t waitpid(pid_t pid, int *status, int options); Fonksiyonun birinci parametresi beklenecek prosesin id'sini belirtmektedir. Anımsanacağı gibi wait fonksiyonu herhangi bir alt prosesi bekliyordu. Oysa waitpid fonksiyonunda biz fonksiyonun hangi alt prosesi bekleyeceğini belirleyebiliyoruz. Fonksiyonun ikinci parametresi yine durum bilgisinin yerleştirileceği int nesnesnenin adresini almaktadır. Üçüncü parametre aşağıdaki aşağıdkai sembolik sabitlerin bir ya da birden fazlasının bit OR ile işleme sokulmasıyla oluşturulabilir: WCONTINUED WNOHANG WUNTRACED Bu parametre 0 da geçilebilmektedir. WNOHANG bekleme yapmadan alt prosesin sonlanıp sonlanmadığını belirlemek amacıyla kullanılmaktadır. Fonksiyonun birinci parametresi özel olarak -1 biçiminde girilirse herhangi bir alt proses beklenir. Başka bir deyişle wait(&status) çağrısı ile waitpid(-1, &status, 0) çağrısı eşdeğerdir. Eğer bu parametre 0 girilirse fonksiyonu çağıran proses ile aynı proses grubunundaki alt proseslerden herhangi bir beklenmektedir. Eğer pid değeri -1 değerindne küçükse fonksiyon bu negatif değerin mutlak değerine ilişkin proses grubunundaki herhangi bir alt prosesi beklemektedir. Proses grupları konusu bu kursta ele alınmayacaktır. waitpid fonksiyonun da ikinci parametresi NULL adres geçilebilir. Bu durumda alt proses beklenir ancak durumsal bilgi elde edilmez. waitpid fonksiyonu da başarı durumunda beklenen prosesin id değerine başarısızlık durumunda -1 değerine geri dönmektedir. Aşağıdaki örnekte üst proses ğüç alt proses yaratmıştır. Bu alt prosesler farklı sürelerde sleep uygulamaktadır. Ancak waitpid fonksiyonu ile bekleme alt proseslerin yaratılma sırasına göre yapılmıştır. Programı çalıştırınca aşağıdaki gibi bir çıktı elde edeceksiniz: $ ./sample child created: 3313 child created: 3314 child running... child created: 3315 child running... child running... child (3313) terminated with 10 child (3314) terminated with 20 child (3315) terminated with 30 Aşağıda bu konuya ilişkin bir örnek verilmiştir: * Örnek 1, #include #include #include #include #include void exit_sys(const char* msg); int main(void) { pid_t pids[3], pid; int status; if ((pids[0] = fork()) == -1) exit_sys("fork"); if (pids[0] == 0) { printf("child running...\n"); sleep(3); exit(10); } else printf("child created: %jd\n", (intmax_t)pids[0]); if ((pids[1] = fork()) == -1) exit_sys("fork"); if (pids[1] == 0) { printf("child running...\n"); sleep(2); exit(20); } else printf("child created: %jd\n", (intmax_t)pids[1]); if ((pids[2] = fork()) == -1) exit_sys("fork"); if (pids[2] == 0) { printf("child running...\n"); sleep(1); exit(30); } else printf("child created: %jd\n", (intmax_t)pids[2]); for (int i = 0; i < 3; ++i) { if ((pid = waitpid(pids[i], &status, 0)) == -1) exit_sys("wait"); if (WIFEXITED(status)) printf("child (%jd) terminated with %d\n", (intmax_t)pid, WEXITSTATUS(status)); else /* if (WIFSIGNALED(status)) */ printf("child terminates via signal!..\n"); } return 0; } void exit_sys(const char* msg) { perror(msg); exit(EXIT_FAILURE); } UNIX/Linux dünyasında "hortlak (zomibie)" proses denilen bir kavram vardır. Üst proses fork ile alt prosesi yarattıktan sonra alt proses üst prosesten önce sonlanırsa işletim sistemi üst prosesin wait fonksiyonlarıyla sonlanmış olan alt prosesin durum bilgisini alabileceğini düşünerek onun proses kontrol bloğunu ve dolayısıyla da proses id'sini boşaltmaz. Proseslerin durum bilgisi genel olarak proses kontrol bloklarında tutulmaktadır. Ancak sonlanmış olan alt prosesler için üst proses wait fonksiyonlarını uygulamazsa alt prosesin proses kontrol bloğu ve proses id'si sistem tarafından boşaltılmadığından dolayı bir sızıntı (leak) oluşturmaktadır. Bu sızıntı birkaç proses için önemsiz olsa da binlerce alt proses yaratan ve bunları wait ile beklemeyen uzun ömürlü prosesler söz konusu olduğunda sistemi çalışamaz hale getirecek derecede ciddi sonuçlar oluşturabilmektedir. İşte sonlandığı halde üst prosesin wait fonksiyonlarını uygulamadığı alt proseslere UNIX/Linux dünyasında "hortlak (zomibe)" proses denilmektedir. Zombie proses sonlanmıştır ancak proses kontrol bloğu tam olarak boşaltılamamıştır. Zaten zombie sözcüğü tam ölememiş, yaşamakla ölmek arasında kalmış canlılar için uydurulmuş bir sözcüktür. Bu anlamda zombie insan bulunmamaktadır. Zombie'lik ancak üst proses devam ederken alt proses sonlanmışsa ve üst proses alt prosesi wait fonksiyonlarıyla beklememişse bu süreç içerisinde oluşmaktadır. Üst prosesin de sonlanmasıyla artık alt prosesin durum bilgisini alacak bir proses kalmadığı için işletim sistemi alt prosesin kaynaklarını (proses kontrol bloğunu) boşaltır ve alt prosesin zombie'lik durumu sona erer. Eğer üst proses alt prosesten daha önce sonlanırsa bu durumdaki alt proseslere "öksüz (orphan)" prosesler denilmektedir. İşletim sistemi bir proses öksüz duruma geldiğinde 1 numaralı proses id'ye sahip olan "init" prosesini öksüz prosesin üst prosesi yapmaktadır. "init" prosesi de öksüz proses sonlandığında wait işlemi uygulayarak onun kaynaklarını boşaltmaktadır. Aşağıdaki örnekte üst proses fork ile alt proses yaratmış ancak alt proses hemen sonlanmıştır. Üst proses ise getchar fonksiyonunda bekletilmiştir. Başka bir ekrandan girip ps komutuyla bu proseslere bakıldığında alt prosesin durum bilgisinin 'Z' harfi ile gösterildiğini ve onun yanında ibaresinin bulunduğunu göreceksiniz. * Örnek 1, #include #include #include #include void exit_sys(const char* msg); int main(void) { pid_t pid; if ((pid = fork()) == -1) exit_sys("fork"); if (pid == 0) { /* child process */ exit(EXIT_SUCCESS); } printf("Press ENTER to exit...\n"); getchar(); return 0; } void exit_sys(const char* msg) { perror(msg); exit(EXIT_FAILURE); } Bazen üst proses alt prosesi yarattıktan sonra onu beklemeden bazı şeyler yapmak isteyebilir. Bu tür durumlarda üst proses wait fonksiyonlarını uygulamadığından dolayı alt proses zombie durumda kalacaktır. Zombie oluşmasının wait ile bekleme yapmadan otomatik engellenmesinin bu sistemlerde iki yolu vardır: -> Üst proses SIGCHLD sinyalini set eder. Alt proses sonlandığında işletim sistemi bu sinyali üst prosese göndermektedir. Üst proses de bu sintyalde asenkron biçimde wait uygular. -> Üst proses işin başında SIGCHLD sinyalini "ignore" edebilir. Bu durumda alt proses sonlanır sonlanmaz işletim sistemi alt prosesin kaynaklarını yok etmektedir. Biz bu kursumuzda UNIX/Linux sistemlerinde "sinyal (signal)" kavramını görmeyeceğiz. Bu konu "UNIX/Linux Sistem Programlama" kurslarında ele alınmaktadır.