Edocti
Fortgeschrittene technische Ausbildung für den modernen Softwareingenieur

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_MSR
  • SS = 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.