GNU/Linux >> Belajar Linux >  >> Linux

Menjelajahi kernel Linux:Rahasia Kconfig/kbuild

Sistem config/build kernel Linux, juga dikenal sebagai Kconfig/kbuild, telah ada sejak lama, sejak kode kernel Linux bermigrasi ke Git. Namun, sebagai infrastruktur pendukung, jarang menjadi sorotan; bahkan pengembang kernel yang menggunakannya dalam pekerjaan sehari-hari mereka tidak pernah benar-benar memikirkannya.

Untuk mempelajari bagaimana kernel Linux dikompilasi, artikel ini akan membahas proses internal Kconfig/kbuild, menjelaskan bagaimana file .config dan file vmlinux/bzImage diproduksi, dan memperkenalkan trik cerdas untuk pelacakan dependensi.

Kconfig

Langkah pertama dalam membangun kernel selalu konfigurasi. Kconfig membantu membuat kernel Linux menjadi sangat modular dan dapat disesuaikan. Kconfig menawarkan banyak target konfigurasi kepada pengguna:

config Perbarui konfigurasi saat ini menggunakan program berorientasi garis
nconfig Perbarui konfigurasi saat ini menggunakan program berbasis menu ncurses
menuconfig Perbarui konfigurasi saat ini menggunakan program berbasis menu
xconfig Perbarui konfigurasi saat ini menggunakan frontend berbasis Qt
gconfig Perbarui konfigurasi saat ini menggunakan frontend berbasis GTK+
konfigurasi lama Perbarui konfigurasi saat ini menggunakan .config yang disediakan sebagai basis
localmodconfig Perbarui modul penonaktifan konfigurasi saat ini yang tidak dimuat
localyesconfig Perbarui konfigurasi saat ini yang mengubah mod lokal menjadi inti
defconfig Konfigurasi baru dengan default dari defconfig yang disediakan oleh Arch
savedefconfig Simpan konfigurasi saat ini sebagai ./defconfig (konfigurasi minimal)
allnoconfig Konfigurasi baru di mana semua opsi dijawab dengan 'tidak'
allyesconfig Konfigurasi baru di mana semua opsi diterima dengan 'ya'
allmodconfig Modul pemilihan konfigurasi baru jika memungkinkan
alldefconfig Konfigurasi baru dengan semua simbol disetel ke default
randconfig Konfigurasi baru dengan jawaban acak untuk semua opsi
listnewconfig Mencantumkan opsi baru
olddefconfig Sama seperti oldconfig tetapi menyetel simbol baru ke nilai defaultnya tanpa diminta
kvmconfig Aktifkan opsi tambahan untuk dukungan kernel tamu KVM
xenconfig Aktifkan opsi tambahan untuk xen dom0 dan dukungan kernel tamu
tinyconfig Konfigurasikan kernel sekecil mungkin

Saya pikir menuconfig adalah yang paling populer dari target ini. Target diproses oleh program host yang berbeda, yang disediakan oleh kernel dan dibangun selama pembuatan kernel. Beberapa target memiliki GUI (untuk kenyamanan pengguna) sementara sebagian besar tidak. Alat dan kode sumber terkait Kconfig sebagian besar berada di bawah scripts/kconfig/ di sumber kernel. Seperti yang dapat kita lihat dari scripts/kconfig/Makefile , ada beberapa program host, termasuk conf , mconf , dan nconf . Kecuali untuk conf , masing-masing bertanggung jawab atas salah satu target konfigurasi berbasis GUI, jadi, conf berurusan dengan sebagian besar dari mereka.

Secara logis, infrastruktur Kconfig memiliki dua bagian:satu mengimplementasikan bahasa baru untuk mendefinisikan item konfigurasi (lihat file Kconfig di bawah sumber kernel), dan yang lainnya mem-parsing bahasa Kconfig dan menangani tindakan konfigurasi.

Sebagian besar target konfigurasi memiliki proses internal yang kira-kira sama (ditunjukkan di bawah):

Terminal Linux

  • 7 emulator terminal teratas untuk Linux
  • 10 alat baris perintah untuk analisis data di Linux
  • Unduh Sekarang:lembar contekan SSH
  • Lembar contekan perintah Linux tingkat lanjut
  • Tutorial baris perintah Linux

Perhatikan bahwa semua item konfigurasi memiliki nilai default.

Langkah pertama membaca file Kconfig di bawah root sumber untuk membangun database konfigurasi awal; kemudian memperbarui database awal dengan membaca file konfigurasi yang ada sesuai dengan prioritas ini:

.config

/lib/modules/$(shell,uname -r)/.config

/etc/kernel-config

/boot /config-$(shell,uname -r)

ARCH_DEFCONFIG

arch/$(ARCH)/defconfig

Jika Anda melakukan konfigurasi berbasis GUI melalui menuconfig atau konfigurasi berbasis baris perintah melalui oldconfig , database diperbarui sesuai dengan kustomisasi Anda. Terakhir, database konfigurasi dibuang ke file .config.

Tetapi file .config bukanlah makanan terakhir untuk pembuatan kernel; inilah mengapa syncconfig target ada. syncconfig dulunya adalah target konfigurasi yang disebut silentoldconfig , tetapi itu tidak melakukan apa yang dikatakan nama lama, jadi namanya diganti. Juga, karena ini untuk penggunaan internal (bukan untuk pengguna), itu dihapus dari daftar.

Berikut adalah ilustrasi dari apa yang syncconfig melakukan:

syncconfig mengambil .config sebagai input dan mengeluarkan banyak file lain, yang terbagi dalam tiga kategori:

  • auto.conf &tristate.conf digunakan untuk pemrosesan teks makefile. Misalnya, Anda mungkin melihat pernyataan seperti ini di makefile komponen: 
    obj-$(CONFIG_GENERIC_CALIBRATE_DELAY) += calibrate.o
  • autoconf.h digunakan dalam file sumber bahasa C.
  • Kosongkan file header di bawah include/config/ digunakan untuk pelacakan ketergantungan konfigurasi selama kbuild, yang dijelaskan di bawah.

Setelah konfigurasi, kita akan mengetahui file dan potongan kode mana yang tidak dikompilasi.

kbuild

Bangunan berdasarkan komponen, disebut make rekursif , adalah cara umum untuk GNU make untuk mengelola proyek besar. Kbuild adalah contoh yang baik dari pembuatan rekursif. Dengan membagi file sumber ke dalam modul/komponen yang berbeda, setiap komponen dikelola oleh makefilenya sendiri. Saat Anda mulai membangun, makefile teratas memanggil makefile setiap komponen dalam urutan yang benar, membangun komponen, dan mengumpulkannya ke eksekutif akhir.

Kbuild mengacu pada berbagai jenis makefile:

  • Makefile adalah makefile teratas yang terletak di root sumber.
  • .config adalah file konfigurasi kernel.
  • arch/$(ARCH)/Makefile adalah arch makefile, yang merupakan pelengkap makefile atas.
  • skrip/Makefile.* menjelaskan aturan umum untuk semua kbuild makefiles.
  • Akhirnya, ada sekitar 500 kbuild makefile .

Makefile teratas menyertakan arch makefile, membaca file .config, turun ke subdirektori, memanggil make pada makefile setiap komponen dengan bantuan rutinitas yang didefinisikan dalam scripts/Makefile.* , membangun setiap objek perantara, dan menautkan semua objek perantara ke vmlinux. Dokumen kernel Documentation/kbuild/makefiles.txt menjelaskan semua aspek makefile ini.

Sebagai contoh, mari kita lihat bagaimana vmlinux diproduksi di x86-64:

Semua .o file yang masuk ke vmlinux terlebih dahulu masuk ke bawaan.a mereka sendiri , yang ditunjukkan melalui variabel KBUILD_VMLINUX_INIT , KBUILD_VMLINUX_MAIN , KBUILD_VMLINUX_LIBS , kemudian dikumpulkan ke dalam file vmlinux.

Lihatlah bagaimana make rekursif diimplementasikan di kernel Linux, dengan bantuan kode makefile yang disederhanakan:

# In top Makefile
vmlinux: scripts/link-vmlinux.sh $(vmlinux-deps)
		+$(call if_changed,link-vmlinux)

# Variable assignments
vmlinux-deps := $(KBUILD_LDS) $(KBUILD_VMLINUX_INIT) $(KBUILD_VMLINUX_MAIN) $(KBUILD_VMLINUX_LIBS)

export KBUILD_VMLINUX_INIT := $(head-y) $(init-y)
export KBUILD_VMLINUX_MAIN := $(core-y) $(libs-y2) $(drivers-y) $(net-y) $(virt-y)
export KBUILD_VMLINUX_LIBS := $(libs-y1)
export KBUILD_LDS          := arch/$(SRCARCH)/kernel/vmlinux.lds

init-y          := init/
drivers-y       := drivers/ sound/ firmware/
net-y           := net/
libs-y          := lib/
core-y          := usr/
virt-y          := virt/

# Transform to corresponding built-in.a
init-y          := $(patsubst %/, %/built-in.a, $(init-y))
core-y          := $(patsubst %/, %/built-in.a, $(core-y))
drivers-y       := $(patsubst %/, %/built-in.a, $(drivers-y))
net-y           := $(patsubst %/, %/built-in.a, $(net-y))
libs-y1         := $(patsubst %/, %/lib.a, $(libs-y))
libs-y2         := $(patsubst %/, %/built-in.a, $(filter-out %.a, $(libs-y)))
virt-y          := $(patsubst %/, %/built-in.a, $(virt-y))

# Setup the dependency. vmlinux-deps are all intermediate objects, vmlinux-dirs
# are phony targets, so every time comes to this rule, the recipe of vmlinux-dirs
# will be executed. Refer "4.6 Phony Targets" of `info make`
$(sort $(vmlinux-deps)): $(vmlinux-dirs) ;

# Variable vmlinux-dirs is the directory part of each built-in.a
vmlinux-dirs    := $(patsubst %/,%,$(filter %/, $(init-y) $(init-m) \
                     $(core-y) $(core-m) $(drivers-y) $(drivers-m) \
                     $(net-y) $(net-m) $(libs-y) $(libs-m) $(virt-y)))

# The entry of recursive make
$(vmlinux-dirs):
		$(Q)$(MAKE) $(build)=$@ need-builtin=1

Resep make rekursif diperluas, misalnya:

make -f scripts/Makefile.build obj=init need-builtin=1

Ini berarti membuat akan masuk ke scripts/Makefile.build untuk melanjutkan pekerjaan membangun setiap bawaan . Dengan bantuan scripts/link-vmlinux.sh , file vmlinux akhirnya berada di bawah root sumber.

Memahami vmlinux vs. bzImage

Banyak pengembang kernel Linux mungkin tidak jelas tentang hubungan antara vmlinux dan bzImage. Sebagai contoh, berikut adalah hubungan mereka di x86-64:

Akar sumber vmlinux dilucuti, dikompresi, dimasukkan ke piggy.S , lalu ditautkan dengan objek rekan lainnya ke arch/x86/boot/compressed/vmlinux . Sementara itu, file bernama setup.bin dibuat di bawah arch/x86/boot . Mungkin ada file ketiga opsional yang memiliki info relokasi, bergantung pada konfigurasi CONFIG_X86_NEED_RELOCS .

Program host bernama build , yang disediakan oleh kernel, membangun dua (atau tiga) bagian ini ke dalam file bzImage terakhir.

Pelacakan ketergantungan

Kbuild melacak tiga jenis dependensi:

  1. Semua file prasyarat (keduanya *.c dan *.h )
  2. CONFIG_ opsi yang digunakan di semua file prasyarat
  3. Ketergantungan baris perintah yang digunakan untuk mengkompilasi target

Yang pertama mudah dimengerti, tapi bagaimana dengan yang kedua dan ketiga? Pengembang kernel sering melihat potongan kode seperti ini:

#ifdef CONFIG_SMP
__boot_cpu_id = cpu;
#endif

Saat CONFIG_SMP perubahan, potongan kode ini harus dikompilasi ulang. Baris perintah untuk mengkompilasi file sumber juga penting, karena baris perintah yang berbeda dapat menghasilkan file objek yang berbeda.

Saat .c file menggunakan file header melalui #include direktif, Anda perlu menulis aturan seperti ini:

main.o: defs.h
	recipe...

Saat mengelola proyek besar, Anda membutuhkan banyak aturan seperti ini; menulis semuanya akan membosankan dan membosankan. Untungnya, sebagian besar kompiler C modern dapat menulis aturan ini untuk Anda dengan melihat #include baris dalam file sumber. Untuk GNU Compiler Collection (GCC), tinggal menambahkan parameter baris perintah:-MD depfile

# In scripts/Makefile.lib
c_flags        = -Wp,-MD,$(depfile) $(NOSTDINC_FLAGS) $(LINUXINCLUDE)     \
                 -include $(srctree)/include/linux/compiler_types.h       \
                 $(__c_flags) $(modkern_cflags)                           \
                 $(basename_flags) $(modname_flags)

Ini akan menghasilkan .d file dengan konten seperti:

init_task.o: init/init_task.c include/linux/kconfig.h \
 include/generated/autoconf.h include/linux/init_task.h \
 include/linux/rcupdate.h include/linux/types.h \
 ...

Kemudian program host fixdep menangani dua dependensi lainnya dengan mengambil depfile dan baris perintah sebagai input, lalu mengeluarkan ..cmd file dalam sintaks makefile, yang merekam baris perintah dan semua prasyarat (termasuk konfigurasi) untuk suatu target. Tampilannya seperti ini:

# The command line used to compile the target
cmd_init/init_task.o := gcc -Wp,-MD,init/.init_task.o.d  -nostdinc ...
...
# The dependency files
deps_init/init_task.o := \
$(wildcard include/config/posix/timers.h) \
$(wildcard include/config/arch/task/struct/on/stack.h) \
$(wildcard include/config/thread/info/in/task.h) \
...
  include/uapi/linux/types.h \
  arch/x86/include/uapi/asm/types.h \
  include/uapi/asm-generic/types.h \
  ...

Sebuah ..cmd file akan disertakan selama pembuatan rekursif, menyediakan semua info ketergantungan dan membantu memutuskan apakah akan membangun kembali target atau tidak.

Rahasia di balik ini adalah fixdep akan mengurai depfile (.d file), lalu parsing semua file dependensi di dalamnya, cari teks untuk semua CONFIG_ string, konversikan ke file header kosong yang sesuai, dan tambahkan ke prasyarat target. Setiap kali konfigurasi berubah, file header kosong yang sesuai juga akan diperbarui, sehingga kbuild dapat mendeteksi perubahan itu dan membangun kembali target yang bergantung padanya. Karena baris perintah juga direkam, mudah untuk membandingkan parameter kompilasi terakhir dan saat ini.

Melihat ke depan

Kconfig/kbuild tetap sama untuk waktu yang lama sampai pengelola baru, Masahiro Yamada, bergabung pada awal 2017, dan sekarang kbuild sedang dalam pengembangan aktif lagi. Jangan kaget jika Anda segera melihat sesuatu yang berbeda dari yang ada di artikel ini.


Linux
  1. Pengujian integrasi berkelanjutan untuk kernel Linux

  2. Linux – Mengonfigurasi, Mengkompilasi, dan Menginstal Kernel Linux Kustom?

  3. Linux – Bagaimana Cara Menentukan Modul Yang Menodai Kernel?

  1. Linux – Berpartisipasi Di Milis Kernel?

  2. Linux – Mengapa Kernel Tidak Dapat Menjalankan Init?

  3. Linux – Bagian Kernel yang Dimiliki Atau Tertutup?

  1. Kernel Linux:5 inovasi teratas

  2. Cara membersihkan cache yang digunakan oleh kernel Linux

  3. Apa sumber kernel Linux saat ini?