Czy proces init może być skryptem powłoki w systemie Linux?


14

Przechodziłem samouczek na temat konfigurowania niestandardowego initramfs, w którym napisano:

Jedyne czego brakuje to / init, plik wykonywalny w katalogu głównym initramfs, który jest wykonywany przez jądro po załadowaniu. Ponieważ sys-apps / busybox zawiera w pełni funkcjonalną powłokę, oznacza to, że możesz napisać swój plik binarny / init jako prosty skrypt powłoki (zamiast robić z niego skomplikowaną aplikację napisaną w Asemblerze lub C, którą musisz skompilować).

i podaje przykład init jako skrypt powłoki, który zaczyna się od #!/bin/busybox sh

Do tej pory miałem wrażenie, że init jest głównym procesem, który jest uruchamiany i że wszystkie inne procesy przestrzeni użytkownika są ostatecznie potomkami init. Jednak w podanym przykładzie tak naprawdę pierwszy proces jest bin/busybox/ shinicjowany.

Czy to poprawna interpretacja? Gdybym na przykład miał w tym momencie dostępnego interpretera, mógłbym napisać init jako skrypt Pythona itp.?

Odpowiedzi:


12

init nie jest „spawnowany” (jako proces potomny), ale raczej exectak:

# Boot the real thing.
exec switch_root /mnt/root /sbin/init

execzastępuje cały proces na miejscu. Ostateczna inicjacja jest nadal pierwszym procesem (pid 1), mimo że była poprzedzona tymi w Initramfs.

Initramfs /init, który jest skryptem powłoki Busybox z pid 1, execs do Busybox switch_root(teraz switch_rootjest to pid 1); ten program zmienia punkty montowania, więc /mnt/rootbędzie nowy /.

switch_rootnastępnie ponownie execs do /sbin/inittwojego prawdziwego głównego systemu plików; w ten sposób twój prawdziwy system inicjujący jest pierwszym procesem z pid 1, który z kolei może spawnować dowolną liczbę procesów potomnych.

Z pewnością można to równie dobrze zrobić za pomocą skryptu Python, jeśli w jakiś sposób udało się upiec Pythona w Initramfs. Chociaż jeśli i tak nie planujesz dołączać do busyboksa, musiałbyś z należytą starannością zaimplementować niektóre jego funkcje (takie jak switch_rootwszystko inne, co zwykle robisz za pomocą prostego polecenia).

Jednak nie działa na jądrach, które nie zezwalają na pliki binarne skryptów ( CONFIG_BINFMT_SCRIPT=y), a raczej w takim przypadku będziesz musiał uruchomić interpreter bezpośrednio i zmusić go do załadowania skryptu.


/nie rozpływa się w powietrzu - jest zamontowany (choć zwykle cała zawartość jest usuwana przed zapisaniem pamięci) . Jest tam jeszcze . switch_rootrobi syscall switchroot- to jest to, co zapewnia jądro, gdy zmienili proces rozruchu w jądrze 2.6. coś, co wymaga initramfs. To jądro robi magię.
mikeserv

1
switchrootSyscall rzeczywiście byłoby dla mnie nowość. Czy masz na to źródło? Jeśli spojrzysz na kod źródłowy switch_root.c, wydaje się, że jest to proces dość ręczny i taki sam, jak opisano w Documentation / filesystems / ramfs-rootfs-initramfs.txt. A jeśli wszystko usuniesz i zamontujesz, w tym momencie prawie zniknie, nie sądzisz?
frostschutz

pivot_rootz drugiej strony jest wywołaniem systemowym. Nie jest jednak używany switch_rooti nie można go używać bez przeskakiwania przez kilka obręczy, a tak czy inaczej nie ma znaczenia dla tej odpowiedzi, więc po prostu ją usunąłem. Szkoda, myślałem, że magia i zniknięcie w powietrzu działają naprawdę dobrze ... :-P
frostschutz

Cóż, może mam zły pomysł na to switch_root- za co przepraszam i dziękuję za pokazanie mi - ale i tak niczego nie znika. initramfs powtarzał korzeniowe i jest tam zawsze dla wszystkich - to jest korzeń.
mikeserv

1
Jak mówią dokumenty, które podlinkowałeś : Ale initramfs to rootfs: nie możesz ani rootfs pivot_root, ani odmontować go. Zamiast tego usuń wszystko z rootfs, aby zwolnić miejsce ( find -xdev / -exec rm '{}' ';'), zamontuj rootfs za pomocą nowego root ( cd /newmount; mount --move . /; chroot .), dołącz stdin / stdout / stderr do nowego / dev / console i uruchom nową init.
mikeserv

4

Wywołanie systemowe jądra Linuksa natywnie nie docenia shebangów

Kiedy wykonywany plik zaczyna się od bajtów magicznych #!, informuje jądro, aby używało go #!/bin/shjako:

  • do i execwywołanie systemowe
  • z plikiem wykonywalnym /bin/sh
  • oraz z argumentem CLI: ścieżka do bieżącego skryptu

Dokładnie tak samo dzieje się po uruchomieniu zwykłego skryptu powłoki użytkownika z:

./myscript.sh

Gdyby plik zaczął się od bajtów magicznych .ELFzamiast #!, jądro wybrałoby moduł ładujący ELF, aby go uruchomić.

Więcej informacji: Dlaczego ludzie piszą #! / Usr / bin / env shebang python w pierwszym wierszu skryptu Python? | Przepełnienie stosu

Kiedy to sobie przypomnisz, łatwo będzie zaakceptować, że /initmoże to być wszystko co jądro może wykonać, w tym skrypt powłoki, a także dlaczego /bin/shbędzie to pierwszy plik wykonywalny w tym przypadku.

Oto minimalny uruchamialny przykład dla tych, którzy chcą go wypróbować: https://github.com/cirosantilli/linux-kernel-module-cheat/tree/cbea7cc02c868711109ae1a261d01fd0473eea0b#custom-init

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.