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 OFFWake-on-LANStatus 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 MinixBMC 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 kernelBooting Pasokan KernelSetelah 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 1Manifestasi Perangkat Keras Kernel: ACPI Tabel dan Pohon PerangkatSaat 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.
$\
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.
RingkasanProses 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.