Entdecken Sie, wie Ihnen eine LED den Einstieg in den Linux-Kernel erleichtert

Entdecken Sie, wie Ihnen eine LED den Einstieg in den Linux-Kernel erleichtert

Bitte fügen Sie eine Beschreibung des Bildes hinzu

Vorwort

Vor kurzem musste ich in einem Projekt das LED-Subsystem verwenden. In Embedded Linux ist es relativ einfach, ein Licht einzuschalten. Sie müssen nur einen bestimmten Wert in die entsprechende Datei im Verzeichnis schreiben, das einem bestimmten Licht entspricht, und schon können Sie die LED ein-/ausschalten/blinken lassen.

# echo 1 > /sys/class/leds/green/brightness // Schalte die LED ein
# echo 0 > /sys/class/leds/green/brightness // Schalte die LED aus
# echo heartbeat > /sys/class/leds/green/trigger // Lasse die LED wie einen Herzschlag blinken

LED-Trigger

Natürlich ist die im Projekt verwendete Beleuchtungsfunktion etwas komplizierter als die oben vorgestellte. Sie ähnelt der Festplattenbeleuchtung, d. h. die LED blinkt, wenn von der Festplatte gelesen oder geschrieben wird. Ich habe das vage Gefühl, dass diese Funktion mit der Triggerdatei zusammenhängen sollte, denn wenn ich diese Datei cate, ist das Wort mmc0 darin enthalten.

Schreiben Sie dann mmc0 in den Trigger und sehen Sie, welche Wirkung es hat.

# echo mmc0 > /sys/klasse/leds/grün/trigger
# Katze /sys/Klasse/LEDs/grün/Trigger
keine rc-feedback kbd-scrolllock kbd-numlock kbd-capslock kbd-kanalock 
kbd-Umschalttaste kbd-Alt-Taste kbd-Strg-Taste kbd-Alt-Taste kbd-Umschalttaste 
kbd-Umschaltsperre kbd-Strg-Sperre kbd-Strg-Sperre 
[mmc0] Heartbeat standardmäßig eingeschaltet, IR-Power-Click axp20x-usb-online

Schreiben von Daten auf die Festplatte

# aa berühren | synchronisieren

Wie durch Zauberei stellte ich fest, dass jedes Mal, wenn ein Befehl zum Schreiben von Daten auf die Festplatte ausgeführt wurde, das grüne Licht auf der Platine einmal aufblinkte.

Durch Nachschlagen in den Informationen habe ich erfahren, dass es sich hierbei um eine Funktion handelt, die mit dem LED-Trigger zusammenhängt.

Einen Moment lang dachte ich

  • Warum kann das Schreiben von mmc0 in Tigger die LED in ein Festplattenlicht verwandeln?
  • Warum können wir das Ein- und Ausschalten des Lichts steuern, indem wir 1/0 in die Helligkeitsdatei schreiben?
  • Warum blinkt die LED, wenn in die Triggerdatei ein Timer geschrieben wird? Gleichzeitig werden zwei Dateien delay_on und delay_off erzeugt, mit denen sich die Blinkfrequenz der Leuchte steuern lässt?

Mir kamen alle möglichen Zweifel und ich wollte unbedingt die Prinzipien hinter diesen Funktionen verstehen.

Entdecken Sie

Probleme führen zum Handeln. Listen Sie die Fragen auf, die Sie zuerst beantworten möchten.

Woher stammen die Verzeichnisse zu den einzelnen Leuchten?

  • Wie werden die Trigger im Trigger generiert?
  • Warum schaltet sich die LED ein/aus, wenn 1/0 in die Helligkeit geschrieben wird?
  • Warum erscheinen beim Schreiben eines Timers in einen Trigger zwei Dateien, delay_on und delay_off?

Beginnen wir mit der Recherche und starten mit dem, was mir letzte Nacht eingefallen ist: led_classdev_register("aaa") generiert ein LED-Verzeichnis.

LED-Geräteregistrierung

Lassen Sie uns ein Experiment durchführen, um festzustellen, ob led_classdev_register() ein Verzeichnis generiert, das dem LED-Licht entspricht.

Ich habe zufällig einen Ort gefunden, an dem ich die folgenden Codezeilen ausführen kann, und sie hinzugefügt, in der Hoffnung, dass das Verzeichnis aaa im Verzeichnis leds generiert werden kann

	Struktur led_classdev *cdev;
	int ret;
	cdev = kzalloc(Größe von(*cdev), GFP_KERNEL);
	wenn (!cdev)
		Rückgabe -ENOMEM;
	cdev->name = "aaa";
	// cdev->brightness_set = ebsa110_led_set;
	// cdev->brightness_get = ebsa110_led_get;
	// cdev->default_trigger = "Herzschlag";
	ret = led_classdev_register(NULL, cdev);
	wenn (ret < 0) {
		kfree(cdev);
		Rückkehr ret;
	}

Kompilieren, brennen, ausführen, anzeigen

# ls /sys/klasse/leds/
aaa grün

Und tatsächlich wurde das Verzeichnis „aaa“, das ich erwartet hatte, unter „leds“ generiert, und mein Selbstvertrauen wuchs enorm!

Später habe ich die zugrunde liegende Anrufbeziehung nachverfolgt:

led_classdev_register()
	of_led_classdev_register() // registriere ein neues Objekt der Klasse led_classdev.
		led_classdev_next_name()
		Gerät_erstellen_mit_Gruppen()
		led_add_brightness_hw_changed()
		list_add_tail() // zur Liste der LEDs hinzufügen
		led_update_helligkeit()
		//led_trigger_set_default()

LED-Verzeichnis

Nachdem ich nun weiß, wie ein bestimmtes Licht registriert wird, möchte ich wissen, wie das LED-Verzeichnis generiert wird. Ich habe den Code durchsucht und es ist nicht schwer, ihn zu finden. Im Folgenden ist die Funktionsaufrufbeziehung im Zusammenhang mit der Generierung des LED-Verzeichnisses aufgeführt:

subsys_initcall(leds_init);
leds_init() // Erstelle die LED-Klasse, d.h. erzeuge das Verzeichnis /sys/class/leds class_create()
		__Klasse_erstellen()
			__class_register()
				kset_register()

durch Analogie verstehen

Später überprüfte ich die Informationen und stellte fest, dass /sys/class/leds eine Klasse ist. Eine Klasse stellt ein Kernel-Subsystem dar. Im Kernel gibt es viele solcher Subsysteme.

Jedes Verzeichnis in /sys/class/ ist eine Klasse und auch ein Subsystem.

# ls /sys/Klasse/
ata_device extcon mdio_bus ptp Ton
ata_link gpio Speicher pwm spi_master
ata_port Grafik Sonstiges RC Wärmebild
bdi hwmon mmc_host Regler tty
Block I2C-Adapter Netz RTC UDC
bsg i2c-dev phy scsi_device vc
DMA-Eingang Stromversorgung SCSI-Disk VT-Konsole
DRM LEDs PPS SCSI_Host Watchdog

Jede Klasse hat spezifische Instanziierungsobjekte, wie grün, aaa

# ls /sys/klasse/leds/
aaa grün

Jedes Objekt hat entsprechende Mitgliedsmethoden/Attribute, wie Helligkeit, Trigger

# ls /sys/klasse/leds/aaa/
Helligkeits-/Einschalttrigger
max_brightness-Subsystem uevent

Es ist so ähnlich wie eine Klasse in C++! Tatsächlich ist es eine Klasse, ein einfacher Vergleich

Bildbeschreibung hier einfügen

Ich werde die Registrierungslogik im jeweiligen Kurs langsam studieren, wenn ich später Zeit habe. Setzen Sie unsere Routenerkundung fort. Beachten Sie, dass sich meine Erkundungsroute hier geändert hat. Sie ist nicht mehr auf die Erkundung des LED-Subsystems beschränkt, sondern hat begonnen, sich auf den Kernel außerhalb des LED-Subsystems auszudehnen.

Generierung eines Klassenverzeichnisses

Kommen wir nun zum Klassenverzeichnis. Nachdem wir nun wissen, woher das Verzeichnis „LEDs“ stammt, fragen wir uns, woher das darüber liegende Klassenverzeichnis stammt.

Folgen Sie dem Code und erhalten Sie

Klassen_init ()
	kset_create_and_add("class", NULL, NULL); // Erstelle dynamisch eine Struktur kset und füge sie zu sysfs hinzu
		kset_create()
			kobject_set_name()
		kset_register()
			kset_init()
			kobject_add_internal()
				kobject_get()
				kobj_kset_join()
					kset_get()
					list_add_tail()
						__list_add()
						{
							nächstes->vorheriges = neu;
							neu->nächstes = nächstes;
							neu->vorherige = vorige;
						}
				create_dir() // Erstelle ein Verzeichnis

start_kernel()

Tatsächlich muss ich nach der Verfolgung bis classes_init() nicht darüber nachdenken, welchen Code ich weiter unten verfolgen soll, sondern kann einfach weiter nach oben verfolgen.

/* Kernel */
start_kernel()
	rest_init() // Mach den Rest non-__init'ed, wir sind jetzt am Leben
		kernel_thread(kernel_init, NULL, CLONE_FS);
		kernel_init()
			kernel_init_freeable()
			/*
			 * Ok, die Maschine ist nun initialisiert. Keines der Geräte
			 * wurden noch nicht berührt, aber das CPU-Subsystem ist aktiv und
			 * läuft, und die Speicher- und Prozessverwaltung funktioniert.
			 *
			 * Jetzt können wir endlich mit der richtigen Arbeit beginnen.
			 */
			führe_grundlegendes_Setup durch ()
			driver_init() // um ihre Subsysteme zu initialisieren.
				devtmpfs_init()
				Geräte_init()
				Busse_init()
				classes_init() // nur classes_init()
				firmware_init()
				hypervisor_init()
				Plattformbusinit ()
				cpu_dev_init()
				Speicher_dev_init()
				container_dev_init()
				von_core_init()

Wie oben erwähnt, habe ich zufällig start_kernel() gefunden, wo der Traum begann. Es war das erste Mal, dass ich das Verfolgen von Kernelcode so interessant fand.

Kernel wird gestartet …

Nachdem ich zu start_kernel() zurückgefunden hatte, fragte ich mich unwillkürlich, wo dieser String „Starting kernel ...“ gedruckt wurde. Ich kann diesen Satz jedes Mal sehen, wenn ich uboot starte. Wäre es nicht toll, wenn ich ihn finden könnte? Leider konnte ich es im Kernelcode nicht finden.

uboot

Zuerst dachte ich, dass „Starting kernel ...“ in start_kernel() gedruckt würde, aber ich konnte es im Kernelcode nicht finden. Zu diesem Zeitpunkt habe ich mich gefragt, ob es in ubbot gedruckt wurde. Es ist sinnvoll, diesen Satz auszudrucken, bevor mit dem Laden des Kernels begonnen wird.

Ich habe in uboot gesucht und gefunden

boot_jump_linux()
	ankündigen und aufräumen()
		printf("\nKernel wird gestartet ...%s\n", fake ? "(Fake-Lauf zur Ablaufverfolgung)" : "");

Hier wird uboot beendet und der Kernel ausgeführt.

Anbei die komplette Anrufbeziehung

Von Uboot zum Kernel und dann zu /sys/class, dann die LED-Klasse registrieren und eine Instanziierung eines LED-Lichts durchführen.

/* uboot */
boot_jump_linux()
	ankündigen und aufräumen()
		printf("\nKernel wird gestartet ...%s\n"); // printf() 
		Bootstage_Mark_Name(BOOTSTAGE_ID_BOOTM_HANDOFF, "Start_Kernel");
		bereinigen_vor_linux()
	kernel_entry(0, machid, r2);
/* Kernel */
start_kernel()
	rest_init() // Mach den Rest non-__init'ed, wir sind jetzt am Leben
		kernel_thread(kernel_init, NULL, CLONE_FS);
		kernel_init()
			kernel_init_freeable()
			/*
			 * Ok, die Maschine ist nun initialisiert. Keines der Geräte
			 * wurden noch nicht berührt, aber das CPU-Subsystem ist aktiv und
			 * läuft, und die Speicher- und Prozessverwaltung funktioniert.
			 *
			 * Jetzt können wir endlich mit der richtigen Arbeit beginnen.
			 */
			führe_grundlegendes_Setup durch ()
			driver_init() // um ihre Subsysteme zu initialisieren.
				devtmpfs_init()
				Geräte_init()
				Busse_init()
				Klassen_init ()
					kset_create_and_add("class", NULL, NULL); // Erstelle dynamisch eine Struktur kset und füge sie zu sysfs hinzu
						kset_create()
							kobject_set_name()
						kset_register()
							kset_init()
							kobject_add_internal()
								kobject_get()
								kobj_kset_join()
									kset_get()
									list_add_tail()
										__list_add()
										{
											nächstes->vorheriges = neu;
											neu->nächstes = nächstes;
											neu->vorherige = vorige;
										}
								create_dir()
				firmware_init()
				hypervisor_init()
				Plattformbusinit ()
				cpu_dev_init()
				Speicher_dev_init()
				container_dev_init()
				von_core_init()

subsys_initcall(leds_init);
leds_init() // Erstelle die LED-Klasse, also das Verzeichnis /sys/class/leds class_create()
		__Klasse_erstellen()
			__class_register()
				kset_register()

led_classdev_register()
	of_led_classdev_register() // registriere ein neues Objekt der Klasse led_classdev.
		led_classdev_next_name()
		Gerät_erstellen_mit_Gruppen()
		led_add_brightness_hw_changed()
		list_add_tail() // zur Liste der LEDs hinzufügen
		led_update_helligkeit()
		//led_trigger_set_default()

Eintrittspunkt ins Leben

Das Obige ist eine Studie des Linux-Kernels aus der Perspektive des LED-Subsystems. Einstiegspunkt zum Studium des Linux-Kernels gefunden.

Wenn wir mit großen Dingen konfrontiert werden, empfinden wir oft Angst. Diese Angst hält uns davon ab, tiefer zu forschen, was dazu führt, dass wir sie nicht verstehen und nicht in der Lage sind, sie zu bewältigen.

Zwei Beispiele:

  • Ein berühmter Marathonläufer berichtete von seinen Erfolgserlebnissen. Er sagte, er habe sich die Strecke immer vorher im Auto angeschaut, sich Orientierungspunkte gemerkt, dann die Distanz in Etappen aufgeteilt und jede Etappe gut gelaufen.
  • Ich habe in der Oberstufe gute Noten in Physik bekommen, weil ich Probleme anders löste als andere. Andere starrten auf die letzte Frage, wenn sie das Prüfungsblatt bekamen, und wollten sofort die Antwort wissen. Ich habe die Frage zunächst überflogen, einige bekannte Bedingungen ermittelt und versucht, die unbekannten Größen anhand der physikalischen Formeln abzuleiten. Ich habe Stück für Stück gefolgert, und manchmal, wenn ich genau hinsah, lag die Antwort direkt vor mir.

Von etwas so Kleinem wie einer Frage, einem Thema oder einer Fähigkeit bis hin zu etwas so Großem wie der Arbeit, dem Leben oder sogar dem ganzen Leben. Wenn wir einen Einstiegspunkt finden, haben wir Glück und können ihn nutzen, um erfolgreich zu sein und eine bessere Zukunft zu erreichen. Ich hoffe, dass jeder 123WORDPRESS.COM in Zukunft unterstützen wird!

Das könnte Sie auch interessieren:
  • Gerätetreiber des Linux-Kernels – Zusammenfassung der grundlegenden Hinweise zum Linux-Kernel
  • Ein Bild, das das Funktionsprinzip des Linux-Kernels zeigt

<<:  Die MySQL-ID beginnt von 1 zu steigen, um das Problem der diskontinuierlichen ID schnell zu lösen

>>:  Stellen Sie die Breite der Tabelle so ein, dass sie sich nicht mit dem Text ändert

Artikel empfehlen

Docker konfiguriert den Speicherort lokaler Images und Container

Verwenden Sie den Befehl „Find“, um Dateien zu fi...

Implementierungsschritte zur Installation eines Redis-Containers in Docker

Inhaltsverzeichnis Redis auf Docker installieren ...

So implementieren Sie das Builder-Muster in Javascript

Überblick Das Builder-Muster ist ein relativ einf...

MySQL-Serie 15: Allgemeine MySQL-Konfiguration und Leistungsstresstest

1. Allgemeine MySQL-Konfiguration Alle folgenden ...

So installieren und verwenden Sie Ubuntu Docker

Inhaltsverzeichnis 1. Automatische Installation m...

Viewport-Parameter für mobile Browser (Web-Frontend-Design)

Mobile Browser platzieren Webseiten in einem virtu...

So verkleinern Sie die Protokolldatei in MYSQL SERVER

Das Transaktionsprotokoll zeichnet die Vorgänge a...

Videojs+Swiper realisiert Taobao-Produktdetailkarussell

In diesem Artikel wird der spezifische Code von v...

Zusammenfassung der drei Regeln für die React-Statusverwaltung

Inhaltsverzeichnis Vorwort Nr.1 Ein Fokus Nr. 2 E...