Erhalten Sie ein umfassendes Verständnis der Funktionsweise des Linux-Konfigurations-/Build-Systems. Das Linux-Kernel-Konfigurations-/Build-System (auch bekannt als Kconfig/kBuild) gibt es schon seit langer Zeit, seitdem der Linux-Kernel-Code auf Git migriert wurde. Als unterstützende Infrastruktur wurde ihr jedoch wenig Aufmerksamkeit geschenkt; selbst Kernel-Entwickler, die sie in ihrer täglichen Arbeit verwenden, denken nie wirklich darüber nach. Um zu untersuchen, wie der Linux-Kernel kompiliert wird, geht dieser Artikel auf die internen Vorgänge von Kconfig/kBuild ein, erklärt, wie .config-Dateien und vmlinux/bzImage-Dateien generiert werden, und stellt einen cleveren Trick zur Abhängigkeitsverfolgung vor. Kconfig Der erste Schritt beim Erstellen eines Kernels ist immer die Konfiguration. Kconfig trägt dazu bei, den Linux-Kernel hochgradig modular und anpassbar zu gestalten. Kconfig stellt dem Benutzer eine Reihe von Konfigurationszielen zur Verfügung:
Ich glaube, dass Menuconfig das beliebteste dieser Ziele ist. Ziele werden von verschiedenen Hostprogrammen verwaltet, die vom Kernel bereitgestellt und während des Kernel-Build-Prozesses generiert werden. Einige Ziele verfügen über eine GUI (zur Benutzerfreundlichkeit), die meisten jedoch nicht. Die kconfig-bezogenen Tools und der Quellcode befinden sich hauptsächlich in scripts/kconfig/ im Kernel-Quellcode. Wir können mit scripts/kconfig/makefile beginnen, dort gibt es mehrere Hostprogramme, darunter CONF, mconf und nconf. Mit Ausnahme von conf ist jedes von ihnen für eines der GUI-basierten Konfigurationsziele verantwortlich, sodass conf sich um die meisten davon kümmert. Logischerweise besteht die Kconfig-Infrastruktur aus zwei Teilen: einem, der die neue Sprache zum Definieren von Konfigurationselementen implementiert (siehe die Kconfig-Dateien unter dem Kernel-Quellcode), und einem anderen, der die Kconfig-Sprache analysiert und Konfigurationsvorgänge handhabt. Der interne Ablauf ist für die meisten Konfigurationsziele ungefähr gleich (wie unten gezeigt): Beachten Sie, dass alle Konfigurationselemente einen Standardwert haben. Im ersten Schritt werden die Kconfig-Dateien im Quellstamm gelesen, um die anfängliche Konfigurationsdatenbank zu erstellen. Anschließend werden die vorhandenen Konfigurationsdateien gelesen, um die anfängliche Datenbank entsprechend dieser Priorität zu aktualisieren:
Wenn Sie eine GUI-basierte Konfiguration über „menuconfig“ oder eine befehlszeilenbasierte Konfiguration über „oldconfig“ durchführen, wird die Datenbank mit Ihren Anpassungen aktualisiert. Speichern Sie abschließend die Konfigurationsdatenbank in einer CONFIG-Datei. Allerdings sind .config-Dateien nicht das Endprodukt eines Kernel-Builds; aus diesem Grund existiert das Syncconfig-Ziel. syncconfig war früher eine Datei mit dem Namen silentoldconfig, aber sie hatte nicht den Zweck, den der alte Name versprach, deshalb wurde sie umbenannt. Da es außerdem für den internen Gebrauch (und nicht für Benutzer) bestimmt ist, wurde es aus der Liste entfernt. Hier ist ein Beispiel dafür, was syncconfig macht: syncconfig verwendet .config als Eingabe und gibt eine Reihe anderer Dateien aus, die in drei Kategorien fallen: auto.conf und tristate.conf werden zum Generieren von Dateien für die Textverarbeitung verwendet. Beispielsweise könnte in der Makefile einer Komponente eine Anweisung wie diese vorkommen: autoconf.h wird in Quelldateien der Sprache C verwendet. Die leere Header-Datei include/config/ wird zur Verfolgung der Konfigurationsabhängigkeiten während kbuild verwendet, was weiter unten erläutert wird. Nach der Profilerstellung wissen wir, welche Dateien und Codesegmente nicht kompiliert wurden. KBuild Der komponentenweise Aufbau, rekursives Make genannt, ist ein gängiger Ansatz in GNU. Erstellen und verwalten Sie ein großes Projekt. KBuild ist ein gutes Beispiel für rekursives Make. Durch die Aufteilung der Quelldateien in verschiedene Module/Komponenten wird jede Komponente durch ein eigenes Makefile verwaltet. Wenn Sie einen Build starten, ruft das Makefile der obersten Ebene das Makefile jeder Komponente in der richtigen Reihenfolge auf, erstellt die Komponenten und sammelt sie in der endgültigen ausführbaren Datei. KBuild bezieht sich auf verschiedene Arten von Makefiles:
Das oberste Makefile enthält das Archmakefile, das die .config-Datei liest, in das Unterverzeichnis geht, make aufruft und jedes Komponenten-Makefile mit Hilfe der darin definierten Routinen implementiert. scripts/Makefile*, erstellt jedes Zwischenobjekt und verknüpft alle Zwischenobjekte in vmlinux. Die Kerndatei Documentation/kbuild/makefiles.txt beschreibt alle Aspekte dieser Makefiles. Sehen wir uns beispielsweise an, wie vmlinux auf x86-64 gestartet wird: (Abbildung basierend auf dem Blog von Richard Y. Steven. Aktualisiert und mit Genehmigung des Autors verwendet.) Alle .o-Dateien, die in vmlinux gehen, gehen zuerst in ihre eigene integrierte .a-Datei, die durch die Variable dargestellt wird. KBUILD_VMLINUX_INIT, KBUILD_VMLINUX_Main, KBUILD_VMLINUX_LIBS und sammeln Sie sie dann in der vmlinux-Datei. Sehen wir uns an, wie rekursives Make im Linux-Kernel mithilfe eines vereinfachten Makefile-Codes implementiert wird: # Im obersten Makefile vmlinux: scripts/link-vmlinux.sh $(vmlinux-deps) +$(Aufruf if_changed,link-vmlinux) # Variablenzuweisungen vmlinux-deps := $(KBUILD_LDS) $(KBUILD_VMLINUX_INIT) $(KBUILD_VMLINUX_MAIN) $(KBUILD_VMLINUX_LIBS) exportiere KBUILD_VMLINUX_INIT := $(head-y) $(init-y) exportiere KBUILD_VMLINUX_MAIN := $(core-y) $(libs-y2) $(drivers-y) $(net-y) $(virt-y) exportiere KBUILD_VMLINUX_LIBS := $(libs-y1) exportiere KBUILD_LDS := arch/$(SRCARCH)/kernel/vmlinux.lds init-y := init/ drivers-y := Treiber/ Sound/ Firmware/ Netz-y := Netz/ libs-y := Bibliothek/ Kern-y := usr/ virt-y := virt/ # In entsprechendes Built-in umwandeln.a init-y := $(patsubst %/, %/built-in.a, $(init-y)) Kern-y := $(patsubst %/, %/built-in.a, $(Kern-y)) Treiber-y := $(patsubst %/, %/built-in.a, $(Treiber-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)) # Richten Sie die Abhängigkeit ein. vmlinux-deps sind alles Zwischenobjekte, vmlinux-dirs # sind falsche Ziele, also kommt jedes Mal diese Regel, das Rezept von vmlinux-dirs # wird ausgeführt. Siehe „4.6 Falsche Ziele“ von „info make“ $(sort $(vmlinux-deps)): $(vmlinux-dirs) ; # Die Variable vmlinux-dirs ist der Verzeichnisteil jedes integrierten.a vmlinux-dirs := $(patsubst %/,%,$(filter %/, $(init-y) $(init-m) \ $(Kern-y) $(Kern-m) $(Treiber-y) $(Treiber-m) \ $(net-y) $(net-m) $(libs-y) $(libs-m) $(virt-y))) # Der Eintrag von rekursivem make $(vmlinux-dirs): $(Q)$(MAKE) $(build)=$@ need-builtin=1 Rekursive Rezepterweiterung, zum Beispiel: Dies bedeutet, dass Make in „scripts/Makefile.build“ geht und mit dem Erstellen aller integrierten Elemente fortfährt. Mithilfe von scripts/link-vmlinux.sh werden die vmlinux-Dateien schließlich im Quellenstammverzeichnis lokalisiert. vmlinux und bzImage verstehen Vielen Linux-Kernel-Entwicklern ist die Beziehung zwischen vmlinux und bzImage möglicherweise nicht klar. So sehen die Zusammenhänge beispielsweise in x86-64 aus: Die Quellstammdatei vmlinux wird entfernt, komprimiert, in piggy.S eingefügt und dann werden andere Peer-Objekte in arch/x86/boot/compressed/vmlinux verknüpft. Gleichzeitig wird eine Datei mit dem Namen setup.bin in arch/x86/boot generiert. Abhängig von config_x86_RELOCS kann es optional eine dritte Datei mit Umzugsinformationen geben. Der Kernel stellt einen Befehl namens „Build“ bereit, der diese zwei (oder drei) Teile in die endgültige bzImage-Datei einbaut. Abhängigkeitsverfolgung KBuild verfolgt drei Arten von Abhängigkeiten:
Das erste ist leicht zu verstehen, aber was ist mit dem zweiten und dritten? Kernel-Entwickler sehen oft Code-Schnipsel wie diese: #ifdef CONFIG_SMP __boot_cpu_id = CPU; #endif Wenn CONFIG_SMP geändert wird, sollte dieser Code neu kompiliert werden. Auch die Befehlszeile, die Sie zum Kompilieren Ihrer Quelldateien verwenden, ist wichtig, da unterschiedliche Befehlszeilen zu unterschiedlichen Objektdateien führen können. Wenn die .C-Datei über die Direktive #include eingebunden wird, müssen Sie eine Regel wie diese schreiben: main.o: defs.h Rezept... Beim Management eines großen Projekts benötigen Sie viele dieser Regeln. Sie alle können sehr mühsam sein. Glücklicherweise können die meisten modernen C-Compiler dies erkennen, indem sie sich die #include-Zeilen in Ihren Quelldateien ansehen. Für die GNU Compiler Collection (GCC) fügen Sie einfach ein Befehlszeilenargument hinzu: -MD depfile # In Skripten/Makefile.lib c_flags = -Wp,-MD,$(depfile) $(NOSTDINC_FLAGS) $(LINUXINCLUDE) \ -include $(srctree)/include/linux/compiler_types.h \ $(__c_flags) $(modkern_cflags) \ $(Basisname_flags) $(Modname_flags) Dadurch wird eine D-Datei mit folgendem Inhalt generiert: init_task.o: init/init_task.c include/linux/kconfig.h \ include/generiert/autoconf.h include/linux/init_task.h \ include/linux/rcupdate.h include/linux/types.h \ ... Das Hostprogramm fixdep kümmert sich dann um die anderen beiden Abhängigkeiten, indem es sie abruft. Die Depfile verwendet eine Befehlszeile als Eingabe und gibt dann eine CMD-Datei in Makefile-Syntax aus, die die Befehlszeile des Ziels und alle Voraussetzungen (einschließlich der Konfiguration) aufzeichnet. Es sieht so aus: # Die zum Kompilieren des Ziels verwendete Befehlszeile cmd_init/init_task.o := gcc -Wp,-MD,init/.init_task.od -nostdinc … ... # Die Abhängigkeitsdateien deps_init/init_task.o := \ $(Platzhalter include/config/posix/timers.h) \ $(Platzhalter include/config/arch/task/struct/on/stack.h) \ $(Platzhalter 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 \ ... In den rekursiven Build-Prozess wird eine CMD-Datei eingebunden, die alle Abhängigkeitsinformationen bereitstellt und bei der Entscheidung hilft, ob das Ziel neu erstellt werden soll. Das Geheimnis dahinter ist, dass Fixdep die Depfile (.d-Datei) analysiert, dann alle darin enthaltenen Abhängigkeitsdateien analysiert, nach dem Text aller Config_Strings sucht, sie in entsprechende leere Header-Dateien konvertiert und sie zu den Voraussetzungen des Ziels hinzufügt. Bei jeder Änderung der Konfiguration wird auch die entsprechende leere Header-Datei aktualisiert, sodass kbuild diese Änderung erkennen und die davon abhängigen Ziele neu erstellen kann. Da auch die Befehlszeile aufgezeichnet wird, ist es einfach, die letzten Kompilierungsparameter mit den aktuellen Kompilierungsparametern zu vergleichen. Ein Blick in die Zukunft Kconfig/kbuild blieb lange Zeit unverändert, bis Anfang 2017 ein neuer Betreuer, Masahiro Yamada, dazukam, und jetzt wird KBuild wieder aktiv weiterentwickelt. Seien Sie nicht überrascht, wenn Sie schnell etwas anderes sehen als in diesem Artikel. Zusammenfassen Das Obige ist der vollständige Inhalt dieses Artikels. Ich hoffe, dass der Inhalt dieses Artikels einen gewissen Lernwert für Ihr Studium oder Ihre Arbeit hat. Vielen Dank für Ihre Unterstützung von 123WORDPRESS.COM. Wenn Sie mehr darüber erfahren möchten, schauen Sie sich bitte die folgenden Links an Das könnte Sie auch interessieren:
|
<<: Lassen Sie uns das Ereignisobjekt in js genauer verstehen
In diesem Artikelbeispiel wird der spezifische Co...
Formularübermittlungscode 1. Quellcode-Analyse &l...
Inhaltsverzeichnis 1. Einführung in die Computert...
Was ist JConsole JConsole wurde in Java 5 eingefü...
Vorwort Ich hatte kürzlich ein Problem bei der Ar...
Remote-SSH installieren und konfigurieren Öffnen ...
Welchen Parameter verwendet der RPM-Befehl zum In...
LocalStorage speichert Boolesche Werte Als ich he...
Korrespondenz zwischen Flutter und CSS im Shadow-...
Beim Öffnen ausländischer Websites werden häufig ...
Inhaltsverzeichnis Definieren der HTML-Struktur E...
Inhaltsverzeichnis Vue+ElementUI-Hintergrundverwa...
1. SHOW PROCESSLIST-Befehl SHOW PROCESSLIST zeigt...
Die EXPLAIN-Anweisung wird im MySQL-Abfrageanweis...
MySQL Installer bietet eine benutzerfreundliche, ...