Ćwiczenie 8 Zarządzanie procesami w systemie Linux

Transkrypt

Ćwiczenie 8 Zarządzanie procesami w systemie Linux
Ćwiczenie 8
Zarządzanie procesami w systemie Linux
1. Cel ćwiczenia
Ćwiczenie wprowadza w zagadnienia związane z zarządzaniem procesami w systemie Linux.
Definiuje obraz procesu w pamięci operacyjnej, priorytet procesu oraz efektywny identyfikator
procesu.
W systemie Linux rozróżniane są procesy jądra oraz procesy użytkownika. Najważniejszym
procesem systemowym jest proces init, który wykonuje funkcje administracyjne w systemie,
obejmujące obsługę prawidłowego przejścia systemu z trybu jednego użytkownika do trybu
wielodostępowego i utworzenie procesu rejestracji użytkowników (getty).
Uruchomienie procesu w systemie polega na umieszczeniu go w pamięci operacyjnej oraz
przydzieleniu procesora. Jądro systemu Linux określa porządek przydziału zasobów do procesów
gotowych do wykonania (szeregowanie zasobów systemu). Wykonuje również szeregowanie
procesów – dokonuje ustalenia kolejności, w jakiej procesor ma być przydzielony do procesów
gotowych do wykonania. Stosowany mechanizm szeregowania preferuje procesy interakcyjne,
które nie wymagają dużo czasu procesora (stosowany jest algorytm round-robin).
2. Przygotowanie do ćwiczenia
•
•
•
•
•
definicja procesu,
algorytm szeregowania procesów w systemie UNIX,
obraz procesu w pamięci operacyjnej,
wirtualna przestrzeń adresowa procesów i pamięć fizyczna,
język ANSI C: definiowanie funkcji, wskaźniki i referencje,
3. Kompilacja
Prezentowane w ćwiczeniu przykłady wymagają podstawowej znajomości języka C. W pierwszym
etapie należy stworzyć plik tekstowy stanowiący kod przykładowego programu (przyklad.c). W
następnym kroku należy dokonać kompilacji z użyciem standardowego kompilatora gcc wydając
polecenie:
gcc przyklad.c –o przyklad
Uruchomienie programu sprowadza się do wydania polecenia: ./przyklad
4. Tworzenie, usuwanie i zawieszanie procesu
Istniejący proces może utworzyć nowy proces za pomocą wywołania systemowego fork.
Wywołanie to polega na rozwidleniu procesu wydającego to zlecenie na dwa procesy: macierzysty i
potomny, które współpracują współbieżnie, niezależnie od siebie. Dla procesu potomnego
inicjowany jest oddzielny obszar pamięci operacyjnej.
Wyższa Szkoła Gospodarki w Bydgoszczy
Instytut Informatyki Stosowanej
Laboratorium systemów operacyjnych (2010)
Poniższy przykład tworzy nowy proces:
#include <stdio.h>
main() {
int pid;
if ((pid = fork()) == 0) printf("proces potomny\n");
else printf("proces macierzysty\n");
}
Powyższy program tworzy nowy proces za pomocą funkcji systemowej fork. Nowo utworzony
proces (potomny) otrzymuje zero jako parametr zwracany przez tą funkcję i wyświetla napis:
proces potomny. Proces wywołujący (macierzysty) otrzymuje identyfikator nowo utworzonego
procesu jako parametr zwracany przez tą funkcję i wyświetla napis: proces macierzysty. Program
dokonuje rozwidlenia procesu na dwa procesy.
proces
macierzysty
wait
proces
macierzysty
fork
proces
potomny
exec
proces
potomny
exit
proces
"zombie"
Zwykle przy tworzeniu procesów potomnych, zakłada się, że będą one wykonywały funkcje inne
niż proces macierzysty. Rodzina funkcji systemowych exec umożliwia procesowi wykonanie kodu
zawartego we wskazanym pliku. Jego realizacja polega zatem na wprowadzeniu w obszar pamięci
operacyjnej procesu nowego kodu i danych. Wywołania funkcji exec (execl, execle, execlp, execv,
execve, execvp) różnią się określeniem ścieżki dostępu do pliku, sposobem przesyłania argumentów
i przekazywaniem parametrów środowiska od procesu macierzystego do potomnego.
Kolejny przykład tworzy dwa procesy, a proces potomny wykonuje polecenie systemowe:
#include <stdio.h>
main() {
char cmd[80];
gets(cmd);
if(fork() == 0) {
printf("proces potomny\n");
execl("/bin/sh","sh","-c",cmd,NULL);
} else printf("proces macierzysty\n");
}
W wywołaniu funkcji execl pełna ścieżka /bin/sh i nazwa shella (sh) uruchamiają powłokę,
argument –c powoduje, że powłoka traktuje łańcuch znaków podany w zmiennej cmd jako jedno
polecenie, a NULL jest wskaźnikiem kończącym listę parametrów funkcji. Proces potomny
wykonywany jest na przemian z procesem macierzystym.
Wyższa Szkoła Gospodarki w Bydgoszczy
Instytut Informatyki Stosowanej
Laboratorium systemów operacyjnych (2010)
Funkcja wait używana jest do synchronizacji wykonania procesu macierzystego i procesu
potomnego. Proces może zawiesić swoje działanie, dopóki któryś z jego procesów potomnych nie
zakończy się.
Proces macierzysty za pomocą sygnału może zażądać informacji, kiedy nastąpi wyjście z procesu
potomnego (lub kiedy zakończy się on nieprawidłowo). Jeżeli proces został osierocony (nastąpiło
wyjście z procesu macierzystego zanim on się zakończył), wtedy w celu obsługi wyjścia z niego
jądro kontaktuje się z procesem systemowym init.
#include <stdio.h>
main() {
int status;
char cmd[80];
gets(cmd);
if(fork() == 0) {
printf("proces potomny\n");
execl("/bin/sh","sh","-c",cmd,NULL);
}
wait(&status);
printf("proces macierzysty\n");
}
W powyższym przykładzie proces macierzysty czeka, aż zakończy się proces potomny. W ogólnym
przypadku zapis:
pid = wait(&status)
oznacza, iż funkcja przyjmuje wskaźnik do zmiennej (&status) określający rodzaj zdarzenia, które
spowoduje wznowienie działania procesu, a zwraca pid – identyfikator procesu, który spowodował
to zdarzenie.
Wartość statusu określają dwa bajty: młodszy – zawiera status zakończenia procesu potomnego (tak
jak to zdefiniował system) i starszy – zawiera status zakończenia procesu potomnego (tak jak to
zdefiniował sam proces potomny).
Proces może zakończyć swoje działanie funkcją exit. W wyniku jej wykonania następuje
zamknięcie plików i skasowanie obrazu pamięci procesu.
#include <stdio.h>
main() {
int status;
int pid;
if((pid=fork()) == -1) {
perror("Nie moge wykonac fork");
exit(1);
} else if(pid == 0) {
printf("potomny: pid potomnego = %d, pid macierzystego = %d\n",
getpid(),getppid());
exit(0);
}
Wyższa Szkoła Gospodarki w Bydgoszczy
Instytut Informatyki Stosowanej
Laboratorium systemów operacyjnych (2010)
wait(&status);
printf("macierzysty: status potomka = %d\n",status);
printf("macierzysty: pid potomnego = %d, pid macierzyst. = %d\n",
pid,getpid());
exit(0);
}
Zakończenie procesu potomnego powoduje przekazanie kodu statusu do procesu macierzystego za
pośrednictwem funkcji systemowej wait. Jednym z zadań funkcji wait jest śledzenie, czy procesy
potomne danego procesu zostały zakończone.
5. Polecenia systemowe
Oprócz funkcji systemowych użytkownik ma do dyspozycji szereg poleceń bezpośrednio
operujących na procesach (np. poznane wcześniej ps i kill).
Polecenie nice daje możliwość wpływania na szeregowanie procesów. Procesy są szeregowane do
wykonania zgodnie z parametrem: priorytet procesu (PRI). Priorytet procesu oraz wartość
parametru nice można uzyskać za pomocą polecenia ps –l. Polecenie nice umożliwia zmniejszenie
priorytetu procesu w celu uniknięcia konfliktu z wykonywaniem innych poleceń z tego samego
terminala:
nice cp plik_a plik_b
Polecenie at powoduje uruchomienie w określonym czasie procesu odpowiadającego danemu
poleceniu lub poleceniom zawartym w pliku. Składnia polecenia jest następująca:
at czas data < plik
Opcja data nie jest obowiązkowa – może być podana jako nazwa miesiąca wraz z następującym po
niej numerem dnia. Nieobowiązkowa opcja +liczba stanowi liczbę użytą wraz z jedną z
następujących jednostek: minutes, hours, days, weeks, months lub years.
at 0815am Jan 3
at now +1 day
at 5 pm Friday
Polecenie nohup (no hang up – nie zawieszaj) zapewnia wykonywanie procesu nawet wtedy, gdy
użytkownik wyloguje się z systemu lub gdy nastąpi przerwanie połączenia terminala z serwerem.
nohup cp plik_1 plik_2 &
6. Literatura
1. Z. Królikowski, M. Sajkowski: System operacyjny UNIX dla początkujących i
zaawansowanych, Wydawnictwo NAKOM, Poznań 1995.
Wyższa Szkoła Gospodarki w Bydgoszczy
Instytut Informatyki Stosowanej
Laboratorium systemów operacyjnych (2010)