Zanim zagłębimy się w definicję wywołania systemu Linuksa i zbadamy szczegóły jego wykonania, najlepiej zacząć od zdefiniowania różnych warstw oprogramowania typowego systemu Linux.
Kernel Linux to wyspecjalizowany program, który uruchamia się i działa na najniższym dostępnym poziomie na sprzęcie. Ma zadanie zorganizowania wszystkiego, co działa na komputerze, w tym obsługę zdarzeń klawiatury, dysku i sieci w celu zapewnienia równolegle wielu programów do wykonywania wielu programów.
Kiedy jądro wykonuje program na poziomie użytkownika, wirtualizuje przestrzeń pamięci, aby programy uważały, że są jedynym procesem uruchomionym w pamięci. Ta ochronna bańka sprzętu i izolacji oprogramowania zwiększa bezpieczeństwo i niezawodność. Nieuprzywilejowana aplikacja nie może uzyskać dostępu do pamięci należącej do innych programów, a jeśli ten program się zawiedzie, jądro kończy się tak, aby nie mogła zaszkodzić reszty systemu.
Zamieszanie bariery z wywołani systemem systemu Linux
Ta warstwa izolacji między nieuprzywilejowanymi aplikacjami stanowi doskonałą granicę do ochrony innych aplikacji i użytkowników w systemie. Jednak bez pewnego sposobu interfejsu z innymi elementami w komputerze i świecie zewnętrznym programy nie byłyby w stanie osiągnąć wiele.
Aby ułatwić interakcję, jądro wyznacza bramę oprogramowania, która umożliwia uruchomionemu programowi poprosić o działanie jądra w jego imieniu. Ten interfejs jest znany jako wywołanie systemowe.
Ponieważ Linux postępuje zgodnie z filozofią UNIX „Everything Is a Plik”, wiele funkcji można wykonać, otwierając i czytanie lub zapisanie do pliku, co może być urządzeniem. Na przykład w systemie Windows możesz użyć funkcji o nazwie CryptGenRandom, aby uzyskać dostęp do losowych bajtów. Ale w systemie Linux można to zrobić, po prostu otwierając „Plik”/dev/urandom i odczytując z niego bajty za pomocą standardowych połączeń systemu wejściowego/wyjściowego pliku. Ta kluczowa różnica pozwala na prostszy interfejs wywołania systemu.
Wazer-cienki opakowanie
W większości aplikacji wywołania systemowe nie są wykonywane bezpośrednio do jądra. Praktycznie wszystkie programy łączą się w standardowej bibliotece C, która zapewnia cienkie, ale ważne opakowanie wokół połączeń systemowych Linux. Biblioteka upewnia się, że argumenty funkcyjne są skopiowane do prawidłowych rejestrów procesora, a następnie wydaje odpowiednie połączenie systemowe Linux. Po otrzymaniu danych z wywołania opakowanie interpretuje wyniki i zwraca je z powrotem do programu w spójny sposób.
Za kulisami
Każda funkcja w programie, który wchodzi w interakcję z systemem, jest ostatecznie przetłumaczona na wywołanie systemowe. Aby to zobaczyć w akcji, zacznijmy od podstawowego przykładu.
void main ()To prawdopodobnie najbardziej trywialny program C, jaki kiedykolwiek zobaczysz. Po prostu zyskuje kontrolę za pośrednictwem głównego punktu wejścia, a następnie wychodzi. Nie zwraca nawet wartości, ponieważ główny jest zdefiniowany jako pustka. Zapisz plik jako CTEST.c i skompilujmy to:
GCC CTEST.C -O CTESTPo skompilowaniu możemy zobaczyć rozmiar pliku jako 8664 bajtów. Może się nieznacznie różnić w twoim systemie, ale powinien wynosić około 8k. To dużo kodu, aby wejść i wyjść! Powodem, dla którego jest to 8K, jest to, że włączono czas wykonawczy LIBC. Nawet jeśli rozebramy symbole, wciąż jest to odrobina ponad 6k.
W jeszcze prostszym przykładzie możemy sprawić, by system Linux wywołuje wychodzenie, a nie zależnie od czasu wykonania C, aby to zrobić dla nas.
void _start ()Tutaj przenosimy 1 do rejestru EAX, wyczyść rejestr EBX (który w inny sposób zawiera wartość zwracania), a następnie wywołać przerwanie połączenia systemowego Linux 0x80 (lub 128 w dziesiętnym). To przerwanie uruchamia jądro, aby przetworzyć nasze wezwanie.
Jeśli skompilujemy nasz nowy przykład, zwany asmtest.c i rozbij symbole i wyklucz standardową bibliotekę:
gcc -s -nostdlib asmtest.C -O asmtestWyprodukujemy binarne mniej niż 1k (w moim systemie daje 984 bajtów). Większość tego kodu to nagłówki wykonywalne. Teraz wywołujemy bezpośrednie połączenie systemowe Linux.
Do wszystkich praktycznych celów
W prawie wszystkich przypadkach nigdy nie będziesz musiał wykonywać bezpośrednich połączeń systemowych w programach C. Jeśli jednak używasz języka asemblera, może nastąpić potrzeba. Jednak w optymalizacji najlepiej byłoby pozwolić funkcjom biblioteki C wykonać wywoływanie systemu i mieć tylko kod krytyczny w wyniku wyników w dyrektywach montażu.
Jeśli chcesz zobaczyć listę wszystkich dostępnych wywołań systemowych dla Linux, możesz sprawdzić te strony referencyjne: Pełna lista wywołań systemowych na Linuxhint.com, Filippo.IO/ Linux-Syscall-Table/ lub Syscalls.Kernelgrok.com