Często zaskakuje mnie to, że chociaż pracuję zawodowo z komputerami od kilku dziesięcioleci, a Linux od dekady, tak naprawdę większość funkcjonalności systemu operacyjnego traktuję jak czarną skrzynkę, a nie magię.
Dzisiaj myślałem o kill
poleceniu i chociaż używam go wiele razy dziennie (zarówno w jego „normalnym”, jak i -9
smaku), muszę przyznać, że absolutnie nie mam pojęcia, jak to działa za kulisami.
Z mojego punktu widzenia, jeśli uruchomiony proces jest „zawieszony”, wywołuję kill
jego PID, a następnie nagle przestaje działać. Magia!
Co tam naprawdę się dzieje? Strony mówią o „sygnałach”, ale na pewno to tylko abstrakcja. Wysyłanie kill -9
do procesu nie wymaga współpracy procesu (jak obsługa sygnału), po prostu go zabija.
- W jaki sposób Linux powstrzymuje proces od zajmowania czasu procesora?
- Czy zostało to usunięte z harmonogramu?
- Czy odłącza proces od otwartych uchwytów plików?
- Jak zwalniana jest pamięć wirtualna procesu?
- Czy istnieje coś takiego jak globalna tabela w pamięci, w której Linux przechowuje odniesienia do wszystkich zasobów zajętych przez proces, a kiedy „zabijam” proces, Linux po prostu przechodzi przez tę tabelę i uwalnia zasoby jeden po drugim?
Naprawdę chciałbym to wszystko wiedzieć!
źródło
kill -9
odniesienie .Odpowiedzi:
Zakładasz, że ponieważ niektóre sygnały można wychwycić i zignorować, wszystkie wymagają współpracy. Ale zgodnie z
man 2 signal
„ sygnałami SIGKILL i SIGSTOP nie można złapać ani zignorować”. SIGTERM może zostać złapany, dlatego zwykłykill
nie zawsze jest skuteczny - ogólnie oznacza to, że coś w module obsługi procesu poszło nie tak. 1Jeśli proces nie definiuje modułu obsługi dla danego sygnału (lub nie może go zdefiniować), jądro wykonuje akcję domyślną. W przypadku SIGTERM i SIGKILL ma to zakończyć proces (chyba, że jego pid wynosi 1; jądro się nie zakończy
init
) 2, co oznacza, że jego uchwyty plików są zamknięte, jego pamięć jest zwrócona do puli systemowej, jego rodzic otrzymuje SIGCHILD, jego osierocone dzieci są dziedziczone przez init itp., tak jakby to wywołałoexit
(patrzman 2 exit
). Proces już nie istnieje - chyba że skończy jako zombie, w którym to przypadku nadal jest wymieniony w tabeli procesów jądra z pewnymi informacjami; dzieje się tak, gdy jego rodzic niewait
i odpowiednio postępować z tymi informacjami. Jednak procesy zombie nie mają już przydzielonej pamięci i dlatego nie mogą kontynuować działania.Myślę, że to wystarczająco dokładne. Pamięć fizyczna jest śledzona według strony (jedna strona zwykle ma wielkość 4 KB), a strony te są pobierane i zwracane do globalnej puli. Jest to trochę bardziej skomplikowane, ponieważ niektóre bezpłatne strony są buforowane na wypadek, gdyby dane, które zawierają, były ponownie wymagane (to znaczy dane, które zostały odczytane z wciąż istniejącego pliku).
Jasne, wszystkie sygnały są abstrakcją. Są konceptualne, podobnie jak „procesy”. Gram trochę w semantykę, ale jeśli masz na myśli, że SIGKILL różni się jakościowo od SIGTERM, to tak i nie. Tak w tym sensie, że nie można go złapać, ale nie w tym sensie, że oba są sygnałami. Analogicznie jabłko nie jest pomarańczą, ale jabłka i pomarańcze, zgodnie z przyjętą definicją, są owocami. SIGKILL wydaje się bardziej abstrakcyjny, ponieważ nie można go złapać, ale wciąż jest to sygnał. Oto przykład obsługi SIGTERM, jestem pewien, że widziałeś je już wcześniej:
Ten proces będzie po prostu spał na zawsze. Możesz uruchomić go w terminalu i wysłać SIGTERM za pomocą
kill
. Wyrzuca rzeczy takie jak:1066 jest moim UID. Pid będzie odpowiadał powłoce, z której
kill
jest wykonywany, lub pid zabicia, jeśli rozwidlisz go (kill 25309 & echo $?
).Ponownie nie ma sensu ustawiać modułu obsługi dla SIGKILL, ponieważ nie będzie on działać. 3 Jeśli ja
kill -9 25309
proces się zakończy. Ale to wciąż sygnał; jądro ma informację o tym, kto wysłał sygnał , jaki to jest sygnał itp.1. Jeśli nie przeglądałeś listy możliwych sygnałów , patrz
kill -l
.2. Kolejny wyjątek, jak wspomina Tim Post poniżej, dotyczy procesów w nieprzerwanym śnie . Nie można ich obudzić, dopóki problem nie zostanie rozwiązany, dlatego WSZYSTKIE sygnały (w tym SIGKILL) zostaną odłożone na czas trwania. Jednak proces nie może celowo stworzyć tej sytuacji.
3. Nie oznacza to, że używanie
kill -9
jest lepszą rzeczą w praktyce. Mój przykładowy program obsługi jest zły w tym sensie, że do tego nie prowadziexit()
. Prawdziwym celem procedury obsługi SIGTERM jest umożliwienie temu procesowi wyczyszczenia plików tymczasowych, a następnie dobrowolnego wyjścia. Jeśli użyjeszkill -9
, nie ma takiej szansy, więc rób to tylko wtedy, gdy część „dobrowolne wyjście” wydaje się nieudana.źródło
-9
bo to jest prawdziwy problem, w jaki sposób nikt nie chce tego umrzeć! ;)kill -9
pewne procesy związane z operacjami we / wy nie będą działać, przynajmniej nie od razu.kill -9
nie można go złapać, proces odbierający go nie może wykonać żadnego czyszczenia (np. Usunięcie plików tymczasowych, zwolnienie pamięci współdzielonej itp.) Przed jego wyjściem. Dlatego używajkill -9
(akakill -kill
) tylko w ostateczności. Zacznij od akill -hup
i / lubkill -term
najpierw, a następnie użyjkill -kill
jako ostatni cios.Każdy proces działa przez zaplanowany czas, a następnie jest przerywany przez sprzętowy zegar, aby dać rdzeń procesora do innych zadań. Dlatego możliwe jest posiadanie znacznie większej liczby procesów niż rdzeni procesora, a nawet uruchomienie całego systemu operacyjnego z dużą ilością procesów na jednym rdzeniu procesora.
Po przerwaniu procesu formant powraca do kodu jądra. Kod ten może następnie podjąć decyzję o nie wznawianiu wykonania przerwanego procesu, bez jakiejkolwiek współpracy ze strony procesu. kill -9 może zakończyć się wykonywaniem w dowolnej linii twojego programu.
źródło
Oto wyidealizowany opis działania zabijania procesu. W praktyce każdy wariant Uniksa będzie miał wiele dodatkowych komplikacji i optymalizacji.
Jądro ma strukturę danych dla każdego procesu, która przechowuje informacje o tym, jaką pamięć mapuje, jakie wątki ma i kiedy są zaplanowane, jakie pliki ma otwarte itp. Jeśli jądro zdecyduje się zabić proces, zapisuje to w struktura danych procesu (i być może w strukturze danych każdego z wątków), że proces ma zostać zabity.
Jeśli jeden z wątków procesu jest obecnie zaplanowany na innym procesorze, jądro może wywołać przerwanie na tym drugim procesorze, aby ten wątek przestał działać szybciej.
Gdy program planujący zauważy, że wątek jest w trakcie procesu, który musi zostać zabity, nie będzie go już planował.
Gdy żaden z wątków procesu nie jest już zaplanowany, jądro zaczyna zwalniać zasoby procesu (pamięć, deskryptory plików,…). Za każdym razem, gdy jądro zwalnia zasób, sprawdza, czy jego właściciel nadal ma aktywne zasoby. Gdy proces nie będzie już posiadał zasobu na żywo (mapowanie pamięci, otwarty deskryptor pliku,…), strukturę danych dla samego procesu można zwolnić, a odpowiedni wpis można usunąć z tabeli procesów.
Niektóre zasoby można natychmiast zwolnić (np. Zwolnienie pamięci, która nie jest używana przez operację We / Wy). Inne zasoby muszą poczekać, na przykład dane opisujące operację we / wy nie mogą zostać zwolnione, gdy operacja we / wy jest w toku (podczas działania DMA , używana jest pamięć, do której uzyskuje dostęp, a anulowanie DMA wymaga kontakt z urządzeniem peryferyjnym). Kierowca takiego zasobu zostanie powiadomiony i może spróbować przyspieszyć anulowanie; gdy operacja nie będzie już w toku, sterownik zakończy proces zwalniania tego zasobu.
(Wpis w tabeli procesów jest w rzeczywistości zasobem należącym do procesu nadrzędnego, który zostaje zwolniony, gdy proces umiera, a rodzic potwierdza zdarzenie ).
źródło