System calls - part two
Häufig wird behauptet, ein system call auf x86 bedeute automatisch einen Context Switch. Tatsächlich läuft Kernel-Code in zwei Kontexten:
- kernel space im Process Context (im Auftrag eines Prozesses)
- kernel space im Interrupt Context (an keinen Prozess gebunden)
Die CPU führt entweder User-Space-Code oder Kernel-Code in einem der beiden Kontexte aus.
Unter einem Context Switch versteht man üblicherweise das Wechseln des aktuellen Prozesses (neue PID durch Scheduler-Preemption). Im Folgenden betonen wir: ein system call erfolgt ohne diesen Wechsel.
__kernel_vsyscall() sieht so aus:
ffffe400 <__kernel_vsyscall>:
ffffe400: 51 push %ecx
ffffe401: 52 push %edx
ffffe402: 55 push %ebp
ffffe403: 89 e5 mov %esp,%ebp
ffffe405: 0f 34 sysenter
...
ffffe413: c3 ret
esp = stack pointer, eip = instruction pointer
Erklärung (Kurzfassung):
– Register %ecx, %edx, %ebp werden auf dem User-Stack gesichert, %esp nach %ebp gespiegelt → dann sysenter.
– jmp __kernel_vsyscall+0x3 ist ein Trick, um effektiv mit bis zu 6 Argumenten zu arbeiten (Standardlimit).
– sysenter setzt die CPU nach Ring0 (CPL=0).
sysenter (x86 fast system call) setzt u. a.:
CS = SYSENTER_CS_MSR,EIP = SYSENTER_EIP_MSRSS = SYSENTER_CS_MSR + 8,ESP = SYSENTER_ESP_MSR
Intel SFRs:
SYSENTER_CS_MSR = 0x174
SYSENTER_ESP_MSR = 0x175
SYSENTER_EIP_MSR = 0x176
Linux (Header /usr/src/linux/include/asm/msr.h):
#define MSR_IA32_SYSENTER_CS 0x174
#define MSR_IA32_SYSENTER_ESP 0x175
#define MSR_IA32_SYSENTER_EIP 0x176
Beim Booten schreibt der Kernel passende Werte (z. B. sysenter_entry) in diese MSRs; wrmsr
überträgt EDX:EAX in den durch ECX adressierten MSR. tss->esp1 verweist auf den Kernel-Stack.
Linux nutzt keinen Hardware Context Switch, legt aber pro CPU einen TSS an (u. a. für Kernel-Stack-Adresse und I/O Permission Bitmap).
Fazit: Der system call ist eine Mode Transition, kein Context Switch; die PID bleibt gleich. User-Preemption kann stattfinden
nach dem system call (wenn need_resched gesetzt) oder nach einem Interrupt mit need_resched.