Assemblersprache - Systemaufrufe

Systemaufrufe sind die einzige Möglichkeit für Benutzerprogramme, mit dem Betriebssystemkernel zu interagieren. Durch Systemaufrufe können Assemblerprogramme Dateien lesen/schreiben, Speicher zuweisen, Prozesse erstellen und vieles mehr.


Was ist ein Systemaufruf?

Benutzerprogramme laufen im eingeschränktenBenutzermodusund können nicht direkt auf Hardware zugreifen oder privilegierte Operationen ausführen.

Wenn ein Programm Funktionen auf Kerneltbene wie E/A-Operationen oder Speicherzuweisung ausführen muss, muss esSystemaufrufeverwenden, um Dienste vom Betriebssystemkernel anzufordern.

Der Ablauf eines Systemaufrufs ist: Das Benutzerprogramm initiiert einen Systemaufruf → die CPU wechselt in den Kernel-Modus → der Kernel führt den entsprechenden Dienst aus → kehrt in den Benutzermodus zurück und setzt die Ausführung fort.

Linux 32位系统调用流程

Man kann sich einen Systemaufruf wie folgt vorstellen:Das Benutzerprogramm ruft das Betriebssystem an und bittet es, etwas zu tun, wozu es selbst keine Berechtigung hat.


Linux-Systemaufrufmechanismus

In 32-Bit-Linux-Systemen wird ein Systemaufruf durch die folgenden Schritte ausgeführt:

  1. wirdDie Systemaufrufnummerin das EAX-Register geschrieben
  2. Parameter in Register wie EBX, ECX, EDX, ESI, EDI legen
  3. Executeint 0x80Der Befehl löst einen Software-Interrupt aus
  4. Die CPU wechselt in den Kernel-Modus, und der Kernel führt den entsprechenden Dienst basierend auf der Aufrufnummer in EAX aus.
  5. Der Kernel schreibt den Rückgabewert in EAX, wechselt zurück in den Benutzermodus und setzt die Ausführung fort.

Übersicht gängiger Systemaufrufe

SystemaufrufAufrufnummer (EAX)FunktionParameters
sys_exit1Programm beendenEBX = Rückgabewert (Exit-Code)
sys_fork2Einen Kindprozess erzeugenKeine Parameter
sys_read3Datei/Eingabe lesenEBX=fd, ECX=buf, EDX=count
sys_write4Datei schreiben / AusgabeEBX=fd, ECX=buf, EDX=count
sys_open5Datei öffnenEBX=Dateiname, ECX=Flags, EDX=Modus
sys_close6Datei schließenEBX=fd
sys_creat8Datei erstellenEBX=Dateiname, ECX=Modus
sys_lseek19Dateizeiger bewegenEBX=fd, ECX=Offset, EDX=whence
sys_brk45Größe des Datensegments anpassen (Speicherreservierung)EBX = neue Adresse

Die vollständige Systemaufrufliste finden Sie unter/usr/include/asm/unistd_32.hSehen Sie es sich dort an. Beachten Sie, dass sich die Nummern von 32-Bit- und 64-Bit-Systemaufrufen unterscheiden.


sys_write – Zeichenkette ausgeben

Der am häufigsten verwendete Systemaufruf, um Inhalte auf dem Bildschirm auszugeben:

Beispiel

; Dateipfad: write_syscall.asm
; Verwenden Sie sys_write, um eine Zeichenkette auszugeben

section .data
    msg db 'Hello, example! Welcome to assembly.', 0xA
    len equ $ - msg

section .text
    global _start

_start:
    ; sys_write aufrufen (4)
    mov eax, 4                  ; Systemaufruf-Nummer 4 = sys_write
    mov ebx, 1                  ; Dateideskriptor 1 = stdout (Standardausgabe)
    mov ecx, msg                ; Adresse der auszugebenden Daten
    mov edx, len                ; Datenlänge (Bytes)
    int 0x80                    ; Systemaufruf auslösen

    ; Bei Erfolg gibt EAX die tatsächliche Anzahl der geschriebenen Bytes zurück

    ; Programm beenden
    mov eax, 1
    mov ebx, 0
    int 0x80

Ausgabe:

$ nasm -f elf32 write_syscall.asm -o write_syscall.o
$ ld -m elf_i386 write_syscall.o -o write_syscall
$ ./write_syscall
Hello, example! Welcome to assembly.

sys_read – Benutzereingabe lesen

Daten von der Standardeingabe (Tastatur) lesen:

Beispiel

; Dateipfad: read_syscall.asm
; Verwenden Sie sys_read, um Benutzereingaben zu lesen und auszugeben

section .bss
    buffer resb 64              ; Einen 64 Byte großen Eingabepuffer reservieren

section .data
    prompt db 'Please enter your name: '
    prompt_len equ $ - prompt
    output_msg db 'Hello, '
    output_msg_len equ $ - output_msg

section .text
    global _start

_start:
    ; Eingabeaufforderung ausgeben
    mov eax, 4
    mov ebx, 1
    mov ecx, prompt
    mov edx, prompt_len
    int 0x80

    ; Benutzereingabe lesen
    mov eax, 3                  ; Systemaufrufnummer 3 = sys_read
    mov ebx, 0                  ; Dateideskriptor 0 = stdin (Standardeingabe)
    mov ecx, buffer             ; Adresse des Eingabepuffers
    mov edx, 64                 ; Maximal 64 Bytes lesen
    int 0x80

    ; EAX gibt die tatsächliche Anzahl der gelesenen Bytes zurück (einschließlich des abschließenden Zeilenumbruchs)
    mov esi, eax                ; Tatsächliche Eingabelänge in esi speichern

    ; "Hello, " ausgeben
    mov eax, 4
    mov ebx, 1
    mov ecx, output_msg
    mov edx, output_msg_len
    int 0x80

    ; Den Eingabeinhalt des Benutzers ausgeben
    mov eax, 4
    mov ebx, 1
    mov ecx, buffer
    mov edx, esi                ; Die tatsächliche Anzahl der eingegebenen Bytes verwenden
    int 0x80

    ; Programm beenden
    mov eax, 1
    mov ebx, 0
    int 0x80

Ausführungsergebnis:

$ nasm -f elf32 read_syscall.asm -o read_syscall.o
$ ld -m elf_i386 read_syscall.o -o read_syscall
$ ./read_syscall
Please enter your name: example
Hello, example

sys_exit – Programm beenden

Jedes Programm muss sys_exit aufrufen, um sich ordnungsgemäß zu beenden; andernfalls führt die CPU die nachfolgenden ungültigen Daten weiter aus, was zu einem Speicherzugriffsfehler (Segmentation Fault) führt.

Beispiel

; Mit verschiedenen Exit-Codes beenden

    mov eax, 1                  ; Systemaufruf-Nummer 1 = sys_exit
    mov ebx, 0                  ; Exit-Code 0 = normales Ende (ein von null verschiedener Wert ist ebenfalls erlaubt)
    int 0x80

Der Exit-Code kann in der Shell überprüft werden über$?Ansicht:

$ ./program
$ echo $?
0

Generische Systemaufruf-Vorlage

Beim Schreiben von Systemaufrufen können Sie der folgenden allgemeinen Vorlage folgen:

Beispiel

; Allgemeine Vorlage für Systemaufrufe
; Anwendbar auf Linux 32-Bit-Systeme

; Schritt 1: Systemaufrufnummer in EAX laden
mov eax,Systemaufrufnummer

; Schritt 2: Argumente der Reihe nach übergeben
mov ebx,Nr.1Argument(e)
mov ecx,Nr.2Argument(e)
mov edx,Nr.3Argument(e)
mov esi,Nr.4Argument(e)
mov edi,Nr.5Argument(e)

; Schritt 3: Systemaufruf auslösen
int 0x80

; Schritt 4: Rückgabewert überprüfen (in EAX)
; Ein negativer Wert weist normalerweise auf einen Fehler hin
cmp eax, 0
jl error_handler              ; Wenn EAX < 0, zur Fehlerbehandlung springen

int 0x80Nach dem Auslösen enthält EAX den Rückgabewert. Die meisten Systemaufrufe geben bei Erfolg einen nicht-negativen Wert und bei einem Fehler einen negativen Fehlercode zurück (z. B. steht -1 für EPERM).

Weitere Erweiterungen