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