Systemprogrammierung unter Linux eine Einführung S. 1 Inhaltsverzeichnis 1 Übersicht der Systemfunktionen ( system calls )...2 1.1 Grundliegende Systemcalls...2 Erfragen der PID des laufenden Prozesses...2 Erfragen er PID des parent-prozesses ( Vaterprozess)...2 Einen child Prozess erzeugen ( Sohnprozess)...2 Starten eines Programmes innerhalb eines Prozesses...3 Einen eindeutigen key ( Schlüssel ) erzeugen...3 1.2 Verfahren zur Prozesskommunikation...4 Signal-Handler...4 Message-Queue...4 Erzeugen / Öffnen einer Message Queue...4 Senden von Nachrichten an eine Message-Queue...5 Abholen von Nachrichten von einer Message-Queue...5 Löschen von einer Message-Queue...6 Shared Memory...6 Erzeugen eines Shared-Memory Segmentes...6 Anbinden an das Shared Memory Segment... 7 Lösen von einem Shared Memory Segment...7 Löschen von Shared Memory Segmenten...7 Semaphore...7 Erzeugen / Öffnen einer Semaphormenge...7 Löschen einer Semaphormenge...8 Operationen auf Semaphormengen...9 2 Programmieren unter Linux...10 Filesystem...10 Dateien...10 Ordner...10 Links...10 Prozesse...... 10 Wichtige Shell Befehle......11 Prinzipien...11 Die Shell...11 Beispiele...12 Entwicklungstools...13 Kompilieren auf der Kommandozeile...14 Kompilieren mit make ( Makefiles )...15
Systemprogrammierung unter Linux eine Einführung S. 2 1 Übersicht der Systemfunktionen ( system calls ) 1.1 Grundliegende Systemcalls Jedem Prozess wird unter UNIX artigen Betriebsystemen eine eindeutige, nicht negative Zahl die PID zugewiesen. Erfragen der PID des laufenden Prozesses #include <unistd.h> pid_t getpid(void) getpid() liefert die PID vom Typ pid_t des Prozesses zurück. Erfragen er PID des parent-prozesses ( Vaterprozess) #include <unistd.h> pid_t getppid(void) getppid() liefert die PID vom Typ pid_t des parent-prozesses zurück Einen child Prozess erzeugen ( Sohnprozess) #include <unistd.h> pid_t fork(void) fork() Erzeugt eine identische Kopie des Prozesses, hierbei werden auch offene Filedescriptoren kopiert sowie alle Programmdaten. Auch der Aufruf der Funktion erfolgt doppelt und liefert im parent-prozess die PID des childs zurück. Im child-prozess liefert sie immer 0 zurück. ## TIPP: Wenn das child sich terminiert, dann muss der parent auf dieses Signal reagieren und ein wait() aufrufen, damit sich der child-prozess ordentlich beenden kann. Ansonsten liegen Zombieprozesse im Speicher, die erst dann sterben wenn der parent sich auch beendet ( der init Prozess von dem alle Prozesse childs sind macht dann das fällige wait() )
Systemprogrammierung unter Linux eine Einführung S. 3 Starten eines Programmes innerhalb eines Prozesses #include <unistd.h> int execl( char * filename, char * arg0,...) execl() liefert ( wie die meisten UNIX system calls ) -1 bei Fehler zurück. Es erwartet den filename sowie die zum korrekten Aufruf benötigten Kommandozeilenargumente. Einen eindeutigen key ( Schlüssel ) erzeugen key_t ftok( const char * path, int key_id) ftok() liefert einen Schlüssel zurück der anhand von der eingestellten key_id ( die nicht 0 sein darf ) immer gleich ist ( da er aus dem übergebenen Schlüsselfile generiert wird ). Der path ist der Pfad + Dateiname des keyfiles das als Grundlage genommen wird.
Systemprogrammierung unter Linux eine Einführung S. 4 1.2 Verfahren zur Prozesskommunikation Signal-Handler #include <signal.h> void signal( int signalnr, void * functionpointer) signal() richtet einen Signal Handler ein der die angegebene Funktion aufruft wenn er ein Signal Event mit der angegebenen signalnr erhält. SIGTERM ( kill Befehl ) SIGINT ( STRG+C) sind hier wohl die für uns wichtigsten Signale ( die Konstanten sind ebenfalls in signal.h defniert ) Message-Queue Erzeugen / Öffnen einer Message Queue #include <sys/msg.h> int msgget( key_t key, int flag) msgget() liefert die ID der erzeugten / geöffneten Message Queue zurück Erwartet einen eindeutigen key ( Schlüssel ) und entsprechende FLAG ( Erstellungsparameter ) IPC_CREAT IPC_EXCL 0600 IPC_CREAT Erzeugt einen Briefkasten IPC_EXCL Schliesst aus das eine Message-Queue mit der selben ID schon vorhanden ist 0600 rw- --- --- Bits als Zugriffsrechte gesetzt d.h. Eigentümer hat Lese u. Schreibrechte, alle anderen haben keine Rechte
Systemprogrammierung unter Linux eine Einführung S. 5 Senden von Nachrichten an eine Message-Queue #include <sys/msg.h> int msgsnd( int id, const void * message, size_t size, int flag) msgsnd() liefert 0 bei Erfolg. erwartet die ID der erzeugten Message-Queue. erwartet die Referenz ( Adresse ) vom Nachrichtsinhalt Die Größe wird per sizeof() Funktion festgestellt sizeof(message) Folgende FLAGs werden wir verwenden: IPC_NOWAIT Prozess blockiert nicht auch wenn die Nachricht nicht abgeschickt werden kann 0 (null) Prozess blockiert wenn Message-Queue voll, läuft erst weiter wenn Nachricht abgeliefert Abholen von Nachrichten von einer Message-Queue #include <sys/msg.h> int msgrcv( int id, void * message, size_t size, long type, int flag) msgrcv() Gibt bei Erfolg die Größe der abgeholten message, sonst -1 erwartet die ID der vorhandenen Message-Queue, Referenz des angelegten Nachrichteninterfaces ( das gefüllt wird ), den Typ der abzuholenden message bei 0 wird z.b. per FIFO Verfahren auf die Message Queue zugegriffen. Folgende FLAGs werden wir verwenden: IPC_NOWAIT Prozess blockiert nicht auch wenn die Nachricht nicht abgeholt werden kann ( weil noch kein Inhalt) 0 (null) Prozess blockiert wenn Message-Queue leer, läuft erst weiter wenn Nachricht abgeliefert
Systemprogrammierung unter Linux eine Einführung S. 6 Löschen von einer Message-Queue #include <sys/msg.h> int msgctl( int id, int command, struct msqid_ds * buffer) msgctl() Liefert 0 bei Erfolg sonst -1 erwartet die ID der Message-Queue, ein command flag: IPC_STAT Abfragen des Status der SHM, dieser wird in buffer geschrieben IPC_RMD Löschen der Message Queue Shared Memory Erzeugen eines Shared-Memory Segmentes #include <sys/shm.h> int shmget( key_t key, int size, int flag) shmget() Liefert bei Erfolg die ID des Shared Memory Segmentes zurück. Erwartet einen key, die Größe des zu reservierenden Speichers, sowie folgende FLAGs: IPC_CREAT Erzeugt eine Shared Memory IPC_EXCL Schliesst aus das eine SHM mit der selben ID schon existiert Rechte 0600 Gleiche Zugriffsrechtevergabe wie in Unix
Systemprogrammierung unter Linux eine Einführung S. 7 Anbinden an das Shared Memory Segment #include <sys/shm.h> void *shmat( int id, void * address, int flag) shmat() Liefert die Referenz auf das Shared Memory Segmentes zurück. Erwartet einen key, die Adresse wo das Segment startet, falls address NULL ist dann wird die erste verfügbare Speicheradresse gewählt, und folgende FLAGs: SHM_RDONLY Nur Lesen von der Shared Memory möglich 0 ( null ) Lesen und Schreiben möglich Lösen von einem Shared Memory Segment #include <sys/shm.h> int shmdt( void * address ) shmdt() Liefert 0 bei Erfolg Erwartet die Adresse des zu lösenden Segments. ## TIPP: Alle Verbindungen zu einem SHM müssen gelöst sein sonst kann es nicht gelöscht werden Löschen von Shared Memory Segmenten #include <sys/shm.h> int shmctl( int id, int command, struct shmid_ds * buffer) shmctl() Liefert bei Erfolg 0 zurück, sonst -1. Erwartet die ID des SHM, das command flag: IPC_STAT Abfragen des Status der SHM in buffer IPC_RMD Löscht das SHM, jedoch erst wenn alle Verbindungen gelöst sind Semaphore Erzeugen / Öffnen einer Semaphormenge #include <sys/sem.h>
Systemprogrammierung unter Linux eine Einführung S. 8 int semget( key_t key, int amount, int flag) semget() Liefert bei Erfolg die ID der Semaphormenge zurück, sonst -1 Wenn eine neue Menge erzeugt wird, muss die Anzahl angegeben werden, wenn nur eine geöffnet werden wird 0 angegeben. Die FLAG Argumente lauten wie folgt: IPC_CREAT erzeugt eine Semaphoremenge IPC_EXCL schliesst aus das eine Semaphormenge mit der selben ID schon existiert Rechte 0600 gleiche Vergabe wie in Unix Löschen einer Semaphormenge #include <sys/sem.h> int semctl( int id, int number, int command [, union senum argument] ) semctl() Liefert bei Erfolg die ID der Semaphormenge zurück, sonst -1 Erwartet die ID einer Semaphormenge, eine Semaphor number ( welcher Semaphor dieser Semaphoremenge), sowie ein command FLAG, Die FLAGs lauten: IPC_STAT erfragt den Status und schreibt die Antwort in die union senum IPC_RMD Löscht die Semaphormenge unabhängig davon ob andere Prozesse darf zugreifen.
Systemprogrammierung unter Linux eine Einführung S. 9 Operationen auf Semaphormengen #include <sys/sem.h> int semop( int id, struct sembuf list[], size_t amount) semop() Liefert bei Erfolg die ID der Semaphormenge zurück, sonst -1 erwartet die ID einer Semaphormenge auf die die Operationen anzuwenden sind, die sembuf Liste, sowie die Anzahl der Elemente in dieser Liste in amount struct sembuf { ushort sem_num; short sem_op; short sem_flg; } sem_num = 0 Wählt den ersten Semaphor aus der Menge für die Operation aus sem_op > 0 Freigeben eines Semaphors sem_op < 0 Anfordern eines Semaphors sem_flg = 0 Prozess wartet so lange bis Semaphor frei wird
Systemprogrammierung unter Linux eine Einführung S. 10 2 Programmieren unter Linux Filesystem Dateien Alles was im Filesystem zu finden ist sind Dateien, alle Devices sind Dateien und lassen sich deswegen auch als solche behandeln ( mit normalen Shellbefehlen bzw. C Funktionsaufrufen read usw.. lässt sich auf sie schreiben und lesen ). Ein weiteres Prinzip ist, das es alle Konfigurationsdateien im Plaintext verfasst sind, nicht wie unter Windows binär komprimierte Dateien wie die Registry. Dadurch kann man mit jedem beliebigen Texteditor die Konfiguration verändern. Außerdem kann man mit beliebigen Filedescriptoren auf Devices zugreifen, sowie Informationen über das System abrufen. Unter /proc bindet der Kernel Dateien ins Dateisystem ein die Informationen über die Hardware des System beinhalten. Es gibt zwei Arten von Dateien binäre und ASCII Dateien. Ordner Sind Dateien vom Typ Ordner, sie enthalten ihren eigenen Namen, sowie eine Liste der Dateien die in ihrem Verzeichnis enthalten sind. Außerdem enthalten sie eine Datei mit dem Namen./ die ein Verweis auf das aktuelle Verzeichnis ist und../ die ein Verweis auf das oben liegende Verzeichnis ist. Links Es gibt zwei Arten von Links, symbolische und harte Links ( symbolic, hard ). Symbolische Links sind Referenzen auf eine Datei ( mit Pfad ), harte Links sind Referenzen auf die selbe Speicheradresse wie die gelinkte Datei, d.h. wenn man eine Datei löscht auf die ein Symlink zeigt ist der Inhalt unwiederbringlich verloren, bei einem Hardlink hat man immer noch die Referenz auf den Inhalt der verwiesenen Datei. Hardlinks allerdings funktionieren nicht über Partitionsgrenzen hinaus, Symlinks sehr wohl. Prozesse Prozesse sind in einer hierarchischen Struktur angeordnet, jeder Prozess hat einen parent Prozess, es gibt nur eine Ausnahme der init Prozess, er ist der Anfang von allem, alle Prozesse sind seine childs. Ein Prozess erbt die Rechte für den Zugriff auf das Filesystem vom aufrufenden Benutzer. Prinzipiell gilt, wenn der parent stirbt, stirbt das child mit ihm ( es gibt aber die Möglichkeit es anders zu implementieren, diese Vorgehensweise wird benötigt wenn man einen daemon programmieren will )
Systemprogrammierung unter Linux eine Einführung S. 11 Wichtige Shell Befehle Prinzipien Jedes Unix Tool tut prinzipiell nur 1 Sache und Unix Tools sind zur Zusammenarbeit mit anderen Unix Tools konzipiert. So kann man viele der unten angegebenen Tools miteinander kombinieren. Die Shell Die Shell das Kommandointerface zum Kernel und somit das einzige das der normale Benutzer zu sehen bekommt. Außerdem hat nahezu jede gebräuchliche Unix Shell eine eigene Scriptsprache, die sich teilweise stark unterscheiden, aber alle übliche Funktionalität zur Arithmetik bereitstellen. ps Zeigt eine Liste der laufenden Prozesse an ipcs Listet alle IPC Ressourcen auf ipcrm <typ> <id> Entfernt manuell IPC Ressourcen ipcrm -q 12345 löscht message queue mit der ID 12345 ipcrm -m 12345 löscht shared memory mit der ID 12345 kill <id> Terminiert Prozesse anhand von übergebener id man <topic> Manual Pages von Linux liefert einem zu nahezu jedem wichtigen Kommandozeilentool und zu jedem System Call eine Anleitung man shmget liefert einem die ausführliche Manual Page zur C Funktion shmget, falls man einmal bei einer shell script funktion landet, dann einfach 2tes oder 3tes Vorkommen anwählen mit man 2 <topic> export <variable>=<inhalt> Exportiert Umgebungsvariablen z.b. für Shellscript welcher Compiler verwendet werden soll, wenn mehrere installiert sind. grep Durchsucht Streams ( Dateien, Bildschirmausgaben usw..) nach dem Vorkommen eines Suchwortes. grep 'Hallo' hallowelt.c( wenn Hallo gefunden wird Hallo ausgegeben) ls -la grep waldgeist ( nur Dateien ausgeben die in der Gruppe oder dem Benutzer waldgeist haben ) ( --> <-- das nennt man Pipe es leitet die Standardausgabe eines Programmes um auf das nachfolgende Programm
Systemprogrammierung unter Linux eine Einführung S. 12 --help Ein Zusatz zum Aufruf eines der obigen Tools liefert einem fast immer die gesuchte Hilfe, manchmal auch -? oder -h Beispiele for schleife for i in 1 2 3 4 5 6 7 8 9 10 do echo $i done if schleife if [ $rc!= 0 ]; then echo $rc fi
Systemprogrammierung unter Linux eine Einführung S. 13 Entwicklungstools In der Konsole: vim ( vorher vimtutor aufrufen und einige Übungen machen) Wichtige ( überlebenswichtige? ) Befehle: ESC : q ( ESC wechseln in den Editmode, : wechseln in Kommandmode, q beenden von vim ) q! beenden ohne zu speichern i ( starten des insert mode ) x ( im Editmode, löschen eines Zeichens) emacs ( sehr mächtig, jedoch auch nicht gerade einfach ) mc ( eine Art Norton Commander Clone für Linux ) In der grafischen Oberfläche: Editoren gvim ( siehe oben ) xemacs ( siehe oben ) Kwrite, ( ab GNOME 2.4 ) Gedit, SciTE IDEs ( Integrated Development Environment ) GNOME Eclipse ( mit CDT Plugin ) Anjuta KDE Kdevelop Hilfreiche Tools für größere Projekte make, autotools, shellscripte ( je nach Shell unterschiedliche Syntax )
Systemprogrammierung unter Linux eine Einführung S. 14 Kompilieren auf der Kommandozeile Zum compilen verwenden wir den gcc ( jeder andere Kommandozeilencompiler ist aber ebenso geeignet, verwendet aber eine andere Syntax) Projekt besteht aus: hallowelt.c Um das zu compilen kann man nurn zuerst das.o file erzeugen und dann die ausführbare binary ( linken ). gcc -c hallowelt.c Hier ensteht die Datei hallowelt.o gcc -o hallowelt hallowelt.o Hier wird aus dem.o file ein binary erstellt ( linken ) Aktuelle Versionen des gcc erlauben einem das alles in einem Aufwasch zu machen indem man folgende Syntax verwendet: gcc -o <programmname> <syntaxfile> gcc -o hallowelt hallowelt.c Hierbei spart man sich das je nach Größe des Projektes lästige.o files das Verzeichnis unübersichtlich machen. Die so erzeugte binary ( ausführbare Datei ) kann man folgendermaßen ausführen:./hallowelt das./ ist nötig um der Shell zu sagen WO es die Datei zum aufrufen findet. Wenn kein./ angegeben wird versucht die Shell die Datei in den Pfaden zu finden die in der Umgebungsvariable PATH eingetragen sind. Da der aktuelle Aufenthaltsort nicht ständig in PATH eingetragen wird, muss man mit./ sagen das er im aktuellen Verzeichnis nach der binary suchen soll. ein export $PATH bringt die eingetragenen Pfade zum Vorschein /sbin:/bin:/usr/sbin:/usr/bin:/usr/bin/x11:/usr/local /sbin:/usr/local/bin
Systemprogrammierung unter Linux eine Einführung S. 15 Kompilieren mit make ( Makefiles ) Da das kompilieren auf der Kommandozeile auf Dauer etwas zeitraubend ist, verwendet man so genannte Makefiles und das GNU Tool make, welches eine Art Interpreter ist und aus Regeln Kommandozeilenaufrufe macht. Projektdateien: hallowelt.c hilfsfunktionen.h hilfsfunktionen.c Die kompilierte binary soll hallowelt heissen Man erstellt nun eine Datei mit dem Namen Makefile Ein Makefile ist folgendermaßen aufgebaut: # all ist ein Standardaufruf der immer gemacht wird wenn make # ohne weitere Übergabeparameter aufgerufen wird # Hier bedeutet es das die Regeln hallowelt und clean # hintereinander aufgerufen werden all: hallowelt clean # Nun müssen wir die oben genannten Regeln definieren # hallowelt besteht aus hallowelt.o ( der Regel) und # hilfsfunktionen.o hallowelt: hallowelt.o hilfsfunktionen.o gcc -o hallowelt hallowelt.o hilfsfunktionen.o # Regel für die Erstellung von hallowelt.o hallowelt.o: hallowelt.c hilfsfunktionen.h gcc -o hallowelt.c # Regel für die Erstellung von hilfsfunktionen.o hilfsfunktionen.o: hilfsfunktionen.c hilfsfunktionen.h gcc -o hilfsfunktionen.c # Regel für clean, entfernen der.o Dateien clean: rm *.o Folgendes passiert nun, make springt zu all --> all sagt ihm er solle die Regel hallowelt ausführen --> hallowelt sagt ihm es besteht aus hallowelt.o --> führt hallowelt.o aus --> führt hilfsfunktionen.o aus --> führt nun endlich hallowelt aus --> ist hallowelt beendet wird clean angesprungen ( und da keine Abhängigkeiten ) sofort ausgeführt.
Systemprogrammierung unter Linux eine Einführung S. 16 Allgemeine Zeilensyntax: <regelname>: <bedingungsregeln> - TAB - <kommandozeilenaktion>