Übung 5
This commit is contained in:
parent
0453269f24
commit
5b651516d2
@ -0,0 +1,16 @@
|
||||
all: p1 p2 p3 p4
|
||||
|
||||
p1:
|
||||
cc -o p1 p1.c
|
||||
|
||||
p2:
|
||||
cc -o p2 p2.c
|
||||
|
||||
p3:
|
||||
cc -o p3 p3.c
|
||||
|
||||
p4:
|
||||
cc -o p4 p4.c
|
||||
|
||||
clean:
|
||||
rm -f p1 p2 p3 p4
|
11
Assets/Betriebssysteme_uebung/u2-anlage/u2-a1-anlage/p1.c
Normal file
11
Assets/Betriebssysteme_uebung/u2-anlage/u2-a1-anlage/p1.c
Normal file
@ -0,0 +1,11 @@
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
|
||||
int main()
|
||||
{
|
||||
printf("%d: code before forking - executed once only\n", getpid());
|
||||
|
||||
fork();
|
||||
|
||||
printf("%d: code after forking - executed by each process\n", getpid());
|
||||
}
|
20
Assets/Betriebssysteme_uebung/u2-anlage/u2-a1-anlage/p2.c
Normal file
20
Assets/Betriebssysteme_uebung/u2-anlage/u2-a1-anlage/p2.c
Normal file
@ -0,0 +1,20 @@
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
int main()
|
||||
{
|
||||
if (fork() == 0)
|
||||
{ // child proc
|
||||
printf("%d: child created by parent %d; executing 'p4'...\n", getpid(), getppid());
|
||||
execl("p4", "p4", NULL);
|
||||
printf("system call execl(): no success\n");
|
||||
}
|
||||
else
|
||||
{ // parent proc
|
||||
printf("%d: parent process\n", getpid());
|
||||
}
|
||||
|
||||
wait(NULL);
|
||||
}
|
22
Assets/Betriebssysteme_uebung/u2-anlage/u2-a1-anlage/p3.c
Normal file
22
Assets/Betriebssysteme_uebung/u2-anlage/u2-a1-anlage/p3.c
Normal file
@ -0,0 +1,22 @@
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
int main()
|
||||
{
|
||||
if (fork() == 0)
|
||||
{ // child proc
|
||||
printf("%d: child created by parent %d; executing 'p4'...\n", getpid(), getppid());
|
||||
execl("p4", "p4", NULL);
|
||||
}
|
||||
else
|
||||
{ // parent proc
|
||||
printf("%d: parent process\n", getpid());
|
||||
}
|
||||
|
||||
fork();
|
||||
printf("%d: terminating\n", getpid());
|
||||
|
||||
wait(NULL);
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
|
||||
int main()
|
||||
{
|
||||
printf("%d: p4 running\n", getpid());
|
||||
}
|
@ -0,0 +1,13 @@
|
||||
all: p5 p6 p7
|
||||
|
||||
p5:
|
||||
gcc -o p5 p5.c
|
||||
|
||||
p6:
|
||||
gcc -o p6 p6.c
|
||||
|
||||
p7:
|
||||
gcc -o p7 p7.c
|
||||
|
||||
clean:
|
||||
rm -f p5 p6 p7
|
23
Assets/Betriebssysteme_uebung/u2-anlage/u2-a2-anlage/p5.c
Normal file
23
Assets/Betriebssysteme_uebung/u2-anlage/u2-a2-anlage/p5.c
Normal file
@ -0,0 +1,23 @@
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
int main()
|
||||
{
|
||||
int pid;
|
||||
|
||||
pid = fork();
|
||||
|
||||
if (pid == 0)
|
||||
{ // child proc looping endlessly
|
||||
printf("%d: child ...\n", getpid());
|
||||
for(;10;);
|
||||
}
|
||||
else
|
||||
{ // parent proc
|
||||
printf("%d: parent; child pid: %d \n", getpid(), pid);
|
||||
wait(NULL); // for child termination
|
||||
printf("%d: parent after wait ... terminating\n", getpid());
|
||||
}
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
#include <stdio.h>
|
||||
|
||||
int main()
|
||||
{
|
||||
printf("p6: calling getchar()\n");
|
||||
getchar();
|
||||
}
|
@ -0,0 +1,8 @@
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
|
||||
int main()
|
||||
{
|
||||
printf("p7: calling sleep()\n");
|
||||
sleep(100);
|
||||
}
|
Binary file not shown.
Before Width: | Height: | Size: 8.9 KiB After Width: | Height: | Size: 8.9 KiB |
Before Width: | Height: | Size: 54 KiB After Width: | Height: | Size: 54 KiB |
10
Assets/Betriebssysteme_uebung/u5-a1-anlage/Makefile
Normal file
10
Assets/Betriebssysteme_uebung/u5-a1-anlage/Makefile
Normal file
@ -0,0 +1,10 @@
|
||||
all: client server
|
||||
|
||||
client:
|
||||
gcc -o msgclient msgclient.c
|
||||
|
||||
server:
|
||||
gcc -o msgserver msgserver.c
|
||||
|
||||
clean:
|
||||
rm -f msgclient msgserver
|
68
Assets/Betriebssysteme_uebung/u5-a1-anlage/msgclient.c
Normal file
68
Assets/Betriebssysteme_uebung/u5-a1-anlage/msgclient.c
Normal file
@ -0,0 +1,68 @@
|
||||
/*
|
||||
* Interprozesskommunikation über Message Queues - msgclient.c
|
||||
*
|
||||
* Funktionsweise: Der Client schickt eine Losung an einen Server. Falls die
|
||||
* Losung die vom Server erwartete war, gibt letzterer dem Client sein
|
||||
* Geheimnis preis.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/ipc.h>
|
||||
#include <sys/msg.h>
|
||||
#include <string.h>
|
||||
|
||||
#define MSGSIZE 100
|
||||
key_t key = 1337;
|
||||
int msgflg = 0666;
|
||||
|
||||
// Typdefinition für Message Queue (muss explizit geschehen, da noch nicht
|
||||
// vorhanden)
|
||||
struct message {
|
||||
long msgType;
|
||||
char msgText[MSGSIZE];
|
||||
};
|
||||
|
||||
// char CODEWORD[] = "<Losung>";
|
||||
char CODEWORD[] = "Losung";
|
||||
|
||||
int main() {
|
||||
struct message msg_snd;
|
||||
struct message msg_rcv;
|
||||
|
||||
// Verbinden mit Message-Queue des Servers
|
||||
printf("Client: Verbinde mich mit Message Queue.\n");
|
||||
int id_q = msgget(key, msgflg);
|
||||
|
||||
if (id_q >= 0) {
|
||||
printf(" OK.\n\n");
|
||||
}
|
||||
|
||||
// Absenden des Codeworts
|
||||
printf("Client: Sende Codewort an den Server.\n");
|
||||
msg_snd.msgType = 5;
|
||||
strcpy(msg_snd.msgText, CODEWORD);
|
||||
|
||||
int result_snd = msgsnd(id_q, &msg_snd, MSGSIZE, 0);
|
||||
|
||||
if (result_snd >= 0) {
|
||||
printf(" OK.\n\n");
|
||||
}
|
||||
|
||||
// Empfangen der Antwort
|
||||
printf("Client: Warte auf Geheimnis.\n");
|
||||
|
||||
// Abholen des Geheimnisses aus der Message-Queue
|
||||
int result_rcv = msgrcv(id_q, &msg_rcv, MSGSIZE, 6, 0);
|
||||
|
||||
if (result_rcv >= 0) {
|
||||
printf(" Geheimnis erhalten.\n\n");
|
||||
}
|
||||
|
||||
// Ausgabe des Geheimnisses
|
||||
printf("Client: Das Geheimnis lautet: '%s'.\n\n", msg_rcv.msgText);
|
||||
|
||||
printf("Client: Ende.\n");
|
||||
|
||||
return 0;
|
||||
}
|
76
Assets/Betriebssysteme_uebung/u5-a1-anlage/msgserver.c
Normal file
76
Assets/Betriebssysteme_uebung/u5-a1-anlage/msgserver.c
Normal file
@ -0,0 +1,76 @@
|
||||
/*
|
||||
* Interprozesskommunikation über Message Queues - msgcserver.c
|
||||
*
|
||||
* Funktionsweise: Der Server erzeugt eine Message Queue und erwartet über diese
|
||||
* die Losung eines Client. Falls die Losung mit der vom Server erwarteten
|
||||
* übereinstimmt, teilt der Server dem Client sein Geheimnis mit.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/ipc.h>
|
||||
#include <sys/msg.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#define MSGSIZE 100
|
||||
|
||||
#define SECRET "GGG ..."
|
||||
key_t key = 1337;
|
||||
int msgflg = IPC_CREAT | 0666; // Vergibt lese und schreibrechte an alle Nutzer, user group others
|
||||
struct message {
|
||||
long msgType;
|
||||
char msgText[MSGSIZE];
|
||||
};
|
||||
|
||||
int main() {
|
||||
struct message msg_rcv;
|
||||
struct message msg_snd;
|
||||
|
||||
// Erzeugen einer Message Queue
|
||||
printf("Server: Erzeuge Message Queue.\n");
|
||||
int id_q = msgget(key, msgflg);
|
||||
|
||||
if (id_q >= 0) {
|
||||
printf(" OK.\n\n");
|
||||
}
|
||||
|
||||
// Losung empfangen
|
||||
printf("Server: Warte auf Losung.\n");
|
||||
int result_rcv = msgrcv(id_q, &msg_rcv, MSGSIZE, 5, 0); // hier war
|
||||
|
||||
if (result_rcv >= 0) {
|
||||
printf(" Losung empfangen.\n\n");
|
||||
}
|
||||
|
||||
// Vergleich
|
||||
if (strncmp(msg_rcv.msgText, "Losung", strlen("Losung")) == 0) {
|
||||
printf("Server: Losung ist korrekt. Sende jetzt das Geheimnis.\n");
|
||||
|
||||
// Senden des Geheimnisses
|
||||
msg_snd.msgType = 5;
|
||||
strcpy(msg_snd.msgText, SECRET);
|
||||
|
||||
int result_snd = msgsnd(id_q, &msg_snd, MSGSIZE, 0);
|
||||
|
||||
if (result_snd >= 0) {
|
||||
printf(" OK.\n\n");
|
||||
}
|
||||
}
|
||||
else {
|
||||
printf("Server: Das war falsch.\n");
|
||||
}
|
||||
|
||||
sleep(1);
|
||||
|
||||
int result_ctl = msgctl(id_q, IPC_RMID, 0);
|
||||
|
||||
if (result_ctl >= 0) {
|
||||
printf("Server: Message Queue gelöscht. Ende.\n\n");
|
||||
return 0; // alles OK
|
||||
}
|
||||
else {
|
||||
printf("Server: Message Queue löschen fehlgeschlagen. Ende.\n\n");
|
||||
return 10; // Fehler
|
||||
}
|
||||
}
|
10
Assets/Betriebssysteme_uebung/u5-a2-anlage/Makefile
Normal file
10
Assets/Betriebssysteme_uebung/u5-a2-anlage/Makefile
Normal file
@ -0,0 +1,10 @@
|
||||
all: client server
|
||||
|
||||
client:
|
||||
gcc -o shmclient shmclient.c
|
||||
|
||||
server:
|
||||
gcc -o shmserver shmserver.c
|
||||
|
||||
clean:
|
||||
rm -f shmclient shmserver
|
76
Assets/Betriebssysteme_uebung/u5-a2-anlage/shmclient.c
Normal file
76
Assets/Betriebssysteme_uebung/u5-a2-anlage/shmclient.c
Normal file
@ -0,0 +1,76 @@
|
||||
/*
|
||||
* Interprozesskommunikation über Shared Memory - shmclient.c
|
||||
*
|
||||
* Funktionsweise: Das Programm kommuniziert mit einem Server mittels Shared
|
||||
* Memory und fragt mit einem Passwortsatz nach einem Geheimnis. Der Server
|
||||
* hinterlegt die Antwort ebenfalls im Shared-Memory-Bereich. Dabei müssen beide
|
||||
* Kommunikationspartner jeweils mit Semaphoren den Ablauf ihrer Kommunikation
|
||||
* steuern.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/shm.h>
|
||||
#include <sys/sem.h>
|
||||
#include <string.h>
|
||||
|
||||
#define SHMSIZE 256
|
||||
#define CODEWORD "Losung"
|
||||
|
||||
// statisch vereinbarte Schlüssel
|
||||
const int shmKey = 1337; // Schlüssel zum Benutzen des Shared Memory
|
||||
const int readSemaphoreKey = 100; // Schlüssel zum Benutzen der Semaphore
|
||||
const int writeSemaphoreKey = 101; // Schlüssel zum Benutzen der Semaphore
|
||||
|
||||
const int textLen = 128;
|
||||
|
||||
int main(int arc, char** argv) {
|
||||
// dynamisch vergebene ID's
|
||||
// Benutzung der Semaphore und des Shared Memory vorbereiten
|
||||
int shmID = shmget(shmKey, 2 * textLen, 0);
|
||||
int readSemID = semget(readSemaphoreKey, 1, 0666);
|
||||
int writeSemID = semget(writeSemaphoreKey, 1, 0666);
|
||||
|
||||
// Anhängen des Shared-Memory-Segments
|
||||
printf("Client: Hänge Shared-Memory-Segment an.\n\n");
|
||||
|
||||
char *shm_ptr;
|
||||
shm_ptr = shmat(shmID, 0, 0);
|
||||
|
||||
char *shm_ptr_alt;
|
||||
shm_ptr_alt = shm_ptr;
|
||||
|
||||
// Absenden der Losung
|
||||
printf("Client: Sende meine Losung.\n\n");
|
||||
|
||||
for (int i = 0; i <= strlen(CODEWORD); i++) {
|
||||
*shm_ptr++ = CODEWORD[i];
|
||||
}
|
||||
|
||||
*shm_ptr = '\0';
|
||||
|
||||
// Signal an den Server, dass die Daten jetzt lesbar sind
|
||||
struct sembuf semaphoreOperation;
|
||||
semaphoreOperation.sem_num = 0;
|
||||
semaphoreOperation.sem_op = 1;
|
||||
semaphoreOperation.sem_flg = SEM_UNDO;
|
||||
semop(writeSemID, &semaphoreOperation, 1);
|
||||
|
||||
// Warten auf die Antwort (das Signal vom Server)
|
||||
semaphoreOperation.sem_num = 0;
|
||||
semaphoreOperation.sem_op = -1;
|
||||
semaphoreOperation.sem_flg = SEM_UNDO;
|
||||
semop(readSemID, &semaphoreOperation, 1);
|
||||
|
||||
// Abolen des Geheimnisses und direkte Ausgabe
|
||||
char *sbuf;
|
||||
sbuf = shmat(shmID,0,0);
|
||||
printf("Client: Das Geheimnis lautet: '%s'.\n\n", sbuf);
|
||||
|
||||
printf("Client: Ende.\n\n");
|
||||
|
||||
// Ausblenden des Shared Memory
|
||||
shmdt(&shmID);
|
||||
|
||||
return 0;
|
||||
}
|
90
Assets/Betriebssysteme_uebung/u5-a2-anlage/shmserver.c
Normal file
90
Assets/Betriebssysteme_uebung/u5-a2-anlage/shmserver.c
Normal file
@ -0,0 +1,90 @@
|
||||
/*
|
||||
* Interprozesskommunikation über Shared Memory - shmserver.c
|
||||
*
|
||||
* Funktionsweise: Das Programm kommuniziert mit einem Server mittels Shared
|
||||
* Memory und fragt mit einem Passwortsatz nach einem Geheimnis. Der Server
|
||||
* hinterlegt die Antwort ebenfalls im Shared-Memory-Bereich. Dabei müssen beide
|
||||
* Kommunikationspartner jeweils mit Semaphoren den Ablauf ihrer Kommunikation
|
||||
* steuern.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/ipc.h>
|
||||
#include <sys/shm.h>
|
||||
#include <sys/sem.h>
|
||||
#include <string.h>
|
||||
|
||||
// SHM
|
||||
#define SHMKEY 1337
|
||||
#define SHMSIZE 256
|
||||
|
||||
// SEM
|
||||
#define RSEMKEY 100
|
||||
#define SSEMKEY 101
|
||||
|
||||
#define SECRET "xxx..."
|
||||
#define Losung "Losung"
|
||||
|
||||
struct sembuf sb[1];
|
||||
int rsem_id, ssem_id;
|
||||
char *sbuf;
|
||||
|
||||
|
||||
|
||||
int main() {
|
||||
// Erzeugen eines Shared-Memory-Segments
|
||||
printf("Server: Erzeuge Shared-Memory-Segment.\n");
|
||||
int id_shm = shmget(SHMKEY, SHMSIZE, IPC_CREAT|0666);
|
||||
|
||||
if (id_shm >= 0) {
|
||||
printf(" OK.\n\n");
|
||||
}
|
||||
|
||||
// Anhängen des Shared-Memory-Segments
|
||||
printf("Server: Hänge Shared-Memory-Segment an.\n");
|
||||
sbuf = shmat(id_shm, 0, 0);
|
||||
|
||||
if (sbuf >= 0) {
|
||||
printf(" OK.\n\n");
|
||||
}
|
||||
|
||||
// Erzeugen Semaphore
|
||||
ssem_id = semget(SSEMKEY, 1, IPC_CREAT|0666);
|
||||
rsem_id = semget(RSEMKEY, 1, IPC_CREAT|0666);
|
||||
|
||||
semctl(ssem_id, 0, SETVAL, 0);
|
||||
semctl(rsem_id, 0, SETVAL, 0);
|
||||
|
||||
printf("Server: Warte auf Semaphor.\n\n");
|
||||
sb[0].sem_num = 0;
|
||||
sb[0].sem_op = -1;
|
||||
sb[0].sem_flg = SEM_UNDO;
|
||||
semop(ssem_id, sb, 1);
|
||||
|
||||
// Warten bis der Client geschrieben hat
|
||||
// Synchronisation
|
||||
printf("Server: Die Losung des Client lautet: '%s'.\n", sbuf);
|
||||
|
||||
if (strncmp(sbuf, Losung, sizeof(Losung)) == 0 ) {
|
||||
printf(" Das war richtig.\n\n");
|
||||
printf("Server: Schreibe das Geheimnis in den Shared Memory.\n\n");
|
||||
snprintf(sbuf, SHMSIZE, "%s", SECRET);
|
||||
}
|
||||
else {
|
||||
printf(" Das war falsch.\n");
|
||||
snprintf(sbuf, 128, "Pech gehabt.");
|
||||
}
|
||||
|
||||
printf("Server: Signalisiere, dass Client nun lesen kann.\n\n");
|
||||
sb[0].sem_num = 0;
|
||||
sb[0].sem_op = 1;
|
||||
sb[0].sem_flg = SEM_UNDO;
|
||||
semop(rsem_id, sb, 1);
|
||||
|
||||
printf("Server: Ende.\n\n");
|
||||
|
||||
shmdt(&id_shm);
|
||||
|
||||
return 0;
|
||||
}
|
10
Assets/Betriebssysteme_uebung/u5-a3-anlage/Makefile
Normal file
10
Assets/Betriebssysteme_uebung/u5-a3-anlage/Makefile
Normal file
@ -0,0 +1,10 @@
|
||||
all: client server
|
||||
|
||||
client:
|
||||
gcc -o pipeclient pipeclient.c
|
||||
|
||||
server:
|
||||
gcc -o pipeserver pipeserver.c
|
||||
|
||||
clean:
|
||||
rm -f pipeclient pipeserver
|
65
Assets/Betriebssysteme_uebung/u5-a3-anlage/pipeclient.c
Normal file
65
Assets/Betriebssysteme_uebung/u5-a3-anlage/pipeclient.c
Normal file
@ -0,0 +1,65 @@
|
||||
/*
|
||||
* Interprozesskommunikation über Named Pipes (FIFOs) - pipeclient.c
|
||||
*
|
||||
* Funktionsweise: Dieses Client-Programm meldet sich über ein benanntes Pipe
|
||||
* bei einem geeigneten Server und fragt mit einer Losung (Passwortsatz) nach
|
||||
* einem Geheimnis. Der Server kontrolliert die Losung. Ist diese korrekt,
|
||||
* antwortet der Server mit dem Geheimnis über die gleiche Pipe, welches auf
|
||||
* geeignete Weise vom Client gelesen werden kann.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <string.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
//char CODEWORD[] = "<Schluessel>";
|
||||
char CODEWORD[] = "<Schluessel>";
|
||||
char buffer[8];
|
||||
|
||||
int main() {
|
||||
// Öffnen der Named Pipe nur zum Schreiben
|
||||
printf("Client: Öffne Pipe nur zum Schreiben.\n");
|
||||
int id_p = open("P1", O_WRONLY); // const char *path, int oflag … / returnt einen Filedeskriptor
|
||||
|
||||
if (id_p >= 0) {
|
||||
printf(" OK.\n\n");
|
||||
}
|
||||
|
||||
// Absenden des Codeworts an Server
|
||||
printf("Client: Übergebe Codewort an den Server.\n");
|
||||
int result_write = write(id_p, CODEWORD, strlen(CODEWORD));
|
||||
|
||||
if (result_write > 0) {
|
||||
printf(" OK.\n\n");
|
||||
}
|
||||
|
||||
close(id_p);
|
||||
|
||||
// Empfangen der Antwort
|
||||
printf("Client: Empfange Geheimnis.\n\n");
|
||||
|
||||
// Öffnen der Pipe zum Lesen
|
||||
printf("Client: Öffne Pipe nur zum Lesen.\n");
|
||||
id_p = open("P1", O_RDONLY);
|
||||
|
||||
if (id_p >= 0) {
|
||||
printf(" OK.\n");
|
||||
}
|
||||
|
||||
// Lesen des Geheimnisses aus der Pipe
|
||||
int result_read = read(id_p, buffer, sizeof(buffer));
|
||||
|
||||
if (result_read > 0) {
|
||||
printf(" Geheimnis erhalten.\n\n");
|
||||
}
|
||||
|
||||
// Ausgabe des Geheimnisses
|
||||
printf("Client: Das Geheimnis lautet: '%s'.\n\n", buffer);
|
||||
|
||||
printf("Client: Ende.\n");
|
||||
|
||||
return 0;
|
||||
}
|
83
Assets/Betriebssysteme_uebung/u5-a3-anlage/pipeserver.c
Normal file
83
Assets/Betriebssysteme_uebung/u5-a3-anlage/pipeserver.c
Normal file
@ -0,0 +1,83 @@
|
||||
/*
|
||||
* Interprozesskommunikation über Named Pipes (FIFOs) - pipeserver.c
|
||||
*
|
||||
* Funktionsweise: Dieses Server-Programm erwartet von seinem Client über ein
|
||||
* von ihm angelegtes benanntes Pipe eine Losung (Passwortsatz). Nach
|
||||
* (erfolgreicher) Überpruefung verrät der Server ein Geheimnis. Anschließend
|
||||
* löscht er das Pipe wieder.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/ipc.h>
|
||||
#include <string.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
|
||||
char buffer[30];
|
||||
|
||||
#define SECRET "xxx ..."
|
||||
|
||||
int main() {
|
||||
// Erzeugen der Named Pipe
|
||||
printf("Server: Erzeuge Named Pipe (FIFO).\n");
|
||||
int rw = mkfifo("P1", 0666); // erstellt PIPE bei const char *pathname hier P1, mit mode mode_t mode = 0_RDWR /erlaubt lesen und schreiben
|
||||
|
||||
if (rw >= 0) {
|
||||
printf(" OK.\n\n");
|
||||
}
|
||||
|
||||
// Öffnen der Pipe zum Lesen
|
||||
printf("Server: Öffne Pipe zum Lesen.\n");
|
||||
int id_p = open("P1", O_RDONLY);
|
||||
|
||||
if (id_p >= 0) {
|
||||
printf(" OK.\n\n");
|
||||
}
|
||||
|
||||
// Losung empfangen
|
||||
printf("Server: Warte auf Losung.\n");
|
||||
int result_read = read(id_p, buffer, sizeof(buffer));
|
||||
|
||||
if (result_read > 0) {
|
||||
printf(" Losung empfangen.\n\n");
|
||||
printf("Received: %s\n", buffer);
|
||||
}
|
||||
|
||||
// Vergleich
|
||||
if (strncmp(buffer, "<Schluessel>", strlen("<Schluessel>")) == 0) { // int strncmp(const char *str1, const char *str2, size_t n)
|
||||
printf("Server: Losung ist korrekt. Übergebe jetzt das Geheimnis.\n\n");
|
||||
|
||||
// Öffnen der Pipe zum Schreiben
|
||||
printf("Server: Öffne dazu Pipe nur zum Schreiben.\n");
|
||||
close(id_p);
|
||||
|
||||
id_p = open("P1", O_WRONLY);
|
||||
|
||||
if (id_p >= 0) {
|
||||
printf(" OK.\n");
|
||||
}
|
||||
|
||||
// Übergeben des Geheimnisses
|
||||
int result_write = write(id_p, SECRET, strlen(SECRET));
|
||||
|
||||
if (result_write > 0) {
|
||||
printf(" OK.\n\n");
|
||||
}
|
||||
}
|
||||
else {
|
||||
printf("Server: Das war falsch.\n");
|
||||
}
|
||||
|
||||
sleep(5);
|
||||
|
||||
// Vernichte Pipe
|
||||
unlink("P1");
|
||||
|
||||
printf("Server: Ende.\n");
|
||||
|
||||
return 0;
|
||||
}
|
BIN
Assets/Betriebssysteme_uebung/u5_a5.png
Normal file
BIN
Assets/Betriebssysteme_uebung/u5_a5.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 109 KiB |
7
Assets/Betriebssysteme_uebung/u6-a1-anlage/Makefile
Normal file
7
Assets/Betriebssysteme_uebung/u6-a1-anlage/Makefile
Normal file
@ -0,0 +1,7 @@
|
||||
all: syscall
|
||||
|
||||
syscall: syscall.c
|
||||
$(CC) -o syscall syscall.c
|
||||
|
||||
clean:
|
||||
rm -f syscall syscall
|
2
Assets/Betriebssysteme_uebung/u6-a1-anlage/run.sh
Normal file
2
Assets/Betriebssysteme_uebung/u6-a1-anlage/run.sh
Normal file
@ -0,0 +1,2 @@
|
||||
#!/bin/sh
|
||||
make --silent ; ./syscall ; echo "[returned $?]"
|
BIN
Assets/Betriebssysteme_uebung/u6-a1-anlage/syscall
Normal file
BIN
Assets/Betriebssysteme_uebung/u6-a1-anlage/syscall
Normal file
Binary file not shown.
105
Assets/Betriebssysteme_uebung/u6-a1-anlage/syscall.c
Normal file
105
Assets/Betriebssysteme_uebung/u6-a1-anlage/syscall.c
Normal file
@ -0,0 +1,105 @@
|
||||
/* syscall.c
|
||||
*
|
||||
* Call OS functions without using libc. The example `my_print` could be done in
|
||||
* different flavors:
|
||||
*
|
||||
* **my_print_64:**
|
||||
* Using the SYSCALL instruction (successor to SYSENTER). Should work for both
|
||||
* IA-64 and AMD64 CPUs, can be ported to `my_print_trap` (see there).
|
||||
* Register usage:
|
||||
*
|
||||
* - RAX: pass syscall number, hold return value from syscall execution
|
||||
* - RDI: syscall argument 1
|
||||
* - RSI: syscall argument 2
|
||||
* - RDX: syscall argument 3
|
||||
*
|
||||
* **my_print_trap:**
|
||||
* Using the trap interrupt to switch to kernel mode. Slower, but
|
||||
* architecturally neutral. Register usage:
|
||||
*
|
||||
* - EAX: pass syscall number, hold return value from syscall execution
|
||||
* - EBX: syscall argument 1
|
||||
* - ECX: syscall argument 2
|
||||
* - EDX: syscall argument 3
|
||||
*/
|
||||
|
||||
#include <asm/unistd.h> /* compile with -m32 for 32-bit syscall numbers,
|
||||
without for 64-bit syscall numbers. */
|
||||
/* simple inline assembler (asm) requires global symbols */
|
||||
#define __NR_exit 60
|
||||
// text buffer pointer
|
||||
char *my_print_text;
|
||||
// text buffer length, print return value
|
||||
int my_print_len, my_print_ret;
|
||||
// write() syscall number
|
||||
int call_write;
|
||||
int exitnum;
|
||||
|
||||
int my_print_64(char *text) {
|
||||
my_print_text = text;
|
||||
|
||||
/* strlen(my_print_text) manually */
|
||||
for (my_print_len = 0; my_print_text[my_print_len]; ++my_print_len);
|
||||
|
||||
/* system call signature:
|
||||
* ssize_t write(int fd, const void *buf, size_t count);
|
||||
*
|
||||
* write() system call number is defined by __NR_write
|
||||
*/
|
||||
call_write = __NR_write;
|
||||
/* stdout is file descriptor no. 1 */
|
||||
asm("mov call_write, %rax"); /* arg 0 (rax): syscall number */
|
||||
asm("mov $1, %rdi"); /* arg 1 (rdi): file descriptor */
|
||||
asm("mov my_print_text, %rsi"); /* arg 2 (rsi): buffer */
|
||||
asm("mov my_print_len, %rdx"); /* arg 3 (rdx): length */
|
||||
asm("syscall"); /* SYSCALL instruction */
|
||||
asm("mov %rax, my_print_ret"); /* save return code (rax) */
|
||||
|
||||
return my_print_ret;
|
||||
}
|
||||
|
||||
int my_print_trap(char *text) {
|
||||
/* system call signature: see my_print_64 */
|
||||
|
||||
/* TODO */
|
||||
|
||||
return 7;
|
||||
}
|
||||
|
||||
/* simple inline assembler (asm) requires global symbols */
|
||||
|
||||
// exit return value
|
||||
int my_exit_status;
|
||||
|
||||
void my_exit_64(int status) {
|
||||
my_exit_status = status;
|
||||
|
||||
/* system call signature:
|
||||
* void exit(int status);
|
||||
*
|
||||
* exit() system call number is defined by __NR_exit
|
||||
*/
|
||||
|
||||
/* TODO */
|
||||
// Syscall
|
||||
//exitnum = __NR_exit;
|
||||
//asm("mov $60, %rax"); // verwende den exitnum / 60 Syscall
|
||||
//asm("mov my_exit_status, %rdi"); //status = myexitstatus oder 0
|
||||
//asm("syscall");
|
||||
//TRAP
|
||||
asm("mov $1, %eax");
|
||||
asm("mov my_exit_status, %ebx");
|
||||
asm("int $0x80");
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
my_print_64("Hello World!\n");
|
||||
|
||||
my_exit_64(42);
|
||||
|
||||
/* never come here, if my_exit_64 works */
|
||||
return 6;
|
||||
}
|
13
Assets/Betriebssysteme_uebung/u6-a2-anlage/Makefile
Normal file
13
Assets/Betriebssysteme_uebung/u6-a2-anlage/Makefile
Normal file
@ -0,0 +1,13 @@
|
||||
CFLAGS = -g
|
||||
|
||||
all: daemon
|
||||
|
||||
clean:
|
||||
rm -f helpers.o daemon.o daemon
|
||||
|
||||
helpers.o: helpers.c helpers.h
|
||||
|
||||
daemon.o: daemon.c helpers.h
|
||||
|
||||
daemon: daemon.o helpers.o
|
||||
$(CC) -o daemon daemon.o helpers.o
|
54
Assets/Betriebssysteme_uebung/u6-a2-anlage/daemon.c
Normal file
54
Assets/Betriebssysteme_uebung/u6-a2-anlage/daemon.c
Normal file
@ -0,0 +1,54 @@
|
||||
#include <stdio.h>
|
||||
#include <signal.h>
|
||||
|
||||
#include "helpers.h"
|
||||
|
||||
void got_signal(int s) {
|
||||
/*
|
||||
* For demonstration purposes only!
|
||||
*
|
||||
* In real-world applications avoid the execution of complex
|
||||
* tasks in signal handlers, this cries for race conditions and
|
||||
* exploits.
|
||||
*
|
||||
* https://web.archive.org/web/20070204064240/http://www.bindview.com:80/
|
||||
% Services/Razor/Papers/2001/signals.cfm
|
||||
*/
|
||||
|
||||
static int count = 2;
|
||||
|
||||
fprintf(stderr, "Caught Ctrl-C, press %i more time%s to really"
|
||||
" quit.\n", count, count == 1 ? "" : "s");
|
||||
|
||||
if (count-- > 1) {
|
||||
/* Catch subsequent SIGINTs */
|
||||
signal(SIGINT, got_signal);
|
||||
}
|
||||
else {
|
||||
/* Don't catch SIGINT anymore */
|
||||
signal(SIGINT, SIG_DFL);
|
||||
}
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
/*
|
||||
* Initialization...
|
||||
*/
|
||||
void load_cfg(int x) // Wrapper um die inkompatibilität durch die fehlerhafte methodendeklaration zu beheben
|
||||
{
|
||||
load_config();
|
||||
}
|
||||
load_config();
|
||||
signal(SIGHUP, load_cfg);
|
||||
signal(SIGINT, got_signal);
|
||||
signal(SIGKILL, SIG_IGN); // Unaufhaltsam, da SIGKILL sich ja eben genau nicht um das Programm kümmern soll, sondern einfach immer töten soll
|
||||
signal(SIGTERM, SIG_IGN); // Aufhaltbar
|
||||
|
||||
/*
|
||||
* Do some work.
|
||||
*/
|
||||
|
||||
main_loop();
|
||||
|
||||
return 0;
|
||||
}
|
BIN
Assets/Betriebssysteme_uebung/u6-a2-anlage/daemon.o
Normal file
BIN
Assets/Betriebssysteme_uebung/u6-a2-anlage/daemon.o
Normal file
Binary file not shown.
103
Assets/Betriebssysteme_uebung/u6-a2-anlage/helpers.c
Normal file
103
Assets/Betriebssysteme_uebung/u6-a2-anlage/helpers.c
Normal file
@ -0,0 +1,103 @@
|
||||
/*
|
||||
* This is just some code behind the curtain.
|
||||
* You really don't need to touch this.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include "helpers.h"
|
||||
|
||||
int do_quit = 0;
|
||||
int do_reload = 0;
|
||||
|
||||
int load_config(void) {
|
||||
static char firsttime_message[] = "Loading";
|
||||
static char subsequent_message[] = "Reloading";
|
||||
static char *message = firsttime_message;
|
||||
|
||||
fprintf(stderr, "%s configuration... ", message);
|
||||
fprintf(stderr, "done.\n");
|
||||
|
||||
message = subsequent_message;
|
||||
do_reload = 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int main_loop(void) {
|
||||
fd_set rfds;
|
||||
struct timeval tv;
|
||||
int retval;
|
||||
char buf[1024];
|
||||
ssize_t len;
|
||||
|
||||
#if 0
|
||||
/* fork() to the background */
|
||||
if(fork()) return 0;
|
||||
#endif
|
||||
|
||||
fprintf(stderr, "I'm just a simple daemon, my PID is: %i\n",
|
||||
getpid());
|
||||
|
||||
while (!do_quit) {
|
||||
/*
|
||||
* Let's just do our really simple job:
|
||||
* Copy stdin to stdout (just another cat(1)).
|
||||
*/
|
||||
|
||||
if (do_reload) {
|
||||
load_config();
|
||||
}
|
||||
|
||||
/* Watch stdin (fd 0) to see when it has input. */
|
||||
FD_ZERO(&rfds);
|
||||
FD_SET(0, &rfds);
|
||||
|
||||
/* Wait up to five seconds. */
|
||||
tv.tv_sec = 5;
|
||||
tv.tv_usec = 0;
|
||||
|
||||
retval = select(1, &rfds, NULL, NULL, &tv);
|
||||
/* Don't rely on the value of tv now! */
|
||||
|
||||
if (retval == -1) {
|
||||
if (errno == EINTR) {
|
||||
continue;
|
||||
}
|
||||
|
||||
perror("select()");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (retval == 0) {
|
||||
/* fprintf(stderr, "Timeout reached.\n"); */
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!FD_ISSET(0, &rfds)) {
|
||||
fprintf(stderr, "BUG: Empty read-set, this should"
|
||||
" never happen. Never ever. Really! :-)\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
len = read(0, buf, sizeof(buf) - 1);
|
||||
if (len < 0) {
|
||||
perror("read()");
|
||||
continue;
|
||||
}
|
||||
|
||||
buf[len] = 0;
|
||||
printf("%s", buf);
|
||||
|
||||
if (len == 0) {
|
||||
do_quit = 1;
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
7
Assets/Betriebssysteme_uebung/u6-a2-anlage/helpers.h
Normal file
7
Assets/Betriebssysteme_uebung/u6-a2-anlage/helpers.h
Normal file
@ -0,0 +1,7 @@
|
||||
#ifndef _HELPERS_H
|
||||
#define _HELPERS_H
|
||||
|
||||
int load_config(void); /* load/reload our daemon's configuration */
|
||||
int main_loop(void); /* our daemon's main task */
|
||||
|
||||
#endif /* _HELPERS_H */
|
BIN
Assets/Betriebssysteme_uebung/u6-a2-anlage/helpers.o
Normal file
BIN
Assets/Betriebssysteme_uebung/u6-a2-anlage/helpers.o
Normal file
Binary file not shown.
19
Assets/Betriebssysteme_uebung/u6-a3-anlage/malloc.c
Normal file
19
Assets/Betriebssysteme_uebung/u6-a3-anlage/malloc.c
Normal file
@ -0,0 +1,19 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
int main(void) {
|
||||
int *p;
|
||||
|
||||
p = malloc(sizeof(int));
|
||||
if(p != NULL) {
|
||||
*p=99;
|
||||
printf("Allokation erfolgreich ... \n");
|
||||
}
|
||||
else {
|
||||
printf("Kein virtueller RAM mehr verfügbar ...\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
free(p);
|
||||
printf("Speicher nun erfolgreich freigegeben ");
|
||||
return EXIT_SUCCESS;
|
||||
}
|
Binary file not shown.
@ -86,7 +86,7 @@
|
||||
|
||||
\begin{document}
|
||||
\begin{myboxii}[Disclaimer]
|
||||
Die Übungen die hier gezeigt werden stammen aus der Vorlesung \textit{Betriebssysteme}! Für die Richtigkeit der Lösungen wird keine Gewähr gegeben.
|
||||
Die Übungen die hier gezeigt werden stammen aus der Vorlesung \textit{Betriebssysteme}! Für die Richtigkeit der Lösungen wird keine Gewähr gegeben. Anlagen sind im Ordner \textit{Assets/Betriebssysteme\_uebung/} zu finden.
|
||||
\end{myboxii}
|
||||
|
||||
|
||||
@ -508,7 +508,7 @@ Bei aufruf von sleep() wird der Prozess sofort alle CPU Ressourcen freigeben, al
|
||||
|
||||
Zwei Prozesse definieren (0,4,4,10) und (2,5,5,8) (Arrival,Burst,Priority,Deadline). Mit RR können die Deadlines nicht eingehalten werden.
|
||||
\begin{center}
|
||||
\includegraphics[width=0.8\linewidth]{Assets/Betriebssystem_uebung/u3_a1.png}
|
||||
\includegraphics[width=0.8\linewidth]{Assets/Betriebssysteme_uebung/u3_a1.png}
|
||||
\end{center}
|
||||
|
||||
%##########################################
|
||||
@ -606,7 +606,7 @@ Siehe auch Linux Manpages
|
||||
\end{itemize}
|
||||
\end{description*}
|
||||
\begin{center}
|
||||
\includegraphics[width=0.8\linewidth]{Assets/Betriebssystem_uebung/u4_a1.png}
|
||||
\includegraphics[width=0.8\linewidth]{Assets/Betriebssysteme_uebung/u4_a1.png}
|
||||
\end{center}
|
||||
%##########################################
|
||||
\subsection{Aufgabe 1: Das Problem des schlafenden Barbiers}
|
||||
@ -662,7 +662,7 @@ Idee: Es gibt vier Ressourcen um die sich die Kunden und der Barbier streiten. D
|
||||
}
|
||||
}
|
||||
\end{lstlisting}
|
||||
|
||||
|
||||
|
||||
%##########################################
|
||||
\subsection{Aufgabe 2: Das Achterbahnproblem}
|
||||
@ -678,8 +678,8 @@ Idee: Es gibt vier Ressourcen um die sich die Kunden und der Barbier streiten. D
|
||||
\vspace{10mm}
|
||||
|
||||
\begin{itemize}
|
||||
\item Autos: warten auf Passagiere (bis voll)
|
||||
\item Passagiere: warten auf Start/Ende
|
||||
\item Autos: warten auf Passagiere (bis voll)
|
||||
\item Passagiere: warten auf Start/Ende
|
||||
\end{itemize}
|
||||
|
||||
\begin{lstlisting}
|
||||
@ -787,9 +787,9 @@ Prozess Car()
|
||||
\vspace{10mm}
|
||||
|
||||
\begin{itemize}
|
||||
\item Lieferant: wartet auf Wartungsauftrag
|
||||
\item Kunden: wartet auf Kaffee
|
||||
\item Maschine: Portionen = 0? Wartet auf Wartung : warten auf Bestellung \& Bezahlung
|
||||
\item Lieferant: wartet auf Wartungsauftrag
|
||||
\item Kunden: wartet auf Kaffee
|
||||
\item Maschine: Portionen = 0? Wartet auf Wartung : warten auf Bestellung \& Bezahlung
|
||||
\end{itemize}
|
||||
|
||||
\begin{lstlisting}
|
||||
@ -911,26 +911,76 @@ ServiceMan{
|
||||
Prinzipiell stehen dafür nachrichtenbasierte und speicherbasierte Kommunikationsmechanismen zur Verfügung. Für welche der existierenden Mechanismen würden sie sich entscheiden, um einerseits Kontrollinformationen und andererseits Mediendatenströme auszutauschen? Begründen Sie Ihre Antwort.\\
|
||||
Hinweis: Klären Sie zuerst, was die prinzipiellen Vor- und Nachteile dieser beiden Kommunikationsvarianten sind. Betrachten Sie anschließend die Kommunikationsmuster und Anforderungen der beiden Klassen (Kontroll- und Multimediadaten), bevor Sie eine Empfehlung geben.
|
||||
}
|
||||
\begin{itemize}
|
||||
\item \textbf{Message Passing vs. Shared Memory} Shared Memory hat die wünschenswerte Eigenschaft, dass die gesamte Kommunikation über implizites Laden und Speichern in einem globalen Adressraum erfolgt.
|
||||
Eine weitere grundlegende Eigenschaft von Shared Memory ist, dass Synchronisation und Kommunikation getrennt sind. Zusätzlich zu den Lade- und Speicheroperationen müssen spezielle Synchronisationsoperationen (Mechanismen) verwendet werden, um zu erkennen, wann Daten produziert und/oder konsumiert wurden.
|
||||
Im Gegensatz dazu wird beim Message Passing ein explizites Kommunikationsmodell verwendet. Explizite Nachrichten werden zwischen den Prozessen ausgetauscht.
|
||||
Synchronisation und Kommunikation sind im Message Passing vereint. Die Erzeugung von entfernten, asynchronen Ereignissen ist ein integraler Bestandteil des Message-Passing-Kommunikationsmodells.
|
||||
Es ist jedoch wichtig, darauf hinzuweisen, dass Shared-Memory- und Message-Passing-Kommunikationsmodelle universell sind, d.h., es ist möglich, das eine zu verwenden, um das andere zu simulieren.
|
||||
Es ist jedoch zu beobachten, dass es einfacher ist, Shared Memory mit Message Passing zu simulieren als umgekehrt.
|
||||
Das liegt im Wesentlichen an der asynchronen Ereignissemantik von Message Passing im Vergleich zur Polling-Semantik des Shared Memory.
|
||||
Das Shared-Memory-Kommunikationsmodell ermöglicht es dem Programmierer, sich auf die mit der Parallelität verbundenen Probleme zu konzentrieren, indem er von den Details der Interprozessorkommunikation entlastet wird.
|
||||
In diesem Sinne stellt das Shared-Memory-Kommunikationsmodell eine geradlinige Erweiterung des Uniprozessor-Programmierparadigmas dar. Darüber hinaus ist die Shared-Memory-Semantik unabhängig vom physikalischen Speicherort und daher offen für die dynamische Optimierung, die das zugrunde liegende Betriebssystem bietet.
|
||||
Auf der anderen Seite ist das Shared-Memory-Kommunikationsmodell im Wesentlichen eine Polling-Schnittstelle. Dies ist ein Nachteil, was die Synchronisation betrifft.
|
||||
Die Nachrichtenweitergabe kann als ein interruptgesteuertes Kommunikationsmodell charakterisiert werden. Bei der Nachrichtenübermittlung enthalten die Nachrichten sowohl Daten als auch Synchronisation in einer einzigen Einheit. Als solches eignet sich das Message-Passing-Kommunikationsmodell für Betriebssystemaktivitäten, bei denen die Kommunikationsmuster im Voraus explizit bekannt sind, z. B. E/A, Interprozessor-Interrupts und Task- und Datenmigration.
|
||||
Auf der anderen Seite leidet das Message Passing unter der Notwendigkeit von Marshaling-Kosten, d. h. den Kosten für das Assemblieren und Disassemblieren der Nachricht.
|
||||
Eine natürliche Schlussfolgerung aus der obigen Diskussion ist, dass sich Shared-Memory- und Message-Passing-Kommunikationsmodelle jeweils für bestimmte Anwendungsdomänen eignen. Shared Memory bietet sich für Anwendungsentwickler an, während Message Passing sich für Betriebssystementwickler anbietet.
|
||||
Es ist daher naheliegend, die Kombination von Shared Memory und Message Passing in Mehrzweck-Multiprozessorsystemen in Betracht zu ziehen. Dies war die Hauptantriebskraft hinter Systemen wie dem Stanford FLexible Architecture for SHared memory (FLASH) System. Dabei handelt es sich um ein Multiprozessorsystem, das die Unterstützung für Shared Memory und Message Passing effizient integriert und dabei sowohl den Hardware- als auch den Software-Overhead minimiert.
|
||||
\item \textbf{Nachrichtenbasiert}: + kein Blockieren bei asynchroner Variante, einfache, einheitliche Nutzung (Architekturunabhängig), keine Synchronisation nötig, - ggf. kopieren der Daten (ineffizient und redundant), Blockieren bei der synchronen Variante, ~ unidirektional (geht nur in eine Richtung)
|
||||
\item \textbf{Speicherbasiert}: + Ideal für große Dateien, da annähernd Verzögerungsfrei, nicht blockierend (im allgemeinen), kann bidirektional verwendet werden - Synchronisation notwenig, gemeinsamer Speicher, also nicht für verteilte Systeme geeignet, aufwändiger bei korrekter Implementierung
|
||||
\item Idee: Verwenden von Speicherbasierterkommunikation für die großen Video \& Audiodateien, da man diese nur schwer \& insbesondere langsam per Nachrichten verschicken kann. Verwendung von Messaepassing für Kontroolldatenströme
|
||||
\end{itemize}
|
||||
|
||||
\vspace{10mm}
|
||||
\paragraph{Frage 2: Synchronisation durch Semaphore}
|
||||
\textit{Bei asynchroner nachrichtenbasierter Kommunikation kommen stets Warteschlangen zum Einsatz, um unterschiedliche Geschwindigkeiten der Sender- und Empfängerprozesse auszugleichen. Der Zugriff auf diese Warteschlangen muss aus verschiedenen Gründen durch Synchronisationsmechanismen (z. B. Semaphore) geregelt werden. Was sind diese Gründe und weshalb sind insgesamt drei Semaphore pro Warteschlange notwendig?}
|
||||
|
||||
Benötigen drei Semaphore: Schreibzugriff auf volle Schlange, Lesezugriff auf leere Schlange, und Zugriffskontrolle.
|
||||
|
||||
\vspace{10mm}
|
||||
\paragraph{Frage 3: Synchronisationsvarianten bei nachrichtenbasierter Kommunikation}
|
||||
\textit{Welche Nachteile asynchroner Kommunikation treten beim Einsatz synchroner Varianten der Sende- und Empfangsoperationen nicht auf? Warum ist es trotzdem manchmal sinnvoll oder unumgänglich, die asynchronen Varianten einzusetzen? Nennen Sie mindestens drei Beispiele realer Applikationen, in denen asynchron kommuniziert wird.}
|
||||
\begin{itemize}
|
||||
\item Pufferspeicher: Größe?
|
||||
\item eventuell Synchronisation notwendig. (z.B. für Datenströme), eventuell extra Techniken zur Benachrichtigung oder Synchronisation
|
||||
\item Trotzdem notwenig wenn:
|
||||
\begin{itemize}
|
||||
\item Hoher Grad an Parallelität notwendig
|
||||
\item Ereignisse sporadisch oder unvorhersehbar (hier synchrones Warten ineffizient)
|
||||
\item Auf Ereignisse zeitnah reagiert werden muss (z.B. in Echtzeitsystemen)
|
||||
\end{itemize}
|
||||
\item Es wird beispielsweise in E-Mails, Whatsapp, SMS asynchron kommuniziert
|
||||
\end{itemize}
|
||||
|
||||
\vspace{10mm}
|
||||
\paragraph{Frage 4: Management asynchroner Ereignisse}
|
||||
\textit{Welche Alternativen haben die Entwickler von Betriebssystemen, um mit asynchron auftretenden
|
||||
Ereignissen (Mausbewegungen, Einstecken von USB-Geräten etc.) umzugehen? Welche Technik
|
||||
erlaubt es auch einem Benutzerprozess, auf asynchrone Ereignisse zu reagieren, ohne direkten
|
||||
Hardwarezugriff zu haben?}
|
||||
\textit{Welche Alternativen haben die Entwickler von Betriebssystemen, um mit asynchron auftretenden Ereignissen (Mausbewegungen, Einstecken von USB-Geräten etc.) umzugehen? Welche Technik erlaubt es auch einem Benutzerprozess, auf asynchrone Ereignisse zu reagieren, ohne direkten Hardwarezugriff zu haben?}
|
||||
\begin{itemize}
|
||||
\item Busy Waiting (Warte in Endlosschleife / sehr ineffizient)
|
||||
\item Polling (Wahl der Zykluszeit)
|
||||
\item Interrupts (HW-Signal, Behandlung über Routine aus IVT)
|
||||
\begin{itemize}
|
||||
\item inline-Prozeduraufruf
|
||||
\item IPC über Botschaften
|
||||
\item pop-up threads
|
||||
\end{itemize}
|
||||
\item Hierzu gibt es die Möglichkeit, dass der Prozessor alle Eingabe-Geräte zyklisch abfragt (Polling). Was bei der Vielzahl an Komponenten in einem Computer bedeuten würde, dass der Prozessor mit nichts anderem mehr beschäftigt wäre.
|
||||
\item Eine Alternative ist die sogenannten Unterbrechungsanforderung (to interrupt, unterbrechen), die dann eintritt, wenn Daten von außen anstehen. Dazu wurde die Möglichkeit geschaffen den Hauptprozessor auf definierte Weise bei der laufenden Arbeit zu unterbrechen.
|
||||
\item Auf Anwendungsebene: Registrieren von eigenen Signalhandlern
|
||||
\end{itemize}
|
||||
|
||||
%##########################################
|
||||
\subsection{Aufgabe 1: Nachrichtenwarteschlangen (Message Queues)}
|
||||
\textit{a) Recherchieren Sie die Funktionsweise, Charakteristiken und Eigenschaften von Message Queues. Wie wird der Kontrollfluss der Prozesse dabei durch das Betriebssystem gesteuert (z. B. Synchronisation durch Blockierungen, durch die Ankunft von Daten usw.)?}
|
||||
\vspace{10mm}
|
||||
\begin{itemize}
|
||||
\item Hier werden Nachrichten von einem Prozess in eine Nachrichtenschlange (Message Queue) geschickt, welche typischerweise nach dem FIFO Prinzip arbeitet. Jede Messagequeue ist durch einen eindeutigen Bezeichner gekennzeichnet, unidirektional und mit festgelegtem Format.
|
||||
\item MessageType: Unterscheidung innerhalb der Warteschlange möglich.
|
||||
\item send blockiert, wenn die Queue voll ist.
|
||||
\item receive blockiert, wenn keine Nachricht mit dem spezifizierten Type in der Queue ist.
|
||||
\item Es gibt auch eine nichtblockierende Variante. (IPC-NOWAIT)
|
||||
\item Das Einsatzgebiet der Warteschlangen ist typischerweise die Datenübergabe zwischen asynchronen Prozessen in verteilten Systemen.
|
||||
\end{itemize}
|
||||
|
||||
\textit{b) In der Anlage zu dieser Übungsaufgabe (u4-a1-anlage) befinden sich ein Server- und ein Client-Programm, die beide Lücken enthalten. Vervollständigen und übersetzen Sie die Programme. Starten Sie anschließend zuerst den Server und dann den Client. Falls Sie die Lücken richtig ausgefüllt haben, muss das Client-Programm ein "Passwort" an den Server senden und anschließend ein Geheimnis ausgeben, das es vom Server als Antwort erhalten hat.}
|
||||
\vspace{10mm}
|
||||
@ -939,6 +989,19 @@ ServiceMan{
|
||||
\subsection{Aufgabe 2: Gemeinsamer Speicher (Shared Memory)}
|
||||
\textit{a) Recherchieren Sie die Funktionsweise, Charakteristiken und Eigenschaften von Shared Memory. Wie wird der Kontrollfluss der Prozesse dabei durch das Betriebssystem gesteuert? (Wann und wodurch erfolgt eine Synchronisation?)}
|
||||
\vspace{10mm}
|
||||
\begin{itemize}
|
||||
\item Shared Memory ist eine durch das Betriebssystem bereitgestellte Möglichkeit, bei welcher mehrere Prozesse gemeinsam auf einen gemeinsamen Speicher zugreifen können. Um dies zu erreichen muss zuerst ein gemeinsamer Datenspeicher angelegt werden. Nachdem dies geschehen ist, muss der Datenspeicher den Prozessen bekanntgemacht werden (einfügen in deren Adressraum), welche darauf zugreifen sollen dürfen.
|
||||
\item Wichtig hierbei: Shared Memory ist eine der schnellsten, wenn nicht die schnellste Art der Interprozesskommunikation, da das Kopieren/Versenden zwischen Clients/Server, bzw. verschiedenen Prozessen entfällt.
|
||||
\item Da man allerdings gemeinsam auf Speicher zugreift, ist eine Synchronisation, meist durch Semaphore oder Monitore unumgänglich.
|
||||
\item shmat() fügt das durch shmid identifizierte Speichersegment an den Adressraum des aufrufenden Prozesses an. Die Anfügeadresse wird durch shmaddr spezifiziert.
|
||||
\item Weiterhin gibt es Einschränkungen bezüglich der Rechte im shmflg Bitmaskargument:
|
||||
\begin{itemize}
|
||||
\item $SHM\_EXEC$: erlaubt eine Ausführung der Segmentinhalte
|
||||
\item $SHM\_RDONLY$: Fall gesetzt, so ist das Segment nur für den Lesezugriff angehängt, ist es nicht gesetzt, so ist Lesen und Schreiben erlaubt.
|
||||
\item $SHM\_REMAP$. Dieses Flag spezifiziert, dass das Mapping des Segments alle bisherigen Mappings im Bereich von shmaddr und den folgenden dateigrößelangen Segment ersetzt.
|
||||
\end{itemize}
|
||||
\item Ist shmat() erfolgreich, so wird $shm\_atime$ auf die jetzige Zeit gesetzt, $shm\_lpid$ auf die ProzessID des aufrufenden Prozess gesetzt und $shm\_nattch$ wird um 1 erhöht.
|
||||
\end{itemize}
|
||||
|
||||
\textit{b) In der Anlage zu dieser Übungsaufgabe (u4-a2-anlage) befinden sich ein Server- und ein Client-Programm, die beide Lücken enthalten. Vervollständigen und übersetzen Sie die Programme. Starten Sie anschließend zuerst den Server und dann den Client. Falls Sie die Lücken richtig ausgefüllt haben, muss das Client-Programm ein "Passwort" an den Server senden und anschließend ein Geheimnis ausgeben, das es vom Server als Antwort erhalten hat.\\
|
||||
Hinweis: An den verwendeten Semaphoroperationen sind keine Änderungen notwendig.}
|
||||
@ -948,7 +1011,18 @@ ServiceMan{
|
||||
\subsection{Aufgabe 3: Benannte Pipes (Named Pipes, FIFOs)}
|
||||
\textit{a) Recherchieren Sie die Funktionsweise, Charakteristiken und Eigenschaften von Pipes und Named Pipes. Wie wird der Kontrollfluss der Prozesse dabei durch das Betriebssystem gesteuert? (Wann und wodurch erfolgt eine Synchronisation?)}
|
||||
\vspace{10mm}
|
||||
|
||||
\begin{itemize}
|
||||
\item Pipes stellen einen unidirektionalen Kommunikationskanal zwischen zwei Prozessen dar. Ein Pipe hat ein Lese- und ein Schreibende, wobei man am Leseende diejenigen Daten lesen kann, welche zuvor auf das Schreibende geschrieben wurden.
|
||||
\item Wenn ein Prozess von einer leeren Pipe lesen will, so blockiert read() bis Daten vorhanden sind. Wenn ein Prozess versucht auf eine volle Pipe zu schreiben, dann wird write() solange blockieren, bis wieder genügend Platz auf der Pipe frei ist.
|
||||
\item Die über Pipes ablaufende Kommunikation ist nachrichtenfrei und bytestromorientiert.
|
||||
\item Eine Pipe hat eine begrenzte Kapazität
|
||||
\item POSIX besagt, dass write() von weniger als $PIPE\_BUF$ Bytes atomar sein muss, bei mehr als $Pipe\_BUF$ Bytes kann es auch nichtatomar sein.
|
||||
\begin{center}
|
||||
\includegraphics[width=0.5\linewidth]{Assets/Betriebssysteme_uebung/u5_a5.png}
|
||||
\end{center}
|
||||
\item Named Pipes / FIFO sind einer Pipe sehr ähnlich, mit dem kleinen Unterschied, dass darauf als Teil des Dateisystems zugegriffen wird. Wenn Prozesse jetzt Daten über FIFO austauschen, dann behandelt der Kernel alle Daten intern ohne sie in das Dateisystem zu schreiben. Somit dienen die FIFO Dateien im Filesystem nur als Referenzpunkt und Namen und geben Prozessen Informationen darüber, an welcher Stelle Prozesse auf die Pipe zugreifen können. Dies bedeutet aber auch, dass hier im Gegensatz zu unbenannten Pipes Prozesse miteinander kommunizieren können, die nicht miteinander verwandt sind.
|
||||
\item Der Kernel verwaltet genau ein Pipe-Objekt für jede FIFO-Spezialdatei, die von mindestens einem Prozess geöffnet wird. Das FIFO muss an beiden Enden (lesend und schreibend) geöffnet werden, bevor Daten übergeben werden können. Normalerweise wird auch das Öffnen der FIFO-Blöcke bis zum anderen Ende geöffnet.
|
||||
\end{itemize}
|
||||
|
||||
\textit{b) In der Anlage zu dieser Übungsaufgabe (u4-a3-anlage) befinden sich ein Server- und ein Client-Programm, die beide Lücken enthalten. Vervollständigen und übersetzen Sie die Programme. Starten Sie anschließend zuerst den Server und dann den Client. Falls Sie die Lücken richtig ausgefüllt haben, muss das Client-Programm ein "Passwort" an den Server senden und anschließend ein Geheimnis ausgeben, das es vom Server als Antwort erhalten hat.}
|
||||
\vspace{10mm}
|
||||
|
Loading…
Reference in New Issue
Block a user