Systém config/build linuxového jádra, také známý jako Kconfig/kbuild, existuje již dlouhou dobu, od doby, kdy kód linuxového jádra migroval na Git. Jako podpůrná infrastruktura je však v centru pozornosti jen zřídka; dokonce ani vývojáři jádra, kteří jej používají při své každodenní práci, o tom nikdy nepřemýšlejí.
Chcete-li prozkoumat, jak se kompiluje linuxové jádro, tento článek se ponoří do interního procesu Kconfig/kbuild, vysvětlí, jak se vytvářejí soubory .config a vmlinux/bzImage, a představí chytrý trik pro sledování závislostí.
Kconfig
Prvním krokem při sestavování jádra je vždy konfigurace. Kconfig pomáhá vytvořit linuxové jádro vysoce modulární a přizpůsobitelné. Kconfig nabízí uživateli mnoho konfiguračních cílů:
config | Aktualizujte aktuální konfiguraci pomocí linkově orientovaného programu |
nconfig | Aktualizujte aktuální konfiguraci pomocí programu založeného na nabídce ncurses |
menuconfig | Aktualizujte aktuální konfiguraci pomocí programu založeného na nabídkách |
xconfig | Aktualizujte aktuální konfiguraci pomocí frontendu založeného na Qt |
gconfig | Aktualizujte aktuální konfiguraci pomocí frontendu založeného na GTK+ |
oldconfig | Aktualizujte aktuální konfiguraci pomocí poskytnutého .config jako základu |
localmodconfig | Aktualizujte aktuální konfiguraci deaktivující moduly nejsou načteny |
localyesconfig | Aktualizujte aktuální konfiguraci převádějící místní mody na jádro |
defconfig | Nová konfigurace s výchozím nastavením z defconfig dodaného Arch |
savedefconfig | Uložte aktuální konfiguraci jako ./defconfig (minimální konfigurace) |
allnoconfig | Nová konfigurace, kde jsou všechny možnosti zodpovězeny 'ne' |
allyesconfig | Nová konfigurace, kde jsou všechny možnosti přijímány s 'ano' |
allmodconfig | Pokud je to možné, nová konfigurace pro výběr modulů |
alldefconfig | Nová konfigurace se všemi symboly nastavenými na výchozí |
randconfig | Nová konfigurace s náhodnou odpovědí na všechny možnosti |
listnewconfig | Seznam nových možností |
olddefconfig | Stejné jako oldconfig, ale nastavuje nové symboly na jejich výchozí hodnotu bez výzvy |
kvmconfig | Povolit další možnosti pro podporu hostujícího jádra KVM |
xenconfig | Povolte další možnosti pro podporu xen dom0 a hostujícího jádra |
tinyconfig | Nakonfigurujte nejmenší možné jádro |
Myslím, že menuconfig je nejoblíbenější z těchto cílů. Cíle jsou zpracovávány různými hostitelskými programy, které jsou poskytovány jádrem a sestavovány během vytváření jádra. Některé cíle mají GUI (pro pohodlí uživatele), zatímco většina ne. Nástroje a zdrojový kód související s Kconfig jsou umístěny hlavně v scripts/kconfig/ ve zdrojovém kódu jádra. Jak můžeme vidět z scripts/kconfig/Makefile , existuje několik hostitelských programů, včetně conf , mconf a nconf . Kromě conf , každý z nich je zodpovědný za jeden z konfiguračních cílů založených na GUI, takže conf se zabývá většinou z nich.
Infrastruktura Kconfig má logicky dvě části:jedna implementuje nový jazyk pro definování konfiguračních položek (viz soubory Kconfig pod zdrojovým kódem jádra) a druhá analyzuje jazyk Kconfig a zabývá se konfiguračními akcemi.
Většina konfiguračních cílů má zhruba stejný interní proces (zobrazeno níže):
Linuxový terminál
- 7 nejlepších emulátorů terminálu pro Linux
- 10 nástrojů příkazového řádku pro analýzu dat v systému Linux
- Stáhnout nyní:SSH cheat sheet
- Cheat sheet pro pokročilé příkazy systému Linux
- Výukové programy příkazového řádku systému Linux
Všimněte si, že všechny položky konfigurace mají výchozí hodnotu.
První krok načte soubor Kconfig pod zdrojovým kořenem, aby se vytvořila počáteční konfigurační databáze; poté aktualizuje počáteční databázi načtením existujícího konfiguračního souboru podle této priority:
.config
/lib/modules/$(shell,uname -r)/.config
/etc/kernel-config
/boot /config-$(shell,uname -r)
ARCH_DEFCONFIG
arch/$(ARCH)/defconfig
Pokud provádíte konfiguraci založenou na GUI pomocí menuconfig nebo konfiguraci pomocí příkazového řádku pomocí oldconfig , databáze se aktualizuje podle vašeho přizpůsobení. Nakonec je konfigurační databáze uložena do souboru .config.
Ale soubor .config není konečným krmivem pro vytváření jádra; to je důvod, proč syncconfig cíl existuje. syncconfig býval konfiguračním cílem zvaným silentoldconfig , ale nedělá to, co říká starý název, takže byl přejmenován. Také proto, že je pro interní použití (ne pro uživatele), byl vypuštěn ze seznamu.
Zde je ukázka toho, co syncconfig dělá:
syncconfig bere .config jako vstup a vydává mnoho dalších souborů, které spadají do tří kategorií:
- auto.conf &tristate.conf se používají pro zpracování textu makefile. V makefile komponenty můžete například vidět příkazy podobné tomuto:
obj-$(CONFIG_GENERIC_CALIBRATE_DELAY) += calibrate.o
- autoconf.h se používá ve zdrojových souborech v jazyce C.
- Vyprázdněte soubory záhlaví pod include/config/ se používají ke sledování závislosti na konfiguraci během kbuild, což je vysvětleno níže.
Po konfiguraci budeme vědět, které soubory a části kódu nejsou zkompilovány.
kbuild
Komponentové vytváření, zvané rekurzivní make , je běžný způsob pro GNU make
řídit velký projekt. Kbuild je dobrým příkladem rekurzivního make. Rozdělením zdrojových souborů do různých modulů/součástí je každá součást spravována vlastním souborem makefile. Když začnete sestavovat, nejvyšší makefile vyvolá makefile každé komponenty ve správném pořadí, sestaví komponenty a shromáždí je do konečného manažera.
Kbuild odkazuje na různé druhy makefiles:
- Makefile je nejvyšší makefile umístěný v kořenovém adresáři zdroje.
- .config je konfigurační soubor jádra.
- arch/$(ARCH)/Makefile je arch makefile, což je doplněk k nejvyššímu makefile.
- scripts/Makefile.* popisuje společná pravidla pro všechny soubory makefild kbuild.
- Nakonec existuje asi 500 kbuild makefiles .
Vrchní makefile obsahuje arch makefile, čte soubor .config, sestupuje do podadresářů, vyvolává make na makefile každé komponenty pomocí rutin definovaných v scripts/Makefile.* , vytvoří každý prostřední objekt a propojí všechny prostřední objekty do vmlinux. Dokument jádra Documentation/kbuild/makefiles.txt popisuje všechny aspekty těchto souborů makefiles.
Jako příklad se podívejme, jak se vmlinux vyrábí na x86-64:
Všechny .o soubory, které jdou do vmlinuxu, jdou nejprve do své vlastní vestavěné.a , což je indikováno pomocí proměnných KBUILD_VMLINUX_INIT , KBUILD_VMLINUX_MAIN , KBUILD_VMLINUX_LIBS , pak jsou shromážděny do souboru vmlinux.
Podívejte se, jak je rekurzivní make implementováno v jádře Linuxu pomocí zjednodušeného kódu makefile:
# 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
Rekurzivní make recept je rozšířen, například:
make -f scripts/Makefile.build obj=init need-builtin=1
To znamená vyrobit přejde do scripts/Makefile.build pokračovat v práci na budování každé vestavěné.a . S pomocí scripts/link-vmlinux.sh , soubor vmlinux je konečně pod zdrojovým kořenem.
Porozumění vmlinux vs. bzImage
Mnoho vývojářů linuxového jádra nemusí mít jasno ve vztahu mezi vmlinux a bzImage. Zde je například jejich vztah v x86-64:
Zdrojový kořen vmlinux je odstraněn, komprimován a vložen do piggy.S a poté propojeny s jinými objekty typu peer do arch/x86/boot/compressed/vmlinux . Mezitím se pod arch/x86/boot vytvoří soubor s názvem setup.bin . V závislosti na konfiguraci CONFIG_X86_NEED_RELOCS může existovat volitelný třetí soubor s informacemi o přemístění .
Hostitelský program s názvem build , poskytovaný jádrem, zabuduje tyto dvě (nebo tři) části do konečného souboru bzImage.
Sledování závislosti
Kbuild sleduje tři druhy závislostí:
- Všechny nezbytné soubory (oba *.c a *.h )
- CONFIG_ možnosti použité ve všech nezbytných souborech
- Závislosti příkazového řádku používané ke kompilaci cíle
První je snadno pochopitelný, ale co druhý a třetí? Vývojáři jádra často vidí části kódu jako tento:
#ifdef CONFIG_SMP
__boot_cpu_id = cpu;
#endif
Když CONFIG_SMP změny, měl by být tento kus kódu překompilován. Příkazový řádek pro kompilaci zdrojového souboru je také důležitý, protože různé příkazové řádky mohou vést k různým objektovým souborům.
Když .c soubor používá hlavičkový soubor prostřednictvím #include musíte napsat pravidlo jako toto:
main.o: defs.h
recipe...
Při řízení velkého projektu potřebujete mnoho takových pravidel; psát je všechny by bylo zdlouhavé a nudné. Naštěstí většina moderních kompilátorů C může tato pravidla napsat za vás, když se podíváte na #include řádků ve zdrojovém souboru. Pro GNU Compiler Collection (GCC) jde pouze o přidání parametru příkazového řádku:-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)
To by vygenerovalo .d soubor s obsahem jako:
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 \
...
Poté hostitelský program fixdep stará se o další dvě závislosti pomocí depfile a příkazový řádek jako vstup, pak výstup .
# 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 \
...
.
Tajemství za tím je, že fixdep analyzuje defile (.d soubor), poté analyzujte všechny závislé soubory uvnitř, vyhledejte v textu všechny CONFIG_ řetězce, převeďte je na odpovídající prázdný hlavičkový soubor a přidejte je do požadavků cíle. Pokaždé, když se změní konfigurace, aktualizuje se také odpovídající prázdný hlavičkový soubor, takže kbuild může tuto změnu detekovat a znovu sestavit cíl, který na ní závisí. Protože se zaznamenává i příkazový řádek, je snadné porovnat poslední a aktuální parametry kompilace.
Výhled
Kconfig/kbuild zůstal po dlouhou dobu stejný, dokud se na začátku roku 2017 nepřipojil nový správce Masahiro Yamada a nyní je kbuild opět v aktivním vývoji. Nebuďte překvapeni, pokud brzy uvidíte něco jiného, než co je v tomto článku.