GNU/Linux >> Znalost Linux >  >> Linux

Zkoumání linuxového jádra:Tajemství Kconfig/kbuild

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í:

  1. Všechny nezbytné soubory (oba *.c a *.h )
  2. CONFIG_ možnosti použité ve všech nezbytných souborech
  3. 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 ..cmd soubor v syntaxi makefile, který zaznamenává příkazový řádek a všechny předpoklady (včetně konfigurace) pro cíl. Vypadá to takto:

# 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 \
  ...

..cmd soubor bude zahrnut během rekurzivního vytváření, poskytne všechny informace o závislostech a pomůže se rozhodnout, zda znovu sestavit cíl nebo ne.

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.


Linux
  1. Linuxové jádro:5 nejlepších inovací

  2. Životní cyklus testování linuxového jádra

  3. Řešení problému roku 2038 v linuxovém jádře

  1. Linux – konfigurace, kompilace a instalace vlastního linuxového jádra?

  2. Linux – Proč nemůže jádro spustit inicializaci?

  3. Jaký je aktuální zdrojový kód jádra Linuxu?

  1. 30 věcí, které jste nevěděli o linuxovém jádře

  2. Analyzujte linuxové jádro pomocí ftrace

  3. Průběžné testování integrace linuxového jádra