Analisis proses boot kernel Linux

Halo semuanya!

Sementara Leonid sedang mempersiapkan pelajaran terbuka pertamanya di kursus Administrator Linux kami, kami terus berbicara tentang memuat kernel Linux.

Ayo pergi!

Memahami cara kerja sistem tanpa kegagalan - Bersiap untuk memperbaiki kerusakan yang tidak terhindarkan

Lelucon tertua di bidang open source adalah pernyataan bahwa "kode itu mendokumentasikannya sendiri." Pengalaman menunjukkan bahwa membaca kode sumber seperti mendengarkan ramalan cuaca: orang cerdas masih akan pergi ke luar dan melihat ke langit. Di bawah ini adalah tip untuk memeriksa dan memeriksa boot sistem Linux menggunakan alat debugging yang sudah dikenal. Analisis proses boot sistem yang berfungsi dengan baik mempersiapkan pengguna dan pengembang untuk menyelesaikan crash yang tidak terhindarkan.

Di satu sisi, proses pengunduhan ternyata sangat sederhana. Kernel dari sistem operasi (kernel) berjalan satu-threaded dan serentak pada satu inti (core), yang mungkin tampak dimengerti bahkan untuk pikiran manusia yang menyedihkan. Tetapi bagaimana kernel OS dimulai? Apa fungsi initrd ( disk RAM untuk inisialisasi awal ) dan yang dilakukan bootloader? Dan tunggu, mengapa LED pada port Ethernet selalu menyala?



Baca terus untuk mendapatkan jawaban untuk ini dan beberapa pertanyaan lainnya; Kode untuk demo dan latihan yang dijelaskan juga tersedia di GitHub .

Mulai boot: status OFF

Wake-on-LAN

Status MATI berarti sistem tidak memiliki daya, bukan? Kesederhanaan yang tampak menipu. Misalnya, LED Ethernet menyala bahkan dalam kondisi ini, karena wake-on-LAN (WOL, wake-up [signal from] jaringan lokal) dihidupkan di sistem Anda. Pastikan dengan menulis:

$# sudo ethtool <interface name> 

Di mana sebaliknya bisa, misalnya, eth0 (ethtool dalam paket Linux dengan nama yang sama). Jika "Wake-on" pada output menunjukkan g, host jarak jauh dapat mem-boot sistem dengan mengirimkan MagicPacket . Jika Anda tidak ingin mengaktifkan sendiri sistem Anda dari jarak jauh dan memberikan kesempatan ini kepada orang lain, nonaktifkan WOL di menu BIOS sistem, atau menggunakan:

 $# sudo ethtool -s <interface name> wol d 

Prosesor yang merespons MagicPacket dapat berupa Baseboard Management Controller (BMC) atau bagian dari antarmuka jaringan.

Mesin Manajemen Intel, Platform Controller Hub, dan Minix

BMC bukan satu-satunya mikrokontroler (MCU) yang dapat "mendengarkan" sistem yang dimatikan secara nominal. Sistem X86_64 memiliki paket perangkat lunak Intel Management Engine (IME) untuk manajemen sistem jarak jauh. Berbagai perangkat, mulai dari server hingga laptop, memiliki teknologi yang memiliki fitur seperti KVM Remote Control atau Layanan Kemampuan Intel. Menurut alat Inte l sendiri , IME memiliki kerentanan yang belum ditambal. Berita buruknya adalah menonaktifkan IME itu sulit. Trammell Hudson menciptakan proyek me_cleaner, yang menghapus beberapa komponen IME yang paling mengerikan, seperti server web tertanam, tetapi pada saat yang sama ada kemungkinan bahwa menggunakan proyek tersebut akan mengubah sistem yang menjalankannya menjadi batu bata.

Firmware IME dan program Mode Manajemen Sistem (SMM) yang mengikutinya saat boot didasarkan pada sistem operasi Minix dan berjalan pada prosesor Platform Controller Hub yang terpisah, bukan CPU utama dari sistem. Kemudian SMM meluncurkan program Universal Extensible Firmware Interface (UEFI) pada prosesor utama, yang telah ditulis lebih dari satu kali . Grup Coreboot meluncurkan proyek Non-Extensible Reduced Firmware (NERF) yang ambisius dan ambisius di Google, yang bertujuan untuk menggantikan tidak hanya UEFI, tetapi juga komponen awal ruang pengguna Linux, seperti systemd. Sementara itu, kami menunggu hasilnya, pengguna Linux dapat membeli laptop dari Purism, System76 atau Dell, di mana IME dinonaktifkan , ditambah, kami dapat berharap untuk laptop dengan prosesor ARM 64-bit .

Loader

Apa yang dilakukan firmware yang dapat di-boot selain meluncurkan spyware yang dicurigai? Tugas bootloader adalah menyediakan prosesor yang baru saja dinyalakan dengan sumber daya yang diperlukan untuk menjalankan sistem operasi serba guna seperti Linux. Selama power-up, tidak hanya memori virtual, tetapi juga DRAM sampai saat menaikkan pengontrolnya. Bootloader kemudian menyalakan catu daya dan memindai bus dan antarmuka untuk menemukan gambar kernel dan sistem file root. Bootloader populer, seperti U-Boot dan GRUB, mendukung kedua antarmuka umum seperti USB, PCI dan NFS, serta perangkat tertanam lainnya yang lebih khusus, seperti NOR- dan NAND-flash. Loader juga berinteraksi dengan perangkat perangkat keras keamanan, seperti Trusted Platform Module (TPM) , untuk membangun rantai kepercayaan sejak awal pengunduhan.


Menjalankan U-boot loader di kotak pasir di server build.

Bootloader U-Boot open source yang populer didukung oleh sistem dari Raspberry Pi hingga perangkat Nintendo, papan mobil, dan Chromebook. Tidak ada log sistem, dan jika terjadi kesalahan, bahkan mungkin tidak ada output konsol. Untuk memudahkan debugging, tim U-Boot menyediakan kotak pasir untuk menguji tambalan pada build host atau bahkan dalam sistem Integrasi Berkelanjutan. Pada sistem dengan alat pengembangan umum seperti Git dan GNU Compiler Collection (GCC) yang diinstal, memahami kotak pasir U-Boot itu mudah.

 $# git clone git://git.denx.de/u-boot; cd u-boot $# make ARCH=sandbox defconfig $# make; ./u-boot => printenv => help 

Itu saja: Anda meluncurkan U-Boot pada x86_64 dan Anda dapat menguji fitur-fitur rumit, misalnya, partisi ulang perangkat penyimpanan fiktif , manipulasi kunci rahasia berbasis TPM, dan hotplug perangkat USB. Kotak pasir U-Boot bisa satu tahap di dalam debugger GDB. Pengembangan menggunakan kotak pasir adalah 10 kali lebih cepat daripada pengujian dengan menimpa bootloader di papan, ditambah, kotak pasir "bata" dapat dipulihkan dengan menekan Ctrl + C.

Peluncuran kernel

Booting Pasokan Kernel

Setelah menyelesaikan tugasnya, bootloader akan beralih ke kode kernel yang dimuat ke dalam memori utama dan mulai menjalankannya, melewati semua parameter baris perintah yang ditentukan pengguna. Program apa itu kernel? file / boot / vmlinuz menunjukkan bahwa ini adalah bzImage. Linux source tree memiliki alat ekstrak-vmlinux yang dapat Anda gunakan untuk mengekstrak file:

 $# scripts/extract-vmlinux /boot/vmlinuz-$(uname -r) > vmlinux $# file vmlinux vmlinux: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), statically linked, stripped 

Kernel adalah file biner yang dapat dieksekusi dan menautkan Format (ELF) , seperti program ruang pengguna Linux. Ini berarti bahwa kita dapat menggunakan perintah binutils seperti readelf untuk mempelajarinya. Bandingkan, misalnya, kesimpulan berikut:

 $# readelf -S /bin/date $# readelf -S vmlinux 

Daftar partisi dalam file biner sebagian besar mirip.

Jadi, kernel harus meluncurkan binari ELF Linux lainnya ... Tapi bagaimana program ruang pengguna dijalankan? Dalam fungsi main() , kan? Tidak juga.

Sebelum menjalankan fungsi main() , program memerlukan konteks eksekusi, termasuk memori heap- (heap) dan stack- (stack), ditambah deskriptor file untuk stdio , stdout dan stderr . Program ruang pengguna mendapatkan sumber daya ini dari perpustakaan standar ( glibc untuk sebagian besar sistem Linux). Pertimbangkan yang berikut ini:

 $# file /bin/date /bin/date: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=14e8563676febeb06d701dbee35d225c5a8e565a, stripped 

Binari ELF memiliki juru bahasa, seperti halnya skrip Bash dan Python. Tapi itu tidak perlu ditentukan melalui #! seperti pada skrip, karena ELF adalah format Linux asli. Penerjemah ELF memasok file biner dengan semua sumber daya yang diperlukan dengan memanggil _start() , suatu fungsi yang tersedia dalam paket sumber glibc , yang dapat dipelajari melalui GDB . Kernel, jelasnya, tidak memiliki juru bahasa, dan harus menyediakan sendiri secara independen, tetapi bagaimana caranya?

Studi memulai kernel dengan GDB memberikan jawaban untuk pertanyaan ini. Untuk memulai, instal paket debugging kernel, yang berisi versi vmlinux belum dipotong, misalnya, apt-get install linux-image-amd64-dbg . Atau kompilasi dan instal kernel Anda sendiri dari beberapa sumber, misalnya, mengikuti instruksi dari Handbook Debian Kernel yang sangat baik. gdb vmlinux diikuti oleh info files menunjukkan bagian ELF init.text . Tunjukkan dimulainya eksekusi program di init.text dengan l *(address) , di mana alamat adalah awal init.text heksadesimal. GDB akan menunjukkan bahwa kernel x86_64 diluncurkan dalam arch/x86/kernel/head_64.S , di mana kami menemukan fungsi build start_cpu0() dan kode yang secara eksplisit membuat stack dan mendekompresi zImage sebelum memanggil x86_64 start_kernel() . Core ARM 32-bit memiliki arch/arm/kernel/head.S. start_kernel() arch/arm/kernel/head.S. start_kernel() adalah arsitektur independen, sehingga fungsinya terletak di kernel init/main.c Kita dapat mengatakan bahwa start_kernel() adalah fungsi Linux main() nyata.

Dari start_kernel () ke PID 1
Manifestasi Perangkat Keras Kernel: ACPI Tabel dan Pohon Perangkat

Saat boot, kernel membutuhkan informasi tentang perangkat keras di samping jenis prosesor yang dikompilasi. Instruksi dalam kode dilengkapi dengan data konfigurasi, yang disimpan secara terpisah. Ada dua metode utama untuk menyimpan data: Pohon perangkat dan tabel ACPI . Dari file-file ini, kernel menemukan peralatan apa yang perlu dijalankan pada setiap boot.

Untuk perangkat tertanam, bagan perangkat (DU) adalah manifes dari peralatan yang dipasang. DU adalah file yang mengkompilasi pada saat yang sama dengan sumber kernel dan biasanya terletak di / boot bersama dengan vmlinux . Untuk melihat apa yang ada di pohon perangkat biner pada perangkat ARM, cukup gunakan perintah strings dari paket binutils dalam file yang namanya sesuai dengan /boot/*.dtb , karena dtb berarti file biner dari pohon perangkat (Device-Tree Binary). Anda dapat mengubah remote control dengan mengedit file seperti JSON yang terdiri dari dan me-restart kompiler dtc khusus yang disediakan dengan sumber kernel. DU adalah file statis yang jalurnya biasanya dilewatkan ke kernel oleh bootloader pada baris perintah, tetapi dalam beberapa tahun terakhir overlay hierarki perangkat telah ditambahkan di mana kernel dapat secara dinamis memuat fragmen tambahan sebagai tanggapan terhadap kejadian hotplug setelah memuat.

Keluarga x86 dan banyak perangkat tingkat bisnis ARM64 menggunakan mekanisme Advanced Configuration and Power Interface ( ACPI) alternatif. Berbeda dengan remote control, informasi ACPI disimpan dalam sistem file virtual /sys/firmware/acpi/tables , yang dibuat oleh kernel saat startup dengan mengakses ROM internal. Untuk membaca tabel ACPI, gunakan perintah acpidump dari paket acpica-tools . Berikut ini sebuah contoh:


Tabel ACPI pada laptop Lenovo siap untuk Windows 2001.

Ya, sistem Linux Anda siap untuk Windows 2001 jika Anda ingin menginstalnya. ACPI memiliki kedua metode dan data, berbeda dengan remote control, yang lebih mirip bahasa deskripsi perangkat keras. Metode ACPI terus aktif setelah boot. Misalnya, jika Anda menjalankan perintah acpi_listen (dari paket apcid), lalu tutup dan buka tutup laptop, Anda akan melihat bahwa fungsi ACPI terus berfungsi selama ini. Menulis ulang sementara dan dinamis dari tabel ACPI dimungkinkan, tetapi perubahan permanen akan membutuhkan interaksi dengan menu BIOS saat boot atau flashing ROM. Alih-alih kompleksitas seperti itu, mungkin Anda harus menginstal coreboot , pengganti firmware sumber terbuka.

Dari start_kernel () ke ruang pengguna

Kode di init/main.c secara mengejutkan mudah dibaca dan, anehnya, masih menggunakan hak cipta asli Linus Torvalds dari tahun 1991-1992. Baris ditemukan di dmesg | head dmesg | head sistem yang berjalan pada dasarnya berasal dari file sumber ini. CPU pertama didaftarkan oleh sistem, struktur data global diinisialisasi, satu demi satu penjadwal, interrupt handler (IRQs), timer dan konsol dinaikkan. Semua cap waktu sebelum menjalankan timekeeping_init() adalah nol. Bagian inisialisasi kernel ini bersifat sinkron, artinya eksekusi hanya dilakukan dalam satu utas. Fungsi tidak dieksekusi sampai yang terakhir selesai dan dikembalikan. Akibatnya, output dmesg akan sepenuhnya dapat direproduksi bahkan di antara kedua sistem, selama mereka memiliki remote control atau tabel ACPI yang sama. Linux juga berperilaku seperti sistem operasi waktu nyata (RTOS) yang berjalan pada MCU, seperti QNX atau VxWorks. Situasi ini disimpan dalam fungsi rest_init() , yang dipanggil oleh start_kernel() pada saat penyelesaiannya.


Deskripsi singkat tentang proses boot kernel awal

rest_init() dengan nama sederhana rest_init() membuat utas baru yang menjalankan kernel_init() , yang kemudian memanggil do_initcalls() . Pengguna dapat memantau operasi initcalls dengan menambahkan initcalls_debug ke baris perintah kernel. Akibatnya, Anda akan mendapatkan entitas dmesg setiap kali Anda menjalankan fungsi initcall . initcalls melewati tujuh level berturut-turut: awal, inti, postcore, lengkung, subsys, fs, perangkat dan belakangan. Bagian yang paling nyata dari initcalls untuk pengguna adalah identifikasi dan pemasangan perangkat periferal prosesor: bus, jaringan, penyimpanan, tampilan, dan sebagainya, disertai dengan pemuatan modul kernel mereka. rest_init() juga membuat utas kedua dalam prosesor boot, yang dimulai dengan menjalankan cpu_idle() sementara scheduler mendistribusikan pekerjaannya.

kernel_init() juga mengatur symmetric multiprocessing (SMP). Di kernel modern, Anda dapat menemukan momen ini di output dmesg dengan baris "Membesarkan CPU sekunder ...". SMP kemudian membuat hot-plug CPU, yang berarti bahwa ia mengelola siklus hidupnya menggunakan mesin negara yang secara kondisional mirip dengan yang digunakan dalam perangkat seperti USB memory-sensing sensing sticks. Sistem manajemen daya kernel sering mematikan masing-masing core (core) dan membangunkannya sesuai kebutuhan sehingga kode CPU hotplug yang sama dipanggil berulang kali pada mesin yang tidak dihuni. Lihatlah bagaimana sistem manajemen daya memanggil hotplug CPU menggunakan alat BCC yang disebut offcputime.py .

Perhatikan bahwa kode di init/main.c hampir selesai dieksekusi ketika smp_init() dijalankan. Prosesor boot menyelesaikan sebagian besar inisialisasi satu kali, yang tidak perlu diulang oleh kernel lain. Namun, utas harus dibuat untuk setiap inti agar dapat mengontrol interupsi (IRQ), workqueue, timer, dan peristiwa daya pada masing-masing. Sebagai contoh, lihat thread prosesor yang melayani softirqs dan workqueues dengan perintah ps -o psr.

 $\# ps -o pid,psr,comm $(pgrep ksoftirqd) PID PSR COMMAND 7 0 ksoftirqd/0 16 1 ksoftirqd/1 22 2 ksoftirqd/2 28 3 ksoftirqd/3 $\# ps -o pid,psr,comm $(pgrep kworker) PID PSR COMMAND 4 0 kworker/0:0H 18 1 kworker/1:0H 24 2 kworker/2:0H 30 3 kworker/3:0H [ . . . ] 

di mana bidang PSR berarti "prosesor". Setiap inti harus memiliki pengatur waktu sendiri dan penangan hotplug cpuhp.

Dan akhirnya, bagaimana ruang pengguna diluncurkan? Menjelang akhir, kernel_init() mencari initrd yang dapat memulai proses init atas namanya. Jika tidak, kernel mengeksekusi init sendiri. Mengapa initrd mungkin dibutuhkan?

Ruang pengguna awal: siapa yang memesan initrd?

Selain bagan perangkat, jalur init lain ke file, secara opsional disediakan oleh kernel saat boot, milik initrd . initrd sering terletak di / boot bersama dengan file bzImage vmlinuz pada x86, atau dengan uImage dan pohon perangkat serupa untuk ARM. Daftar isi intrd dapat dilihat menggunakan alat lsinitramfs , yang merupakan bagian dari paket initramfs-tools-core . Gambar distribusi initrd berisi direktori minimum /bin , /sbin dan /etc , serta modul kernel dan file dalam /scripts . Semuanya harus tampak kurang asing, karena initrd sebagian besar mirip dengan sistem file root Linux yang disederhanakan. Kesamaan ini agak menyesatkan, karena hampir semua file yang dapat dieksekusi di /bin dan /sbin di dalam ramdisk adalah symlink ke biner BusyBox , yang membuat direktori / bin dan / sbin 10 kali lebih kecil daripada di glibc .

Mengapa mencoba membuat initrd jika satu-satunya yang dilakukannya adalah memuat beberapa modul dan menjalankan init pada sistem file root biasa? Pertimbangkan sistem file root terenkripsi. Dekripsi mungkin tergantung pada pemuatan modul kernel yang disimpan di /lib/modules sistem file root ... dan, seperti yang diharapkan, di initrd . Modul crypto dapat dikompilasi secara statis ke dalam kernel, dan tidak dimuat dari file, tetapi ada beberapa alasan untuk menolaknya. Sebagai contoh, kompilasi statis kernel dengan modul mungkin membuatnya terlalu besar untuk muat dalam penyimpanan yang tersedia, atau kompilasi statis dapat melanggar persyaratan lisensi perangkat lunak. Tidak mengherankan, driver penyimpanan, jaringan, dan HID (perangkat input manusia) juga dapat diwakili dalam initrd - pada dasarnya setiap kode yang bukan merupakan bagian dari kernel yang diperlukan untuk me-mount sistem file root. Juga di initrd, pengguna dapat menyimpan kode ACPI mereka sendiri untuk tabel .


Bersenang-senang dengan shell penyelamatan dan initrd khusus.

initrd juga bagus untuk menguji sistem file dan perangkat penyimpanan. Letakkan alat pengujian di initrd dan jalankan tes dari memori, bukan dari objek tes.

Akhirnya, ketika init berjalan, sistem sedang berjalan! Karena prosesor sekunder sudah berjalan, mesin tersebut telah menjadi makhluk yang tidak sinkron, berurutan, tidak dapat diprediksi, dan berkinerja tinggi yang kita semua kenal dan cintai. Memang, ps -o pid,psr,comm -p menunjukkan bahwa proses init ruang pengguna tidak lagi berjalan pada prosesor boot.

Ringkasan

Proses boot Linux terdengar terlarang, mengingat jumlah perangkat lunak yang terpengaruh, bahkan pada perangkat embedded sederhana. Di sisi lain, proses booting cukup sederhana, karena tidak ada kompleksitas berlebihan yang disebabkan oleh crowding out multitasking, RCU dan kondisi balapan. Hanya memperhatikan kernel dan PID 1, orang dapat mengabaikan pekerjaan hebat yang dilakukan oleh boot loader dan prosesor tambahan untuk mempersiapkan platform untuk peluncuran kernel. Kernel tentu berbeda dari program Linux lainnya, tetapi menggunakan alat untuk bekerja dengan binari ELF lainnya akan membantu untuk lebih memahami strukturnya. Mempelajari proses boot yang bisa diterapkan akan mempersiapkan crash di masa depan.

AKHIR

Kami menunggu komentar dan pertanyaan Anda, seperti biasa, baik di sini atau di pelajaran terbuka kami di mana Leonid akan terpesona.

Source: https://habr.com/ru/post/id425505/


All Articles