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.
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:
- wirdDie Systemaufrufnummerin das EAX-Register geschrieben
- Parameter in Register wie EBX, ECX, EDX, ESI, EDI legen
- Execute
int 0x80Der Befehl löst einen Software-Interrupt aus - Die CPU wechselt in den Kernel-Modus, und der Kernel führt den entsprechenden Dienst basierend auf der Aufrufnummer in EAX aus.
- Der Kernel schreibt den Rückgabewert in EAX, wechselt zurück in den Benutzermodus und setzt die Ausführung fort.
Übersicht gängiger Systemaufrufe
| Systemaufruf | Aufrufnummer (EAX) | Funktion | Parameters |
|---|---|---|---|
| sys_exit | 1 | Programm beenden | EBX = Rückgabewert (Exit-Code) |
| sys_fork | 2 | Einen Kindprozess erzeugen | Keine Parameter |
| sys_read | 3 | Datei/Eingabe lesen | EBX=fd, ECX=buf, EDX=count |
| sys_write | 4 | Datei schreiben / Ausgabe | EBX=fd, ECX=buf, EDX=count |
| sys_open | 5 | Datei öffnen | EBX=Dateiname, ECX=Flags, EDX=Modus |
| sys_close | 6 | Datei schließen | EBX=fd |
| sys_creat | 8 | Datei erstellen | EBX=Dateiname, ECX=Modus |
| sys_lseek | 19 | Dateizeiger bewegen | EBX=fd, ECX=Offset, EDX=whence |
| sys_brk | 45 | Größ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
; 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
; 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
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
; 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
Weitere Erweiterungen
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).