Folien - Freie Universität Berlin
Transcription
Folien - Freie Universität Berlin
Benutzerprogramme und Benutzer-Adressraum Prof. Dr. Margarita Esponda Freie Universität Berlin WS 2011/2012 M. Esponda-Argüero 1 Benutzerprogramme ⇒ Prozess Compiler Assembler cc as src1.o src1.s src1.c cc src2.c 101010 101010 101010 101010 101010 101010 101010 101010 as 101010 101010 101010 101010 101010 101010 101010 101010 src2.o src2.s Prozessabbild ELF Executable and Linkable Format Unix-Standard-Binärformat Stapel Linker ld 101010 101010 101010 101010 101010 101010 101010 101010 a.out Loader OS BSS ... Daten cc srcx.c as srcx.s 101010 101010 101010 101010 101010 101010 101010 101010 Programm srcx.o M. Esponda-Argüero 2 Ausführbare Dateien Betriebssysteme brauchen ein spezielles Format: mit mindestens: - Ein Kopf mit Information über Daten- und Programm-Startadressen - die eigentlichen Daten + Programmteile - Eine Symboltabelle mit zwei Listen - selbst definierte Namen - Namen, die in anderen Modulen definiert sind M. Esponda-Argüero 3 ELF-Datei 0 ELF-Kopfinformation magische Zahl, Typ (.o, exec, .so), usw. Programmkopf-Tabelle ELF header Program header table (required for executables) .text section .data section .bss section .symtab .rel.txt .rel.data .debug Section header table (required for relocatables) Seitengröße, virtuelle Adresse der Speichersegmente (Sektionen), Segment-Größe. .text section Code .data section initialisierte statische Daten .bss section nicht-initialisierte statische Daten .symtab section Symbol-Tabelle statische Variablen- und Funktionsnamen, usw. .rel.text section .rel.data section .debug section M. Esponda-Argüero 4 ELF Header: E L F Magic: 7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00 Class: ELF32 Data: 2's complement, little endian Version: 1 (current) OS/ABI: UNIX - System V ABI Version: 0 Type: EXEC (Executable file) Machine: Intel 80386 Version: 0x1 Entry point address: 0x80482d0 Start of program headers: 52 (bytes into file) Start of section headers: 10148 (bytes into file) Flags: 0x0 Size of this header: 52 (bytes) Size of program headers: 32 (bytes) Number of program headers: 6 Size of section headers: 40 (bytes) Number of section headers: 29 Section header string table index: 26 M. Esponda-Argüero 5 Adressbindung Die Adressbindung des Programms und der Daten kann zu drei verschiedenen Zeitpunkten stattfinden. Übersetzungszeit - absolute Adressen werden generiert - bei veränderter Startadresse muss das Programm neu übersetzt werden Ladezeit - der Übersetzer muss relocatable code erzeugen - die Adressbindung wird verzögert, bis das Programm im Speicher geladen wird Ausführungszeit - ein Programm kann während seiner Ausführung im Speicher verschoben werden. Eine neue Adressbindung kann zur Laufzeit stattfinden M. Esponda-Argüero 6 Adressbindung Quellprogramm Übersetzer + Assembler Übersetzungszeit Objekt-Code Andere Objektmodule des Programms Systembibliothek Linker Lademodule Ladezeit Lader Dynamisch ladbare Systembibliothek Im Speicher ausführbares Programm Ausführungszeit M. Esponda-Argüero Dynamisches Laden - Ein Programmmodul wird erst geladen, wenn es gebraucht wird. - Nicht benutzte Programmteile werden nie geladen. - Dadurch wird eine bessere Speicherbenutzung erreicht. - Sehr nützlich, wenn viele Programmmodule nur selten benutzt werden. - Spezielle Unterstützung des Betriebssystems ist dafür nicht notwendig. - Gutes dynamisches Laden wird durch gutes Programmdesign erreicht. M. Esponda-Argüero 8 Dynamisches Linking Einige Betriebssysteme benutzen statisches Linking. Mit dynamischem Linking wird ein stub verwendet, um eine Systemfunktion zu finden. Ohne dynamisches Linking muss jeder Prozess in dem ausführbaren Programm eine Kopie der Systemfunktionen, die er verwendet, einfügen. Dynamisches Linking erlaubt die gemeinsame Verwendung von Systembibliotheken. Neue Versionen der Systembibliotheken können benutzt werden, ohne dass die Programme neu übersetzt werden müssen. M. Esponda-Argüero 9 Benutzerprogramme ⇒ Prozess Compiler Assembler cc as src1.o src1.s src1.c cc src2.c 101010 101010 101010 101010 101010 101010 101010 101010 101010 101010 101010 101010 101010 101010 101010 101010 as src2.o src2.s cc srcx.c Unix-Standard-Binärformat Stapel Linker ld 101010 101010 101010 101010 101010 101010 101010 101010 Loader OS BSS 101010 101010 101010 101010 101010 101010 101010 101010 as srcx.s Executable and Linkable Format a.out ... Prozess-Layout ELF srcx.o Dynamic libraries *.dll Daten Programm Static libraries *.a M. Esponda-Argüero 10 Absolute und relozierbare Lademodule Programm Objektmodul 1024: Programm 0: Programm jmp a jmp 1500 jmp 476 load z: load 2000 load 976 a: 1500: Daten z: Symbolische Adressen 476: Daten 2000: Daten 976: Lademodul mit absoluten Adressen Lademodul mit relativen Adressen M. Esponda-Argüero 11 Namenbindung + Adressbindung Beispiel: exam.c int z = 2; Verschiebbare Objektdateien int main() { int ret = func(); exit (0); } func.c extern int z; int func() { return *e_p + x + y; } .text System-Daten .data & .bss 0 Headers System-Code main() exam.o int *z_p = &z; int x = 5; int y; System-Code Ausführbare Objektdatei .text func() main() .text int z = 2 .data func.o System-Code System-Daten int z = 2 func() int *z_p = &z .text .data .bss .data int x = 5 int y int x = 5 int y int *z_p = &z .bss .symtab .debug M. Esponda-Argüero 12 stdio.c Beispiel: int printf( char *fmt, ...) { ... main.c } extern float sin(); int scanf( char *fmt, ...) { ... extern printf(); extern scanf(); main() { double x, result; } math.c double sin(double x) { printf( "number: " ); static double res, lastx; scanf( "%f", &x ); if (x != lastx) { result = sin(x); lastx = x; printf( "sin is %f\n", result ); … berechnet sin(x) … } } return res; } M. Esponda-Argüero 13 Objektdatei main.o main.c extern float sin(); extern printf(); extern scanf(); main() { double x, result; printf( "number: " ); scanf( "%f", &x ); 20 32 40 56 ... call ... call ... call ... call .text printf scanf sin printf def: main@T:0 result = sin(x); printf( "sin is %f\n", result ); } .symtab .rel ref: printf@T:20, T:56 ref: scanf@T:32 ref: sin@T:40 M. Esponda-Argüero 14 Objektdatei math.c double sin(double x) { static double res, lastx; if (x != lastx) { lastx = x; … berechnet sin(x) … } return res; } math.o ... 8 load lastx ... 20 store lastx ... 204 load res 0 res: 8 lastx: .text .data def: sin@T:0 def: lastx@D:0 def: res@D:8 .symtab ref: lastx@T:8, T:20 ref: res@T:204 .rel M. Esponda-Argüero 15 Memory map: Nach dem ersten Durchgang: 836 .data Symbol table: stdio.o 708 math.o 700 main: 0 .data sin: 64 .text lastx: 700 result: 708 printf: 314 scanf: 508 stdio.o .text 276 math.o 64 Verschiebung 40 main.o ... call 0 ... .text main.o .text 0 a.out main.o math.o ref: sin@T:40 sin: 64 .rel .symtab ... 40 call 64 ... .text M. Esponda-Argüero 16 Dynamisches Linking/Laden printf.c Hi.c gcc gcc printf.o ar Hi.o Prozesse können schneller gestartet werden und brauchen Linker shared Library weniger Speicher Hi.out Run-time Loader Loader Speicher M. Esponda-Argüero 17 Logische vs. Physikalische Adressen Das Konzept einer logischen Speicheradresse, die eine Bindung zu einer getrennten physikalischen Adresse hat, spielt eine zentrale Rolle in der Speicherverwaltung. Die CPU arbeitet mit logischen oder virtuellen Adressen. Die MMU arbeitet direkt mit den physikalischen Adressen. M. Esponda-Argüero 18 Memory Management Unit (MMU) Die MMU ist der Teil der Hardware, der logische Adressen in physikalische Adressen umrechnet. Der Wert des Relokation-Registers wird zu jeder logischen Adresse addiert. Das Benutzerprogramm arbeitet nur mit logischen Adressen. Er sieht nie die physikalische Adresse, welche die MMU sieht. Die Relocation- und Limit-Register sind Teil des Prozesskontextes und werden von dem Dispatcher als Teil des Kontextwechsels des Prozesses geladen. M. Esponda-Argüero 19 Logische vs. Physikalische Adressen RelokationRegister Hauptspeicher 12000 CPU logische Adresse 770 + 770 physikalische Adresse 12770 MMU M. Esponda-Argüero 20 Logische vs. Physikalische Adressen Virtuelle Adressen Physikalische Adressen 4096: 1024: free 0: 512: MMU 0: 1024: Betriebssystem 0: Kernel 0: M. Esponda-Argüero Virtueller Speicher Die Programme generieren nur virtuelle Adressen. Prozess virtuelle Adresse SpeicherVerwaltung MMU+BS physische Adresse Hauptspeicher Bei Rechnern mit virtuellem Speicher geht die Adresse nicht direkt an den Hauptspeicher, FestplattenAdresse Der Adressraum wird in Hauptspeicher und Hintergrundspeicher aufgeteilt. sondern an die MMU Memory Managment Unit, die die virtuelle Adresse auf die Hintergrundspeicher physikalische Adresse abbildet. M. Esponda-Argüero Copy-on-Write Vaterprozess Hauptspeicher Seite A Seite B Seite C Kindprozess Der fork()-Systemaufruf ist sehr effizient, weil der Kindprozess am Anfang die gleichen Seiten wie der Vaterprozess benutzt. Die gemeinsamen Seiten Seite A werden als Copy-on-Write Seite B markiert. Nur wenn eine Seite C Seite verändert wird, wird kopiert. Seite C (Kopie) Zustand, nachdem der Vaterprozess die Seite C verändert hat M. Esponda-Argüero vfork() Das vfork(). Virtuelle fork() Beim vfork wird der Elternprozess suspendiert, und der Kindprozess benutzt den Adressraum des Elternprozesses ohne Verwendung von Copy-on-Write. Wenn der Kindprozess eine Seite verändert, verändert sich der Adressraum des Elternprozesses. Es wird verwendet, wenn wir sicher sind, dass unmittelbar nach dem vfork-Systemaufruf ein exec-Systemaufruf angewendet wird, der einen neuen Adressraum für den Kindprozess erzeugt. vfork wird sehr oft für die effiziente Implementierung von Shells verwendet. M. Esponda-Argüero Memory-Mapped-Dateien Ein Prozess kann eine Datei in einem Teilbereich seines virtuellen Adressraumes einblenden. Ein Teil der Datei wird auf die eingeblendeten Seitenspeicher eingelesen. Anstatt durch eine Reihe von zeitaufwändigen Lese- und Schreibe-Systemaufrufen kann auf die Datei wie auf ein großes Zeichenfeld im Speicher direkt zugegriffen werden. Dateien können dann gemeinsam von mehreren Prozessen benutzt werden, indem diese die entsprechende Seite in ihrem virtuellen Speicherraum einblenden lassen. M. Esponda-Argüero MMAP verschiedene Objekte werden im Prozess-Adressraum abgebildet Virtuelle Adressraum ProzessAdressraum MMAP Frame buffer Festplatte Shared memory M. Esponda-Argüero 26 MMAP #include <sys/mman.h> MAP_SHARED file descriptor ... void * mmap (void *addr, size_t len, int prot , int flags, int fd, off_t offset); Virtuellen Speicher des Prozesses Prozessabbild Stapel mmap (addr, len, flags, fd, offset) BSS Daten A Programm M. Esponda-Argüero Memory-Mapped-Dateien void * mmap( void * addr, size_t len, int prot, int flags, int fildes, off_t off); - addr "Wunschadresse", ab welcher die Daten im Adressraum erscheinen sollen. Die Adresse muss ein vielfaches der Seitengröße sein. Wenn eine 0 angegeben wird, entscheidet das Betriebssystem über die Adresse. Als Rückgabewert wird die tatsächliche Adresse zurückgegeben, oder MAP_FAILED (-1). - len Länge des Speicherbereiches. - fd file descriptor der Datei - prot Zugriffsrechte auf den Speicher, die mit den Zugriffsrechte auf die Datei passen müssen . - flags kann das Verhalten von mmap beeinflussen Folgende Makros sind definiert: MAP_FIXED ein Fehler wird zurückgegeben, Wenn die angegebene Adresse nicht benutzt werden kann. MAP_SHARED Der physikalische Speicher wird von verschiedenen Prozessen gemeinsam benutzt und Änderungen werden in allen Prozessen sofort sichtbar. MAP_PRIVATE Bei Änderungen bekommt der jeweilige Prozess eine eigene Kopie des Speichers. - Mit offset wird die Startposition innerhalb der Datei angegeben. M. Esponda-Argüero Memory-Mapped-Dateien physikalischer Speicher Logischer Speicher von Prozess A Logischer Speicher von Prozess B Datei M. Esponda-Argüero 29 Memory-Mapped-Dateien #include <unistd.h> #include < sys/mman.h> void * mmap( void * addr, size_t len, int prot, int flags, int fildes, off_t off); #include <unistd.h> #include <sys/types.h> Adresse, auf die die Datei eingeblendet wird. Hier entscheidet sich das Betriebssystem #include <sys/mman.h> int fd, pagesize; Länge der Daten char *data; fd = fopen( "foo" , O_RDONLY ); pagesize = getpagesize(); data = mmap((caddr_t)0, pagesize, PROT_READ, MAP_SHARED, fd, offset); M. Esponda-Argüero Memory-Mapped-Dateien void * munmap( void * addr, size_t len ); munmap macht das einblenden der Datei in den entsprechenden Speicherbereich wieder rückgängig. Der Speicherinhalt wird in die Datei zurückgeschrieben. Beim Beenden eines Prozesses werden alle eingeblendeten Speicherbereiche wieder freigegeben. M. Esponda-Argüero 31 Unterbrechungen Zwei Kategorien: Interne Unterbrechungen (internal interrupts) Synchrone Unterbrechungen weil diese während der Ausführung von Anweisungen innerhalb des Prozessors verursacht und festgestellt werden Exceptions Programfehler: faults, traps, and aborts Software: INT 3 Machine-check exceptions Externe Unterbrechungen (external interrupts) Asynchrone Unterbrechungen verursacht von Hardware außerhalb des Prozessors - system timer Interrupt Sources Hardware-Ein-/Ausgabe-Geräte Software: INT n - keyboard - serial port - disk M. Esponda-Argüero 32 Unterbrechungen in 80x86 Architektur Die 80x86 CPU-Architektur unterstützt 256 verschiedene "interrupts" ⇒ es gibt 256 "interrupt handler", die in der IDT "interrupt descriptor table" definiert sind. M. Esponda-Argüero 33 Systemaufrufe - von Betriebssystem bereitgestellte Schnittstelle - zum Interagieren mit der Hardware für die Durchführung von privilegierten Operationen - verursachen Unterbrechungen - werden mit Hilfe von Wrapper-Funktionen innerhalb der C-Bibliotheken bereit gestellt. Z.B. malloc(), open(), socket(), read(), write(), usw. - Zwei Wege für die Umschaltung von User-Modus zum Kernel-Modus in der x80-Architekturen sind: "int $0x80" oder "sysenter" Was passiert? - wechselt ins Kernel-Modus - Registern werden gespeichert - Systemaufruf wird gestartet. Die ID des Systemaufrufs zusammen mit den Parametern werden in dem Benutzerstapel gepuscht, bevor die Ausführung des Systemaufrufs gestartet wird. M. Esponda-Argüero 34 Beispiel eines Systemaufrufs Benutzermodus #include <stdio.h> main() { printf("My first C-Program"); } /* C Standardbibliothek */ int printf( const char *format, ...){ … write() } Kernmodus return trap write()-Systemaufruf M. Esponda-Argüero 35 Benutzerprogramme und System-Aufrufe Benutzer-Modus Prozess A Prozess B ... Systemaufruf sys_call_table Kernel-Modus System Syscall_Handler Treiber Prozess C Treiber service dispatcher .text ALIGN ENTRY(sys_call_table) .long sys_ni_syscall .long sys_exit .long sys_fork .long sys_read .long sys_write .long sys_open .long sys_clos .long sys_waitpid .long sys_creat .long sys_link .long sys_chmod System services Treiber M. Esponda-Argüero 36 Systemaufrufe Benutzer-Modus read(){ … … read() int 0x80 ... … } systemcall() Kernel-Modus … sys_read(){ sys_read() … … } ret_from_sys_call() ... zwei spezielle Befehle: int Interrupt-Befehl iret Interrupt-Return-Befehl M. Esponda-Argüero 37 Parameter-Übergabe 1. Direkt durch die Register eax enthält die ID des Systemauf Intel ebx, ecx, edx, esi, edi für weitere Parameter Probleme: maximal 6 Parameter Werte-Bereich auf 32 Bits beschränkt Vorteile: sehr schnell 2. Ausführungsstapel die Parameter werden vom Stapel geholt M. Esponda-Argüero 38 Parameter-Übergabe 3. Die Adresse einer Tabelle, in der die Parameter sind, wird in ein Register geschrieben. x Linux und Solaris Register Benutzerprogramm Betriebssystem M. Esponda-Argüero 39 Systemaufrufe Nach der Parameterübergabe ... EntryPoint: switch to kernel stack User stack save context User memory Registers check Register0 call the real code pointed by Register0 restore context switch to user stack iret (change to user mode and return) Kernel stack Kernel memory Registers M. Esponda-Argüero 40 Entwurf-Entscheidungen Allgemeine Aspekte: - Was ist einfacher? - Was ist übersichtlicher? - wie groß ist der Ausführungs-Overhead? Spezielle Aspekte: - Wertbereich von Parameter bzw. Ergebnisse wie sicher sind die Programme wie viele CPUs? Implementierung-Aufwand M. Esponda-Argüero 41 x86 Schutz-Ringe Betriebssystem Kernel Betriebssystem Dienste Level 0 Level 1 Anwendungen Level 2 Level 3 Privilegierte Operationen nur in "Level 0" M. Esponda-Argüero 42 Implementierung von Systemaufrufen Jeder Systemaufruf bekommt eine Zahl zugeordnet. Diese Zahl wird als Index in einer Tabelle verwendet, mit Hilfe derer der Systemaufruf stattfindet. .text ALIGN ENTRY(sys_call_table) .long sys_ni_syscall /* 0 - old "setup()" system call*/ .long sys_exit .long sys_fork .long sys_read .long sys_write .long sys_open /* 5 */ .long sys_close .long sys_waitpid .long sys_creat .long sys_link .long sys_unlink /* 10 */ .long sys_execve .long sys_chdir .long sys_time .long sys_mknod .long sys_chmod /* 15 */ ... M. Esponda-Argüero 43 Ausnahmen und Unterbrechungen Hardware GeräteUnterbrechung System Service Call sys_call_table System service dispatcher Interrupt service routines System services Hardware-Exceptions Software-Exceptions Exception dispatcher Exception handlers VM-Exceptions VM manager’s pager M. Esponda-Argüero 44 Linux-Dateien für Systemaufrufe Wichtige Dateien: arch/i386/kernel/entry.S Systemaufrufe low-level fault handling routines. include/asm-i386/unistd.h Systemaufrufe-IDs und Makros. kernel/sys.c die eigentliche Implementierungen der Funktionen sind hier. M. Esponda-Argüero 45