Betriebssysteme Letschert - Übung 1-4

Aufgabe 4

  1. Was ist ein Systemaufruf?

    Ein Systemaufruf ruft Betriebssystemfunktionen auf. Betriebssystemfunktionen sind Bestandteil der Kernels.

  2. Wie und warum unterscheiden sich normale Funktionsaufrufe von Systemaufrufen?

    Systemaufrufe rufen sog. privilegierte Funktionen auf, die ein Benutzerprogramm nicht ausführen dürfte. Deshalb muss der Zugriff darauf genau kontrolliert werden.

  3. Welche Unterstützung liefert die Hardware bei einem Systemaufruf?

    Systemaufrufe werden normalerweise als Software-Interrupts implementiert. Der i386 bietet hierbei eine Zugriffskontrolle über Interrupt-Gates. Die tatsächliche Sprungadresse bleibt dem Programm verborgen, d.h. sie kann sich auch ändern.
    Beim i386 wäre auch eine Lösung über Call-Gates möglich.

  4. Warum muss ein Programm mit einem Systemaufruf beendet werden?

    Die Beendigung eines Prozesses incl. dem Bereinigen der internen Datenstrukturen und der Rückkehr in den aufrufenden Prozess sind privlegierte Operationen. Würde der Prozess sich durch ein einfaches return 0; beenden können, müsste dazu die Rücksprungadresse auf dem Stack liegen und könnte daher manipuliert werden.

  5. Vergleichen Sie den Aufruf eines Unterprogramms (z.B. einer Funktion in C) mit dem Start eines Programms. Was passiert bei einem Funktionsaufruf, was bei einem Programmstart?

    Unterprogramme werden mit dem Assemblerbefehl call (i386) aufgerufen.
    Programme werden mit den Systemaufrufen fork()/execve() (Unix) bzw. CreateProcess (Windows) gestartet. Das Programm muss geladen werden, der Stack initialisiert werden usw.

  6. Übersetzen Sie die beiden folgenden Programme und vergleichen Sie die Größe der erzeugten ausführbaren Programme. Erklären Sie den Unterschied!
    Progr. 1 -----------
    char buf[50000] = "0";
    int main() {
      return 0;
    }
    --------------------
    Progr. 2 -----------
    int main() {
      char buf[50000] = "0";
      return 0;
    }
    

    In Progr. 1 wird buf im zur Kompilierzeit Datenspeicher angelegt, in Progr. 2 dagegen zur Laufzeit auf dem Stack.

    insgesamt 88
    -rwxr-xr-x   1 cjw      users       61711 Mär 25 16:14 n6-1
    -rw-r--r--   1 cjw      users          49 Mär 25 16:13 n6-1.cc
    -rwxr-xr-x   1 cjw      users       11813 Mär 25 16:14 n6-2
    -rw-r--r--   1 cjw      users          51 Mär 25 16:14 n6-2.cc
    

  7. In C++ sollte die Funktion main den Typ int haben. Sie kann einen Int-Wert zurückgeben, muss aber nicht. Ein fehlendes return in main entspricht nach C++-Sprachstandard einem return 0;. Stellen Sie fest, ob der GNU-Compiler sich entsprechend verhält. Übersetzen Sie dazu Testprogramme mit der Option -S in Assemblercode.

    ---- schnipp ----
    main:
    .LFB1:
    	pushl %ebp
    .LCFI0:
    	movl %esp,%ebp
    .LCFI1:
    	xorl %eax,%eax    // eax = rückgabewert wird auf null gesetzt
    	jmp .L2
    	.p2align 4,,7
    .L2:
    	movl %ebp,%esp
    	popl %ebp
    	ret
    ---- schnapp ----
    

  8. Wohin kehrt die main-Funktion eines C/C++-Programms zurück? Ins Betriebssystem? Was genau ist der Unterschied zwischen return 0;, exit(0); und _exit(0);?

    main() wird nicht vom Betriebssystem aufgerufen, sondern von einer Initialisierungsroutine im Programm. Das Betriebssystem springt an das Label _start. main() kehrt in die Initilisierungsroutine zurück, die _exit() aufruft.

    return 0;

    exit(0); ruft die exit-Funktion der C-Library auf. Die ruft atexit() auf, schliesst noch offene Dateien und ruft dann _exit() auf.

    _exit() ist der Systemaufruf.