From 00dff9b1d9c3a20ce4b091e092d38f913479a5ac Mon Sep 17 00:00:00 2001 From: Stefan Zabka Date: Wed, 13 May 2020 12:16:35 +0200 Subject: Added all given material for exercise 2 --- 02_exercise/Makefile | 32 ++++++ 02_exercise/array.c | 59 ++++++++++ 02_exercise/array.h | 142 +++++++++++++++++++++++++ 02_exercise/beispiele/dup/Makefile | 18 ++++ 02_exercise/beispiele/dup/dup.c | 51 +++++++++ 02_exercise/beispiele/fork_example/Makefile | 18 ++++ 02_exercise/beispiele/fork_example/fork.c | 36 +++++++ 02_exercise/beispiele/fork_signal/Makefile | 19 ++++ 02_exercise/beispiele/fork_signal/fork.c | 39 +++++++ 02_exercise/beispiele/fork_signal/signal.c | 21 ++++ 02_exercise/beispiele/pipe_bidirect/Makefile | 19 ++++ 02_exercise/beispiele/pipe_bidirect/log.txt | 5 + 02_exercise/beispiele/pipe_bidirect/pingpong.c | 124 +++++++++++++++++++++ 02_exercise/beispiele/pipe_example/Makefile | 18 ++++ 02_exercise/beispiele/pipe_example/pipe.c | 60 +++++++++++ 02_exercise/prog.c | 51 +++++++++ 02_exercise/shell.c | 16 +++ 17 files changed, 728 insertions(+) create mode 100644 02_exercise/Makefile create mode 100644 02_exercise/array.c create mode 100644 02_exercise/array.h create mode 100644 02_exercise/beispiele/dup/Makefile create mode 100644 02_exercise/beispiele/dup/dup.c create mode 100644 02_exercise/beispiele/fork_example/Makefile create mode 100644 02_exercise/beispiele/fork_example/fork.c create mode 100644 02_exercise/beispiele/fork_signal/Makefile create mode 100644 02_exercise/beispiele/fork_signal/fork.c create mode 100644 02_exercise/beispiele/fork_signal/signal.c create mode 100644 02_exercise/beispiele/pipe_bidirect/Makefile create mode 100644 02_exercise/beispiele/pipe_bidirect/log.txt create mode 100644 02_exercise/beispiele/pipe_bidirect/pingpong.c create mode 100644 02_exercise/beispiele/pipe_example/Makefile create mode 100644 02_exercise/beispiele/pipe_example/pipe.c create mode 100644 02_exercise/prog.c create mode 100644 02_exercise/shell.c diff --git a/02_exercise/Makefile b/02_exercise/Makefile new file mode 100644 index 0000000..c43966f --- /dev/null +++ b/02_exercise/Makefile @@ -0,0 +1,32 @@ +#!/usr/bin/make +.SUFFIXES: +.PHONY: all run pack clean +TAR = shell prog +SRC = $(wildcard *.c) +OBJ = $(SRC:%.c=%.o) +PCK = lab-2.zip + +CFLAGS = -std=gnu11 -c -g -Os -Wall -Werror -MMD -MP + +DEP = $(OBJ:%.o=%.d) +-include $(DEP) + +%.o: %.c + $(CC) $(CFLAGS) $< -o $@ + +all: $(TAR) + +prog: prog.o + $(CC) -o $@ $^ + +shell: $(filter-out prog.o,$(OBJ)) + $(CC) -o $@ $^ + +run: all + ./shell + +pack: + zip $(PCK) $(SRC) $(wildcard *.h) Makefile + +clean: + $(RM) $(RMFILES) $(TAR) $(OBJ) $(DEP) $(PCK) diff --git a/02_exercise/array.c b/02_exercise/array.c new file mode 100644 index 0000000..4f08366 --- /dev/null +++ b/02_exercise/array.c @@ -0,0 +1,59 @@ +/***************************************************************************//** + * @file array.c + * @author Dorian Weber + * @brief Implementation des generalisierten Arrays. + ******************************************************************************/ + +#include "array.h" +#include +#include +#include + +/* ********************************************************* public functions */ + +/* (die runden Klammern um einige Funktionsnamen sind notwendig, da Makros + * gleichen Namens existieren und der Präprozessor diese expandieren würde) */ + +void* (arrayInit)(size_t capacity, size_t size) { + ArrayHdr *hdr = malloc(sizeof(*hdr) + size*capacity); + + if (hdr == NULL) + return NULL; + + hdr->len = 0; + hdr->cap = capacity; + + return hdr + 1; +} + +void arrayRelease(void* self) { + free(((ArrayHdr*) self) - 1); +} + +void* (arrayPush)(void* self, size_t size) { + ArrayHdr *hdr = ((ArrayHdr*) self) - 1; + + if (hdr->len == hdr->cap) { + hdr->cap *= 2; + hdr = realloc(hdr, sizeof(*hdr) + size*hdr->cap); + + if (hdr == NULL) { + fputs("program ran out of heap memory\n", stderr); + exit(-1); + } + } + + ++hdr->len; + return hdr + 1; +} + +void (arrayPop)(void* self) { + ArrayHdr *hdr = ((ArrayHdr*) self) - 1; + assert(hdr->len > 0); + --hdr->len; +} + +/* Symbol für die Inline-Funktionen erzeugen und aus diesem Modul exportieren */ +extern void arrayClear(void* self); +extern int arrayIsEmpty(const void* self); +extern size_t arrayLen(const void* self); diff --git a/02_exercise/array.h b/02_exercise/array.h new file mode 100644 index 0000000..78d21c9 --- /dev/null +++ b/02_exercise/array.h @@ -0,0 +1,142 @@ +/***************************************************************************//** + * @file array.h + * @author Dorian Weber + * @brief Generischer Array-Typ. + * + * @details + * Diese Datei enthält die Schnittstelle eines generischen Array-Typs. Um den + * Element-Typ des Arrays festzulegen, deklariert man eine Variable als Zeiger + * auf den Element-Typ. Diese kann danach initialisiert und so benutzt werden, + * als wäre sie ein Zeiger auf ein Array variabler Länge. + * + * Hier ist ein Nutzungsbeispiel: + * @code + * int *array; + * + * arrayInit(array); + * arrayPush(array) = 1; + * arrayPush(array) = 2; + * arrayPush(array) = 3; + * + * while (!arrayIsEmpty(array)) + * printf("%i\n", arrayPop(array)); + * + * arrayRelease(array); + * @endcode + * + * Viele der genutzten Funktionen sind in Form von Makros implementiert, die die + * Variable, die den Zeiger auf das Array hält, aktualisieren, obwohl es so + * aussieht, als würde sich dieser Wert niemals ändern. Das macht es riskant + * mehr als einen Zeiger auf das Array zu halten, da jede Vergrößerung des + * Arrays alle Zeiger auf und in das Array potentiell invalidiert. Obwohl diese + * Fehlerquelle subtil und äußerst schwer diagnostizierbar ist - es ist eine + * extrem einfache Abstraktion - führt diese Datenstruktur meiner Meinung nach + * trotzdem zu einem massiven Produktivitätsgewinn; man sollte sich nur in etwa + * im Klaren über die unterliegende Implementation sein, um genau die Untermenge + * von Programmiertechniken auszuwählen, die korrekt funktioniert. + ******************************************************************************/ + +#ifndef ARRAY_H_INCLUDED +#define ARRAY_H_INCLUDED + +/* *** includes ************************************************************* */ + +#include + +/* *** structures *********************************************************** */ + +/**@brief Arrayheader. + * + * Diese Struktur wird jedem Array im Speicher vorangestellt und beinhaltet + * Informationen über Kapazität und aktuelle Auslastung des Arrays. Die + * Arrayelemente schließen sich dieser Struktur unmittelbar an, so dass der + * Nutzer von dieser versteckten Information nichts bemerkt. + */ +typedef struct ArrayHdr +{ + size_t len; /**<@brief Anzahl der Array-Elemente. */ + size_t cap; /**<@brief Kapazität des reservierten Speichers. */ +} ArrayHdr; + +/* *** interface ************************************************************ */ + +/**@internal + * @brief Initialisiert und gibt einen Zeiger auf den Start des Arrays zurück. + * @param capacity initiale Kapazität + * @param size Größe der Arrayelemente + * @return ein Zeiger auf den Start des Arrays, falls erfolgreich,\n + * \c NULL im Falle eines Speicherfehlers + */ +extern void* arrayInit(size_t capacity, size_t size); + +/**@brief Initialisiert ein neues Array. + * @param self das Array + * @return 0, falls keine Fehler bei der Initialisierung aufgetreten sind,\n + * -1 ansonsten + */ +#define arrayInit(self) \ + ((self = arrayInit(8, sizeof((self)[0]))) == NULL ? -1 : 0) + +/**@brief Gibt das Array und alle assoziierten Strukturen frei. + * @param self das Array + */ +extern void arrayRelease(void* self); + +/**@internal + * @brief Reserviert Platz für einen neuen Wert im Array. + * @param self das Array + * @param size Größe der Arrayelemente + * @return der neue Zeiger auf den Start des Arrays + */ +extern void* arrayPush(void* self, size_t size); + +/**@brief Legt einen Wert auf das Array. + * @param self das Array + */ +#define arrayPush(self) \ + (self = arrayPush(self, sizeof((self)[0])), (self)+arrayLen(self)-1)[0] + +/**@brief Entfernt das oberste Element des Arrays. + * @param self das Array + */ +extern void arrayPop(void* self); + +/**@brief Entfernt und liefert das oberste Element des Arrays. + * @param self das Array + * @return das oberste Element von \p self + */ +#define arrayPop(self) \ + (arrayPop(self), (self)+arrayLen(self))[0] + +/**@brief Gibt das oberste Element des Arrays zurück. + * @param self das Array + * @return das oberste Element von \p self + */ +#define arrayTop(self) \ + (self)[arrayLen(self) - 1] + +/**@brief Setzt die Länge des Arrays auf 0 zurück. +* @param self das Array +*/ +inline void arrayClear(void* self) { + ((ArrayHdr*) self)[-1].len = 0; +} + +/**@brief Gibt zurück, ob das Array leer ist. + * @param self das Array + * @return 0, falls nicht leer\n + 1, falls leer + */ +inline int arrayIsEmpty(const void* self) { + return ((ArrayHdr*) self)[-1].len == 0; +} + +/**@brief Gibt die Anzahl der Array-Elemente zurück. + * @param self das Array + * @return Anzahl der Elemente des Arrays + */ +inline size_t arrayLen(const void* self) { + return ((ArrayHdr*) self)[-1].len; +} + +#endif /* ARRAY_H_INCLUDED */ diff --git a/02_exercise/beispiele/dup/Makefile b/02_exercise/beispiele/dup/Makefile new file mode 100644 index 0000000..0f73ac4 --- /dev/null +++ b/02_exercise/beispiele/dup/Makefile @@ -0,0 +1,18 @@ +#!/usr/bin/make +.SUFFIXES: + +CFLAGS = -c -Os -Wall -Werror + +%.o: %.c + $(CC) $(CFLAGS) $^ -o $@ + +%: %.o + $(CC) -o $@ $^ + +all: dup + +run: all + ./dup + +clean: + $(RM) $(RMFILES) dup diff --git a/02_exercise/beispiele/dup/dup.c b/02_exercise/beispiele/dup/dup.c new file mode 100644 index 0000000..ea3c5a6 --- /dev/null +++ b/02_exercise/beispiele/dup/dup.c @@ -0,0 +1,51 @@ +#include +#include +#include + +/* https://www.youtube.com/watch?v=gaXigSu72A4 */ +static inline void die(const char* msg) { + perror(msg); + exit(EXIT_FAILURE); +} + +int main(int argc, char **argv) { + int pdes[2]; + pid_t child; + + if (pipe(pdes) < 0) + die("pipe()"); + + if ((child = fork()) < 0) + die("fork()"); + + if (child == 0) { + /* child process */ + close(pdes[0]); + close(1); /* close stdout */ + + if (dup(pdes[1]) < 0) + die("dup()"); + + /* now stdout and pdes[1] are equivalent (dup returns lowest free descriptor) */ + if (execlp("cat", "cat", "/etc/passwd", NULL) < 0) + die("execlp()"); + + exit(EXIT_SUCCESS); + } + else { + /* parent process */ + close(0); /* close stdin */ + close(pdes[1]); + + if (dup(pdes[0]) < 0) + die("dup()"); + + /* now stdin and pdes[0] are equivalent (dup returns lowest free descriptor) */ + if (execlp("wc", "wc", "-l", NULL) < 0) + die("execlp()"); + + exit(EXIT_SUCCESS); + } + + return 0; +} diff --git a/02_exercise/beispiele/fork_example/Makefile b/02_exercise/beispiele/fork_example/Makefile new file mode 100644 index 0000000..8f69ed9 --- /dev/null +++ b/02_exercise/beispiele/fork_example/Makefile @@ -0,0 +1,18 @@ +#!/usr/bin/make +.SUFFIXES: + +CFLAGS = -c -Os -Wall -Werror + +%.o: %.c + $(CC) $(CFLAGS) $^ -o $@ + +%: %.o + $(CC) -o $@ $^ + +all: fork + +run: all + ./fork + +clean: + $(RM) $(RMFILES) fork diff --git a/02_exercise/beispiele/fork_example/fork.c b/02_exercise/beispiele/fork_example/fork.c new file mode 100644 index 0000000..7377b0f --- /dev/null +++ b/02_exercise/beispiele/fork_example/fork.c @@ -0,0 +1,36 @@ +#include +#include +#include +#include + +int main(void) { + pid_t proc_id; + int status = 0; + + proc_id = fork(); + + if (proc_id < 0) { + fprintf(stderr, "fork error\n"); + fflush(stderr); + return EXIT_FAILURE; + } + + if (proc_id == 0) { + /* child process */ + printf("[child] process id: %d\n", (int) getpid()); + + char* args[] = {"sleep", "1", NULL}; + execvp(args[0], args); + exit(-1); + } + else { + /* parent */ + printf("[parent] process id: %d\n", (int) getpid()); + pid_t child_id = wait(&status); + + printf("[parent] child %d returned: %d\n", + child_id, WEXITSTATUS(status)); + } + + return EXIT_SUCCESS; +} diff --git a/02_exercise/beispiele/fork_signal/Makefile b/02_exercise/beispiele/fork_signal/Makefile new file mode 100644 index 0000000..499b8cb --- /dev/null +++ b/02_exercise/beispiele/fork_signal/Makefile @@ -0,0 +1,19 @@ +#!/usr/bin/make +.SUFFIXES: + +CFLAGS = -c -O0 + +%.o: %.c + $(CC) $(CFLAGS) $^ -o $@ + +%: %.o + $(CC) -o $@ $^ + +all: fork signal + +run: all + ./fork 10 + ./signal + +clean: + $(RM) $(RMFILES) fork signal diff --git a/02_exercise/beispiele/fork_signal/fork.c b/02_exercise/beispiele/fork_signal/fork.c new file mode 100644 index 0000000..b144577 --- /dev/null +++ b/02_exercise/beispiele/fork_signal/fork.c @@ -0,0 +1,39 @@ +#include +#include +#include +#include +#include + +int main(int argc, const char* argv[]) { + pid_t pid; + int h; + int count; + + if (argc < 2) { + printf("Usage: ./fork process#\n"); + return 0; + } + + count = atoi(argv[1]) - 1; + printf("Process#: %d\n", count + 1); + printf("Parent started\n"); + + pid = 1; + for (h = 0; h < count; ++h) { + if ((pid = fork()) < 0) + printf("fork error\n"); + else if (pid == 0) { + printf("Child %d started\n", h); + sleep(1); + printf("Child %d finished\n", h); + exit(0); + } + } + + printf("Parent waiting\n"); + for (h = 0; h < count; ++h) { + wait(NULL); + } + printf("Parent finished\n"); + return EXIT_SUCCESS; +} diff --git a/02_exercise/beispiele/fork_signal/signal.c b/02_exercise/beispiele/fork_signal/signal.c new file mode 100644 index 0000000..500d2e1 --- /dev/null +++ b/02_exercise/beispiele/fork_signal/signal.c @@ -0,0 +1,21 @@ +#include +#include +#include +#include + +void sig_handler(int signo) { + const char msg[] = "Speicherzugriffsfehler!\n"; + write(2, msg, sizeof(msg) - 1); + exit(-1); +} + +int main(void) { + if (signal(SIGSEGV, sig_handler) == SIG_ERR) { + perror("signal"); + exit(-1); + } + + int array[1] = {0}; + array[1000000] = 3; + return 0; +} diff --git a/02_exercise/beispiele/pipe_bidirect/Makefile b/02_exercise/beispiele/pipe_bidirect/Makefile new file mode 100644 index 0000000..361ac97 --- /dev/null +++ b/02_exercise/beispiele/pipe_bidirect/Makefile @@ -0,0 +1,19 @@ +#!/usr/bin/make +.SUFFIXES: + +CFLAGS = -c -Os -Wall -Werror + +%.o: %.c + $(CC) $(CFLAGS) $^ -o $@ + +%: %.o + $(CC) -o $@ $^ + +all: pingpong + +run: all + @echo "writing into log.txt; stop execution with CTRL+C" + ./pingpong + +clean: + $(RM) $(RMFILES) pingpong diff --git a/02_exercise/beispiele/pipe_bidirect/log.txt b/02_exercise/beispiele/pipe_bidirect/log.txt new file mode 100644 index 0000000..fbdc2c7 --- /dev/null +++ b/02_exercise/beispiele/pipe_bidirect/log.txt @@ -0,0 +1,5 @@ +[15234]: ping +[15233]: pong +[15234]: ping +[15233]: pong +Programmabbruch durch Nutzer diff --git a/02_exercise/beispiele/pipe_bidirect/pingpong.c b/02_exercise/beispiele/pipe_bidirect/pingpong.c new file mode 100644 index 0000000..ac5eb1c --- /dev/null +++ b/02_exercise/beispiele/pipe_bidirect/pingpong.c @@ -0,0 +1,124 @@ +#include +#include +#include +#include + +static int split(int fd[2]); +static int redir(int fd, const char* file); +static void sig_handler(int sig); + +static int parent; + +int main(void) { + ssize_t bytes; + char buf[256]; + int fd[2]; + + // disable buffering for stdout + setvbuf(stdout, NULL, _IONBF, 0); + + // redirect stdout to write into a file + if (redir(1, "log.txt") < 0) { + perror("redir()"); + exit(-1); + } + + // install the signal handler for Ctrl+C + if (signal(SIGINT, sig_handler) == SIG_ERR) { + perror("signal()"); + exit(-1); + } + + // split processes and assign file descriptors for communication + switch (split(fd)) { + case -1: // error + exit(-1); + + case 0: // child + while ((bytes = read(fd[0], buf, sizeof(buf))) > 0) { + printf("[%u]: %s\n", getpid(), buf); + sleep(1); + write(fd[1], "pong", 5); + } + break; + + case 1: // parent + parent = 1; + write(fd[1], "ping", 5); + while ((bytes = read(fd[0], buf, sizeof(buf))) > 0) { + printf("[%u]: %s\n", getpid(), buf); + sleep(1); + write(fd[1], "ping", 5); + } + break; + } + + // close the file descriptors + close(fd[0]); + close(fd[1]); + + return 0; +} + +int split(int fd[2]) { + int pfd[2][2]; + + if (pipe(pfd[0]) < 0) { + perror("pipe()"); + goto err_pipe1; + } + + if (pipe(pfd[1]) < 0) { + perror("pipe()"); + goto err_pipe2; + } + + switch (fork()) { + case -1: + perror("fork()"); + goto err_fork; + + case 0: // child process + close(pfd[0][1]); // close write for child + close(pfd[1][0]); // close read for parent + fd[0] = pfd[0][0]; // keep read for child + fd[1] = pfd[1][1]; // keep write for parent + return 0; + + default: // parent process + close(pfd[1][1]); // close write for parent + close(pfd[0][0]); // close read for child + fd[0] = pfd[1][0]; // keep read for parent + fd[1] = pfd[0][1]; // keep write for child + return 1; + } + +err_fork: + close(pfd[1][0]); + close(pfd[1][1]); +err_pipe2: + close(pfd[0][0]); + close(pfd[0][1]); +err_pipe1: + return -1; +} + +int redir(int fd, const char* file) { + int fd2 = open(file, O_WRONLY | O_CREAT | O_TRUNC, 0644); + + if (fd2 < 0) + return -1; + + dup2(fd2, fd); + close(fd2); + + return 0; +} + +void sig_handler(int sig) { + if (parent) { + const char msg[] = "Programmabbruch durch Nutzer\n"; + write(1, msg, sizeof(msg) - 1); + } + exit(0); +} diff --git a/02_exercise/beispiele/pipe_example/Makefile b/02_exercise/beispiele/pipe_example/Makefile new file mode 100644 index 0000000..d69473e --- /dev/null +++ b/02_exercise/beispiele/pipe_example/Makefile @@ -0,0 +1,18 @@ +#!/usr/bin/make +.SUFFIXES: + +CFLAGS = -c -Os -Wall -Werror + +%.o: %.c + $(CC) $(CFLAGS) $^ -o $@ + +%: %.o + $(CC) -o $@ $^ + +all: pipe + +run: all + ./pipe + +clean: + $(RM) $(RMFILES) pipe diff --git a/02_exercise/beispiele/pipe_example/pipe.c b/02_exercise/beispiele/pipe_example/pipe.c new file mode 100644 index 0000000..3589c63 --- /dev/null +++ b/02_exercise/beispiele/pipe_example/pipe.c @@ -0,0 +1,60 @@ +#include +#include +#include +#include + +#define BUF_SIZE 32 + +int pipe_fds[2]; + +void term(const char *msg) { + if (msg) { + fprintf(stderr, "Error: %s\n", msg); + fflush(stderr); + } + + int close_fail = 0; + int ret = close(pipe_fds[0]); + + if (ret < 0) { + fprintf(stderr, "Error: Couldn't close reading end of pipe\n"); + fflush(stderr); + close_fail = 1; + } + + ret = close(pipe_fds[1]); + + if (ret < 0) { + fprintf(stderr, "Error: Couldn't close writing end of pipe\n"); + fflush(stderr); + close_fail = 1; + } + + exit((msg || close_fail) ? EXIT_FAILURE : EXIT_SUCCESS); +} + +int main(void) { + int ret = pipe(pipe_fds); + + if (ret < 0) + term("Couldn't create pipe"); + + char out_buf[] = "hello"; + ret = write(pipe_fds[1], out_buf, strlen(out_buf) + 1); + + if (ret < 0) + term("Couldn't write to pipe"); + + printf("send msg: %s\n", out_buf); + + char in_buf[BUF_SIZE]; + memset(in_buf, 0, BUF_SIZE); + + ret = read(pipe_fds[0], in_buf, BUF_SIZE - 1); + + if (ret < 0) + term("Couldn't read from pipe"); + + printf("recv msg: %s\n", in_buf); + return 0; +} diff --git a/02_exercise/prog.c b/02_exercise/prog.c new file mode 100644 index 0000000..17b727a --- /dev/null +++ b/02_exercise/prog.c @@ -0,0 +1,51 @@ +#include +#include +#include +#include + +static unsigned int hash(const void* key, size_t size) { + const char* ptr = key; + unsigned int hval; + + for (hval = 0x811c9dc5u; size --> 0; ++ptr) { + hval ^= *ptr; + hval *= 0x1000193u; + } + + return hval; +} + +static inline int roll(int sides) { + return rand() / (RAND_MAX + 1.0) * sides; +} + +int main(int argc, const char* argv[]) { + int rc, delay; + time_t now = time(0); + + /* initialize the random number generator */ + srand(hash(&now, sizeof(now))); + + /* determine exit code and time delay */ + switch (argc) { + default: + delay = atoi(argv[1]); + rc = atoi(argv[2]); + break; + + case 2: + delay = atoi(argv[1]); + rc = roll(2) - 1; + break; + + case 1: + delay = roll(10); + rc = roll(2) - 1; + break; + } + + printf("Delay: %ds, Exit Code: %d\n", delay, rc); + sleep(delay); + + return rc; +} diff --git a/02_exercise/shell.c b/02_exercise/shell.c new file mode 100644 index 0000000..0cea1f6 --- /dev/null +++ b/02_exercise/shell.c @@ -0,0 +1,16 @@ +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "array.h" + +int main(void) { + return 0; +} -- cgit v1.2.3-54-g00ecf