Als ich „LDD3“ noch einmal las, fand ich einen Satz, den ich ignoriert hatte: „Der Kernel hat einen sehr kleinen Stapel, er ist möglicherweise nur so klein wie eine 4096-Byte-Seite.“ Als Antwort auf diesen Satz habe ich einfach Erfahren Sie mehr über den „Kernel-Stack“ des Prozesses.
Was ist der „Kernel-Stack“ eines Prozesses? Im Lebenszyklus jedes Prozesses wird er durch Systemaufrufe zwangsläufig im Kernel gefangen. Nach der Ausführung eines Systemaufrufs und dem Einfangen im Kernel ist der von diesen Kernelcodes verwendete Stapel nicht der ursprüngliche Stapel im Benutzerbereich, sondern ein Stapel im Kernelbereich, der der „Kernelstapel“ des Prozesses ist.
Zum Beispiel implementiert ein einfacher Zeichentreiber den open()
方法。在这个驱动程序挂载后,应用程序通过glib库调用Linux的open()
Systemaufruf. Wenn ein Systemaufruf ausgeführt und im Kernel abgefangen wird, wechselt der Prozessor in den privilegierten Modus (der spezifische Übergangsmechanismus variiert je nach Prozessorarchitektur). Beispielsweise ist für ARM der Stapelzeiger (SP) im Normalmodus und im Benutzermodus ein anderes Register). Zu diesem Zeitpunkt wird als Stapelzeiger der Kernel-Stapelzeiger verwendet, der auf den vom Kernel für jeden Prozess zugewiesenen Kernel-Stack-Speicherplatz zeigt.
Die Rolle des Kernel-Stacks Mein persönliches Verständnis ist: Nach dem Fall in den Kernel gibt es in Systemaufrufen auch Funktionsaufrufe und automatische Variablen, die Stack-Unterstützung erfordern. Der User-Space-Stack ist offensichtlich unsicher und erfordert daher Kernel-Stack-Unterstützung. Darüber hinaus wird der Kernel-Stack auch zum Speichern einiger Informationen der Anwendungsschicht vor Systemaufrufen verwendet (z. B. User-Space-Stack-Zeiger und Systemaufrufparameter).
Die Beziehung zwischen dem Kernel-Stack und der Prozessstruktur Jeder Prozess erhält beim Erstellen einen Kernel-Stack-Speicherplatz. Die Korrespondenz zwischen dem Kernel-Stack und dem Prozess wird durch die Zeigermitglieder in den beiden Strukturen vervollständigt: (1) struct task_struct Eine Struktur, die beim Erlernen der Linux-Prozessverwaltung erlernt werden muss. Es stellt einen Prozess im Kernel dar und zeichnet alle Statusinformationen des Prozesses auf. Diese Struktur ist in Sched.h (includelinux) definiert. Es gibt eine Mitgliederstruktur. void *stack
,它指向下面的内核栈结构体的“栈底”。 在系统运行时,宏current
获得的是当前进程的struct task_struct
(2) Kernel-Stack-Struktur-Union thread_union
1. union thread_union { 2. struct thread_info thread_info; 3. unsigned long stack[THREAD_SIZE/sizeof(long)]; 4. };
1. /* 2. \* low level task data that entry.S needs immediate access to. 3. \* __switch_to() assumes cpu_context follows immediately after cpu_domain. 4. */ 5. struct thread_info { 6. unsigned long flags; /* low level flags */ 7. int preempt_count; /* 0 => preemptable, bug */ 8. mm_segment_t addr_limit; /* address limit */ 9. struct task_struct *task; /* main task structure */ 10. struct exec_domain *exec_domain; /* execution domain */ 11. __u32 cpu; /* cpu */ 12. __u32 cpu_domain; /* cpu domain */ 13. struct cpu_context_save cpu_context; /* cpu context */ 14. __u32 syscall; /* syscall number */ 15. __u8 used_cp[16]; /* thread used copro */ 16. unsigned long tp_value; 17. struct crunch_state crunchstate; 18. union fp_state fpstate __attribute__((aligned(8))); 19. union vfp_state vfpstate; 20. \#ifdef CONFIG_ARM_THUMBEE 21. unsigned long thumbee_state; /* ThumbEE Handler Base register */ 22. \#endif 23. struct restart_block restart_block; 24. };
Der Schlüssel ist der Task-Member, der auf die Struktur struct task_struct des erstellten Prozesses verweist
Das Stack-Mitglied ist der Kernel-Stack. Hier ist ersichtlich, dass sich der Kernel-Stack-Speicherplatz und thread_info einen Speicherplatz teilen. Wenn der Kernel-Stack überläuft, wird thread_info zerstört und das System stürzt ab~~~Die Beziehung zwischen Kernel-Stack – struct thread_info – struct task_struct ist in der folgenden Abbildung dargestellt:
Generierung des Kernel-Stacks
Wenn ein Prozess erstellt wird, weist der Systemaufruf der Fork-Familie Speicherplatz für den Kernel-Stack und die Struktur task_struct zu. Der aufrufende Prozess ist:
Systemaufrufe der Fork-Familie –>do_fork –>copy_process –>dup_task_struct
In der Funktion dup_task_struct:
1. static struct task_struct *dup_task_struct(struct task_struct *orig) 2. { 3. struct task_struct *tsk; 4. struct thread_info *ti; 5. unsigned long *stackend; 6. 7. int err; 8. 9. prepare_to_copy(orig); 10. 11. **tsk = alloc_task_struct();** 12. if (!tsk) 13. return NULL; 14. 15. **ti = alloc_thread_info(tsk);** 16. if (!ti) { 17. free_task_struct(tsk); 18. return NULL; 19. } 20. 21. err = arch_dup_task_struct(tsk, orig); 22. if (err) 23. goto out; 24. 25. **tsk->stack = ti;** 26. 27. err = prop_local_init_single(&tsk->dirties); 28. if (err) 29. goto out; 30. 31. **setup_thread_stack(tsk, orig);** 32. ......
Alloc_task_struct verwendet den Slab-Allokator des Kernels, um den Speicherplatz von struct task_struct für den zu erstellenden Prozess zuzuweisen
Alloc_thread_info verwendet das Partnersystem des Kernels, um Kernel-Stack-Speicherplatz (Union Thread_Union) für den zu erstellenden Prozess zuzuweisen
bezieht sich auf die Struktur task_struct und den Kernel-Stack
In setup_thread_stack(tsk, orig); sind der Kernel-Stack und die Struktur task_struct verknüpft:
1. static inline void setup_thread_stack(struct task_struct *p, struct task_struct *org) 2. { 3. *task_thread_info(p) = *task_thread_info(org); 4. task_thread_info(p)->task = p; 5. }
Kernel-Stack-Größe
Da jedem Prozess ein Kernel-Stack-Speicherplatz zugewiesen wird, ist es unmöglich, eine große Menge zuzuweisen. Diese Größe ist architekturabhängig und wird im Allgemeinen in Seiten gemessen. Tatsächlich ist es die THREAD_SIZE, die wir oben gesehen haben. Dieser Wert beträgt im Allgemeinen 4 KB oder 8 KB. Für die ARM-Architektur ist dies in Thread_info.h (archarmincludeasm) definiert,
1. \#define THREAD_SIZE_ORDER 1 2. \#define THREAD_SIZE 8192 3. \#define THREAD_START_SP (THREAD_SIZE - 8)
Der Kernel-Stack von ARM ist also 8 KB groß
Was Sie bei der Programmierung von (Kernel-)Treibern beachten sollten:
Aufgrund der Begrenzung des Stapelspeichers sollte beim Schreiben von Treibern (insbesondere bei Low-Level-Funktionen, die von Systemaufrufen verwendet werden) darauf geachtet werden, Codes zu vermeiden, die viel Stapelspeicher verbrauchen, wie z. B. rekursive Algorithmen, die Größe lokaler automatischer Variablen Definitionen usw.
Das obige ist der detaillierte Inhalt vonDen Kernel-Stack des Linux-Prozesses verstehen. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!