Einführung in die Systemprogrammierung unter Linux Systemsoftware Praktikum Inhalt Übersicht über benötigte Systemfunktionen Programmieren unter Linux Grundlegendes Message-Queue Shared Memory Semaphore Netzwerkprogrammierung Wichtige Shell-Befehle Werkzeuge Kompilieren, Linken und Ausführen Beispielprogramm
Grundlegende Systemfunktionen Die PID eines Prozesses Erfragen der PID eines Eltern-Prozesses Einen Kindprozess erzeugen Einbinden von einem Programm in einen bestehenden Prozess Einen Eindeutigen System-Key Erzeugen Einrichten eines einfachen Signal-Handlers Die PID eines Prozesses #include <sys/types.h> #include <unistd.h> pidnr getpid(void) liefert die PID(pidnr) des Prozesses zurück.
Erfragen der PID eines Eltern-Prozesses #include <sys/types.h> #include <unistd.h> ppidnr getppid(void) liefert die PID(ppidnr) des Eltern-Prozesses zurück. Einen Kindprozess erzeugen #include <sys/types.h> #include <unistd.h> pidnr fork(void) liefert im Elternprozess die PID(pidnr) des Kindprozesses, -1 bei Fehler. Im Sohnprozess ist der Rückgabewert immer 0. Der erzeugte Sohnprozeß erbt vom Vater alle Zustände,Umgebungsvariablen, Programmdaten und den Programmcode.
Einbinden von einem Programm in einen bestehenden Prozess #include <unistd.h> int execl(char* dateiname, char* arg0, ) liefert -1 bei Fehler. Erwartet den Dateinamen mit Pfadangabe und Kommandozeilenargumente Einen Eindeutigen System-Key Erzeugen #include <sys/types.h> #include <sys/ipc.h> key_t ftok(const char *pfad, int projekt_id) liefert den Systemschlüssel, (Schlüssel wird für z.b. Message-Queue benötigt). erwartet einen existierenden Pfadnamen../ ist ausreichend. erwartet eine einmalige Projekt-ID die nicht 0 sein darf(am besten MatrikelNR).
Einrichten eines einfachen Signal-Handlers #include <signal.h> void signal(int signalnr, void* funktionszeiger) richtet einen Signal-Handler ein. erwartet die Signalnummer und die auszuführende Funktion. SIGTERM SIGINT Signalnummer für den kill Befehl Signalnummer für STRG+C Message-Queue Kreieren/öffnen einer Message-Queue Senden von Nachrichten über Message-Queues Empfangen von Nachrichten über Message-Queues Status und Löschen von Message-Queues
Kreieren/öffnen einer Message-Queue #include <sys/types.h> #include <sys/ipc.h> #include <sys/msg.h> int msgget(key_t schlüssel, int flag) liefert die ID der erzeugten Message-Queue. Erwartet einen eindeutigen Systemschlüssel und entsprechende Flag-Argumente, Beispielsweise: IPC_CREAT IPC_EXCL 0600 IPC_CREAT erzeugt einen Briefkasten, IPC_EXCL schließt aus, dass eine Message-Queue mit derselben ID schon existiert. 0600 gibt dem Briefkasten die passenden Schreib- und Leserechte. ( 0600: Lese- und Schreibrecht für den Eigentümer, für alle anderen Benutzer keine Rechte ). Senden von Nachrichten über Message-Queues #include <sys/types.h> #include <sys/ipc.h> #include <sys/msg.h> int msgsnd(int id, const void *nachricht, in size, int flag) liefert 0 bei Erfolg. erwartet die ID der erzeugten Message-Queue. erwartet die Referenz der Nachricht die in einem Stuct liegt z.b.: struct meine_mesg{ long mtype; char mtext[256]; } zudem wird die Größe der Nachricht ( sizeof(meine_mesg) ) und ein Flag-Argument erwartet z.b.: IPC_NOWAIT Der Prozess blockiert nicht. 0 Der Prozess wird blockiert wenn Warteschlange voll ist und wieder freigegeben falls in der Warteschlange wieder Platz ist.
Empfangen von Nachrichten über Message-Queues #include <sys/types.h> #include <sys/ipc.h> #include <sys/msg.h> int msgrcv(int id, void *nachricht, sizeof(meine_mesg), long type, int flag) liefert bei Erfolg die Größe der Empfangenen Message, sonst -1. erwartet die ID der vorhandenen Message-Queue, die Referenz des Nachrichtenstructs, die Größe (sizeof(meine_mesg)), den Typ der zu Empfangenden Message, Beispielsweise: Typ=0 für FIFO und zudem noch ein Flag: IPC_NOWAIT Prozess wird nicht blockiert, falls keine Nachricht in Warteschlange. 0 Prozess wird blockiert solange bis eine Nachricht vom passenden Typ in der Warteschlange steckt. Status und Löschen von Message-Queues #include <sys/types.h> #include <sys/ipc.h> #include <sys/msg.h> int msgctl(int id, int kommando, struct msqid_ds *puffer ) liefert 0 bei Erfolg, sonst -1. erwartet die ID der vorhandenen Message-Queue und ein Kommandoflag: IPC_STAT Abfragen des Status der SHM, wird an die Adresse puffer geschrieben. msqid_ds ist eine Struktur in der Informationen über den Status der Shared-Memory abgelegt sind. IPC_RMID Löschen der Message-Queue mit all ihren Daten. Dabei werden die Benutzer und ihre Rechte berücksichtigt.
Shared Memory Erzeugen einer Shared Memory Anbinden eines Shared-Memory-Segments an den Prozess Loslösen eines angebundenen Shared-Memory-Segments Status und Löschen von Shared-Memory-Segmenten Erzeugen eines Shared-Memory-Segments #include<sys/types.h> #include<sys/ipc.h> #include<sys/shm.h> int shmget(key_t key, int size, int flag) liefert bei Erfolg die Kennung des Shared-Memory-Segments zurück. Erwartet einen eindeutigen Systemschlüssel und die Größe des minimalen Shared-Memory-Segments. ( z.b. 100 * sizeof(eintrag) ), entsprechende Flag-Argumente, Beispielsweise: IPC_CREAT IPC_EXCL 0600 IPC_CREAT erzeugt eine Shared-Memory, IPC_EXCL schließt aus, dass eine Shared-Memory mit der selben ID schon existiert. 0600 gibt der Shared-Memory die passenden Schreib- und Leserechte. ( 0600: Lese- und Schreibrecht für den Eigentümer, für alle anderen Benutzer keine Rechte ).
Anbinden eines Shared-Memory-Segments an den Prozess #include<sys/types.h> #include<sys/ipc.h> #include<sys/shm.h> void* shmat(int id, void* adresse, int flag) liefert die Adresse des Shared-Memory-Segmentes zurück. Erwartet einen eindeutigen Systemschlüssel. Die Adresse an die das Segment angebunden wird hängt vom adresse Argument und dem flag ab: adresse==null erste verfügbare Adresse SHM_RDONLY nur Lesen möglich 0 Lesen und Schreiben möglich Loslösen eines angebundenen Shared-Memory- Segments #include<sys/types.h> #include<sys/ipc.h> #include<sys/shm.h> int shmdt(void* adresse) liefert 0 bei Erfolg. Erwartet eine Adresse des zu loszulösenden Segments.
Status und Löschen von Shared-Memory-Segmenten #include<sys/types.h> #include<sys/ipc.h> #include<sys/shm.h> int shmctl(int id, int kommando, struct shmid_ds *puffer) liefert bei Erfolg 0 zurück, sonst -1. erwartet die ID des Shared-Memory -Segments das beim Anlegen erzeugt wurde und ein Kommandoflag: IPC_STAT shmid_ds IPC_RMID Abfragen des Status der Shared-Memory. Die Struktur wird an die Adresse puffer geschrieben. ist eine Struktur in der Informationen über den Status der Shared-Memory abgelegt sind. Löscht das Segment, Shared-Memory wird jedoch erst wirklich gelöscht, wenn. Der letze Prozess der darauf zugreift sich beendet oder die Anbindung aufhebt (detached). Semaphore Erzeugen oder Öffnen einer Semaphormenge Status und Löschen einer Semaphormenge Operationen auf Semaphormengen
Erzeugen oder Öffnen einer Semaphormenge #include<sys/types.h> #include<sys/ipc.h> #include<sys/sem.h> int semget(key_t schlüssel, int anzahl, int flag) liefert bei Erfolg die ID einer Semaphormenge sonst -1. Wenn eine neue Semaphormenge erzeugt wird muss das anzahl Argument angegeben werden. Wird eine existierende Menge geöffnet so wird 0 angegeben. erwartet Flag-Argumente, Beispielsweise: IPC_CREAT IPC_EXCL 0600 IPC_CREAT erzeugt eine, Semaphormenge schließt aus, dass eine Semaphormenge mit der selben ID schon existiert. 0600 gibt der Semaphormenge die passenden Schreib- und Leserechte. Status und Löschen einer Semaphormenge #include<sys/types.h> #include<sys/ipc.h> #include<sys/sem.h> int semctl(int id, int nummer, int kommando, union semun argument) liefert bei Erfolg 0, sonst -1. erwartet die ID einer Semaphormenge die beim Anlegen erzeugt wurde, eine Semaphornummer, ein Kommandoflag und Argumente für das Kommando. Kommandoflags: IPC_STAT IPC_RMID erfragt den Status und schreibt ihn in die argument Struktur. Löscht die Semaphormenge, unabhängig davon ob andere Prozesse darauf zugreifen.
Operationen auf Semaphormengen #include<sys/types.h> #include<sys/ipc.h> #include<sys/sem.h> int semop(int id, struct sembuf liste[], size_t anzahlop) liefert bei Erfolg 0, sonst -1. erwartet die ID einer Semaphormenge auf die die Operation anzuwenden ist, die sembuf Struktur-Liste, und die Anzahl der Elemente in dieser Liste in anzahlop. 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 ( gibt einen Semaphor frei ) sem_op<0 Anfordern ( fordert einen Semaphor an ) sem_flg=0 Anfordernder Prozess wartet bis ein Semaphor frei wird Netzwerkprogrammierung Stichwort Sockets - Socket-Api ist ein abstrakter Vermittler zwischen unterschiedlichen Protokollen - Verbindungsorientierte, Alternative zu FIFO s - Vollduplex fähig. - Lokal und Netzwerk tauglich. Server Client socket socket bind listen accept Verbindung aufgebaut connect
Big Endian vs. Little Endian in TCP/IP - TCP/IP -> Big Endian (niederwertigste Byte an höchster Adresse) - Intel ->Little Endian - Funktionen lösen diese Probleme in dem sie die lokale Byteanordnung in die Netzwerkreihenfolge konvertieren und andersherum. Programmieren unter Linux Wichtige Shell-Befehle Werkzeuge Kompilieren, Linken und Ausführen Beispielprogramm
Wichtige Shell-Befehle Befehl: Beschreibung: Beispiel: Befehl --help Liefert zu den meisten Befehlen weitere Hilfe. ps --help ps ps zeigt eine Liste der laufenden Prozesse an ps -al ipcs Listet alle benutzen ipc-ressourcen auf ipcs ipcrm Entfernt manuell ipc-resourcen ipcrm shm 12345 Löscht die Shared-Memory mit dem key 12345 kill Terminiert Prozesse kill 1234 Terminiert den Prozess mit dem key 1234 Sendet SIGTERM Signal an den Prozess man Funktion Liefert die Beschreibung zu einer man getpid Funktion oder Befehl Liefert die Beschreibung zur Systemfunktion getpid() Oder im Konqueror-Browser man:getpid eingeben. Werkzeuge Entwicklungsumgebung: KDevelop Einfache Texteditoren: Kate, KWrite, Emacs Textkonsolen: Bourne-Shell Compiler GNU Compiler Collection
Kompilieren, Linken und Ausführen Kompilieren Ein C-Programm wird an der Konsole zuerst kompiliert und dann gelinkt. Als Compiler wird die GNU Compiler Collection ( gcc ) verwendet. Zum Kompilieren der Datei beispiel.c wird der Befehl gcc c beispiel.c verwendet. es wird die Datei beispiel.o erstellt. Kompilieren, Linken und Ausführen Linken Zum Linken der Objektdatei wird der Befehl gcc o beispiel beispiel.o verwendet. Jetzt kann die erzeugte Datei beispiel.o mit dem Befehl rm beispiel.o gelöscht werden.
Kompilieren, Linken und Ausführen Ausführen Das erzeugte Programm hat den Dateinamen beispiel. Es kann nun mit dem Befehl./beispiel ausgeführt werden. Makefile Bei Programmen mit mehreren Modulen ist es mühsam diese Befehle nacheinander an der Konsole auszuführen. Für diese Fälle ist das sog. Makefile vorgesehen. Dort trägt man die auszuführenden Befehle für alle Module ein. Wie ein Makefile aufgebaut ist zeigt dieses Beispiel: Aus den Dateien beispiel.c funktionen.h funktionen.c soll das Programm beispiel erzeugt werden. Das Modul beispiel benötigt die Funktionen aus dem Modul funktionen.
Makefile Die Datei mit dem Namen makefile sieht für dieses Beispiel so aus: all: beispiel clean beispiel: beispiel.o funktionen.o gcc -o beispiel beispiel.o funktionen.o beispiel.o: beispiel.c funktionen.h gcc -c beispiel.c funktionen.o: funktionen.c funktionen.h gcc c funktionen.c clean: rm *.o Man muss jetzt nur noch den Befehl make ausführen. Das Programm sucht automatisch nach einer Datei mit dem Namen makefile oder Makefile und führt alle enthaltenen Befehle nacheinander aus. Beispielprogramm PID Message-Queue Signal-Handler