Do spisu tresci tematu 2

2.1.3 Opis funkcji wait



Spis tresci


Wprowadzenie

Funkcja wait wstrzymuje proces wywolujacy do momentu, az jeden z procesow potomnych przestanie dzialac. Jezeli zakonczony potomek juz jest, to funkcja wraca natychmiast.
Implementacja funkcji wait jest funkcja systemowa sys_wait4, Funkcje ta wywoluje inna funkcja systemowa - sys_waitpid, a ta zas funkcja sys_wait.
Parametrem funkcji wait jest wskaznik do zmiennej typu int. Wywolanie wait(s) odpowiada wywolaniu sys_wait4(-1,s,0,NULL). Pod adres s funkcja ma wpisac przyczyne zakonczenia procesu.

Procesy brane pod uwage

Funkcja wait poszukujac procesu potomnego, ktory przestal dzialac rozpatruje procesy ZOMBIE i STOPPED.

ZOMBIE

Proces moze osiagnac ten stan na dwa sposoby. Pierwszy z nich to wywolanie funkcji exit.Jedna z czynnosci wykonywanych w tej funkcji jest zapamietanie argumentu (czyli kodu wyjscia) w polu exit_code struktury task_struct odpowiadajacej konczacemu sie procesowi. Scislej rzecz biorac zapamietywane jest 8 najmlodszych bitow argumentu przesunietych o osiem pozycji w lewo.
Dla wielu sygnalow czynnoscia domyslna jest zakonczenie procesu. Jesli proces nie zmieni obslugi takiego sygnalu i otrzyma go, to wtedy rowniez przechodzi w stan ZOMBIE, o czym mozemy sie przekonac analizujac funkcje systemowa do_signal, Do pola exit_code jest wtedy wpisywany numer sygnalu (ewentualnie ze znacznikiem pamieci), ktory przyniosl procesowi smierc. Numer ten nie jest przesuwany tak jak argument funkcji exit.
Wspomniany znacznik pamieci to siodmy bit liczac od zera. Jest ustawiany, jesli konczacy sie proces generuje plik core, czyli zrzut pamieci wewnetrznej. Dzieje sie tak przy standardowej obsludze sygnalow SIGQUIT, SIGILL,SIGTRAP, SIGABRT, SIGFPE i SIGSEGV (dotyczy dostarczonego Linuxa 2.0, Stevens podaje jeszcze inne sygnaly dla systemow 4.3BSD i V).

STOPPED

Proces przechodzi w stan STOPPED po otrzymaniu sygnalu SIGSTOP, SIGTSTP, SIGTTIN, SIGTTOU (2 ostatnie nie wystepuja w systemie V). Uprzednio numer sygnalu, ktory zatrzymal proces jest wpisywany do pola exit_code. Ojciec przy przegladaniu swoich synow w funkcji wait w przypadku procesu STOPPED sprawdza, czy w tym polu jest zero. Jesli nie, to wait zwraca identyfikator znalezionego procesu wczesniej wyzerowawszy to pole0. Jesli tak, to znaczy, ze ojciec juz sie dowiedzial o zatrzymaniu procesu potomnego i nie ma sensu informowac go o tym powtornie. Wtedy przeszukiwanie jest kontynuowane.

Wartosci przekazywane przez funkcje

Sytuacje normalne

Funkcja wait przekazuje dwie wartosci:
1) zwraca identyfikator zakonczonego procesu potomnego
2) pod podany adres wpisuje przyczyne zakonczenia procesu. Jesli byl to proces ZOMBIE to wpisywana jest zawartosc pola exit_code. W przypadku procesu STOPPED wartosc z exit_code przesuwana jest o osiem bitow w lewo, zas na wolne miejsce wpisywana jest wartosc 0x7f. Dzieki takiej organizacji proces macierzysty zawsze moze rozroznic, czy proces potomny, ktory przestal dzialac jest zatrzymany, czy tez byl procesem ZOMBIE i z jakiego powodu.

 proces potomny:          8 starszych bitow      8 mlodszych bitow

 wywolal exit             argument dla exit            0x00
 zabity sygnalem                0x00              numer sygnalu (*)
 zatrzymany                 numer sygnalu              0x7f
(*) - w tym bajcie oprocz numeru sygnalu zapisanego na 5-ciu najmlodszych bitach jest takze zapisywana na najstarszym bicie informacja czy proces wygenerowal plik core (zrzut pamieci)

Sytuacje bledne

Po wystapieniu bledu funkcja biblioteczna wait zwraca -1, a do zmiennej errno wpisuje kod bledu otrzymany z wywolania sys_wait4.
Mozliwe bledy to:
- ECHILD (proces wywolujacy w ogole nie ma potomkow)
- EFAULT (adres podany jako parametr jest niewlasciwy - nalezy do przestrzeni w ktorej proces nie moze pisac)
- ERESTARTSYS (w trakcie oczekiwania na smierc potomka proces dostal niespodziewany sygnal, t.zn. rozny od SIGCHLD i nie zablokowany)

Algorytm funkcji

Najpierw funkcja sprawdza (verify_area), czy adres podany jako argument jest wlasciwy. Jesli proces nie moze pisac po tym obszarze, to funkcja konczy sie zwracajac blad. Wyjatkiem jest adres zerowy, ktory podany jako argument powoduje to, ze nic pod niego nie jest wpisywane, zatem nie wymaga on weryfikacji. Nastepnie (przy wywolaniu przez wait) proces wpisuje sie do swojej kolejki wait_chldexit (pole struktury task_struct). Ma to zastosowanie w funkcji notify_parent (wykonywanej przy okazji do_exit poprzez exit_notify) , w ktorej proces budzi swoj proces macierzysty czekajacy wewnatrz wait na smierc potomka.
Nastepny krok to przegladanie swoich synow. Przy wywolaniu funkcji przez wait argument pid jest rowny -1, zatem funkcja sprawdza wszystkie procesy potomne procesu wywolujacego. Po znalezieniu pierwszego 'dobrego' procesu funkcja zwraca jego identyfikator po wykonaniu czynnosci odpowiednich dla danego rodzaju procesu. W przypadku ZOMBIE sa to zwiekszenie czasu uzytkownika i systemu procesu macierzystego (pola cutime,cstime) o czas zuzyty przez potomka, wpisanie zawartosci pola exit_code pod adres podany jako argument (o ile jest niezerowy) i usuniecie pozostalosci po procesie zakonczonym. Dla procesu STOPPED funkcja wpisuje odpowiednia wartosc pod podany adres i ustawia pole exit_code na 0. Funkcja oczywiscie nie zwalnia miejsca po procesie, ani tez nie wznawia tego procesu.
Jesli proces w ogole nie ma synow to funkcja zwraca blad. Jesli ma, lecz wszyscy 'dzialaja' to proces musi byc wstrzymany. Wczesniej funkcja sprawdza, czy nie nadszedl jakis nieoczekiwany sygnal, t.zn. rozny od SIGCHLD i nie zablokowany. Jesli nadszedl, to funkcja zwraca blad, jesli nie, to proces zasypia (schedule), jako TASK_INTERRUPTIBLE, czyli dopuszczajac przerwania. Po obudzeniu proces przechodzi ponownie do przegladania synow.

Funkcja wait a ignorowanie sygnalu smierci potomka

Proces moze ustawic obsluge sygnalu SIGCHLD na SIG_IGN. Oznacza to, ze proces macierzysty nie jest zainteresowany stanem koncowym swoich procesow potomnych, zatem nie ma sensu przechowywac w systemie procesow ZOMBIE. Dlatego w momencie obslugi sygnalu SIGCHLD (do_signal), jesli proces go ignoruje, jadro samo (za proces) bedzie wywolywac funkcje sys_wait4 z opcja WNOHANG do skutku, t.zn. az nie bedzie procesow ZOMBIE. Powstaje pytanie, co wtedy gdy proces ignorujacy sygnal SIGCHLD bedzie czekal wewnatrz funkcji wait i jeden z jego potomkow stanie sie procesem ZOMBIE. Kluczowy jest moment obslugi sygnalu. W systemie opisywanym przez Bacha proces ZOMBIE zostanie usuniety. Proces macierzysty zas dalej bedzie czekal wewnatrz funkcji wait, az do smierci wszystkich swoich potomkow i wtedy sys_wait4 zwroci blad ECHILD. Natomiast w dostarczonym systemie Linux 2.0, jak rowniez 1.2.8 (LAB) proces nie zdazy obsluzyc sygnalu przed ponownym przeszukiwaniem swoich synow, zatem znajdzie ZOMBIEGO, usunie go i zwroci jego pid. Demostruje to program wait_prg1.c.
Natomiast w ogole automatyczne usuwanie procesow ZOMBIE przy ignorowaniu sygnalu SIGCHLD dziala, co demonstruje program wait_prg2.c.

Zrodla informacji:

  1. Zrodla jadra:
  2. Maurice J. Bach, Budowa systemu operacyjnego UNIX, WNT 1995
  3. W. Richard Stevens, Programowanie zastosowan sieciowych w systemie UNIX , WNT 1995,1996

Autor: Marcin Wrozek