Czasami, gdy piszę program w Linuksie i ulega awarii z powodu jakiegoś błędu, stanie się procesem nieprzerywalnym i będzie działał wiecznie, dopóki nie uruchomię ponownie komputera (nawet jeśli się wyloguję). Moje pytania to:
- Co powoduje, że proces staje się nieprzerywalny?
- Jak mogę temu zapobiec?
- To prawdopodobnie głupie pytanie, ale czy istnieje sposób, aby je przerwać bez ponownego uruchamiania komputera?
linux
scheduling
preemption
Jason Baker
źródło
źródło
TASK_UNINTERUPTIBLE
stan, gdy system nie jest w stanie bezczynności, zbierając w ten sposób na siłę dane i czekając na przesłanie, gdy superużytkownik wyjdzie? Byłaby to kopalnia złota dla hakerów do pobierania informacji, powrotu do stanu zombie i przesyłania informacji przez sieć w trybie bezczynności. Niektórzy mogą argumentować, że jest to jeden ze sposobów tworzeniaBlackdoor
uprawnień, które są, aby wchodzić i wychodzić z dowolnego systemu zgodnie z życzeniem. Jestem głęboko przekonany, że tę lukę można zapieczętować na dobre, eliminując `` TASK_UNINTERUPTIBOdpowiedzi:
Proces nieprzerywalny to proces znajdujący się w wywołaniu systemowym (funkcja jądra), którego nie może przerwać sygnał.
Aby zrozumieć, co to oznacza, musisz zrozumieć koncepcję przerywanego wywołania systemowego. Klasycznym przykładem jest
read()
. Jest to wywołanie systemowe, które może zająć dużo czasu (sekundy), ponieważ może potencjalnie obejmować obracanie się dysku twardego lub poruszanie głowami. Przez większość tego czasu proces będzie spał, blokując się na sprzęcie.Gdy proces śpi w wywołaniu systemowym, może odebrać sygnał asynchroniczny Unix (powiedzmy SIGTERM), wtedy dzieje się co następuje:
Wczesny powrót z wywołania systemowego umożliwia kodowi przestrzeni użytkownika natychmiastową zmianę jego zachowania w odpowiedzi na sygnał. Na przykład czyste zakończenie w reakcji na SIGINT lub SIGTERM.
Z drugiej strony, niektórych wywołań systemowych nie można w ten sposób przerywać. Jeśli z jakiegoś powodu system wywoła blokady, proces może pozostawać w tym stanie nie do zabicia na czas nieokreślony.
LWN opublikował w lipcu fajny artykuł, który poruszył ten temat.
Aby odpowiedzieć na pierwotne pytanie:
Jak temu zapobiec: dowiedz się, który sterownik powoduje problemy i albo przestań go używać, albo zostań hakerem jądra i napraw go.
Jak zabić nieprzerywalny proces bez ponownego uruchamiania: w jakiś sposób zakończyć wywołanie systemowe. Często najskuteczniejszym sposobem zrobienia tego bez naciskania wyłącznika zasilania jest pociągnięcie za przewód zasilający. Możesz także zostać hakerem jądra i zmusić sterownik do używania TASK_KILLABLE, jak wyjaśniono w artykule LWN.
źródło
Gdy proces jest w trybie użytkownika, można go przerwać w dowolnym momencie (przełączenie do trybu jądra). Kiedy jądro powraca do trybu użytkownika, sprawdza, czy są jakieś oczekujące sygnały (w tym te, które są używane do zabicia procesu, takie jak
SIGTERM
iSIGKILL
). Oznacza to, że proces można zabić tylko po powrocie do trybu użytkownika.Powodem, dla którego procesu nie można zabić w trybie jądra, jest to, że może on potencjalnie uszkodzić struktury jądra używane przez wszystkie inne procesy na tej samej maszynie (w ten sam sposób zabicie wątku może potencjalnie uszkodzić struktury danych używane przez inne wątki w tym samym procesie) .
Kiedy jądro musi zrobić coś, co może zająć dużo czasu (na przykład czekając na potok napisany przez inny proces lub czekając, aż sprzęt coś zrobi), zasypia, oznaczając siebie jako śpiącego i wywołując harmonogram, aby przełączył się na inny proces (jeśli nie ma procesu, który nie jest uśpiony, przełącza się na proces „fikcyjny”, który mówi procesorowi, aby nieco zwolnił i siedzi w pętli - pętli bezczynności).
Jeśli sygnał jest wysyłany do procesu uśpienia, należy go obudzić, zanim powróci do przestrzeni użytkownika, a tym samym przetworzy oczekujący sygnał. Tutaj mamy różnicę między dwoma głównymi rodzajami snu:
TASK_INTERRUPTIBLE
, przerywany sen. Jeśli zadanie jest oznaczone tą flagą, śpi, ale można je obudzić sygnałami. Oznacza to, że kod, który oznaczył zadanie jako uśpione oczekuje na możliwy sygnał, a po przebudzeniu sprawdzi go i wróci z wywołania systemowego. Po obsłużeniu sygnału wywołanie systemowe może zostać automatycznie ponownie uruchomione (i nie będę wchodził w szczegóły, jak to działa).TASK_UNINTERRUPTIBLE
, nieprzerwany sen. Jeśli zadanie jest oznaczone tą flagą, nie spodziewa się, że zostanie obudzone przez coś innego niż to, na co czeka, ponieważ nie można go łatwo ponownie uruchomić lub programy oczekują, że wywołanie systemowe będzie atomowe. Można to również wykorzystać do snu, o którym wiadomo, że jest bardzo krótki.TASK_KILLABLE
(wspomniany w artykule LWN, do którego prowadzi odpowiedź ddaa) to nowy wariant.To odpowiada na twoje pierwsze pytanie. A co do drugiego pytania: nie da się uniknąć nieprzerywalnych uśpień, są one normalne (zdarza się na przykład za każdym razem, gdy proces czyta / zapisuje z / na dysk); jednak powinny trwać tylko ułamek sekundy. Jeśli trwają znacznie dłużej, zwykle oznacza to problem sprzętowy (lub problem ze sterownikiem urządzenia, który wygląda tak samo dla jądra), w którym sterownik urządzenia czeka, aż sprzęt wykona coś, co nigdy się nie wydarzy. Może to również oznaczać, że używasz NFS i serwer NFS jest wyłączony (czeka na przywrócenie serwera; możesz także użyć opcji „intr”, aby uniknąć problemu).
Wreszcie powodem, dla którego nie można odzyskać, jest ten sam powód, dla którego jądro czeka na powrót do trybu użytkownika, aby dostarczyć sygnał lub zabić proces: mogłoby to potencjalnie uszkodzić struktury danych jądra (kod oczekujący na przerwanie uśpienia może otrzymać błąd, który mówi mu aby powrócić do przestrzeni użytkownika, gdzie proces może zostać zabity; kod oczekujący na nieprzerywany tryb uśpienia nie oczekuje żadnego błędu).
źródło
Nieprzerwane procesy ZWYKLE oczekują na operacje we / wy po wystąpieniu błędu strony.
Rozważ to:
Proces / zadanie nie może zostać przerwane w tym stanie, ponieważ nie obsługuje żadnych sygnałów; gdyby tak się stało, wystąpiłby kolejny błąd strony i wróciłby tam, gdzie był.
Kiedy mówię „proces”, naprawdę mam na myśli „zadanie”, co w Linuksie (2.6) z grubsza tłumaczy się na „wątek”, który może mieć indywidualny wpis „grupy wątków” w / proc lub nie
W niektórych przypadkach może to długo czekać. Typowym przykładem może być sytuacja, w której plik wykonywalny lub plik mmap znajduje się w sieciowym systemie plików, w którym wystąpiła awaria serwera. Jeśli I / O w końcu się powiedzie, zadanie będzie kontynuowane. Jeśli w końcu się nie powiedzie, zadanie zwykle otrzyma SIGBUS lub coś takiego.
źródło
Na twoje trzecie pytanie: myślę, że możesz zabić nieprzerwane procesy, uruchamiając
sudo kill -HUP 1
. Zrestartuje init bez kończenia działających procesów, a po jego uruchomieniu moje nieprzerwane procesy zniknęły.źródło
Jeśli mówisz o procesie "zombie" (który jest oznaczony jako "zombie" w wyjściu ps), to jest to nieszkodliwy zapis na liście procesów, który czeka, aż ktoś zbierze jego kod powrotu i można go bezpiecznie zignorować.
Czy mógłbyś opisać, czym jest dla Ciebie „nieprzerwany proces”? Czy przetrwa „zabij -9” i radośnie się ugrzęźnie? Jeśli tak jest, to utknęło w jakimś wywołaniu systemowym, które utknęło w jakimś sterowniku, i utkniesz z tym procesem do ponownego uruchomienia (a czasami lepiej jest zrestartować wkrótce) lub rozładowania odpowiedniego sterownika (co jest mało prawdopodobne) . Możesz spróbować użyć „strace”, aby dowiedzieć się, gdzie utknął twój proces i uniknąć tego w przyszłości.
źródło