Wszystkie nowoczesne procesory mają zdolność przerywania aktualnie wykonywanej instrukcji maszyny. Zapisują wystarczający stan (zwykle, ale nie zawsze, na stosie), aby umożliwić późniejsze wznowienie wykonania, tak jakby nic się nie wydarzyło (zwykle przerwana instrukcja jest restartowana). Następnie zaczynają wykonywać procedurę obsługi przerwań , która jest po prostu większym kodem maszynowym, ale umieszczona w specjalnej lokalizacji, aby procesor wiedział z wyprzedzeniem. Procedury obsługi przerwań są zawsze częścią jądra systemu operacyjnego: komponent, który działa z największymi uprawnieniami i jest odpowiedzialny za nadzorowanie wykonywania wszystkich innych komponentów. 1,2
Przerwania mogą być synchroniczne , co oznacza, że są wyzwalane przez sam procesor jako bezpośrednią odpowiedź na coś, co zrobiła aktualnie wykonywana instrukcja, lub asynchroniczne , co oznacza, że zdarzają się w nieprzewidywalnym czasie z powodu zdarzenia zewnętrznego, takiego jak dane przychodzące do sieci Port. Niektórzy ludzie rezerwują termin „przerwanie” dla przerwania asynchronicznego i zamiast tego nazywają przerwanie synchroniczne „pułapkami”, „błędami” lub „wyjątkami”, ale wszystkie te słowa mają inne znaczenie, więc będę się trzymał „przerwania synchronicznego”.
Obecnie większość nowoczesnych systemów operacyjnych ma pojęcie procesów . Mówiąc najprościej, jest to mechanizm, za pomocą którego komputer może uruchamiać więcej niż jeden program w tym samym czasie, ale jest to również kluczowy aspekt tego, jak systemy operacyjne konfigurują ochronę pamięci , co jest cechą większości (ale niestety wciąż nie wszystkie ) nowoczesne procesory. To idzie w parze z pamięcią wirtualną, czyli możliwość zmiany mapowania między adresami pamięci a rzeczywistymi lokalizacjami w pamięci RAM. Ochrona pamięci pozwala systemowi operacyjnemu nadać każdemu procesowi osobną część pamięci RAM, do której tylko on może uzyskać dostęp. Pozwala także systemowi operacyjnemu (działającemu w imieniu niektórych procesów) wyznaczyć regiony pamięci RAM jako tylko do odczytu, wykonywalne, współużytkowane przez grupę współpracujących procesów itp. Część pamięci będzie dostępna tylko dla jądro. 3)
Tak długo, jak każdy proces uzyskuje dostęp do pamięci tylko w sposób, w jaki procesor jest skonfigurowany tak, aby pozwalał, ochrona pamięci jest niewidoczna. Kiedy proces łamie reguły, CPU generuje synchroniczne przerwanie, prosząc jądro o uporządkowanie. Regularnie zdarza się, że proces tak naprawdę nie złamał reguł, tylko jądro musi trochę popracować, aby proces mógł być kontynuowany. Na przykład, jeśli strona pamięci procesu musi zostać „eksmitowana” do pliku wymiany, aby zwolnić miejsce w pamięci RAM na coś innego, jądro oznaczy tę stronę jako niedostępną. Następnym razem, gdy proces spróbuje go użyć, CPU wygeneruje przerwanie ochrony pamięci; jądro pobierze stronę z wymiany, umieści ją z powrotem tam, gdzie była, oznaczy ją ponownie jako dostępną i wznowi wykonanie.
Załóżmy jednak, że proces naprawdę złamał zasady. Próbował uzyskać dostęp do strony, na której nigdy nie było zmapowane RAM, lub próbował wykonać stronę oznaczoną jako niezawierająca kodu maszynowego ani nic takiego. Rodzina systemów operacyjnych zwanych ogólnie „Unix” używa sygnałów do radzenia sobie z tą sytuacją. 4 Sygnały są podobne do przerwań, ale są generowane przez jądro i przetwarzane przez procesy, a nie generowane przez sprzęt i uruchamiane przez jądro. Procesy mogą definiować procedury obsługi sygnałówwe własnym kodzie i poinformuj jądro, gdzie się znajdują. Te procedury obsługi sygnałów zostaną następnie wykonane, przerywając w razie potrzeby normalny przepływ sterowania. Wszystkie sygnały mają numer i dwa nazwiska, z których jedno jest tajemniczym akronimem, a drugie nieco mniej tajemniczym zwrotem. Sygnał generowany, gdy proces łamie zasady ochrony pamięci, jest (zgodnie z konwencją) numerem 11, a jego nazwy to SIGSEGV
„Błąd segmentacji”. 5,6
Ważną różnicą między sygnałami a przerwaniami jest to, że dla każdego sygnału istnieje domyślne zachowanie . Jeśli system operacyjny nie zdefiniuje modułów obsługi dla wszystkich przerwań, jest to błąd w systemie operacyjnym, a cały komputer ulegnie awarii, gdy procesor spróbuje wywołać brakujący moduł obsługi. Ale procesy nie są zobowiązane do definiowania procedur obsługi sygnałów dla wszystkich sygnałów. Jeśli jądro generuje sygnał dla procesu, a sygnał ten pozostawił swoje domyślne zachowanie, jądro po prostu pójdzie naprzód i zrobi wszystko, co domyślne, i nie przeszkadza procesowi. Większość domyślnych zachowań sygnałów to „nic nie rób” lub „zakończ ten proces i być może również zrzuć rdzeń”. SIGSEGV
jest jednym z tych ostatnich.
Podsumowując, mamy proces, który złamał zasady ochrony pamięci. Procesor zawiesił proces i wygenerował przerwanie synchroniczne. Jądro wypełniło to przerwanie i wygenerowało SIGSEGV
sygnał dla procesu. Załóżmy, że proces nie skonfigurował modułu obsługi sygnałów SIGSEGV
, więc jądro wykonuje domyślne zachowanie, które polega na zakończeniu procesu. Ma to takie same skutki jak _exit
wywołanie systemowe: otwarte pliki są zamykane, pamięć jest zwalniana itp.
Do tego momentu nic nie wydrukowało żadnych wiadomości, które człowiek może zobaczyć, a powłoka (lub, bardziej ogólnie, proces nadrzędny właśnie zakończonego procesu) nie była w ogóle zaangażowana. SIGSEGV
idzie do procesu, który złamał zasady, a nie jego rodzic. Następny krok w sekwencji jest jednak to, aby powiadomić procesu nadrzędnego, że jej dziecko zostało zakończone. To może zdarzyć się na kilka różnych sposobów, z których najprostsza jest, gdy rodzic jest już czeka na tego zgłoszenia, korzystając z jednej z wait
wywołań systemowych ( wait
, waitpid
, wait4
, etc). W takim przypadku jądro po prostu spowoduje powrót tego wywołania systemowego i dostarczy procesowi nadrzędnemu kod o nazwie „ status wyjścia”. 7 Status wyjścia informuje rodzica, dlaczego proces potomny został zakończony; w takim przypadku dowie się, że dziecko zostało zakończone z powodu domyślnego zachowania SIGSEGV
sygnału.
Proces nadrzędny może następnie zgłosić zdarzenie człowiekowi, drukując wiadomość; programy powłoki prawie zawsze to robią. Twój crsh
kod nie zawiera do tego kodu, ale i tak się dzieje, ponieważ procedura biblioteki C system
uruchamia w pełni funkcjonalną powłokę /bin/sh
„pod maską”. crsh
jest dziadkiem w tym scenariuszu; powiadomienie o procesie nadrzędnym jest oznaczone przez /bin/sh
, co powoduje wydruk jego zwykłej wiadomości. Następnie /bin/sh
samo się kończy, ponieważ nie ma już nic do roboty, a implementacja biblioteki C system
odbiera to powiadomienie o wyjściu. Możesz zobaczyć to powiadomienie o wyjściu w kodzie, sprawdzając wartość zwracanąsystem
; ale nie powie ci to, że proces wnuka zmarł w wyniku awarii, ponieważ został pochłonięty przez proces powłoki pośredniej.
Przypisy
Niektóre systemy operacyjne nie implementują sterowników urządzeń jako części jądra; jednak wszystkie programy obsługi przerwań nadal muszą być częścią jądra, podobnie jak kod konfigurujący ochronę pamięci, ponieważ sprzęt nie pozwala na nic innego niż jądro.
Może istnieć program o nazwie „hypervisor” lub „manager maszyny wirtualnej”, który jest nawet bardziej uprzywilejowany niż jądro, ale dla celów tej odpowiedzi można go uznać za część sprzętu .
Jądro jest programem , ale to nie to proces; jest bardziej jak biblioteka. Wszystkie procesy od czasu do czasu wykonują części kodu jądra, oprócz własnego kodu. Może istnieć wiele „wątków jądra”, które tylko wykonują kod jądra, ale nie dotyczą nas tutaj.
Jedynym systemem operacyjnym, z którym prawdopodobnie będziesz mieć do czynienia, którego nie można uznać za implementację Uniksa, jest oczywiście Windows. W tej sytuacji nie używa sygnałów. (Rzeczywiście, nie ma sygnałów; w systemie Windows <signal.h>
interfejs jest całkowicie sfałszowany przez bibliotekę C.) Zamiast tego używa czegoś zwanego „ obsługą wyjątków strukturalnych ”.
Niektóre naruszenia ochrony pamięci generują SIGBUS
(„Błąd magistrali”) zamiast SIGSEGV
. Linia między nimi jest nieokreślona i różni się w zależności od systemu. Jeśli napisałeś program, który definiuje moduł obsługi SIGSEGV
, prawdopodobnie dobrym pomysłem jest zdefiniowanie tego samego modułu obsługi SIGBUS
.
„Błąd segmentacji” to nazwa przerwania wygenerowanego z powodu naruszenia ochrony pamięci przez jeden z komputerów z oryginalnym Uniksem , prawdopodobnie PDP-11 . „ Segmentacja ” jest rodzajem ochrony pamięci, ale obecnie termin „ błąd segmentacji ” odnosi się ogólnie do każdego rodzaju naruszenia ochrony pamięci.
Wszystkie inne sposoby, w jakie proces nadrzędny może zostać powiadomiony o zakończeniu dziecka, kończą się tym, że rodzic dzwoni wait
i otrzymuje status wyjścia. Po prostu coś innego dzieje się najpierw.
crsh
to świetny pomysł na tego rodzaju eksperymenty. Dziękujemy za poinformowanie nas o tym i idei, która za tym stoi.