├── .gitignore ├── README.md ├── lab01 ├── Makefile ├── README.md └── countdown.c ├── lab02 ├── Makefile ├── README.md ├── client.c └── collatzlib │ ├── Makefile │ ├── collatz.c │ └── collatz.h ├── lab03 ├── README.md ├── zad01 │ ├── .gitignore │ ├── Makefile │ ├── reverse.c │ └── test_generator.c └── zad02 │ ├── .gitignore │ ├── Makefile │ └── file_sizes.c ├── lab04 ├── README.md ├── zad01 │ ├── Makefile │ └── spawn_processes.c └── zad02 │ ├── .gitignore │ ├── Makefile │ └── execute.c ├── lab05 ├── README.md ├── zad01 │ ├── Makefile │ └── signal_demo.c └── zad02 │ ├── Makefile │ ├── catcher.c │ └── sender.c ├── lab06 ├── README.md ├── zad01 │ ├── Makefile │ └── integral.c └── zad02 │ ├── .gitignore │ ├── Makefile │ ├── integral_calculator.c │ ├── integral_manager.c │ └── integral_pipe_specs.h ├── lab07 ├── Makefile ├── README.md ├── client.c ├── protocol_specs.h └── server.c ├── lab08 ├── Makefile ├── README.md ├── printers_system.c ├── shared_memory_specs.h └── users_simulator.c ├── lab09 ├── Makefile ├── README.md ├── grid.c ├── grid.h └── life.c ├── lab10 ├── Makefile ├── README.md └── santa.c ├── lab11 ├── Makefile ├── README.md ├── client.c ├── protocol_specs.h └── server.c ├── lab12 └── README.md ├── zad kol1_wt_1315 ├── Task1 │ ├── Makefile │ └── main.c ├── Task2 │ ├── Makefile │ └── main.c └── Task3 │ ├── Makefile │ └── main.c ├── zad kol1_wt_1500 ├── Task1 │ ├── Makefile │ └── main.c ├── Task2 │ ├── Makefile │ ├── file1 │ ├── file2 │ ├── fsorted │ └── main.c └── Task3 │ ├── Makefile │ ├── calc │ └── main.c ├── zad_kol1_sr_1645 ├── Task1 │ ├── Makefile │ └── main.c ├── Task2 │ ├── Makefile │ └── main.c └── Task3 │ ├── Makefile │ ├── file1 │ ├── file2 │ └── main.c ├── zad_kol2_wt_1315 ├── task1 │ ├── Makefile │ └── main.c ├── task2 │ ├── Makefile │ └── main.c └── task3 │ ├── main.c │ └── makefile └── zad_kol2_wt_1500 ├── Task2 ├── main.c └── makefile ├── task1 ├── Makefile └── main.c └── task3 ├── Makefile └── main.c /.gitignore: -------------------------------------------------------------------------------- 1 | /.idea/ 2 | /cmake-build-*/ 3 | **/build/ 4 | /.vscode/ 5 | *.o 6 | *.so -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Operating-Systems 2 | Repository for the Operating Systems course at AGH UST. 3 | 4 | # Covered topics 5 | You can check out task contents in Polish under following links: 6 | 1. [Lab 1 Makefile](lab01/README.md) - Introduction to makefiles 7 | 2. [Lab 2 Libraries ](lab02/README.md) - compiling nad linking static and shared libraries 8 | 3. [Lab 3 Files](lab03/README.md) - using system libraries to manage files 9 | 4. [Lab 4 Processes](lab04/README.md) - creating and managing UNIX processes 10 | 5. [Lab 5 Signals](lab05/README.md) - sending and handling UNIX signals 11 | 6. [Lab 6 Pipes](lab06/README.md) - communication between processes using pipes 12 | 7. [Lab 7 IPC](lab07/README.md) - simple chat using inter process communication queues 13 | 8. [Lab 8 IPC](lab08/README.md) - managing memory shared by multiple processes using semaphores 14 | 9. [Lab 9 Threads](lab09/README.md) - using threads library to optimize Conway's Game of Life 15 | 10. [Lab 10 Thread synchronization](lab10/README.md) - mechanisms for thread synchronization to solver Santa Clause problem 16 | 11. [Lab 11 TCP-IP Sockets](lab11/README.md) - create simple chat based on TCP/IP sockets 17 | 12. [LAB 12 UDP Sockets](lab12/README.md) - create simple chat based on UDP sockets -------------------------------------------------------------------------------- /lab01/Makefile: -------------------------------------------------------------------------------- 1 | # C compiler command 2 | CC=gcc 3 | # defines path to put all binary files 4 | BUILD_DIR=build 5 | 6 | # default build configuration, i.e `make all BUILD=release` or `make all BUILD=debug` overwrites this flag 7 | BUILD := debug 8 | 9 | # C flags common in all build configurations 10 | cflags.common := -std=c11 -Wall 11 | # disable all optimizations and add debug information in debug build 12 | cflags.debug := -g -O0 13 | # enable O2 optimization in release mode 14 | cflags.release := -O2 15 | # build CFLAGS variable based on selected build configuration 16 | CFLAGS := ${cflags.${BUILD}} ${cflags.common} 17 | 18 | .PHONY: all countdown clean 19 | 20 | all: 21 | make countdown 22 | 23 | countdown: countdown.c 24 | mkdir -p $(BUILD_DIR) 25 | $(CC) $(CFLAGS) -o $(BUILD_DIR)/$@ $< 26 | 27 | clean: 28 | rm -rf $(BUILD_DIR) -------------------------------------------------------------------------------- /lab01/README.md: -------------------------------------------------------------------------------- 1 | # Lab 1 - Podstawy Makefile 2 | 1. Zapoznaj się z koncepcją plików Makefile: https://www.gnu.org/software/make/manual/html_node/Introduction.html 3 | 2. Zainstaluj/skonfiguruj IDE, w którym będziesz pracować przez resztę laboratoriów. (np. VS Code, Vim, etc.) 4 | 3. Stwórz nowy projekt w IDE. 5 | 4. Napisz prosty program countdown.c, który będzie w pętli odliczał od 10 do 0 i wypisywał aktualną liczbę na konsolę (każda liczba w nowej linii). 6 | 5. Stwórz plik Makefile, za pomocą którego skompilujesz swój program. 7 | Makefile powinien zawierać co najmniej trzy targety: `all`, `countdown`, `clean`.
8 | `all` — powinien budować wszystkie targety (na razie tylko countdown, w kolejnych zadaniach będziemy dodawać nowe targety).
9 | `countdown` — buduje program countdown.c
10 | `clean` — usuwa wszystkie pliki binarne, czyści stan projektu
11 | Użyj zmiennych CXX oraz CFLAGS do zdefiniowania kompilatora (gcc) i flag compilacji (-Wall, ...). 12 | Dodatkowo w Makefile powinien być specjalny target .PHONY. 13 | 6. Skompiluj i uruchom program. 14 | 7. Korzystając z gdb (lub lldb) zademonstruj zatrzymywanie programu (breakpoint) wewnątrz pętli. Wypisz aktualny indeks pętli, kontynuuj wykonywanie programu i wypisz następny.
15 | https://sourceware.org/gdb/current/onlinedocs/gdb.html/
16 | https://sourceware.org/gdb/current/onlinedocs/gdb.html/Sample-Session.html#Sample-Session
17 | https://lldb.llvm.org/use/tutorial.html
18 | 8. Skonfiguruj swoje IDE do współpracy z Makefile. Postaw breakpoint (graficznie, klikając) w środku pętli. Uruchom program w trybie Debug i zademonstruj, jak można podejrzeć wartość indeksu pętli i kontynuować wykonywanie programu. 19 | 9. Usuń breakpoint i kontnynuuj wykonywanie programu do końca. -------------------------------------------------------------------------------- /lab01/countdown.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int main() { 4 | for(int i = 10; i >= 0; i--) { 5 | printf("%d\n", i); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /lab02/Makefile: -------------------------------------------------------------------------------- 1 | # C compiler command 2 | CC=gcc 3 | # defines path to put all binary files 4 | BUILD_DIR=build 5 | 6 | # default build configuration, i.e `make all BUILD=release` or `make all BUILD=debug` overwrites this flag 7 | BUILD := debug 8 | 9 | # C flags common in all build configurations 10 | # -Wall - enable all warnings 11 | # -std=c11 - set c standard to 11 12 | cflags.common := -std=c11 -Wall 13 | # disable all optimizations and add debug information in debug build 14 | cflags.debug := -g -O0 15 | # enable O2 optimization in release mode 16 | cflags.release := -O2 17 | # build CFLAGS variable based on selected build configuration 18 | CFLAGS := ${cflags.${BUILD}} ${cflags.common} 19 | 20 | .PHONY: all client_static client_shared_so client_shared_dlopen test_static test_shared_so test_shared_dlopen clean 21 | 22 | # create all client executables 23 | all: 24 | make client_static 25 | make client_shared_so 26 | make client_shared_dlopen 27 | 28 | # create client executable which uses staticly linked library 29 | # first compile static library if it doesn't exist 30 | # to link object file with static library we just need to pass path to static library (.a file) at the end of gcc command 31 | client_static: client.c 32 | mkdir -p $(BUILD_DIR) 33 | make -C collatzlib collatz_static BUILD=$(BUILD) 34 | $(CC) $(CFLAGS) -o $(BUILD_DIR)/$@ $< collatzlib/build/libcollatz_static.a 35 | 36 | 37 | # create client dynamically linked at compilation (using so file) 38 | # first compile shared library if it doesn't exist 39 | # then compile executable linked to .so file 40 | # gcc options: 41 | # -L -specify path where linked library can be found 42 | # -lcollatz_shared -specify library to be linked -lcollatz_shared means that linker will look for file named libcollatz_shared.so in specified in -L option 43 | # -Wl - option passing arguments to linker 44 | # -Wl,-rpath= - pass -rpath= argument to linker, rpath itself is path for runtime library search, so the program knows where to look for .so file 45 | client_shared_so: client.c 46 | mkdir -p $(BUILD_DIR) 47 | make collatz_shared -C collatzlib BUILD=$(BUILD) 48 | $(CC) $(CFLAGS) -L ./collatzlib/build $< -lcollatz_shared -Wl,-rpath=./collatzlib/build -o $(BUILD_DIR)/$@ 49 | 50 | # create client that uses dlopen for use of shared library 51 | # just conventional gcc build with -ldl flag to link libdl used by dlopen 52 | # -D flag passses define macro for the preprocessor 53 | client_shared_dlopen: client.c 54 | mkdir -p $(BUILD_DIR) 55 | make collatz_shared -C collatzlib BUILD=$(BUILD) 56 | $(CC) $(CFLAGS) -o $(BUILD_DIR)/$@ $< -ldl -D DYNAMIC_DLOPEN 57 | 58 | # example program execution 59 | test_static: 60 | ./$(BUILD_DIR)/client_static 61 | 62 | test_shared_so: 63 | ./$(BUILD_DIR)/client_shared_so 64 | 65 | test_shared_dlopen: 66 | ./$(BUILD_DIR)/client_shared_dlopen 67 | 68 | clean: 69 | rm -rf $(BUILD_DIR) 70 | make -C collatzlib clean -------------------------------------------------------------------------------- /lab02/README.md: -------------------------------------------------------------------------------- 1 | # LAB2 - Biblioteki w systemie Unix 2 | 3 | Biblioteki są kluczowymi składnikami każdego systemu operacyjnego, a ich zrozumienie jest niezbędne dla skutecznego tworzenia i zarządzania aplikacjami. W ramach tego ćwiczenia przyjrzymy się trzem głównym typom bibliotek: statycznym, współdzielonym i dynamicznym. 4 | 5 | Biblioteki statyczne są skompilowanymi plikami binarnymi, które są dołączane do programów podczas kompilacji. Są one integralną częścią aplikacji i wszystkie funkcje zawarte w bibliotece statycznej są kopiowane do pliku wykonywalnego programu. W przeciwieństwie do tego biblioteki współdzielone są ładowane do pamięci podczas uruchamiania programu i mogą być współużytkowane przez wiele aplikacji. Natomiast biblioteki dynamiczne są podobne do bibliotek współdzielonych, ale są ładowane do pamięci podczas uruchamiania programu i mogą być ładowane i usuwane dynamicznie w trakcie działania aplikacji. 6 | 7 | Aby zademonstrować tworzenie i korzystanie z bibliotek zaimplementujemy bibliotekę pomagającą w badaniu problemu Collatza. 8 | 9 | Problem Collatza 10 | Problem Collatza (Collatz Conjecture), znana również jako problem 3n+1, to jedno z najbardziej znanych nierozwiązanych problemów w matematyce. Zakłada, że ​​startując od dowolnej dodatniej liczby całkowitej, można ją zredukować do 1, wykonując iteracyjne kroki na podstawie następujących reguł: jeśli liczba jest parzysta, podziel ją przez 2; jeśli jest nieparzysta, pomnóż przez 3 i dodaj 1. Mimo prostoty zasady, problem pozostaje nierozwiązany dla dowolnej liczby startowej, choć jest uznawany za prawdziwy ze względu na obserwacje empiryczne dla wielu początkowych wartości. 11 | 12 | Zadanie: 13 | 14 | 1. Stwórz bibliotekę w języku C wystawiającą klientom następujące dwie funkcje: 15 | - `int collatz_conjecture(int input)` - funkcja realizująca regułę Collatza postaci: 16 | ```math 17 | c_{n+1} = 18 | \begin{cases} 19 | \frac{1}{2}c_n & \text{gdy} c_n \text{jest parzysta}\\ 20 | 3c_n + 1 & \text{gdy} c_n \text{jest nieparzysta} 21 | \end{cases} 22 | ``` 23 | Funkcja ta przyjmuje jedną liczbę typu całkowitego. Jeżeli jest ona parzysta, podziel ją przez 2 i zwróć wynik. Jeżeli jest nieparzysta, pomnóż ją przez 3 i dodaj 1, a następnie zwróć wynik. 24 | - `int test_collatz_convergence(int input, int max_iter)` - funkcja sprawdzająca po ilu wywołaniach collatz_conjecture zbiega się do 1. Powinna ona wywoływać regułę Collatza najpierw na liczbie wejściowej a później na wyniku otrzymanym z reguły. W celu ochrony przed zbyt długim zapętlaniem się funkcji drugi parametr stanowi górną granicę liczby iteracji. W przypadku gdy funkcja wykona maksymalną ilość iteracji i nie znajdzie wyniku 1, wtedy zwróć -1. 25 | 26 | 2. W pliku makefile utwórz dwa wpisy: do kompilacji statycznej biblioteki i do kompilacji współdzielonej. 27 | 28 | 29 | 3. Napisz klienta korzystającego z kodu biblioteki, klient powinien sprawdzać kilka liczb, wykorzystując test_collatz_convergence, tj. po ilu iteracjach wynik zbiegnie się do 1 i wypisać liczbę iteracji na standardowe wyjście. Klient powinien korzystać z biblioteki na 3 sposoby: 30 | Jako biblioteka statyczna 31 | Jako biblioteka współdzielona (linkowana dynamicznie) 32 | Jako biblioteka ładowana dynamicznie 33 | Dla każdego z wariantów utwórz odpowiedni wpis w Makefile. Do realizacji biblioteki dynamicznej użyj definicji stałej (-D) i dyrektywy preprocesora, aby zmodyfikować sposób działania klienta. 34 | 35 | 4. Wyświetl zawartość plików binarnych wszystkich wariantów klienta przy pomocy programu objdump, znajdź miejsca wywołania funkcji test_collatz_convergence omów różnice w kodzie binarnym. Dla większej jasności kodu kompiluj bez optymalizacji -O0. -------------------------------------------------------------------------------- /lab02/client.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #ifndef DYNAMIC_DLOPEN 4 | #include "collatzlib/collatz.h" 5 | #endif //DYNAMIC_DLOPEN 6 | 7 | #ifdef DYNAMIC_DLOPEN 8 | #include "dlfcn.h" 9 | 10 | int (*collatz_conjecture)(int input); 11 | int (*test_collatz_convergence)(int input, int max_iter); 12 | #endif //DYNAMIC_DLOPEN 13 | 14 | int main() { 15 | #ifdef DYNAMIC_DLOPEN 16 | void* dll_handle = dlopen("collatzlib/build/libcollatz_shared.so", RTLD_LAZY); 17 | if (!dll_handle) { 18 | fprintf(stderr, "Error: %s\n", dlerror()); 19 | return 1; 20 | } 21 | 22 | collatz_conjecture = dlsym(dll_handle, "collatz_conjecture"); 23 | test_collatz_convergence = dlsym(dll_handle, "test_collatz_convergence"); 24 | #endif //DYNAMIC_DLOPEN 25 | 26 | printf("%d\n", collatz_conjecture(10)); 27 | printf("%d\n", test_collatz_convergence(10, 100)); 28 | } -------------------------------------------------------------------------------- /lab02/collatzlib/Makefile: -------------------------------------------------------------------------------- 1 | # C compiler command 2 | CC=gcc 3 | # defines path to put all binary files 4 | BUILD_DIR=build 5 | 6 | # default build configuration, i.e `make all BUILD=release` or `make all BUILD=debug` overwrites this flag 7 | BUILD := debug 8 | 9 | # C flags common in all build configurations 10 | # -Wall - enable all warnings 11 | # -std=c11 - set c standard to 11 12 | cflags.common := -std=c11 -Wall 13 | # disable all optimizations and add debug information in debug build 14 | cflags.debug := -g -O0 15 | # enable O2 optimization in release mode 16 | cflags.release := -O2 17 | # build CFLAGS variable based on selected build configuration 18 | CFLAGS := ${cflags.${BUILD}} ${cflags.common} 19 | 20 | .PHONY: all collatz_static collatz_shared clean 21 | 22 | # build both static and shared library 23 | all: 24 | make collatz_static BUILD=$(BUILD) 25 | make collatz_shared BUILD=$(BUILD) 26 | 27 | # build only static library 28 | # gcc options: 29 | # -c - compile only, files will be only compiled, but not linked 30 | # ar command packs compilation result (object file) into .a archive 31 | # ar options: 32 | # r - insert with replacement, if archive exists and contains given file, file inside archive will be replaced 33 | # c - create, creates archive if it does not exist 34 | # s - updates archive index 35 | collatz_static: collatz.c 36 | mkdir -p $(BUILD_DIR) 37 | $(CC) $(CFLAGS) -o $(BUILD_DIR)/$@ -c $< 38 | ar rcs $(BUILD_DIR)/lib$@.a $(BUILD_DIR)/$@ 39 | 40 | # build only shared library 41 | # first we compile sources to object file with -fPIC option 42 | # than we use GCC to crated shared object file (.so) 43 | # gcc options: 44 | # -c - compile only, files will be only compiled, but not linked 45 | # -fPIC - create Position Indexed Code, code accesses all constant adrress through special offset table (Global Offset Table) so it can be loaded dynamically on the start of the program 46 | # -shared - create shared object file 47 | collatz_shared: collatz.c 48 | mkdir -p $(BUILD_DIR) 49 | $(CC) $(CFLAGS) -fPIC -o $(BUILD_DIR)/$@.o -c $< 50 | $(CC) -shared -o $(BUILD_DIR)/lib$@.so $(BUILD_DIR)/$@.o 51 | 52 | clean: 53 | rm -rf $(BUILD_DIR) -------------------------------------------------------------------------------- /lab02/collatzlib/collatz.c: -------------------------------------------------------------------------------- 1 | #include "collatz.h" 2 | 3 | int collatz_conjecture(int input) { 4 | if (input % 2 == 0) 5 | return input / 2; 6 | return 3 * input + 1; 7 | } 8 | 9 | int test_collatz_convergence(int input, int max_iter) { 10 | int iter = 0; 11 | while (input != 1 && iter < max_iter) { 12 | input = collatz_conjecture(input); 13 | iter++; 14 | } 15 | 16 | if (iter == max_iter && input != 1) 17 | return -1; 18 | 19 | return iter; 20 | } -------------------------------------------------------------------------------- /lab02/collatzlib/collatz.h: -------------------------------------------------------------------------------- 1 | #ifndef COLLATZ_H 2 | #define COLLATZ_H 3 | 4 | int collatz_conjecture(int input); 5 | int test_collatz_convergence(int input, int max_iter); 6 | 7 | #endif // COLLATZ_H -------------------------------------------------------------------------------- /lab03/README.md: -------------------------------------------------------------------------------- 1 | # LAB 3 - Operacje na plikach 2 | W zadaniu 1 można wybrać do zaimplementowania tylko jeden wariant: 3 | 4 | - albo `fopen(), fseek(), fread(), fwrite(), fclose()` 5 | - albo `open(), lseek(), read(), write(), close()` 6 | 7 | Wybrany wariant należy opracować na dwa sposoby: 8 | 9 | - Czytanie po 1 znaku. 10 | - Czytanie bloków po 1024 znaki (plik wynikowy powinien być identyczny jak w wariancie 1.) 11 | 12 | Dla obu sposobów implementacji należy przeprowadzić pomiar czasu wykonywania. Wyniki należy przedstawić w formie pliku `pomiar_zad_2.txt` 13 | 14 | ## Zadanie 1 15 | Napisz program, który kopiuje zawartość jednego pliku do drugiego, odwróconą bajt po bajcie. 16 | 17 | Wskazówki: Wywołania w rodzaju `fseek(infile, +1024, SEEK_END)` lub `lseek(in, +1024, SEEK_END)` są zupełnie legalne i nie powodują żadnych skutków ubocznych. Aby po przeczytaniu bloku znaków cofnąć się na początek poprzedniego bloku, należy jako drugi argument funkcji `fseek(..., ..., SEEK_CUR)` lub `lseek(...., ..., SEEK_CUR)` podać podwojoną długość bloku ze znakiem minus. Działanie programu należy zweryfikować następująco: 18 | 19 | 1) odwrócić krótki plik tekstowy, podejrzeć wynik, sprawdzić szczególnie początkowe i końcowe znaki. 20 | 21 | 2) .`/reverse plik_binarny tmp1 ; ./reverse tmp1 tmp2 ; diff -s tmp2 plik_binarny` 22 | 23 | 3) można też porównać (`diff -s`) wynik działania programu i wynik polecenia `tac < plik_wejściowy | rev > plik_wyjściowy` 24 | 25 | ## Zadanie 2 26 | Napisz program, który będzie przeglądał bieżący katalog, korzystając z funkcji `opendir(), readdir() i stat()`. Dla każdego znalezionego pliku, który nie jest katalogiem, czyli `!S_ISDIR(bufor_stat.st_mode)`, należy wypisać rozmiar i nazwę pliku. Ponadto na koniec należy wypisać sumaryczny rozmiar wszystkich plików. Nie należy przeglądać podkatalogów! Sumaryczny rozmiar plików należy przechowywać w zmiennej typu long long i wypisywać ją przez format %lld. 27 | 28 | Działanie programu porównaj z działaniem polecenia `wc --bytes *` -------------------------------------------------------------------------------- /lab03/zad01/.gitignore: -------------------------------------------------------------------------------- 1 | test_files 2 | test_temp 3 | pomiar_zad_2.txt -------------------------------------------------------------------------------- /lab03/zad01/Makefile: -------------------------------------------------------------------------------- 1 | # C compiler command 2 | CC=gcc 3 | # defines path to put all binary files 4 | BUILD_DIR=build 5 | 6 | # default build configuration, i.e `make all BUILD=release` or `make all BUILD=debug` overwrites this flag 7 | BUILD := debug 8 | 9 | # C flags common in all build configurations 10 | # -Wall - enable all warnings 11 | # -std=c11 - set c standard to 11 12 | cflags.common := -std=c11 -Wall 13 | # disable all optimizations and add debug information in debug build 14 | cflags.debug := -g -O0 15 | # enable O2 optimization in release mode 16 | cflags.release := -O2 17 | # build CFLAGS variable based on selected build configuration 18 | CFLAGS := ${cflags.${BUILD}} ${cflags.common} 19 | # using direct path to time binary to be able to format time output 20 | TIME := /usr/bin/time 21 | # name and path to raport file 22 | RAPORT := pomiar_zad_2.txt 23 | 24 | .PHONY: all tests clean 25 | 26 | # create all executables 27 | all: reverse reverse_bytes test_generator 28 | 29 | # create reverse executable configured to reverse files byte by byte with BYTES macro 30 | reverse_bytes: reverse.c 31 | mkdir -p $(BUILD_DIR) 32 | $(CC) $(CFLAGS) -o $(BUILD_DIR)/$@ $< -D BYTES 33 | 34 | # create reverse executable configured to reverse files in blocks 35 | reverse: reverse.c 36 | mkdir -p $(BUILD_DIR) 37 | $(CC) $(CFLAGS) -o $(BUILD_DIR)/$@ $< 38 | 39 | # compile random file generator 40 | test_generator: test_generator.c 41 | mkdir -p $(BUILD_DIR) 42 | $(CC) $(CFLAGS) -o $(BUILD_DIR)/$@ $< 43 | 44 | # target performing functionality and performance tests 45 | tests: 46 | make ftests REVERSE=reverse BUILD=release 47 | make ftests REVERSE=reverse_bytes BUILD=release 48 | 49 | rm -f pomiar_zad_2.txt 50 | touch pomiar_zad_2.txt 51 | 52 | make ptests REVERSE=reverse BUILD=release 53 | @echo >> pomiar_zad_2.txt 54 | make ptests REVERSE=reverse_bytes BUILD=release 55 | 56 | # helper target generating test files with helper generator app 57 | # @ used so commands are not echo'ed while make executed 58 | gen_tests: test_generator 59 | @ mkdir -p test_files 60 | @ ./$(BUILD_DIR)/test_generator 512 test_files/small_file 61 | @ ./$(BUILD_DIR)/test_generator 15000 test_files/medium_file 62 | @ ./$(BUILD_DIR)/test_generator 53248 test_files/large_file 63 | @ ./$(BUILD_DIR)/test_generator 10240000 test_files/huge_file 64 | @ ./$(BUILD_DIR)/test_generator 102400000 test_files/monumental_file 65 | 66 | # functionality tests, reverses multiple possible files and compares it with result of rev command on the same file 67 | # also @ is used to silence command echoing 68 | # -s in diff means that equality is considered a sucess 69 | ftests: $(REVERSE) gen_tests 70 | @ mkdir -p test_temp 71 | 72 | @ ./$(BUILD_DIR)/$(REVERSE) test_files/small_file test_temp/small_file_rev 73 | @ ./$(BUILD_DIR)/$(REVERSE) test_temp/small_file_rev test_temp/small_file_rev_rev 74 | 75 | @ ./$(BUILD_DIR)/$(REVERSE) test_files/medium_file test_temp/medium_file_rev 76 | @ ./$(BUILD_DIR)/$(REVERSE) test_temp/medium_file_rev test_temp/medium_file_rev_rev 77 | 78 | @ ./$(BUILD_DIR)/$(REVERSE) test_files/large_file test_temp/large_file_rev 79 | @ ./$(BUILD_DIR)/$(REVERSE) test_temp/large_file_rev test_temp/large_file_rev_rev 80 | 81 | @ ./$(BUILD_DIR)/$(REVERSE) test_files/huge_file test_temp/huge_file_rev 82 | @ ./$(BUILD_DIR)/$(REVERSE) test_temp/huge_file_rev test_temp/huge_file_rev_rev 83 | 84 | @ diff -s test_files/small_file test_temp/small_file_rev_rev 85 | @ rev test_files/small_file | diff -s test_temp/small_file_rev - 86 | @ diff -s test_files/medium_file test_temp/medium_file_rev_rev 87 | @ rev test_files/medium_file | diff -s test_temp/medium_file_rev - 88 | @ diff -s test_files/large_file test_temp/large_file_rev_rev 89 | @ rev test_files/large_file | diff -s test_temp/large_file_rev - 90 | @ diff -s test_files/huge_file test_temp/huge_file_rev_rev 91 | @ rev test_files/huge_file | diff -s test_temp/huge_file_rev - 92 | 93 | # performance test, creates test file with time that took to reverse given file 94 | # '%e' - Elapsed real (wall clock) time used by the process, in seconds. -f formats time output 95 | ptests: $(REVERSE) gen_tests 96 | @ echo "PROGRAM: $(REVERSE)" >> $(RAPORT) 97 | @ echo "Small file time: " `$(TIME) -f '%e' ./$(BUILD_DIR)/$(REVERSE) test_files/small_file test_temp/small_file_rev 2>&1` "[s]" >> $(RAPORT) 98 | @ echo "Medium file time: " `$(TIME) -f '%e' ./$(BUILD_DIR)/$(REVERSE) test_files/medium_file test_temp/medium_file_rev 2>&1` "[s]" >> $(RAPORT) 99 | @ echo "Large file time: " `$(TIME) -f '%e' ./$(BUILD_DIR)/$(REVERSE) test_files/large_file test_temp/large_file_rev 2>&1` "[s]" >> $(RAPORT) 100 | @ echo "Huge file time: " `$(TIME) -f '%e' ./$(BUILD_DIR)/$(REVERSE) test_files/huge_file test_temp/huge_file_rev 2>&1` "[s]" >> $(RAPORT) 101 | @ echo "Monumental file time: " `$(TIME) -f '%e' ./$(BUILD_DIR)/$(REVERSE) test_files/monumental_file test_temp/monumental_file_rev 2>&1` "[s]" >> $(RAPORT) 102 | 103 | # removes all unnecesery build objects 104 | clean: 105 | rm -rf $(BUILD_DIR) test_files test_temp 106 | rm -f $(RAPORT) -------------------------------------------------------------------------------- /lab03/zad01/reverse.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #if defined(BYTES) 4 | // reverse function reading file byte by byte 5 | void reverse(FILE* input_file, FILE* output_file){ 6 | //seek to the character next to the end of the file 7 | fseek(input_file, -1, SEEK_END); 8 | // get the length of the file 9 | long bytes_to_read = ftell(input_file) + 1; 10 | 11 | // while we didn't read whole file we read next byte 12 | while(bytes_to_read > 0){ 13 | // read current byte 14 | fprintf(output_file, "%c", getc(input_file)); 15 | bytes_to_read--; //mark byte as read 16 | 17 | //seek to bytes back from current position 18 | //2 because if we read one file we move the pointer one to the front, so we have to move it 2 bytes backward 19 | // to read next byte 20 | fseek(input_file, -2, SEEK_CUR); 21 | } 22 | } 23 | 24 | #elif !defined(BYTES) 25 | //reverse function that reverses file in 1024 byte blocks 26 | #define BUFFER_SIZE 1024 27 | void reverse(FILE* input_file, FILE* output_file){ 28 | // seek to the end of the file and get total amount of bytes in file 29 | fseek(input_file, 0, SEEK_END); 30 | long bytes_left = ftell(input_file); 31 | 32 | // declare buffer for read bytes 33 | char buffer[BUFFER_SIZE]; 34 | 35 | while(bytes_left > 0){ 36 | //calculate how many bytes we can read in iteration 37 | // if we have left less then BUFFER_SIZE bytes left 38 | // we can't call seek with BUFFER_SIZE because it won't move pointer 39 | // so we seek with only available bytes to read 40 | // so we move to the begining of the file 41 | long bytes_to_read = bytes_left > BUFFER_SIZE ? BUFFER_SIZE : bytes_left; 42 | fseek(input_file, -bytes_to_read, SEEK_CUR); 43 | 44 | //we read calculated amount of bytes to the buffer 45 | size_t bytes_read = fread(buffer, sizeof(char), bytes_to_read, input_file); 46 | 47 | //iterate over the buffer and reverse it contents 48 | char c; 49 | for(int i = 0; i < bytes_read/2; i++){ 50 | c = buffer[i]; 51 | buffer[i] = buffer[bytes_read - 1 - i]; 52 | buffer[bytes_read - 1 - i] = c; 53 | } 54 | 55 | //we write reversed buffer to the file 56 | fwrite(buffer, sizeof(char), bytes_read, output_file); 57 | 58 | //we can always seek BUFFER_SIZE hear, either we have read all bytes from the file 59 | // or we have more than BUFFER_SIZE bytes to seek 60 | fseek(input_file, -BUFFER_SIZE, SEEK_CUR); 61 | 62 | bytes_left -= bytes_read; //update amount of bytes left 63 | } 64 | } 65 | 66 | #endif 67 | 68 | /** 69 | * argv[0] - execution command - always passed as default 70 | * argv[1] - input file path 71 | * argv[2] - output file path 72 | */ 73 | int main(int argc, char** argv) { 74 | // if we didn't get 3 arguments we cannot procceed 75 | if (argc != 3) { 76 | printf("Usage: reverse \n"); 77 | return -1; 78 | } 79 | 80 | // try to open input file, stop program if failed 81 | FILE* input_file = fopen(argv[1], "r"); 82 | if (input_file == NULL) { 83 | printf("Error: Cannot open file '%s'\n", argv[1]); 84 | return -1; 85 | } 86 | 87 | // try to open output file, stop program if failed 88 | FILE* output_file = fopen(argv[2], "w"); 89 | if (output_file == NULL) { 90 | printf("Error: Cannot open file '%s'\n", argv[2]); 91 | return -1; 92 | } 93 | 94 | // call reverse function 95 | reverse(input_file, output_file); 96 | 97 | // remember to close files 98 | // but technically not necessary at the really end of the program - files will be freed automatically 99 | fclose(input_file); 100 | fclose(output_file); 101 | } -------------------------------------------------------------------------------- /lab03/zad01/test_generator.c: -------------------------------------------------------------------------------- 1 | /** 2 | * Simple util to generate random files with given number of bytes 3 | */ 4 | #include 5 | #include 6 | #include 7 | 8 | /** 9 | * argv[0] - execution command - always passed as default 10 | * argv[1] - number of bytes to generate 11 | * argv[2] - output file path 12 | */ 13 | int main(int argc, char** argv) { 14 | // if we didn't get 3 arguments we cannot procceed 15 | if(argc < 3){ 16 | printf("Usage: test_generator !\n"); 17 | return -1; 18 | } 19 | 20 | // try to open input file, stop program if failed 21 | FILE* file = fopen(argv[2], "w"); 22 | if(file == NULL){ 23 | printf("Failed to open file: %s!\n", argv[2]); 24 | return -1; 25 | } 26 | 27 | //convert string argument into long 28 | long number_of_files = strtol(argv[1], NULL, 10); 29 | 30 | //generate wanted amount of bytes to the file 31 | for(int i = 0; i < number_of_files; i++) { 32 | fputc(rand()%('z' - 'a' + 1) + 'a', file); 33 | } 34 | 35 | //close output file 36 | fclose(file); 37 | } -------------------------------------------------------------------------------- /lab03/zad02/.gitignore: -------------------------------------------------------------------------------- 1 | test_directory -------------------------------------------------------------------------------- /lab03/zad02/Makefile: -------------------------------------------------------------------------------- 1 | # C compiler command 2 | CC=gcc 3 | # defines path to put all binary files 4 | BUILD_DIR=build 5 | 6 | # default build configuration, i.e `make all BUILD=release` or `make all BUILD=debug` overwrites this flag 7 | BUILD := debug 8 | 9 | # C flags common in all build configurations 10 | # -Wall - enable all warnings 11 | # -std=c11 - set c standard to 11 12 | cflags.common := -std=c11 -Wall 13 | # disable all optimizations and add debug information in debug build 14 | cflags.debug := -g -O0 15 | # enable O2 optimization in release mode 16 | cflags.release := -O2 17 | # build CFLAGS variable based on selected build configuration 18 | CFLAGS := ${cflags.${BUILD}} ${cflags.common} 19 | 20 | .PHONY: all tests clean 21 | 22 | # create all executables 23 | all: file_sizes 24 | 25 | # compile file_sizes program 26 | file_sizes: file_sizes.c 27 | mkdir -p $(BUILD_DIR) 28 | $(CC) $(CFLAGS) -o $(BUILD_DIR)/$@ $< 29 | 30 | # generate test directory with example files 31 | gen_tests: 32 | mkdir -p test_directory 33 | 34 | touch test_directory/file1 35 | touch test_directory/file2 36 | touch test_directory/file3 37 | touch test_directory/file4 38 | touch test_directory/file5 39 | 40 | echo "asdaklsdjaklsdjaklsdjlaksdkjsdfkdjngjsdfjkdasnfj" > test_directory/file1 41 | echo "asdadasdahfghhffhhffghfghfh" > test_directory/file2 42 | echo "asdajghkjghkjdffgfdhfdghhhhhhhhhhhfhgdshssssssssssssssshsfgwerwrwrewerR" > test_directory/file3 43 | echo "ASDADAfgsdfgdsgfsdfgsdgdsfgsdgsdgsdgsdfg" > test_directory/file4 44 | echo "dfsfsdfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffasdddddddddddddddddddddddddddddddddddffffsdfs" > test_directory/file5 45 | 46 | # execute program 47 | tests: gen_tests file_sizes 48 | @echo "------ Program output ------" 49 | ./$(BUILD_DIR)/file_sizes test_directory 50 | @echo "------ wc --bytes output ------" 51 | wc --bytes test_directory/* 52 | 53 | # removes all unnecesery build objects 54 | clean: 55 | rm -rf $(BUILD_DIR) test_directory -------------------------------------------------------------------------------- /lab03/zad02/file_sizes.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | //define buffer for path, guarantees to be filled with 0 7 | #define PATH_BUFFER_SIZE 1024 8 | char path_buffer[PATH_BUFFER_SIZE]; 9 | 10 | /** 11 | * argv[0] - execution command - always passed as default 12 | * argv[1] - path to the directory to be searched 13 | */ 14 | int main(int argc, char** argv) { 15 | // if we didn't get 2 arguments we cannot procceed 16 | if(argc < 2) { 17 | printf("Usage: reverse \n"); 18 | return -1; 19 | } 20 | 21 | // try to open directory, if it failed, print info and exit 22 | DIR* dir_handle = opendir(argv[1]); 23 | if(dir_handle == NULL){ 24 | printf("Could not open directory at path: %s \n", argv[1]); 25 | return -1; 26 | } 27 | 28 | // construct begining of the path file buffer 29 | size_t dir_path_length = strlen(argv[1]); 30 | 31 | //check if path will fit in the buffer 32 | if(dir_path_length + 1 >= PATH_BUFFER_SIZE - 1){ 33 | printf("Given path is too long! \n"); 34 | return -1; 35 | } 36 | memcpy(path_buffer, argv[1], dir_path_length); 37 | 38 | // add / if it was not added in initial path 39 | if(path_buffer[dir_path_length - 1] != '/'){ 40 | path_buffer[dir_path_length] = '/'; 41 | dir_path_length += 1; 42 | } 43 | 44 | // declare stat structure, it will be filled for each file 45 | struct stat file_status; 46 | 47 | // variable for accumulating sum of file sizes in directory 48 | unsigned long long total_size = 0; 49 | 50 | // read first directory 51 | struct dirent* dir_file = readdir(dir_handle); 52 | 53 | // check if there is more files to procceed in the directory 54 | while(dir_file != NULL){ 55 | size_t file_name_length = strlen(dir_file->d_name); 56 | 57 | // raport error if file path does not fit in the path buffer 58 | if(dir_path_length + file_name_length >= PATH_BUFFER_SIZE - 1){ 59 | printf("Path to the file doesn't feet in 1024-bytes buffer!"); 60 | return -1; 61 | } 62 | 63 | // construct total file path buffer 64 | memcpy(&path_buffer[dir_path_length], dir_file->d_name, file_name_length); 65 | path_buffer[dir_path_length + file_name_length] = 0; 66 | 67 | // fill stat structure 68 | stat(path_buffer, &file_status); 69 | 70 | // check if file is not a directory 71 | if(!S_ISDIR(file_status.st_mode)){ 72 | //if it is not print out file size and name and accumulate bytes count 73 | printf("%8ld %s\n",file_status.st_size, path_buffer); 74 | total_size += file_status.st_size; 75 | } 76 | 77 | // read next dir 78 | dir_file = readdir(dir_handle); 79 | } 80 | 81 | // print out total count of bytes 82 | printf("%llu total\n", total_size); 83 | 84 | // remember to close directory 85 | closedir(dir_handle); 86 | return 0; 87 | } -------------------------------------------------------------------------------- /lab04/README.md: -------------------------------------------------------------------------------- 1 | 2 | # Tworzenie procesów. Środowisko procesu, sterowanie procesami 3 | ## Zadanie 1 4 | 5 | Napisz program, który przyjmuje jeden argument: `argv[1]`. Program ma utworzyć `argv[1]` procesów potomnych. Każdy proces potomny ma wypisać na standardowym wyjściu w jednym wierszu dwa identyfikatory: identyfikator procesu macierzystego i swój własny. Na końcu standardowego wyjścia proces macierzysty ma wypisać `argv[1]`. Wskazówka: aby program na pewno wypisywał `argv[1]` jako ostatni wiersz standardowego wyjścia, należy użyć funkcji systemowej `wait()`. 6 | ## Zadanie 2 7 | Napisz program, który przyjmuje jeden argument: `argv[1]` — ścieżkę katalogu. Program powinien wypisać na standardowym wyjściu swoją nazwę, korzystając z funkcji `printf()`. Zadeklaruj zmienną globalną global, a następnie zmienną lokalną local. W zależności od zwróconej wartości przez `fork()` dokonaj obsługi błędu, wykonaj proces rodzica / proces potomny. W procesie potomnym: 8 | 9 | 1. wyświetl komunikat „child process”, 10 | 2. dokonaj inkrementacji zmiennych global i local, 11 | 3. wyświetl komunikat „child pid = %d, parent pid = %d” 12 | 4. wyświetl komunikat „child's local = %d, child's global = %d” 13 | 5. wykonaj program /bin/ls z argumentem `argv[1]`, korzystając z funkcji `execl()`, zwracając przy tym jej kod błędu. 14 | 15 | W procesie rodzica: 16 | 17 | 1. wyświetl komunikat „parent process” 18 | 2. wyświetl komunikat “parent pid = %d, child pid = %d” 19 | 3. wyświetl komunikat “Child exit code: %d” 20 | 4. wyświetl komunikat “Parent's local = %d, parent's global = %d” 21 | 5. zwróć stosowny kod błędu. 22 | 23 | -------------------------------------------------------------------------------- /lab04/zad01/Makefile: -------------------------------------------------------------------------------- 1 | # C compiler command 2 | CC=gcc 3 | # defines path to put all binary files 4 | BUILD_DIR=build 5 | 6 | # default build configuration, i.e `make all BUILD=release` or `make all BUILD=debug` overwrites this flag 7 | BUILD := debug 8 | 9 | # C flags common in all build configurations 10 | # -Wall - enable all warnings 11 | # -std=c11 - set c standard to 11 12 | cflags.common := -std=c11 -Wall 13 | # disable all optimizations and add debug information in debug build 14 | cflags.debug := -g -O0 15 | # enable O2 optimization in release mode 16 | cflags.release := -O2 17 | # build CFLAGS variable based on selected build configuration 18 | CFLAGS := ${cflags.${BUILD}} ${cflags.common} 19 | 20 | .PHONY: all clean 21 | 22 | # create all executables 23 | all: spawn_processes 24 | 25 | # create spawn_processes executable 26 | spawn_processes: spawn_processes.c 27 | mkdir -p $(BUILD_DIR) 28 | $(CC) $(CFLAGS) -o $(BUILD_DIR)/$@ $< 29 | 30 | test: spawn_processes 31 | ./$(BUILD_DIR)/spawn_processes 10 32 | 33 | # removes all unnecesery build objects 34 | clean: 35 | rm -rf $(BUILD_DIR) -------------------------------------------------------------------------------- /lab04/zad01/spawn_processes.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | /** 9 | * argv[0] - execution command - always passed as default 10 | * argv[1] - number of processes to spawns 11 | */ 12 | int main(int argc, char** argv) { 13 | // if we didn't get 2 arguments we cannot procceed 14 | if(argc < 2) { 15 | printf("Usage: %s \n", argv[0]); 16 | return -1; 17 | } 18 | 19 | /* convert argument into long value */ 20 | long number_of_processes = strtol(argv[1], NULL, 10); 21 | 22 | /* ensure that converted number is greater than 0 */ 23 | if(number_of_processes < 0){ 24 | printf("Number of processes must be a positive number\n"); 25 | return -1; 26 | } 27 | 28 | /* spawn child processes with fork */ 29 | for(int i = 0; i < number_of_processes; i++) { 30 | pid_t pid = fork(); 31 | 32 | if(pid == 0) { 33 | /* execute child's code */ 34 | 35 | printf("Child process pid %d\n", getpid()); 36 | printf("Parent process pid %d\n", getppid()); 37 | 38 | /* exit with successful return code */ 39 | exit(0); 40 | } 41 | } 42 | 43 | /* wait for all child processes to end*/ 44 | while(wait(NULL) > 0); 45 | 46 | /* print number of processes*/ 47 | printf("Number of processes: %ld\n", number_of_processes); 48 | 49 | return 0; 50 | } -------------------------------------------------------------------------------- /lab04/zad02/.gitignore: -------------------------------------------------------------------------------- 1 | test_dir -------------------------------------------------------------------------------- /lab04/zad02/Makefile: -------------------------------------------------------------------------------- 1 | # C compiler command 2 | CC=gcc 3 | # defines path to put all binary files 4 | BUILD_DIR=build 5 | 6 | # default build configuration, i.e `make all BUILD=release` or `make all BUILD=debug` overwrites this flag 7 | BUILD := debug 8 | 9 | # C flags common in all build configurations 10 | # -Wall - enable all warnings 11 | # -std=c11 - set c standard to 11 12 | cflags.common := -std=c11 -Wall 13 | # disable all optimizations and add debug information in debug build 14 | cflags.debug := -g -O0 15 | # enable O2 optimization in release mode 16 | cflags.release := -O2 17 | # build CFLAGS variable based on selected build configuration 18 | CFLAGS := ${cflags.${BUILD}} ${cflags.common} 19 | 20 | .PHONY: all clean 21 | 22 | # create all executables 23 | all: spawn_processes 24 | 25 | # create execute executable 26 | execute: execute.c 27 | mkdir -p $(BUILD_DIR) 28 | $(CC) $(CFLAGS) -o $(BUILD_DIR)/$@ $< 29 | 30 | # execute build library with test directory, second call will fail and return error code 2 which will crash make run 31 | test: execute 32 | mkdir -p test_dir 33 | touch test_dir/file1 34 | touch test_dir/file2 35 | 36 | ./$(BUILD_DIR)/execute test_dir 37 | @echo "Return code: $$?" 38 | ./$(BUILD_DIR)/execute asdmasdk 39 | @echo "Return code: $$?" 40 | 41 | # removes all unnecesery build objects 42 | clean: 43 | rm -rf $(BUILD_DIR) test_dir -------------------------------------------------------------------------------- /lab04/zad02/execute.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | // declare global variable 9 | int global = 0; 10 | 11 | /** 12 | * argv[0] - execution command - always passed as default 13 | * argv[1] - directory path for ls command 14 | */ 15 | int main(int argc, char** argv) { 16 | // if we didn't get 3 arguments we cannot procceed 17 | if(argc < 2) { 18 | printf("Usage: %s \n", argv[0]); 19 | return -1; 20 | } 21 | 22 | /* print program name */ 23 | printf("%s \n", strrchr(argv[0], '/') + 1); 24 | 25 | /* declar local variable */ 26 | int local = 0; 27 | 28 | /* spawn child process for ls execution */ 29 | pid_t pid = fork(); 30 | 31 | if (pid == 0) { 32 | /* Execute childs code */ 33 | printf("child process\n"); 34 | global++; 35 | local++; 36 | 37 | printf("child pid = %d, parent pid = %d \n", getpid(), getppid()); 38 | printf("child's local = %d, child's global = %d \n", local, global); 39 | 40 | /* ecxecute ls command and get error code */ 41 | int status = execl("/bin/ls", "ls", "-l", argv[1], NULL); 42 | 43 | /* exit from child with ls return code */ 44 | exit(status); 45 | } 46 | 47 | /* wait for end of child execution and get error code */ 48 | int status = 0; 49 | wait(&status); 50 | int child_return_status = WEXITSTATUS(status); 51 | 52 | /* print parent process */ 53 | printf("parent process\n"); 54 | printf("parent pid = %d, child pid = %d \n", getpid(), pid); 55 | printf("Child exit code: %d \n", child_return_status); 56 | printf("parent's local = %d, parent's global = %d \n", local, global); 57 | 58 | /* return child error code*/ 59 | return child_return_status; 60 | } -------------------------------------------------------------------------------- /lab05/README.md: -------------------------------------------------------------------------------- 1 | # Zarządzanie sygnałami 2 | 3 | Rodzaje sygnałów: `SIGINT`, `SIGQUIT`, `SIGKILL`, `SIGTSTP`, `SIGSTOP`, `SIGTERM`, `SIGSEGV`, `SIGHUP`, `SIGALARM`, `SIGCHLD`, `SIGUSR1`, `SIGUSR2` 4 | 5 | Sygnały czasu rzeczywistego: `SIGRTMIN`, `SIGRTMIN+n`, `SIGRTMAX` 6 | 7 | Przydatne polecenia Unix: `kill`, `ps` 8 | 9 | Przydatne funkcje systemowe: `kill`, `raise`, `sigqueue`, `signal`, `sigaction`, `sigemptyset`, `sigfillset`, `sigaddset`, `sigdelset`, `sigismember`, `sigprocmask`, `sigpending`, `pause`, `sigsuspend` 10 | ## Zadanie 1 11 | Napisz program demonstrujący różne reakcje na przykładowy sygnał `SIGUSR1` w zależności od ustawionych dyspozycji. Reakcja na sygnał `SIGUSR1` programu powinna zależeć od wartości argumentu z linii poleceń. Argument ten może przyjmować wartości: `none`, `ignore`, `handler`, `mask`. Program w zależności od parametru odpowiednio: nie zmienia reakcji na sygnał, ustawia ignorowanie, instaluje handler obsługujący sygnał (działający w ten sposób, że wypisuje komunikat o jego otrzymaniu), maskuje ten sygnał oraz sprawdza czy wiszący/oczekujący sygnał jest widoczny. Następnie przy pomocy funkcji `raise` wysyła sygnał do samego siebie oraz wykonuje odpowiednie dla danej opcji, opisane wyżej działania. 12 | 13 | ## Zadanie 2 14 | Napisz dwa programy: sender program wysyłający sygnały `SIGUSR1` i catcher - program odbierający te sygnały. Program catcher jest uruchamiany jako pierwszy, wypisuje swój numer PID i czeka na sygnały `SIGUSR1`. Po każdorazowym odebraniu sygnału `SIGUSR1` przez program catcher powinno nastąpić potwierdzenie odbioru tego sygnału. W tym celu, catcher wysyła do sendera sygnał `SIGUSR1` informujący o odbiorze sygnału. Sender powinien wysłać kolejny sygnał dopiero po uzyskaniu tego potwierdzenia. Czekanie na takie potwierdzenie może odbywac się wywołując funkcję `sigsuspend`. Wraz z każdym sygnałem przesłanym przez sender powinien zostać przesłany tryb pracy programu catcher za pomocą funkcji `sigqueue`. Możliwe tryby pracy: 15 | 16 | 1. Wypisanie na standardowym wyjściu liczb od 1 do 100 17 | 18 | 2. Wypisanie na standardowym wyjściu liczby otrzymanych żądań zmiany 19 | trybu pracy od początku działania programu 20 | 3. Zakończenie działania programu catcher. 21 | 22 | PID sendera catcher pobiera ze struktury `siginfo_t` po przechwyceniu od niego sygnału. Program sender jako pierwszy parametr przyjmuje PID procesu catcher. Drugi parametr określa tryb pracy procesu catcher - w jednym wywołaniu może być przekazany jeden taki tryb. Program catcher działa aż do momentu otrzymania trybu 3 (zakończenie działania programu catcher). Program sender działa do momentu otrzymania potwierdzenia otrzymania przez catcher przesłanego trybu, po czym kończy pracę. Program sender można wywołać wielokrotnie aż do zakończenia pracy przez catcher. 23 | 24 | *UWAGA! W żaden sposób nie opóźniamy wysyłania sygnałów, wszelkie "gubienie" sygnałów jest zjawiskiem naturalnym*. -------------------------------------------------------------------------------- /lab05/zad01/Makefile: -------------------------------------------------------------------------------- 1 | # C compiler command 2 | CC=gcc 3 | # defines path to put all binary files 4 | BUILD_DIR=build 5 | 6 | # default build configuration, i.e `make all BUILD=release` or `make all BUILD=debug` overwrites this flag 7 | BUILD := debug 8 | 9 | # C flags common in all build configurations 10 | # -Wall - enable all warnings 11 | # -std=c11 - set c standard to 11 12 | cflags.common := -std=c11 -Wall 13 | # disable all optimizations and add debug information in debug build 14 | cflags.debug := -g -O0 15 | # enable O2 optimization in release mode 16 | cflags.release := -O2 17 | # build CFLAGS variable based on selected build configuration 18 | CFLAGS := ${cflags.${BUILD}} ${cflags.common} 19 | 20 | .PHONY: all clean 21 | 22 | # create all executables 23 | all: signal_demo 24 | 25 | # create signal_demo executable 26 | signal_demo: signal_demo.c 27 | mkdir -p $(BUILD_DIR) 28 | $(CC) $(CFLAGS) -o $(BUILD_DIR)/$@ $< 29 | 30 | test: signal_demo 31 | ./$(BUILD_DIR)/signal_demo ignore 32 | ./$(BUILD_DIR)/signal_demo handler 33 | ./$(BUILD_DIR)/signal_demo mask 34 | 35 | ./$(BUILD_DIR)/signal_demo none 36 | 37 | # removes all unnecesery build objects 38 | clean: 39 | rm -rf $(BUILD_DIR) -------------------------------------------------------------------------------- /lab05/zad01/signal_demo.c: -------------------------------------------------------------------------------- 1 | #define _XOPEN_SOURCE 700 // hacky approach, might be necessary sometimes to enable sig* functions 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | /** 9 | * @brief Handler that updates current status based on argument get from signal 10 | * 11 | * @param argument argument coming from signal 12 | */ 13 | void SIGUSR1_handler(int signal_number){ 14 | printf("Received SIGUSR1 signal with number: %d\n", signal_number); 15 | } 16 | 17 | /** 18 | * argv[0] - execution command - always passed as default 19 | * argv[1] - disposition, what to do with received signal 20 | */ 21 | int main(int argc, char** argv) { 22 | // if we didn't get 3 arguments we cannot proceed 23 | if(argc < 2) { 24 | printf("Usage: %s \n", argv[0]); 25 | return -1; 26 | } 27 | 28 | /* 29 | I have decided to put `raise` in all if statements to be consistant with 30 | with raise in mask mode - we have to put raise in between instructions 31 | to demonstrate pending signal when mask is selected 32 | */ 33 | 34 | if(!strcmp("ignore", argv[1])) { 35 | /* Ignore signal */ 36 | signal(SIGUSR1, SIG_IGN); 37 | raise(SIGUSR1); // send signal to ourself 38 | } 39 | else if(!strcmp("handler", argv[1])) { 40 | /* Use defined handler as reaction to signal */ 41 | signal(SIGUSR1, SIGUSR1_handler); 42 | raise(SIGUSR1); // send signal to ourself 43 | } 44 | else if(!strcmp("mask", argv[1])) { 45 | /* setup mask as signal set */ 46 | sigset_t sigset; 47 | sigemptyset(&sigset); 48 | sigaddset(&sigset, SIGUSR1); 49 | 50 | /* mask signal */ 51 | sigprocmask(SIG_SETMASK, &sigset, NULL); 52 | 53 | raise(SIGUSR1); // send signal to ourself 54 | 55 | /* Fetch all pending signals */ 56 | sigset_t pending_signals; 57 | sigpending(&pending_signals); 58 | 59 | /* Check if our signal is in pending set */ 60 | printf("Is signal pending? : %i\n", sigismember(&pending_signals, SIGUSR1)); 61 | } else { 62 | /*Use default handler for the signal*/ 63 | signal(SIGUSR1, SIG_DFL); 64 | raise(SIGUSR1); // send signal to ourself 65 | } 66 | return 0; 67 | } -------------------------------------------------------------------------------- /lab05/zad02/Makefile: -------------------------------------------------------------------------------- 1 | # C compiler command 2 | CC=gcc 3 | # defines path to put all binary files 4 | BUILD_DIR=build 5 | 6 | # default build configuration, i.e `make all BUILD=release` or `make all BUILD=debug` overwrites this flag 7 | BUILD := debug 8 | 9 | # C flags common in all build configurations 10 | # -Wall - enable all warnings 11 | # -std=c11 - set c standard to 11 12 | cflags.common := -std=c99 -Wall 13 | # disable all optimizations and add debug information in debug build 14 | cflags.debug := -g -O0 15 | # enable O2 optimization in release mode 16 | cflags.release := -O2 17 | # build CFLAGS variable based on selected build configuration 18 | CFLAGS := ${cflags.${BUILD}} ${cflags.common} 19 | 20 | .PHONY: all clean 21 | 22 | # create all executables 23 | all: sender catcher 24 | 25 | # create sender executable 26 | sender: sender.c 27 | mkdir -p $(BUILD_DIR) 28 | $(CC) $(CFLAGS) -o $(BUILD_DIR)/$@ $< 29 | 30 | # create catcher executable 31 | catcher: catcher.c 32 | mkdir -p $(BUILD_DIR) 33 | $(CC) $(CFLAGS) -o $(BUILD_DIR)/$@ $< 34 | 35 | 36 | # simple starter targets 37 | PID := 0 38 | ARGUMENT := 1 39 | run_sender: sender 40 | ./$(BUILD_DIR)/sender $(PID) $(ARGUMENT) 41 | 42 | run_catcher: catcher 43 | ./$(BUILD_DIR)/catcher 44 | 45 | 46 | # removes all unnecesery build objects 47 | clean: 48 | rm -rf $(BUILD_DIR) -------------------------------------------------------------------------------- /lab05/zad02/catcher.c: -------------------------------------------------------------------------------- 1 | #define _XOPEN_SOURCE 700 // hacky approach, might be necessary sometimes to enable sig* functions 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | /** global variables necessary for handling different modes*/ 10 | /** make status volatile so it does not get optimized by compiler, 11 | * without volatile it will work with -O0 but not with -O2 because compiler 12 | * doesn't see the path leading to change this variable (since the handler is called by system, not explicitly by our program) so it optimizes every usage 13 | * of this variable as it was constant value. 14 | * 15 | * We have to tell compiler explicitly that we don't want to optimize the variable 16 | */ 17 | volatile int status = -1; 18 | volatile int status_changes = 0; 19 | 20 | /** 21 | * @brief Handler that updates current status based on argument get from signal 22 | * 23 | * @param argument argument coming from signal 24 | */ 25 | void argument_handler(int argument){ 26 | status_changes++; 27 | status = argument; 28 | } 29 | 30 | /** 31 | * @brief Handler for signal action, it is called when proccess receives an signal 32 | * 33 | * @param signo number of the signal that was received 34 | * @param info structure holding data held by signal 35 | * @param extra 36 | * 37 | */ 38 | void SIGUSR1_action(int signo, siginfo_t *info, void *extra){ 39 | /* Retrieve information from signal info structure */ 40 | int int_val = info->si_int; 41 | printf("Received status: %d from pid: %d\n", int_val, info->si_pid); 42 | 43 | /* Update current status */ 44 | argument_handler(int_val); 45 | 46 | /* Send feedback signal to sender */ 47 | kill(info->si_pid, SIGUSR1); 48 | } 49 | 50 | int main() { 51 | /* Print out current catcher's PID*/ 52 | printf("Catcher PID: %d\n", getpid()); 53 | 54 | /* Register new signal action */ 55 | struct sigaction action; 56 | action.sa_sigaction = SIGUSR1_action; // register callback 57 | action.sa_flags = SA_SIGINFO; // set flag to SIGINFO so handler will be called with all 3 arguments 58 | sigemptyset(&action.sa_mask); // clear all signal masks during handler 59 | 60 | sigaction(SIGUSR1, &action, NULL); // proper registration of the signal action 61 | 62 | /* Infinitely do tasks as in the readme. Status 3 is exit status.*/ 63 | while(1) { 64 | switch(status){ 65 | case 1: 66 | for(int i = 1; i <= 100; i++){ 67 | printf("%i, ", i); 68 | } 69 | printf("\n"); 70 | status = -1; 71 | break; 72 | case 2: 73 | printf("So far status has changed %d times\n", status_changes); 74 | status = -1; 75 | break; 76 | case 3: 77 | printf("Received exit signal! Exiting...\n"); 78 | exit(0); 79 | break; 80 | default: 81 | break; //no work to do 82 | } 83 | } 84 | 85 | return 0; 86 | } -------------------------------------------------------------------------------- /lab05/zad02/sender.c: -------------------------------------------------------------------------------- 1 | #define _XOPEN_SOURCE 700 // hacky approach, might be necessary sometimes to enable sig* functions 2 | 3 | #include 4 | #include 5 | 6 | #include 7 | #include 8 | 9 | #include 10 | #include 11 | #include 12 | 13 | /** 14 | * @brief simple handler for SIGUSR1 signal 15 | * 16 | * @param signo number of the signal that was risen 17 | */ 18 | void SIGUSR1_handler(int signo) { 19 | printf("Confirmation received\n"); 20 | } 21 | 22 | /** 23 | * argv[0] - execution command - always passed as default 24 | * argv[1] - pid of the catcher program 25 | * argv[2] - mode that will be send to catcher 26 | */ 27 | int main(int argc, char** argv) { 28 | if(argc < 3) { 29 | printf("Usage: %s \n", argv[0]); 30 | return -1; 31 | } 32 | /* Printf current process PID */ 33 | printf("Sender PID: %d\n", getpid()); 34 | 35 | /* Register the feedback handler */ 36 | signal(SIGUSR1, SIGUSR1_handler); 37 | 38 | /* Convert arguments to actual numbers */ 39 | long signal_pid = strtol(argv[1], NULL, 10); 40 | long signal_argument = strtol(argv[2], NULL, 10); 41 | 42 | /* Store mode argument in proper union */ 43 | union sigval value = {signal_argument}; 44 | 45 | /* send signal to catcher process with given argument */ 46 | sigqueue(signal_pid, SIGUSR1, value); 47 | printf("Signal sent with argument: %ld\n", signal_argument); 48 | 49 | /* mask signals so only SIGUSR1 and SIGINT can interrupt the suspend */ 50 | sigset_t mask; 51 | sigfillset(&mask); 52 | 53 | sigdelset(&mask, SIGUSR1); 54 | sigdelset(&mask, SIGINT); 55 | 56 | /* Wait for the feedback*/ 57 | sigsuspend(&mask); 58 | return 0; 59 | } -------------------------------------------------------------------------------- /lab06/README.md: -------------------------------------------------------------------------------- 1 | # Potoki 2 | 3 | ## Zadanie 1 4 | 5 | Napisz program, który liczy numerycznie wartość całki oznaczonej z funkcji $$f(x) = \frac{4}{x^2+1}$$ w przedziale od 0 do 1 metodą prostokątów (z definicji całki oznaczonej Riemanna). Pierwszy parametr programu to szerokość każdego prostokąta, określająca dokładność obliczeń. Obliczenia należy rozdzielić na n procesów potomnych (n drugi parametr wywołania programu), tak by każdy z procesów liczył inny fragment ustalonego wyżej przedziału. Każdy z procesów powinien wynik swojej części obliczeń przesłać przez potok nienazwany do procesu macierzystego. Każdy proces potomny do komunikacji z procesem macierzystym powinien używać osobnego potoku. Proces macierzysty powinien oczekiwać na wyniki uzyskane od wszystkich procesów potomnych po czym powinien dodać te wyniki cząstkowe i wyświetlić wynik na standardowym wyjściu. W programie zmierz, wypisz na konsolę i zapisz do pliku z raportem czasy realizacji dla różnej liczby procesów potomnych oraz różnych dokładności obliczeń. Dokładności obliczeń należy dobrać w ten sposób by obliczenia trwały co najmniej kilka sekund. 6 | 7 | ## Zadanie 2 8 | Napisz program, który liczy numerycznie wartość całki oznaczonej tak jak w zadaniu 1. Obliczenia powinny zostać przeprowadzone w ten sposób, że pierwszy program czyta ze standardowego wejścia przedział w jakim całka ma być policzona a następnie przesyła przez potok nazwany do programu drugiego wartości określające ten przedział. Drugi program po otrzymaniu informacji liczy całkę w otrzymanym przedziale i odsyła do programu pierwszego wynik obliczeń. Po otrzymaniu wyniku obliczeń, program pierwszy wyświetla wynik. -------------------------------------------------------------------------------- /lab06/zad01/Makefile: -------------------------------------------------------------------------------- 1 | # C compiler command 2 | CC=gcc 3 | # defines path to put all binary files 4 | BUILD_DIR=build 5 | 6 | # default build configuration, i.e `make all BUILD=release` or `make all BUILD=debug` overwrites this flag 7 | BUILD := debug 8 | 9 | # C flags common in all build configurations 10 | # -Wall - enable all warnings 11 | # -std=c11 - set c standard to 11 12 | cflags.common := -std=c11 -Wall 13 | # disable all optimizations and add debug information in debug build 14 | cflags.debug := -g -O0 15 | # enable O2 optimization in release mode 16 | cflags.release := -Ofast 17 | # build CFLAGS variable based on selected build configuration 18 | CFLAGS := ${cflags.${BUILD}} ${cflags.common} 19 | # using direct path to time binary to be able to format time output 20 | TIME := /usr/bin/time 21 | 22 | .PHONY: all clean 23 | 24 | # create all executables 25 | all: integral 26 | 27 | # create signal_demo executable 28 | integral: integral.c 29 | mkdir -p $(BUILD_DIR) 30 | $(CC) $(CFLAGS) -o $(BUILD_DIR)/$@ $< -lm 31 | 32 | test: integral 33 | @echo "20 processes " `$(TIME) -f '%e' ./$(BUILD_DIR)/integral 0.0000000001 20 2>&1` "[s]" 34 | @echo "12 processes " `$(TIME) -f '%e' ./$(BUILD_DIR)/integral 0.0000000001 12 2>&1` "[s]" 35 | @echo "5 processes " `$(TIME) -f '%e' ./$(BUILD_DIR)/integral 0.0000000001 5 2>&1` "[s]" 36 | @echo "2 processes " `$(TIME) -f '%e' ./$(BUILD_DIR)/integral 0.0000000001 2 2>&1` "[s]" 37 | @echo "1 process " `$(TIME) -f '%e' ./$(BUILD_DIR)/integral 0.0000000001 1 2>&1` "[s]" 38 | 39 | example: integral 40 | ./$(BUILD_DIR)/integral 0.00001 18 41 | 42 | # removes all unnecesery build objects 43 | clean: 44 | rm -rf $(BUILD_DIR) -------------------------------------------------------------------------------- /lab06/zad01/integral.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #define min(a, b) (a < b ? a : b) 12 | 13 | typedef unsigned long long ull; 14 | 15 | /////////////////////////////////////////////////////////////////// 16 | // integral parameters 17 | double range_start = 0.0; 18 | double range_stop = 1.0; 19 | 20 | double f(double x){ 21 | return 4/(x*x + 1); 22 | } 23 | /////////////////////////////////////////////////////////////////// 24 | 25 | /** 26 | * @brief function calculating integral in given range 27 | * 28 | * @param interval_start start of the interval 29 | * @param interval_stop end of the interval 30 | * @param fun pointer to the function that will be integrated 31 | * @param interval_width width of the single rectangle 32 | * @param number_of_intervals number of rectangles that will be used to calculate integral 33 | */ 34 | double calculate_integral(double interval_start, double interval_stop, double (*fun)(double), double interval_width, ull number_of_intervals){ 35 | if(interval_stop - interval_start < interval_width) 36 | return fun((interval_start + interval_stop)/ 2.0)*(interval_stop - interval_start); 37 | 38 | double sum = 0.0; 39 | for(ull i = 0; i < number_of_intervals; i++){ 40 | sum += fun((interval_start + interval_width/2.0)); 41 | interval_start += interval_width; 42 | } 43 | 44 | return sum * interval_width; 45 | } 46 | 47 | /** 48 | * argv[0] - execution command - always passed as default 49 | * argv[1] - width of single rectangle that constructs 0...1 range 50 | * argv[2] - number of processes that should be spawn to parallel the work 51 | */ 52 | int main(int argc, char** argv) { 53 | // if we didn't get 3 arguments we cannot proceed 54 | if(argc < 3) { 55 | printf("Usage: %s \n", argv[0]); 56 | return -1; 57 | } 58 | 59 | // convert precision to double variable 60 | double interval_width = strtod(argv[1], NULL); 61 | 62 | // convert number of processes to long variable 63 | long num_of_processes = strtol(argv[2], NULL, 10); 64 | 65 | /** 66 | * check if there is enough intervals for all the processes 67 | */ 68 | if(ceil((range_stop - range_start)/interval_width) < num_of_processes) { 69 | printf("To much processes needed for given interval range"); 70 | return -1; 71 | } 72 | 73 | /** 74 | * calculate number of intervals per process 75 | */ 76 | ull total_intervals_count = (ull)ceil((double)(range_stop - range_start)/interval_width); 77 | ull intervals_per_process = total_intervals_count/num_of_processes; 78 | 79 | 80 | double interval_start = range_start; 81 | double interval_stop = range_start; 82 | 83 | /** 84 | * create file descriptors for pipes 85 | */ 86 | int pipes_fd[num_of_processes][2]; 87 | 88 | /** 89 | * dispatch task for each process 90 | */ 91 | for(int i = 0; i < num_of_processes; i++){ 92 | interval_stop = min(range_stop, interval_start + intervals_per_process*interval_width); 93 | 94 | // create pipe for integral result 95 | if(pipe(pipes_fd[i]) < 0) { 96 | printf("Failed to create pipe\n"); 97 | return -1; 98 | } 99 | // create new process 100 | pid_t pid = fork(); 101 | 102 | if(pid == 0){ 103 | // close output of the pipe in child process 104 | close(pipes_fd[i][0]); 105 | 106 | // calculate integral 107 | double integral_result = calculate_integral(interval_start, interval_stop, f, interval_width, intervals_per_process); 108 | 109 | //write result to the pipe 110 | if(write(pipes_fd[i][1], &integral_result, sizeof(integral_result)) < 0) { 111 | printf("Failed to write to the pipe\n"); 112 | return -1; 113 | } 114 | 115 | exit(0); 116 | } 117 | 118 | // close input of the pipe in parent process 119 | close(pipes_fd[i][1]); 120 | 121 | interval_start = interval_stop; 122 | } 123 | 124 | double sum = 0.0; 125 | 126 | /** 127 | * try to read integral results from every pipe 128 | * and sum them up, read will block parent process until 129 | * there is data in pipe to read 130 | */ 131 | for(int i = 0; i < num_of_processes; i++){ 132 | double integral_result; 133 | if(read(pipes_fd[i][0], &integral_result, sizeof(integral_result)) < 0) { 134 | printf("Failed to read from the pipe\n"); 135 | return -1; 136 | } 137 | sum += integral_result; 138 | } 139 | 140 | /** 141 | * print final result 142 | */ 143 | printf("total result: %lf\n", sum); 144 | 145 | return 0; 146 | } -------------------------------------------------------------------------------- /lab06/zad02/.gitignore: -------------------------------------------------------------------------------- 1 | *.fifo -------------------------------------------------------------------------------- /lab06/zad02/Makefile: -------------------------------------------------------------------------------- 1 | # C compiler command 2 | CC=gcc 3 | # defines path to put all binary files 4 | BUILD_DIR=build 5 | 6 | # default build configuration, i.e `make all BUILD=release` or `make all BUILD=debug` overwrites this flag 7 | BUILD := debug 8 | 9 | # C flags common in all build configurations 10 | # -Wall - enable all warnings 11 | # -std=c11 - set c standard to 11 12 | cflags.common := -std=c11 -Wall 13 | # disable all optimizations and add debug information in debug build 14 | cflags.debug := -g -O0 15 | # enable O2 optimization in release mode 16 | cflags.release := -O2 17 | # build CFLAGS variable based on selected build configuration 18 | CFLAGS := ${cflags.${BUILD}} ${cflags.common} 19 | # using direct path to time binary to be able to format time output 20 | TIME := /usr/bin/time 21 | 22 | .PHONY: all clean 23 | 24 | # create all executables 25 | all: integral_calculator integral_manager 26 | 27 | # create signal_demo executable 28 | integral_manager: integral_manager.c 29 | mkdir -p $(BUILD_DIR) 30 | $(CC) $(CFLAGS) -o $(BUILD_DIR)/$@ $< 31 | 32 | integral_calculator: integral_calculator.c 33 | mkdir -p $(BUILD_DIR) 34 | $(CC) $(CFLAGS) -o $(BUILD_DIR)/$@ $< 35 | 36 | run_calculator: integral_calculator 37 | ./$(BUILD_DIR)/integral_calculator 38 | 39 | run_manager: integral_manager 40 | ./$(BUILD_DIR)/integral_manager 41 | 42 | # removes all unnecesery build objects 43 | clean: 44 | rm -rf $(BUILD_DIR) 45 | rm -rf *.fifo -------------------------------------------------------------------------------- /lab06/zad02/integral_calculator.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "integral_pipe_specs.h" 9 | 10 | #define min(a, b) (a < b ? a : b) 11 | 12 | /// @brief function to calculate the value of the function at a given point 13 | /// @param x point at which the function should be calculated 14 | double f(double x){ 15 | return 4/(x*x + 1); 16 | } 17 | 18 | /// @brief function to calculate the integral of a given function in a given range 19 | /// @param range range of the integral 20 | /// @param fun function to be integrated 21 | double calculate_integral(integral_range_t* range, double (*fun)(double)){ 22 | double sum = 0.0; 23 | double interval_width = (range->range_stop - range->range_start)/range->number_of_intervals; 24 | 25 | double interval_start = range->range_start; 26 | 27 | for(ull i = 0; i < range->number_of_intervals; i++){ 28 | sum += fun((interval_start + interval_width/2.0)); 29 | interval_start += interval_width; 30 | } 31 | 32 | return sum * interval_width; 33 | } 34 | 35 | /// @brief function to unlink the pipes - remove them from the filesystem 36 | void unlink_pipes(){ 37 | unlink(IN_PIPE_NAME); 38 | unlink(OUT_PIPE_NAME); 39 | } 40 | 41 | int main() { 42 | // try to create the named pipes 43 | if(mkfifo(IN_PIPE_NAME, S_IRWXU) != 0) { 44 | printf("Failed to create input pipe"); 45 | return -1; 46 | } 47 | if(mkfifo(OUT_PIPE_NAME, S_IRWXU) != 0) { 48 | printf("Failed to create output pipe"); 49 | unlink_pipes(); 50 | return -1; 51 | } 52 | 53 | // try to open input file descriptor 54 | int input_pipe_fd = open(IN_PIPE_NAME, O_RDONLY); 55 | if(input_pipe_fd < 0){ 56 | printf("Failed to open input pipe"); 57 | unlink_pipes(); 58 | return -1; 59 | } 60 | 61 | // try to open output file descriptor 62 | int output_pipe_fd = open(OUT_PIPE_NAME, O_WRONLY); 63 | if(output_pipe_fd < 0){ 64 | printf("Failed to open output pipe"); 65 | unlink_pipes(); 66 | return -1; 67 | } 68 | 69 | integral_range_t range; 70 | double integral_result; 71 | 72 | // wait for the input from the input pipe and calculate the integral, then write the result to the output pipe 73 | while(read(input_pipe_fd, &range, sizeof(range)) > 0) { 74 | integral_result = calculate_integral(&range, f); 75 | if(write(output_pipe_fd, &integral_result, sizeof(integral_result)) < 0) { 76 | printf("Failed to write to output pipe"); 77 | break; 78 | } 79 | } 80 | 81 | 82 | // close and unlink the pipes 83 | close(input_pipe_fd); 84 | close(output_pipe_fd); 85 | unlink_pipes(); 86 | return 0; 87 | } -------------------------------------------------------------------------------- /lab06/zad02/integral_manager.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "integral_pipe_specs.h" 9 | 10 | int main() { 11 | integral_range_t range; 12 | 13 | 14 | /** 15 | * open named pipes for communication 16 | */ 17 | int input_pipe_fd = open(IN_PIPE_NAME, O_WRONLY); 18 | int output_pipe_fd = open(OUT_PIPE_NAME, O_RDONLY); 19 | 20 | double integral_result; 21 | 22 | /** 23 | * listen on std input for integral range specs and write them to the pipe, later wait for the calculated result 24 | */ 25 | while(1) { 26 | printf("Integral range start: \n"); 27 | if(scanf("%lf", &range.range_start) < 0) { 28 | printf("Failed to read from the stdin\n"); 29 | return -1; 30 | } 31 | printf("Integral range stop: \n"); 32 | if(scanf("%lf", &range.range_stop) < 0) { 33 | printf("Failed to read from the stdin\n"); 34 | return -1; 35 | } 36 | printf("Integral number of intervals: \n"); 37 | if(scanf("%llu", &range.number_of_intervals) < 0) { 38 | printf("Failed to read from the stdin\n"); 39 | return -1; 40 | } 41 | 42 | if(write(input_pipe_fd, &range, sizeof(range)) < 0) { 43 | printf("Failed to write to the pipe\n"); 44 | return -1; 45 | } 46 | if(read(output_pipe_fd, &integral_result, sizeof(integral_result)) < 0) { 47 | printf("Failed to read from the pipe\n"); 48 | return -1; 49 | } 50 | 51 | printf("Integral result: %lf\n\n", integral_result); 52 | } 53 | 54 | /** 55 | * close the pipes 56 | */ 57 | close(input_pipe_fd); 58 | close(output_pipe_fd); 59 | 60 | return 0; 61 | } -------------------------------------------------------------------------------- /lab06/zad02/integral_pipe_specs.h: -------------------------------------------------------------------------------- 1 | #ifndef INTEGRAL_PIPE_SPECS_H 2 | #define INTEGRAL_PIPE_SPECS_H 3 | 4 | typedef unsigned long long ull; 5 | 6 | /** name of the pipes */ 7 | #define IN_PIPE_NAME "int_calc_in.fifo" 8 | #define OUT_PIPE_NAME "int_calc_out.fifo" 9 | 10 | /** struct for the data to be pushed */ 11 | typedef struct { 12 | double range_start; 13 | double range_stop; 14 | 15 | ull number_of_intervals; 16 | } integral_range_t; 17 | 18 | #endif //INTEGRAL_PIPE_SPECS_H -------------------------------------------------------------------------------- /lab07/Makefile: -------------------------------------------------------------------------------- 1 | # C compiler command 2 | CC=gcc 3 | # defines path to put all binary files 4 | BUILD_DIR=build 5 | 6 | # default build configuration, i.e `make all BUILD=release` or `make all BUILD=debug` overwrites this flag 7 | BUILD := debug 8 | 9 | # C flags common in all build configurations 10 | # -Wall - enable all warnings 11 | # -std=c11 - set c standard to 11 12 | cflags.common := -std=c11 -Wall 13 | # disable all optimizations and add debug information in debug build 14 | cflags.debug := -g -O0 15 | # enable O2 optimization in release mode 16 | cflags.release := -O2 17 | # build CFLAGS variable based on selected build configuration 18 | CFLAGS := ${cflags.${BUILD}} ${cflags.common} 19 | 20 | .PHONY: all clean 21 | 22 | # create all executables 23 | all: client server 24 | 25 | # create signal_demo executable 26 | client: client.c 27 | mkdir -p $(BUILD_DIR) 28 | $(CC) $(CFLAGS) -o $(BUILD_DIR)/$@ $< 29 | 30 | server: server.c 31 | rm -rf /dev/mqueue/* 32 | mkdir -p $(BUILD_DIR) 33 | $(CC) $(CFLAGS) -o $(BUILD_DIR)/$@ $< 34 | 35 | run_client: client 36 | ./$(BUILD_DIR)/client 37 | 38 | run_server: server 39 | ./$(BUILD_DIR)/server 40 | 41 | # removes all unnecesery build objects 42 | clean: 43 | rm -f /dev/mqueue/* 44 | rm -rf $(BUILD_DIR) -------------------------------------------------------------------------------- /lab07/README.md: -------------------------------------------------------------------------------- 1 | # Zadanie 1. Prosty chat 2 | 3 | Napisz prosty program typu klient-serwer, w którym komunikacja zrealizowana jest za pomocą kolejek komunikatów. 4 | Serwer po uruchomieniu tworzy nową kolejkę komunikatów. Za pomocą tej kolejki klienci będą wysyłać komunikaty do serwera. Komunikaty wysyłane przez klientów mogą zawierać polecenie oznaczające pierwsze nawiązanie połaczenia z serwerem (INIT) lub jeśli wcześniej połączenie zostało już nawiązane: identyfikator klienta wraz z komunikatem, który ma zostać przekazany przez serwer do wszystkich pozostałych klientów. W odpowiedzi na polecenie INIT, serwer ma przesłać identyfikator nadany nowemu klientowi. 5 | Klient bezpośrednio po uruchomieniu tworzy kolejkę z unikalnym kluczem IPC i wysyła jej klucz do serwera wraz z komunikatem INIT. Po otrzymaniu takiego komunikatu, serwer otwiera kolejkę klienta, przydziela klientowi identyfikator (np. numer w kolejności zgłoszeń) i odsyła ten identyfikator do klienta (komunikacja w kierunku serwer->klient odbywa się za pomocą kolejki klienta). Po otrzymaniu identyfikatora, klient może wysłać do serwera komunikaty, które serwer będzie przesyłał do wszystkich pozostałych klientów. Komunikaty te są czytane ze standardowego wejścia. Klient po uruchomieniu tworzy drugi proces, który powinien odbierać komunikaty wysyłane przez serwer (przy użyciu kolejki komunikatów klienta) i wyświetlać te komunikaty na standardowym wyjściu. 6 | Klient i serwer należy napisać w postaci osobnych programów. Serwer musi być w stanie pracować jednocześnie z wieloma klientami. Dla uproszczenia można przyjąć, że serwer przechowuje informacje o klientach w statycznej tablicy (rozmiar tablicy ogranicza liczbę klientów, którzy mogą się zgłosić do serwera). 7 | 8 | Powyższe zadanie można zrealizować wykorzystując mechanizmy System V lub POSIX. -------------------------------------------------------------------------------- /lab07/client.c: -------------------------------------------------------------------------------- 1 | #define _XOPEN_SOURCE 700 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #include "protocol_specs.h" 14 | 15 | /** signal handler to exit from loop */ 16 | volatile bool should_close = false; 17 | 18 | void SIGNAL_handler(int signum) { 19 | should_close = true; 20 | } 21 | 22 | // helper macro for getting minimum of two values 23 | #define MIN(a, b) (a < b ? a : b) 24 | 25 | int main() { 26 | /* create unique identifier for client queue */ 27 | /* I set name to be associated with clients pid because it increases chances of generating unique queue name */ 28 | pid_t pid = getpid(); 29 | char queue_name[CLIENT_QUEUE_NAME_SIZE] = {0}; 30 | sprintf(queue_name, "/simple_chat_client_queue_%d", pid); 31 | 32 | /** 33 | * Fill in structure speicifing options for creation of client queue 34 | */ 35 | struct mq_attr attributes = { 36 | .mq_flags = 0, 37 | .mq_msgsize = sizeof(message_t), 38 | .mq_maxmsg = 10 39 | }; 40 | 41 | /** 42 | * Create client queue with options for server -> client communication 43 | * - O_RDWR - open for reading and writing 44 | * - O_CREAT - create queue if it does not exist 45 | * - S_IRUSR | S_IWUSR - set permissions for user to read and write 46 | */ 47 | mqd_t mq_client_descriptor = mq_open(queue_name, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR, &attributes); 48 | if(mq_client_descriptor < 0) 49 | perror("mq_open client"); 50 | 51 | /** 52 | * Try opening server queue for client -> server communication 53 | */ 54 | mqd_t mq_server_descriptor = mq_open(SERVER_QUEUE_NAME, O_RDWR, S_IRUSR | S_IWUSR, NULL); 55 | if(mq_server_descriptor < 0) { 56 | printf("Most likely server not opened!\n"); 57 | perror("mq_open server"); 58 | } 59 | 60 | /** 61 | * Setup INIT message with client queue name 62 | */ 63 | message_t message_init = { 64 | .type = INIT, 65 | .identifier = -1 66 | }; 67 | 68 | // copy queue name to message buffer, MIN is used to prevent buffer overflow 69 | memcpy(message_init.text, queue_name, MIN(CLIENT_QUEUE_NAME_SIZE - 1, strlen(queue_name))); 70 | 71 | // send INIT message to server 72 | if(mq_send(mq_server_descriptor, (char*)&message_init, sizeof(message_init), 10) < 0){ 73 | perror("mq_send init"); 74 | } 75 | 76 | // create pipe for communication between parent (writing to server) and child (reading from server) processes 77 | int to_parent_pipe[2]; 78 | if(pipe(to_parent_pipe) < 0) 79 | perror("pipe"); 80 | 81 | // register signal handler for closing client to all signals 82 | for (int sig = 1; sig < SIGRTMAX; sig++) { 83 | signal(sig, SIGNAL_handler); 84 | } 85 | 86 | // create child process for listening to messages from server 87 | pid_t listener_pid = fork(); 88 | if (listener_pid < 0) 89 | perror("fork listener"); 90 | else if (listener_pid == 0) { 91 | // close reading end of the pipe 92 | close(to_parent_pipe[0]); 93 | message_t receive_message; 94 | while(!should_close) { 95 | // receive message from server (block until message is received) 96 | mq_receive(mq_client_descriptor, (char*)&receive_message, sizeof(receive_message), NULL); 97 | switch(receive_message.type) { 98 | /* we received standard message from server with text data */ 99 | case MESSAGE_TEXT: 100 | printf("Received from id: %d message: %s\n", receive_message.identifier, receive_message.text); 101 | break; 102 | /* we received client identifier from server */ 103 | case IDENTIFIER: 104 | printf("Received identifier from server: %d\n", receive_message.identifier); 105 | // write client identifier to parent process 106 | write(to_parent_pipe[1], &receive_message.identifier, sizeof(receive_message.identifier)); 107 | break; 108 | default: 109 | printf("Unknown message type in client queue: %d", receive_message.type); 110 | break; 111 | } 112 | } 113 | printf("Exiting from receive loop\n"); 114 | exit(0); 115 | } else { 116 | //close writing end of the pipe 117 | close(to_parent_pipe[1]); 118 | int identifier = -1; 119 | 120 | // wait and read client identifier from child process 121 | if(read(to_parent_pipe[0], &identifier, sizeof(identifier)) < 0) 122 | perror("read identifier"); 123 | 124 | char* buffer = NULL; 125 | while(!should_close) { 126 | // get message queue attributes 127 | mq_getattr(mq_server_descriptor, &attributes); 128 | // check if we can send more messages to server 129 | if(attributes.mq_curmsgs >= attributes.mq_maxmsg) { 130 | printf("Server is busy, please wait\n"); 131 | continue; 132 | } 133 | 134 | /** 135 | * save version of scanf - allocates memory with string of given length thanks to %ms parameter specifier, 136 | * needs to be free after 137 | * prevent buffer overflow 138 | * */ 139 | if(scanf("%ms", &buffer) == 1) { 140 | // setup message for sending to server 141 | message_t send_message = { 142 | .type = MESSAGE_TEXT, 143 | .identifier = identifier 144 | }; 145 | // copy message to message buffer, MIN is used to prevent buffer overflow 146 | memcpy(send_message.text, buffer, MIN(MESSAGE_BUFFER_SIZE - 1, strlen(buffer))); 147 | 148 | // send message to server 149 | mq_send(mq_server_descriptor, (char*)&send_message, sizeof(send_message), 10); 150 | 151 | // free memory allocated by scanf 152 | free(buffer); 153 | buffer = NULL; 154 | } else 155 | perror("scanf input"); 156 | } 157 | 158 | printf("Exiting from sending loop\n"); 159 | 160 | /* If we did make connection notify server about closing */ 161 | if(identifier != -1){ 162 | /** Prepare notification for the server that client is closing */ 163 | message_t message_close = { 164 | .type = CLIENT_CLOSE, 165 | .identifier = identifier 166 | }; 167 | /* Notify server that client is closing */ 168 | mq_send(mq_server_descriptor, (char*)&message_close, sizeof(message_close), 10); 169 | } 170 | 171 | /* Close quees */ 172 | mq_close(mq_server_descriptor); 173 | mq_close(mq_client_descriptor); 174 | 175 | /** Delete created queues file descriptor */ 176 | mq_unlink(queue_name); 177 | } 178 | } -------------------------------------------------------------------------------- /lab07/protocol_specs.h: -------------------------------------------------------------------------------- 1 | #ifndef PROTOCOL_SPECS_H 2 | #define PROTOCOL_SPECS_H 3 | 4 | #define MESSAGE_BUFFER_SIZE 2048 5 | #define SERVER_QUEUE_NAME "/simple_chat_server_queue" 6 | 7 | #define CLIENT_QUEUE_NAME_SIZE 40 8 | #define MAX_CLIENTS_COUNT 3 9 | 10 | /* Enum for defining message type */ 11 | typedef enum { 12 | INIT, 13 | IDENTIFIER, 14 | MESSAGE_TEXT, 15 | CLIENT_CLOSE 16 | } message_type_t; 17 | 18 | /* structure defined for the message */ 19 | typedef struct { 20 | message_type_t type; 21 | 22 | int identifier; 23 | char text[MESSAGE_BUFFER_SIZE]; 24 | } message_t; 25 | 26 | #endif //PROTOCOL_SPECS_H -------------------------------------------------------------------------------- /lab07/server.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "protocol_specs.h" 9 | 10 | /** signal handler to exit from loop */ 11 | volatile bool should_close = false; 12 | 13 | void SIGNAL_handler(int signum) { 14 | should_close = true; 15 | } 16 | 17 | int main() { 18 | /** 19 | * Fill in structure speicifing options for creation of client queue 20 | */ 21 | struct mq_attr attributes = { 22 | .mq_flags = 0, 23 | .mq_msgsize = sizeof(message_t), 24 | .mq_maxmsg = 10 25 | }; 26 | 27 | /** 28 | * Create server queue with options for client -> server communication 29 | * - O_RDWR - open for reading and writing 30 | * - O_CREAT - create queue if it does not exist 31 | * - S_IRUSR | S_IWUSR - set permissions for user to read and write 32 | */ 33 | mqd_t mq_descriptor = mq_open(SERVER_QUEUE_NAME, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR, &attributes); 34 | if(mq_descriptor < 0) 35 | perror("mq_open server"); 36 | 37 | message_t receive_message; 38 | 39 | /** 40 | * Array of client queues descriptors 41 | * - initialized with -1 to indicate that id is not used 42 | */ 43 | mqd_t client_queues[MAX_CLIENTS_COUNT]; 44 | for (int i = 0; i < MAX_CLIENTS_COUNT; i++) 45 | client_queues[i] = -1; 46 | 47 | 48 | // register signal handler for closing client to all signals 49 | for (int sig = 1; sig < SIGRTMAX; sig++) { 50 | signal(sig, SIGNAL_handler); 51 | } 52 | 53 | while(!should_close) { 54 | // receive message from server queue 55 | mq_receive(mq_descriptor, (char*)&receive_message, sizeof(receive_message), NULL); 56 | 57 | switch(receive_message.type) { 58 | /* Requested initialization from the client */ 59 | case INIT: 60 | /* Find first available id */ 61 | int id = 0; 62 | while(client_queues[id] != -1 && id < MAX_CLIENTS_COUNT) id++; 63 | 64 | /* If all id's are used, we skip initialization for that client */ 65 | if(id == MAX_CLIENTS_COUNT){ 66 | printf("Max number of clients has connected, can't open another connection\n"); 67 | continue; 68 | } 69 | 70 | /* Open client queue with name received from client */ 71 | client_queues[id] = mq_open(receive_message.text, O_RDWR, S_IRUSR | S_IWUSR, NULL); 72 | if(client_queues[id] < 0) 73 | perror("mq_open client"); 74 | 75 | /* Send identifier to client */ 76 | message_t send_message = { 77 | .type = IDENTIFIER, 78 | .identifier = id 79 | }; 80 | 81 | mq_send(client_queues[id], (char*)&send_message, sizeof(send_message), 10); 82 | printf("Registered connection with client at id: %d\n", id); 83 | break; 84 | /* Received standard message to be broadcasted */ 85 | case MESSAGE_TEXT: 86 | /* Loop over all possible id's and broadcast received message to all connected clients */ 87 | for (int identifier = 0; identifier < MAX_CLIENTS_COUNT; identifier++){ 88 | if(identifier != receive_message.identifier && identifier != -1){ 89 | /* Broadcast received message */ 90 | mq_send(client_queues[identifier], (char*)&receive_message, sizeof(receive_message), 10); 91 | } 92 | } 93 | break; 94 | /* Received message from client informing that client has been closed */ 95 | case CLIENT_CLOSE: 96 | /* Close client queue */ 97 | mq_close(client_queues[receive_message.identifier]); 98 | /* Mark that id is not used */ 99 | client_queues[receive_message.identifier] = -1; 100 | printf("Closed connection with client at id: %d\n", receive_message.identifier); 101 | break; 102 | default: 103 | printf("Unexpected message type in server queue: %d \n", receive_message.type); 104 | break; 105 | } 106 | } 107 | 108 | printf("Exiting server\n"); 109 | 110 | /** 111 | * Close all client queues 112 | */ 113 | for (int i = 0; i < MAX_CLIENTS_COUNT; i++){ 114 | if(client_queues[i] != -1) 115 | mq_close(client_queues[i]); 116 | } 117 | 118 | /** 119 | * Close and unlink server queue 120 | */ 121 | mq_close(mq_descriptor); 122 | mq_unlink(SERVER_QUEUE_NAME); 123 | 124 | return 0; 125 | } -------------------------------------------------------------------------------- /lab08/Makefile: -------------------------------------------------------------------------------- 1 | # C compiler command 2 | CC=gcc 3 | # defines path to put all binary files 4 | BUILD_DIR=build 5 | 6 | # default build configuration, i.e `make all BUILD=release` or `make all BUILD=debug` overwrites this flag 7 | BUILD := debug 8 | 9 | # C flags common in all build configurations 10 | # -Wall - enable all warnings 11 | # -std=c11 - set c standard to 11 12 | cflags.common := -std=c11 -Wall 13 | # disable all optimizations and add debug information in debug build 14 | cflags.debug := -g -O0 15 | # enable O2 optimization in release mode 16 | cflags.release := -O2 17 | # build CFLAGS variable based on selected build configuration 18 | CFLAGS := ${cflags.${BUILD}} ${cflags.common} 19 | 20 | .PHONY: all clean 21 | 22 | # create all executables 23 | all: printers_system users_simulator 24 | 25 | # create executables 26 | printers_system: printers_system.c 27 | mkdir -p $(BUILD_DIR) 28 | $(CC) $(CFLAGS) -o $(BUILD_DIR)/$@ $< -lrt -lpthread 29 | 30 | users_simulator: users_simulator.c 31 | rm -rf /dev/mqueue/* 32 | mkdir -p $(BUILD_DIR) 33 | $(CC) $(CFLAGS) -o $(BUILD_DIR)/$@ $< -lrt -lpthread 34 | 35 | run_printers_system: printers_system 36 | ./$(BUILD_DIR)/printers_system 5 37 | 38 | run_users_simulator: users_simulator 39 | ./$(BUILD_DIR)/users_simulator 10 40 | 41 | # removes all unnecesery build objects 42 | clean: 43 | rm -rf $(BUILD_DIR) -------------------------------------------------------------------------------- /lab08/README.md: -------------------------------------------------------------------------------- 1 | # Zadanie 1. Symulator systemu wydruku 2 | 3 | Wykorzystując semafory i pamięć wspólną z IPC Systemu V lub standardu POSIX napisz program symulujący działanie systemu wydruku: 4 | 5 | System składa się z N użytkowników oraz M drukarek. Każdy z użytkowników może wysłać do systemu zadanie wydruku tekstu składającego się z 10 znaków. Drukarka, która nie jest aktualnie zajęta, podejmuje się zadania "wydruku" tekstu danego zadania. Wydruk w zadaniu polega na wypisaniu na standardowym wyjściu znaków wysłanych wcześniej do wydruku w ten sposób, że każdy następny znak wpisywany jest co jedną sekundę. Jeżeli wszystkie drukarki są zajęte to zlecenia wydruku są kolejkowane w opisywanym systemie wydruku. Jeżeli kolejka jest pełna to użytkownik chcący zlecić zadanie wydruku czeka do momentu gdy będzie można zlecenie wpisać do kolejki. 6 | 7 | Każdy z N użytkowników powinien przesyłać do systemu wydruku zadanie wydruku 10 losowych znaków (od 'a' do 'z') a następnie odczekać losową liczbe sekund. Zadania zlecenia wydruku i odczekania powinny być wykonane w nieskończonej pętli. 8 | 9 | Należy zsynchronizować prace użytkowników oraz drukarek. Należy użyć mechanizmów System V lub POSIX. -------------------------------------------------------------------------------- /lab08/printers_system.c: -------------------------------------------------------------------------------- 1 | #define _XOPEN_SOURCE 700 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #include "shared_memory_specs.h" 15 | 16 | volatile bool should_close = false; 17 | 18 | /** 19 | * @brief Signal handler for closing client 20 | * 21 | * @param signum - signal number 22 | */ 23 | void SIGNAL_handler(int signum) { 24 | should_close = true; 25 | } 26 | 27 | /** 28 | * argv[0] - execution command - always passed as default 29 | * argv[1] - number of printers to spawn 30 | */ 31 | int main(int argc, char** argv) { 32 | if(argc < 2) { 33 | printf("Usage: %s \n", argv[0]); 34 | return -1; 35 | } 36 | // convert argument defining number of printers to spawn 37 | long number_of_printers = strtol(argv[1], NULL, 10); 38 | 39 | // check if number of printers is not too big 40 | if (number_of_printers > MAX_PRINTERS) { 41 | printf("Number of printers is too big, maximum number of printers is %d\n", MAX_PRINTERS); 42 | return -1; 43 | } 44 | 45 | /* Try to create and open file descriptor for shared memory region */ 46 | int memory_fd = shm_open(SHARED_MEMORY_DESCRIPTOR_NAME, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR); 47 | if(memory_fd < 0) 48 | perror("shm_open"); 49 | 50 | /* Specify shared memory size */ 51 | if(ftruncate(memory_fd, sizeof(memory_map_t)) < 0) 52 | perror("ftruncate"); 53 | 54 | /* try to map shared memory region to program address space */ 55 | memory_map_t* memory_map = mmap(NULL, sizeof(memory_map_t), PROT_READ | PROT_WRITE, MAP_SHARED, memory_fd, 0); 56 | if (memory_map == MAP_FAILED) 57 | perror("mmap"); 58 | 59 | /* Clean shared memory region */ 60 | memset(memory_map, 0, sizeof(memory_map_t)); 61 | 62 | /* Set number of accessible printers */ 63 | memory_map->number_of_printers = number_of_printers; 64 | 65 | // register signal handler for closing client to all signals 66 | for (int sig = 1; sig < SIGRTMAX; sig++) { 67 | signal(sig, SIGNAL_handler); 68 | } 69 | 70 | /* spawn all the printers */ 71 | for (int i = 0; i < number_of_printers; i++){ 72 | /* initialize unnamed semaphores */ 73 | sem_init(&memory_map->printers[i].printer_semaphore, 1, 1); 74 | 75 | pid_t printer_pid = fork(); 76 | if(printer_pid < 0) { 77 | perror("fork"); 78 | return -1; 79 | } 80 | else if(printer_pid == 0) { 81 | /* Printer process - wait for printing request and print data */ 82 | while(!should_close) { 83 | /* if user has signalized that printer should print, start printing data from the buffer */ 84 | if (memory_map->printers[i].printer_state == PRINTING) { 85 | 86 | /* print data from the buffer with 1s delay after each character*/ 87 | for (int j = 0; j < memory_map->printers[i].printer_buffer_size; j++) { 88 | printf("%c", memory_map->printers[i].printer_buffer[j]); 89 | sleep(1); 90 | } 91 | 92 | /* add newline and flush data to be displayed on the screen */ 93 | printf("\n"); 94 | fflush(stdout); 95 | 96 | /* reset printer state */ 97 | memory_map->printers[i].printer_state = WAITING; 98 | 99 | /* increment semaphore to signalize that printing has been done */ 100 | sem_post(&memory_map->printers[i].printer_semaphore); 101 | } 102 | } 103 | exit(0); 104 | } 105 | } 106 | 107 | /* wait for all printers to finish */ 108 | while(wait(NULL) > 0) {} 109 | 110 | /* destroy semaphores */ 111 | for (int i = 0; i < number_of_printers; i++) 112 | sem_destroy(&memory_map->printers[i].printer_semaphore); 113 | 114 | 115 | /* clean up */ 116 | munmap(memory_map, sizeof(memory_map_t)); 117 | 118 | /* close shared memory file descriptor */ 119 | shm_unlink(SHARED_MEMORY_DESCRIPTOR_NAME); 120 | } -------------------------------------------------------------------------------- /lab08/shared_memory_specs.h: -------------------------------------------------------------------------------- 1 | #ifndef SHARED_MEMORY_SPEC_H 2 | #define SHARED_MEMORY_SPEC_H 3 | 4 | #define SHARED_MEMORY_DESCRIPTOR_NAME "printer_system_shared_memory" 5 | #include 6 | 7 | #define MAX_PRINTERS 256 8 | #define MAX_PRINTER_BUFFER_SIZE 256 9 | #define MAX_SEMAPHORE_NAME 40 10 | 11 | typedef enum { 12 | WAITING = 0, 13 | PRINTING = 1 14 | } printer_state_t; 15 | 16 | typedef struct { 17 | sem_t printer_semaphore; 18 | char printer_buffer[MAX_PRINTER_BUFFER_SIZE]; 19 | size_t printer_buffer_size; 20 | printer_state_t printer_state; 21 | } printer_t; 22 | 23 | typedef struct { 24 | printer_t printers[MAX_PRINTERS]; 25 | int number_of_printers; 26 | } memory_map_t; 27 | 28 | #endif //SHARED_MEMORY_SPEC_H -------------------------------------------------------------------------------- /lab08/users_simulator.c: -------------------------------------------------------------------------------- 1 | #define _XOPEN_SOURCE 700 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #include "shared_memory_specs.h" 16 | 17 | volatile bool should_close = false; 18 | 19 | /** 20 | * @brief Signal handler for closing client 21 | * 22 | * @param signum - signal number 23 | */ 24 | void SIGNAL_handler(int signum) { 25 | should_close = true; 26 | } 27 | 28 | /** 29 | * @brief Generate random string of given length, function will append \0 at the end of string 30 | * 31 | * @param buffer - buffer to store generated string 32 | * @param length - length of generated string 33 | */ 34 | void generate_random_string(char* buffer, int length) { 35 | for(int i = 0; i < length; i++) { 36 | buffer[i] = 'a' + rand() % 26; 37 | } 38 | buffer[length] = '\0'; 39 | } 40 | 41 | /** 42 | * argv[0] - execution command - always passed as default 43 | * argv[1] - number of users to spawn 44 | */ 45 | int main(int argc, char** argv) { 46 | if(argc < 2) { 47 | printf("Usage: %s \n", argv[0]); 48 | return -1; 49 | } 50 | 51 | /* convert argument defining number of users to spawn */ 52 | long number_of_users = strtol(argv[1], NULL, 10); 53 | 54 | /* open descriptor defining shared memory that will be mapped */ 55 | int memory_fd = shm_open(SHARED_MEMORY_DESCRIPTOR_NAME, O_RDWR, S_IRUSR | S_IWUSR); 56 | if(memory_fd < 0) 57 | perror("shm_open"); 58 | 59 | /* try to map shared memory region to program address space with READ and WRITE permission with flag that memory will be shared */ 60 | memory_map_t* memory_map = mmap(NULL, sizeof(memory_map_t), PROT_READ | PROT_WRITE, MAP_SHARED, memory_fd, 0); 61 | if (memory_map == MAP_FAILED) 62 | perror("mmap"); 63 | 64 | /* Define buffer for random string */ 65 | char user_buffer[MAX_PRINTER_BUFFER_SIZE] = {0}; 66 | 67 | // register signal handler for closing client to all signals 68 | for (int sig = 1; sig < SIGRTMAX; sig++) { 69 | signal(sig, SIGNAL_handler); 70 | } 71 | 72 | 73 | int seed = 0; 74 | // spawn users 75 | for (int i = 0; i < number_of_users; i++){ 76 | seed += 10; 77 | pid_t user_pid = fork(); 78 | if (user_pid < 0) { /* Failed to spawn all users */ 79 | perror("fork"); 80 | return -1; 81 | } 82 | else if(user_pid == 0) { 83 | srand(seed); 84 | /* Try to dispatch data to the printers in random manner */ 85 | while(!should_close) { 86 | /* Generate random message */ 87 | generate_random_string(user_buffer, 10); 88 | 89 | /** 90 | * try finding printer that is not busy at the moment 91 | */ 92 | int printer_index = -1; 93 | for (int j = 0; j < memory_map->number_of_printers; j++) { 94 | int val; 95 | sem_getvalue(&memory_map->printers[j].printer_semaphore, &val); 96 | if(val > 0) { 97 | printer_index = j; 98 | break; 99 | } 100 | } 101 | 102 | /** 103 | * if all printers are busy, dispatch work to random printer to uniformly distribute work 104 | */ 105 | if(printer_index == -1) 106 | printer_index = rand() % memory_map->number_of_printers; 107 | 108 | /* try to decrement semaphore, blocks program if the printer is currently busy */ 109 | if(sem_wait(&memory_map->printers[printer_index].printer_semaphore) < 0) 110 | perror("sem_wait"); 111 | 112 | /* copy data to printer buffer */ 113 | memcpy(memory_map->printers[printer_index].printer_buffer, user_buffer, MAX_PRINTER_BUFFER_SIZE); 114 | memory_map->printers[printer_index].printer_buffer_size = strlen(user_buffer); 115 | 116 | /* set printer state to printing */ 117 | memory_map->printers[printer_index].printer_state = PRINTING; 118 | 119 | printf("User %d is printing on printer %d\n", i, printer_index); 120 | fflush(stdout); 121 | 122 | /* sleep for random amount of time before trying to dispatch new work */ 123 | sleep(rand() % 3 + 1); 124 | } 125 | exit(0); 126 | } 127 | } 128 | 129 | // wait for all children to finish 130 | while(wait(NULL) > 0) {}; 131 | 132 | // unmap memory map 133 | munmap(memory_map, sizeof(memory_map_t)); 134 | } -------------------------------------------------------------------------------- /lab09/Makefile: -------------------------------------------------------------------------------- 1 | # C compiler command 2 | CC=gcc 3 | # defines path to put all binary files 4 | BUILD_DIR=build 5 | 6 | # default build configuration, i.e `make all BUILD=release` or `make all BUILD=debug` overwrites this flag 7 | BUILD := debug 8 | 9 | # C flags common in all build configurations 10 | # -Wall - enable all warnings 11 | # -std=c11 - set c standard to 11 12 | cflags.common := -std=c11 -Wall 13 | # disable all optimizations and add debug information in debug build 14 | cflags.debug := -g -O0 15 | # enable O2 optimization in release mode 16 | cflags.release := -O2 17 | # build CFLAGS variable based on selected build configuration 18 | CFLAGS := ${cflags.${BUILD}} ${cflags.common} 19 | 20 | .PHONY: all clean 21 | 22 | # create all executables 23 | all: life 24 | 25 | # create executables 26 | life: grid.c life.c 27 | mkdir -p $(BUILD_DIR) 28 | $(CC) $(CFLAGS) grid.c life.c -o $(BUILD_DIR)/life -lncursesw -lpthread -lm 29 | 30 | run_life: life 31 | ./$(BUILD_DIR)/life 32 | 33 | # removes all unnecesery build objects 34 | clean: 35 | rm -rf $(BUILD_DIR) -------------------------------------------------------------------------------- /lab09/README.md: -------------------------------------------------------------------------------- 1 | # Gra w życie 2 | Napisz współbieżną wersję gry w życie, gdzie obliczanie stanu planszy jest rozdzielone na wątki. Utwórz n wątków, gdzie n to parametr programu, i każdemu przypisz komórki, za które dany wątek jest odpowiedzialny. Rozdziel prace pomiędzy wątkami maksymalnie równomiernie. 3 | 4 | Każdy z utworzonych wątków powinien obliczać stan komórek, za które jest odpowiedzialny w następnym kroku czasowym. Wątek główny powinien zajmować się tylko zlecaniem prac i wyświetlaniem wyników. Wątki powinny być tworzone tylko raz na początku programu i powinny wykonywać swoją pracę raz na iterację. (sugestia rozwiązania: `pause()` i `pthread_kill()`) 5 | 6 | Do skompilowania gry potrzebna jest biblioteka ncurses. W niektórych dystrybucjach Linux-a trzeba zainstalować nagłówki tej biblioteki. Np.: `apt install libncurses-dev`. 7 | 8 | Gra wykorzystuje dwie tablice dla obecnego i następnego kroku czasowego. Każdy wątek zapisuje dane tylko do swoich komórek tablicy następnego kroku, a zatem dostęp do tych tablic nie musi być synchronizowany. 9 | 10 | Pomiędzy wyświetleniami planszy program czeka 0,5 sekundy na wykonanie obliczeń. W tym zadaniu ignorujemy możliwość wystąpienia sytuacji, gdzie jakiś wątek nie zdążył wykonać obliczeń w wyznaczonym czasie, tj. żadna dodatkowa synchronizacja wątków nie jest wymagana. -------------------------------------------------------------------------------- /lab09/grid.c: -------------------------------------------------------------------------------- 1 | #include "grid.h" 2 | #include 3 | #include 4 | #include 5 | 6 | 7 | char *create_grid() 8 | { 9 | return malloc(sizeof(char) * GRID_WIDTH * GRID_HEIGHT); 10 | } 11 | 12 | void destroy_grid(char *grid) 13 | { 14 | free(grid); 15 | } 16 | 17 | void draw_grid(char *grid) 18 | { 19 | for (int i = 0; i < GRID_HEIGHT; ++i) 20 | { 21 | // Two characters for more uniform spaces (vertical vs horizontal) 22 | for (int j = 0; j < GRID_WIDTH; ++j) 23 | { 24 | if (grid[i * GRID_WIDTH + j]) 25 | { 26 | mvprintw(i, j * 2, "■"); 27 | mvprintw(i, j * 2 + 1, " "); 28 | } 29 | else 30 | { 31 | mvprintw(i, j * 2, " "); 32 | mvprintw(i, j * 2 + 1, " "); 33 | } 34 | } 35 | } 36 | 37 | refresh(); 38 | } 39 | 40 | void init_grid(char *grid) 41 | { 42 | for (int i = 0; i < GRID_WIDTH * GRID_HEIGHT; ++i) 43 | grid[i] = rand() % 2 == 0; 44 | } 45 | 46 | bool is_alive(int row, int col, char *grid) 47 | { 48 | 49 | int count = 0; 50 | for (int i = -1; i <= 1; i++) 51 | { 52 | for (int j = -1; j <= 1; j++) 53 | { 54 | if (i == 0 && j == 0) 55 | { 56 | continue; 57 | } 58 | int r = row + i; 59 | int c = col + j; 60 | if (r < 0 || r >= GRID_HEIGHT || c < 0 || c >= GRID_WIDTH) 61 | { 62 | continue; 63 | } 64 | if (grid[GRID_WIDTH * r + c]) 65 | { 66 | count++; 67 | } 68 | } 69 | } 70 | 71 | if (grid[row * GRID_WIDTH + col]) 72 | { 73 | if (count == 2 || count == 3) 74 | return true; 75 | else 76 | return false; 77 | } 78 | else 79 | { 80 | if (count == 3) 81 | return true; 82 | else 83 | return false; 84 | } 85 | } 86 | 87 | void update_grid(char *src, char *dst) 88 | { 89 | for (int i = 0; i < GRID_HEIGHT; ++i) 90 | { 91 | for (int j = 0; j < GRID_WIDTH; ++j) 92 | { 93 | dst[i * GRID_WIDTH + j] = is_alive(i, j, src); 94 | } 95 | } 96 | } -------------------------------------------------------------------------------- /lab09/grid.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | #define GRID_WIDTH 30 5 | #define GRID_HEIGHT 30 6 | 7 | char *create_grid(); 8 | void destroy_grid(char *grid); 9 | void draw_grid(char *grid); 10 | void init_grid(char *grid); 11 | bool is_alive(int row, int col, char *grid); 12 | void update_grid(char *src, char *dst); -------------------------------------------------------------------------------- /lab09/life.c: -------------------------------------------------------------------------------- 1 | // declare feature test macros to enable certain functions (sigaction, usleep) 2 | #define _XOPEN_SOURCE 700 3 | #define _DEFAULT_SOURCE 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include "grid.h" 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | #define MIN(a, b) ((a) < (b) ? (a) : (b)) 18 | #define THREAD_COUNT 12 // define number of threads to use - don't exceed number of threads on your CPU 19 | 20 | /** 21 | * Define structure that will be passed to the thread function 22 | * as an argument 23 | */ 24 | typedef struct { 25 | int cell_start; // index of the starting cell (inclusive) 26 | int cell_end; // index of the ending cell (exclusive) 27 | 28 | char** background; // pointer to the background array 29 | char** foreground; // pointer to the foreground array 30 | } thread_args_t; 31 | 32 | /* declare dummy signal handler so the signal doesn't crash program */ 33 | void dummy_handler(int signo) {__asm__("nop");} 34 | 35 | void* thread_function(void* arg) { 36 | /* dereference arguments struct */ 37 | thread_args_t* args = (thread_args_t*)arg; 38 | 39 | /* thread loop */ 40 | while (true) { 41 | /* pause thread until any signal comes */ 42 | pause(); 43 | 44 | /* iterate over assigned cel */ 45 | for (int i = args->cell_start; i < args->cell_end; i++) { 46 | int row = i / GRID_WIDTH; // translate index to row 47 | int col = i % GRID_WIDTH; // translate index to column 48 | 49 | /* update cell in background array */ 50 | (*args->background)[i] = is_alive(row, col, *args->foreground); 51 | } 52 | } 53 | } 54 | 55 | int main() 56 | { 57 | /** 58 | * Set up signal handler for threads 59 | * We need to set up a dummy signal handler so the signal doesn't crash the program 60 | * We will use the signal to wake up the threads 61 | */ 62 | struct sigaction sa; 63 | sa.sa_handler = dummy_handler; 64 | sigemptyset(&sa.sa_mask); 65 | sa.sa_flags = 0; 66 | sigaction(SIGUSR1, &sa, NULL); 67 | 68 | srand(time(NULL)); 69 | setlocale(LC_CTYPE, ""); 70 | initscr(); // Start curses mode 71 | 72 | char *foreground = create_grid(); 73 | char *background = create_grid(); 74 | char *tmp; 75 | 76 | /* Declare arrays to hold threads and thread arguments */ 77 | pthread_t threads[THREAD_COUNT]; 78 | thread_args_t args[THREAD_COUNT]; 79 | 80 | int cells_per_thread = (int)ceil(GRID_HEIGHT * GRID_WIDTH / THREAD_COUNT); 81 | for (int i = 0; i < THREAD_COUNT; i++) { 82 | // Assign cells to threads 83 | args[i].cell_start = i * cells_per_thread; 84 | args[i].cell_end = MIN((i + 1) * cells_per_thread, GRID_HEIGHT * GRID_WIDTH); // exclusive, safety check for the last thread 85 | 86 | /** 87 | * We use pointers to the 1D array to be able to shuffle pointers between frames 88 | * instead of copying the whole array 89 | */ 90 | args[i].foreground = &foreground; 91 | args[i].background = &background; 92 | 93 | // create threads without any special attributes and with argument structure 94 | pthread_create(&threads[i], NULL, thread_function, &args[i]); 95 | } 96 | 97 | init_grid(foreground); 98 | 99 | while (true) 100 | { 101 | draw_grid(foreground); 102 | 103 | // Wake up threads with declared signals 104 | for(int i = 0; i < THREAD_COUNT; i++) { 105 | pthread_kill(threads[i], SIGUSR1); 106 | } 107 | 108 | usleep(500 * 1000); 109 | 110 | // Step simulation 111 | //update_grid(foreground, background); 112 | 113 | tmp = foreground; 114 | foreground = background; 115 | background = tmp; 116 | } 117 | 118 | endwin(); // End curses mode 119 | destroy_grid(foreground); 120 | destroy_grid(background); 121 | 122 | return 0; 123 | } 124 | -------------------------------------------------------------------------------- /lab10/Makefile: -------------------------------------------------------------------------------- 1 | # C compiler command 2 | CC=gcc 3 | # defines path to put all binary files 4 | BUILD_DIR=build 5 | 6 | # default build configuration, i.e `make all BUILD=release` or `make all BUILD=debug` overwrites this flag 7 | BUILD := debug 8 | 9 | # C flags common in all build configurations 10 | # -Wall - enable all warnings 11 | # -std=c11 - set c standard to 11 12 | cflags.common := -std=c11 -Wall 13 | # disable all optimizations and add debug information in debug build 14 | cflags.debug := -g -O0 15 | # enable O2 optimization in release mode 16 | cflags.release := -O2 17 | # build CFLAGS variable based on selected build configuration 18 | CFLAGS := ${cflags.${BUILD}} ${cflags.common} 19 | 20 | .PHONY: all clean 21 | 22 | # create all executables 23 | all: santa 24 | 25 | # create executables 26 | santa: santa.c 27 | mkdir -p $(BUILD_DIR) 28 | $(CC) $(CFLAGS) -o $(BUILD_DIR)/$@ $< -lpthread 29 | 30 | run: santa 31 | ./$(BUILD_DIR)/santa 32 | 33 | # removes all unnecesery build objects 34 | clean: 35 | rm -rf $(BUILD_DIR) -------------------------------------------------------------------------------- /lab10/README.md: -------------------------------------------------------------------------------- 1 | # Problem Świętego Mikołaja 2 | 3 | Święty Mikołaj śpi w swoim warsztacie na biegunie północnym i może być obudzony tylko sytuacji gdy wszystkie 9 reniferów wróciło z wakacji. 4 | 5 | Należy zaimplementować program, w którym Mikołaj oraz renifery to osobne wątki. 6 | Zachowania reniferów: 7 | 8 | - Są na wakacjach w ciepłych krajach losowy okres czasu (5-10s) 9 | - Wracaja na biegun północny (Komunikat: Renifer: czeka _ reniferów na Mikołaja, ID), jeśli wracający renifer jest dziewiątym reniferem to wybudza Mikołaja (Komunikat: Renifer: wybudzam Mikołaja, ID). 10 | - Dostarczają wraz z Mikołajem zabawki grzecznym dzieciom (i studentom którzy nie spóźniają się z dostarczaniem zestawów) przez (2-4s). 11 | - Lecą na wakacje. 12 | 13 | Zachowania Mikołaja: 14 | 15 | - Śpi. 16 | - Kiedy zostaje wybudzony (Komunikat: Mikołaj: budzę się) to dostarcza wraz z reniferami zabawki (Komunikat: Mikołaj: dostarczam zabawki) (2-4s). 17 | - Wraca do snu (Komunikat: Mikołaj: zasypiam). 18 | 19 | Program należy zaimplementować korzystając z wątków i mechanizmów synchronizacji biblioteki POSIX Threads. Po uruchomieniu programu wątek główny tworzy wątki dla Mikołaja oraz reniferów. Możemy założyć że Mikołaj dostarczy 4 razy prezenty, po czym kończy działanie wszystkich wątków. Do spania Mikołaja powinny być wykorzystane Warunki Sprawdzające (Condition Variables). Użycie odpowiednich mechanizmów ma zagwarantować niedopouszczenie, np. do zdarzeń: Mikołaj śpi chociaż czeka na niego 9 reniferów. -------------------------------------------------------------------------------- /lab10/santa.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | /** 7 | * Declare global variables for easy access between threads 8 | */ 9 | 10 | /* mutex and condition variable to wake up santa */ 11 | pthread_mutex_t santa_mutex = PTHREAD_MUTEX_INITIALIZER; 12 | pthread_cond_t santa_cond = PTHREAD_COND_INITIALIZER; 13 | 14 | /* declare mutexes for all reindeers */ 15 | pthread_mutex_t reindeers_mutexes[9] = { 16 | PTHREAD_MUTEX_INITIALIZER, 17 | PTHREAD_MUTEX_INITIALIZER, 18 | PTHREAD_MUTEX_INITIALIZER, 19 | PTHREAD_MUTEX_INITIALIZER, 20 | PTHREAD_MUTEX_INITIALIZER, 21 | PTHREAD_MUTEX_INITIALIZER, 22 | PTHREAD_MUTEX_INITIALIZER, 23 | PTHREAD_MUTEX_INITIALIZER, 24 | PTHREAD_MUTEX_INITIALIZER 25 | }; 26 | 27 | /* declare mutex and counter to count reindeers that has come back */ 28 | int reindeers_back_count = 0; 29 | pthread_mutex_t reindeers_back_mutex = PTHREAD_MUTEX_INITIALIZER; 30 | 31 | /* declare thread handles for santa and reindeer threads*/ 32 | pthread_t santa_thread; 33 | pthread_t reindeers_threads[9]; 34 | 35 | /** 36 | * @brief Function that is executed by reindeer threads 37 | * 38 | * @param arg - id of the reindeer 39 | * 40 | * @return NULL 41 | */ 42 | void* reindeer_thread_handler(void* arg){ 43 | // dereference argument pointer to get reindeer id 44 | int id = *(int*)arg; 45 | // set cancel type to asynchronous to be able to cancel thread immediately 46 | pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL); 47 | 48 | // lock reindeer mutex to prevent cycling until reindeers do not come back from santa claus - when santa releases their mutexes 49 | pthread_mutex_lock(&reindeers_mutexes[id]); 50 | while(1) { 51 | // reindeer is on vacation for 5-10 seconds 52 | sleep(rand() % 6 + 5); 53 | 54 | // lock counter mutex to increment counter of reindeers that has come back 55 | pthread_mutex_lock(&reindeers_back_mutex); 56 | printf("Renifer: czeka %d reniferów, %d\n", reindeers_back_count, id); 57 | 58 | // increment counter and check if all reindeers are back 59 | reindeers_back_count++; 60 | if (reindeers_back_count == 9) { 61 | printf("Renifer: budzę Mikołaja, %d\n", id); 62 | // if all reindeers are back, wake up santa and reset counter 63 | pthread_cond_signal(&santa_cond); 64 | reindeers_back_count = 0; 65 | } 66 | 67 | // unlock counter mutex 68 | pthread_mutex_unlock(&reindeers_back_mutex); 69 | 70 | // wait for santa to release reindeer mutex 71 | pthread_mutex_lock(&reindeers_mutexes[id]); 72 | 73 | // reindeer is flying on vacation 74 | printf("Renifer: lecę na wakacje, %d\n", id); 75 | } 76 | 77 | return NULL; 78 | } 79 | 80 | void* santa_thread_handler(void* arg) { 81 | // cycle 4 times to deliver toys 82 | for (int i = 0; i < 4; i++) { 83 | // wait for reindeers to wake up santa 84 | pthread_cond_wait(&santa_cond, &santa_mutex); 85 | printf("Mikołaj: budzę się\n"); 86 | 87 | printf("Mikołaj: dostarczam zabawki\n"); 88 | // santa is delivering toys for 2-4 seconds 89 | sleep(rand() % 3 + 2); 90 | 91 | // release all reindeers 92 | for (int j = 0; j < 9; j++) { 93 | pthread_mutex_unlock(&reindeers_mutexes[j]); 94 | } 95 | 96 | printf("Mikołaj: zasypiam\n"); 97 | } 98 | 99 | // cancel all reindeer threads 100 | for (int j = 0; j < 9; j++) { 101 | pthread_cancel(reindeers_threads[j]); 102 | } 103 | 104 | return NULL; 105 | } 106 | 107 | int main() { 108 | int ids[9]; 109 | // create santa and reindeer threads 110 | pthread_create(&santa_thread, NULL, santa_thread_handler, NULL); 111 | for (int i = 0; i < 9; i++) { 112 | ids[i] = i; 113 | pthread_create(&reindeers_threads[i], NULL, reindeer_thread_handler, &ids[i]); 114 | } 115 | 116 | // wait for santa and reindeer threads to finish 117 | pthread_join(santa_thread, NULL); 118 | for (int i = 0; i < 9; i++) { 119 | pthread_join(reindeers_threads[i], NULL); 120 | } 121 | 122 | printf("Koniec\n"); 123 | 124 | return 0; 125 | } -------------------------------------------------------------------------------- /lab11/Makefile: -------------------------------------------------------------------------------- 1 | # C compiler command 2 | CC=gcc 3 | # defines path to put all binary files 4 | BUILD_DIR=build 5 | 6 | # default build configuration, i.e `make all BUILD=release` or `make all BUILD=debug` overwrites this flag 7 | BUILD := debug 8 | 9 | # C flags common in all build configurations 10 | # -Wall - enable all warnings 11 | # -std=c11 - set c standard to 11 12 | cflags.common := -std=c11 -Wall 13 | # disable all optimizations and add debug information in debug build 14 | cflags.debug := -g -O0 15 | # enable O2 optimization in release mode 16 | cflags.release := -O2 17 | # build CFLAGS variable based on selected build configuration 18 | CFLAGS := ${cflags.${BUILD}} ${cflags.common} 19 | 20 | .PHONY: all clean 21 | 22 | # create all executables 23 | all: client server 24 | 25 | # create signal_demo executable 26 | client: client.c 27 | mkdir -p $(BUILD_DIR) 28 | $(CC) $(CFLAGS) -o $(BUILD_DIR)/$@ $< 29 | 30 | server: server.c 31 | mkdir -p $(BUILD_DIR) 32 | $(CC) $(CFLAGS) -o $(BUILD_DIR)/$@ $< 33 | 34 | run_client: client 35 | ./$(BUILD_DIR)/client $(NAME) 127.0.0.1 5000 36 | 37 | run_server: server 38 | ./$(BUILD_DIR)/server 127.0.0.1 5000 39 | 40 | # removes all unnecesery build objects 41 | clean: 42 | rm -rf $(BUILD_DIR) -------------------------------------------------------------------------------- /lab11/README.md: -------------------------------------------------------------------------------- 1 | # Prosty chat z wykorzystaniem TCP/IP 2 | 3 | Napisz prosty chat typu klient-serwer w którym komunikacja pomiędzy uczestmnikami czatu / klientami / klientami i serwerem realizoiwana jest za pośrednictwem socketów z życiem protokołu strumieniowego. 4 | 5 | Adres / port serwera podawany jest jako argument jego uruchomienia 6 | 7 | Klient przyjmuje jako swoje argumenty: 8 | 9 | - swoją nazwę/identyfikator (string o z góry ograniczonej długości) 10 | - adres serwera (adres IPv4 i numer portu) 11 | 12 | Protokół komunikacyjny powinien obsługiwać następujące operacje: 13 | 14 | - LIST: 15 | Pobranie z serwera i wylistowanie wszystkich aktywnych klientów 16 | - 2ALL string: 17 | Wysłania wiadomości do wszystkich pozostałych klientów. Klient wysyła ciąg znaków do serwera, a serwer rozsyła ten ciąg wraz z identyfikatorem nadawcy oraz aktualną datą do wszystkich pozostałych klientów 18 | - 2ONE id_klienta string: 19 | Wysłanie wiadomości do konkretnego klienta. Klient wysyła do serwera ciąg znaków podając jako adresata konkretnego klienta o identyfikatorze z listy aktywnych klientów. Serwer wysyła ten ciąg wraz z identyfikatorem klienta-nadawcy oraz aktualną datą do wskazanego klienta. 20 | - STOP: Zgłoszenie zakończenia pracy klienta. Powinno skutkować usunięciem klienta z listy klientów przechowywanej na serwerze 21 | - ALIVE - serwer powinien cyklicznie "pingować" zarejestrowanych klientów, aby zweryfikować że wciąż odpowiadają na żądania, a jeśli nie - usuwać ich z listy klientów. 22 | - Klient przy wyłączeniu Ctrl+C powinien wyrejestrować się z serwera 23 | 24 | Dla uproszczenia można przyjąć, że serwer przechowuje informacje o klientach w statycznej tablicy (rozmiar tablicy ogranicza liczbę klientów, którzy mogą jednocześnie byc uczestnikami czatu). -------------------------------------------------------------------------------- /lab11/client.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #include "protocol_specs.h" 14 | 15 | volatile bool should_close = false; 16 | 17 | // signal handler for SIGINT to close the client 18 | void sigint_handler(int signo) { 19 | should_close = true; 20 | } 21 | 22 | int main(int argc, char** argv) { 23 | // check for correct usage 24 | if (argc < 3) { 25 | printf("Usage: %s \n", argv[0]); 26 | return -1; 27 | } 28 | 29 | // register signal handler for SIGINT 30 | signal(SIGINT, sigint_handler); 31 | 32 | // parse ip and port 33 | char* client_identifier = argv[1]; 34 | uint32_t ip_address = inet_addr(argv[2]); 35 | uint16_t port = (uint16_t)strtol(argv[3], NULL, 10); 36 | 37 | // create socket with ip and port as tcp ip socket 38 | struct sockaddr_in addr = { 39 | .sin_addr.s_addr = ip_address, 40 | .sin_port = htons(port), 41 | .sin_family = AF_INET 42 | }; 43 | 44 | // create socket 45 | int socket_fd = socket(AF_INET, SOCK_STREAM, 0); 46 | 47 | // connect to server 48 | if(connect(socket_fd, (struct sockaddr*)&addr, sizeof(addr)) < 0) 49 | perror("connect"); 50 | 51 | // send alive message to notify server of client 52 | request_message_t alive_message = { 53 | .request_type = ALIVE 54 | }; 55 | strncpy(alive_message.sender_client_id, client_identifier, MAX_CLIENT_ID_LEN); 56 | send(socket_fd, &alive_message, sizeof(alive_message), MSG_DONTWAIT); 57 | 58 | // create listener process 59 | pid_t listener_pid = fork(); 60 | if (listener_pid < 0) 61 | perror("fork listener"); 62 | else if (listener_pid == 0) { 63 | while (!should_close) { 64 | request_message_t message; 65 | // receive message from server 66 | if(recv(socket_fd, &message, sizeof(message), MSG_WAITALL) < 0) { 67 | perror("recv"); 68 | continue; 69 | 70 | } 71 | // handle message 72 | switch(message.request_type) { 73 | case LIST: 74 | // print list of clients 75 | for (int i = 0; i < message.payload.list.list_length; i++) { 76 | printf("%s\n", message.payload.list.identifiers_list[i]); 77 | } 78 | break; 79 | case TOALL: 80 | printf("TOALL FROM: %s: %s\n", message.sender_client_id, message.payload.to_all); 81 | break; 82 | case TOONE: 83 | printf("TOONE FROM: %s: %s\n", message.sender_client_id, message.payload.to_one.message); 84 | break; 85 | case ALIVE: 86 | send(socket_fd, &alive_message, sizeof(alive_message), MSG_DONTWAIT); 87 | break; 88 | default: 89 | printf("Invalid response type! \n"); 90 | break; 91 | } 92 | } 93 | } else { 94 | // main client process to send messages 95 | char* request_type_input_buffer = NULL; 96 | while (!should_close) { 97 | // read request type from user 98 | if(scanf("%ms", &request_type_input_buffer) == 1) { 99 | request_message_t message; 100 | // fill message with request type and sender client id 101 | strncpy(message.sender_client_id, client_identifier, MAX_CLIENT_ID_LEN); 102 | 103 | // handle request type 104 | if (strncmp(request_type_input_buffer, "LIST", 4) == 0){ 105 | message.request_type = LIST; 106 | 107 | // send message to server 108 | if(send(socket_fd, &message, sizeof(message), MSG_DONTWAIT) < 0) 109 | printf("err sendd"); 110 | } else if (strncmp(request_type_input_buffer, "2ALL", 4) == 0) { 111 | message.request_type = TOALL; 112 | // read message from user 113 | scanf(MAX_MESSAGE_STRING_LEN_SCANF_STR, message.payload.to_all); 114 | send(socket_fd, &message, sizeof(message), MSG_DONTWAIT); 115 | } else if (strncmp(request_type_input_buffer, "2ONE", 4) == 0) { 116 | message.request_type = TOONE; 117 | // read target client id and message from user 118 | scanf(MAX_CLIENT_ID_LEN_SCANF_STR, message.payload.to_one.target_client_id); 119 | scanf(MAX_MESSAGE_STRING_LEN_SCANF_STR, message.payload.to_one.message); 120 | send(socket_fd, &message, sizeof(message), MSG_DONTWAIT); 121 | } else { 122 | printf("Invalid request type! \n"); 123 | } 124 | 125 | // free buffer 126 | free(request_type_input_buffer); 127 | } else 128 | perror("scanf input"); 129 | 130 | } 131 | 132 | // send stop message to server 133 | request_message_t stop_message = { 134 | .request_type = STOP 135 | }; 136 | strncpy(stop_message.sender_client_id, client_identifier, MAX_CLIENT_ID_LEN); 137 | send(socket_fd, &stop_message, sizeof(stop_message), MSG_DONTWAIT); 138 | } 139 | 140 | close(socket_fd); 141 | 142 | return 0; 143 | } 144 | -------------------------------------------------------------------------------- /lab11/protocol_specs.h: -------------------------------------------------------------------------------- 1 | #ifndef PROTOCOL_SPECS_H_ 2 | #define PROTOCOL_SPECS_H_ 3 | 4 | /// 5 | /// Protocol specifications 6 | /// 7 | #define MAX_CLIENTS 40 8 | #define MAX_CLIENT_ID_LEN 64 9 | #define MAX_MESSAGE_STRING_LEN 128 10 | 11 | #define MAX_CLIENTS_SCANF_STR "%40s" 12 | #define MAX_CLIENT_ID_LEN_SCANF_STR "%64s" 13 | #define MAX_MESSAGE_STRING_LEN_SCANF_STR "%128s" 14 | 15 | /** 16 | * Enum representing message sent type 17 | */ 18 | typedef enum { 19 | LIST, 20 | TOALL, 21 | TOONE, 22 | STOP, 23 | ALIVE 24 | } request_type_t; 25 | 26 | /** 27 | * @brief Struct representing a request message 28 | */ 29 | typedef struct { 30 | request_type_t request_type; 31 | char sender_client_id[MAX_CLIENT_ID_LEN]; 32 | // payload stored inside union 33 | union { 34 | struct { 35 | char target_client_id[MAX_CLIENT_ID_LEN]; 36 | char message[MAX_MESSAGE_STRING_LEN]; 37 | } to_one; 38 | char to_all[MAX_MESSAGE_STRING_LEN]; 39 | struct { 40 | char identifiers_list[MAX_CLIENTS][MAX_CLIENT_ID_LEN]; 41 | int list_length; 42 | } list; 43 | } payload; 44 | } request_message_t; 45 | 46 | #endif // PROTOCOL_SPECS_H_ -------------------------------------------------------------------------------- /lab11/server.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #include "protocol_specs.h" 15 | 16 | volatile bool should_close = false; 17 | 18 | // signal handler for SIGINT to close the server 19 | void sigint_handler(int signo) { 20 | should_close = true; 21 | } 22 | 23 | int main(int argc, char** argv) { 24 | // check for correct usage 25 | if (argc < 3) { 26 | printf("Usage: %s \n", argv[0]); 27 | return -1; 28 | } 29 | 30 | // register signal handler for SIGINT 31 | signal(SIGINT, sigint_handler); 32 | 33 | // parse ip and port 34 | uint32_t ip_address = inet_addr(argv[1]); 35 | uint16_t port = (uint16_t)strtol(argv[2], NULL, 10); 36 | 37 | // create socket with ip and port as tcp ip socket 38 | struct sockaddr_in addr = { 39 | .sin_addr.s_addr = ip_address, 40 | .sin_port = htons(port), 41 | .sin_family = AF_INET 42 | }; 43 | 44 | // create socket 45 | int socket_fd = socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK, 0); 46 | 47 | int t = 1; 48 | // set socket options 49 | setsockopt(socket_fd, SOL_SOCKET, SO_REUSEADDR, &t, sizeof(int)); 50 | // bind socket to address 51 | if(bind(socket_fd, (struct sockaddr*)&addr, sizeof(addr)) < 0) 52 | perror("bind"); 53 | 54 | // listen for connections 55 | if(listen(socket_fd, MAX_CLIENTS) < 0) 56 | perror("listen"); 57 | 58 | // array to store client fds 59 | int clients_fd_array[MAX_CLIENTS]; 60 | for (int i = 0; i < MAX_CLIENTS; i++) 61 | clients_fd_array[i] = -1; 62 | 63 | // array to store client ids 64 | bool clients_id_set[MAX_CLIENTS] = {0}; 65 | char clients_id_array[MAX_CLIENTS][MAX_CLIENT_ID_LEN] = {0}; 66 | clock_t clients_alive_timeout[MAX_CLIENTS]; 67 | 68 | clock_t ping_time = clock(); 69 | 70 | // main listen loop 71 | while (!should_close) { 72 | int client_fd; 73 | // accept new client 74 | if((client_fd = accept(socket_fd, NULL, 0)) > 0) { 75 | int i = 0; 76 | // find first empty slot in clients_fd_array 77 | while (i < MAX_CLIENTS){ 78 | if (clients_fd_array[i] == -1) { 79 | clients_fd_array[i] = client_fd; 80 | clients_alive_timeout[i] = clock(); 81 | 82 | printf("Client accepted at index: %d\n", i); 83 | break; 84 | } 85 | 86 | i++; 87 | } 88 | 89 | // close connection if client limit reached 90 | if (i == MAX_CLIENTS) { 91 | printf("Client limit reached\n"); 92 | close(client_fd); 93 | } 94 | } 95 | 96 | // check for messages from clients 97 | for (int i = 0; i < MAX_CLIENTS; i++) { 98 | if (clients_fd_array[i] == -1) 99 | continue; 100 | 101 | // read message from client 102 | request_message_t message; 103 | if(recv(clients_fd_array[i], &message, sizeof(message), MSG_DONTWAIT) > 0) { 104 | // handle message based on request type 105 | switch(message.request_type) { 106 | case TOALL: 107 | // send message to all clients except sender 108 | printf("TO_ALL: %s FROM: %s \n", message.payload.to_all, message.sender_client_id); 109 | for (int j = 0; j < MAX_CLIENTS; j++) 110 | if (clients_fd_array[j] != -1 && i != j) 111 | send(clients_fd_array[j], &message, sizeof(message), MSG_DONTWAIT); 112 | break; 113 | 114 | case TOONE: 115 | // send message to specific client 116 | printf("TO_ONE: %s %s FROM: %s \n", message.payload.to_one.target_client_id, message.payload.to_one.message, message.sender_client_id); 117 | // find client with target_client_id and send message 118 | for (int i = 0; i < MAX_CLIENTS; i++) 119 | if (clients_fd_array[i] != -1 && strncmp(clients_id_array[i], message.payload.to_one.target_client_id, MAX_CLIENT_ID_LEN) == 0) 120 | send(clients_fd_array[i], &message, sizeof(message), MSG_DONTWAIT); 121 | break; 122 | 123 | case LIST: 124 | // send list of all clients 125 | printf("LIST FROM: %s\n", message.sender_client_id); 126 | int length = 0; 127 | for (int j = 0; j < MAX_CLIENTS; j++) 128 | if (clients_fd_array[j] != -1){ 129 | length++; 130 | strncpy(message.payload.list.identifiers_list[j], clients_id_array[j], MAX_CLIENT_ID_LEN); 131 | } 132 | message.payload.list.list_length = length; 133 | 134 | send(clients_fd_array[i], &message, sizeof(message), MSG_DONTWAIT); 135 | break; 136 | 137 | case ALIVE: 138 | // update client alive timeout 139 | printf("ALIVE FROM: %s\n", message.sender_client_id); 140 | clients_alive_timeout[i] = clock(); 141 | if (!clients_id_set[i]) 142 | strncpy(clients_id_array[i], message.sender_client_id, MAX_CLIENT_ID_LEN); 143 | break; 144 | 145 | case STOP: 146 | // close client connection 147 | printf("STOP FROM: %s\n", message.sender_client_id); 148 | clients_fd_array[i] = -1; 149 | clients_id_set[i] = false; 150 | break; 151 | } 152 | 153 | // flush stdout 154 | fflush(stdout); 155 | } 156 | } 157 | 158 | // send ALIVE messages to all clients every second 159 | if ((clock() - ping_time) / CLOCKS_PER_SEC > 1) { 160 | request_message_t alive_message = { 161 | .request_type = ALIVE 162 | }; 163 | // send alive message to all clients 164 | for (int i = 0; i < MAX_CLIENTS; i++) 165 | if (clients_fd_array[i] != -1) 166 | send(clients_fd_array[i], &alive_message, sizeof(alive_message), MSG_DONTWAIT); 167 | ping_time = clock(); 168 | } 169 | 170 | // check for client timeouts 171 | for (int i = 0; i < MAX_CLIENTS; i++) 172 | // close client connection if timeout 173 | if (clients_fd_array[i] != -1 && (clock() - clients_alive_timeout[i]) / CLOCKS_PER_SEC > 5) { 174 | printf("Client %s timed out\n", clients_id_array[i]); 175 | close(clients_fd_array[i]); 176 | clients_fd_array[i] = -1; 177 | clients_id_set[i] = false; 178 | } 179 | } 180 | 181 | // close all client connections 182 | for (int i = 0; i < MAX_CLIENTS; i++) 183 | if (clients_fd_array[i] != -1) 184 | close(clients_fd_array[i]); 185 | 186 | return 0; 187 | } 188 | -------------------------------------------------------------------------------- /lab12/README.md: -------------------------------------------------------------------------------- 1 | # Prosty chat z wykorzystaniem UDP 2 | 3 | Zmodyfikuj rozwiązanie zadania z poprzedniego laboratorium w taki sposób aby komunikacja pomiędzy klientami a serwerem odbywała się z użyciem protokołu datagramowego -------------------------------------------------------------------------------- /zad kol1_wt_1315/Task1/Makefile: -------------------------------------------------------------------------------- 1 | CFLAGS = -Wall 2 | CC = gcc 3 | 4 | main: main.o 5 | 6 | clean: 7 | rm -f main.o main 8 | 9 | test: main 10 | @echo -e "\n\nTESTING\n\n" 11 | ./main 12 | ./main 2 13 | ./main 1 14 | ./main 9 15 | 16 | all: main test 17 | -------------------------------------------------------------------------------- /zad kol1_wt_1315/Task1/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | int main(int argc, char *argv[]){ 10 | pid_t p; 11 | switch (p=fork()) { 12 | case -1:{ 13 | perror("fork"); 14 | exit(1);} 15 | case 0:{ //proces potomny 16 | sleep(3); 17 | exit(3);} 18 | default: { //proces macierzysty 19 | int status; 20 | if (argc>1) 21 | if (atoi(argv[1])==2) 22 | // Wyślij do potomka sygnał SIGINT 23 | kill(p, SIGINT); 24 | 25 | else if (atoi(argv[1])==9) 26 | // Wyślij do potomka sygnał SIGKILL 27 | kill(p, SIGKILL); 28 | 29 | else printf("Sygnał nieobsłużony\n"); 30 | else printf("Zakończenie przez EXIT\n"); 31 | // Czekaj na proces potomny i pobierz jego status zakończenia 32 | // 1) Jeśli proces zakończył się przez exit, wypisz wartość statusu 33 | // 2) Jeśli proces zakończył się sygnałem, wypisz numer sygnału 34 | // W obu przypadkach powinna zostać zwrócona informacja "Potomek o PID=xxx zakończył się kodem/sygnałem xxx 35 | if(wait(&status) < 0) { 36 | perror("wait"); 37 | return -1; 38 | } 39 | 40 | if(WIFEXITED(status)) { 41 | printf("Potomek o PID=%d zakończył się kodem: %d", p, WEXITSTATUS(status)); 42 | } else if(WIFSIGNALED(status)) { 43 | printf("Potomek o PID=%d zakończył się sygnałem: %d", p, WTERMSIG(status)); 44 | 45 | } 46 | } 47 | } 48 | return 0; 49 | } 50 | -------------------------------------------------------------------------------- /zad kol1_wt_1315/Task2/Makefile: -------------------------------------------------------------------------------- 1 | CFLAGS = -Wall 2 | CC = gcc 3 | 4 | main: main.o 5 | 6 | clean: 7 | rm -f main.o main 8 | 9 | test: main 10 | @echo -e "\n\nTESTING\n\n" 11 | ./main 22 12 | ./main -9 13 | ./main 0 14 | ./main cos 15 | 16 | all: main test 17 | -------------------------------------------------------------------------------- /zad kol1_wt_1315/Task2/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | 8 | int main(int argc, char* argv[]) 9 | { 10 | 11 | if(argc !=2){ 12 | printf("Not a suitable number of program parameters\n"); 13 | exit(1); 14 | } 15 | 16 | int toChildFD[2]; 17 | int toParentFD[2]; 18 | 19 | pipe(toChildFD); 20 | pipe(toParentFD); 21 | 22 | int val1,val2,val3 = 0; 23 | 24 | pid_t pid; 25 | 26 | if((pid = fork()) == 0) { 27 | 28 | //odczytaj z potoku nienazwanego wartosc przekazana przez proces macierzysty i zapisz w zmiennej val2 29 | close(toChildFD[1]); 30 | read(toChildFD[0], &val2, sizeof(val2)); 31 | 32 | val2 = val2 * val2; 33 | 34 | //wyslij potokiem nienazwanym val2 do procesu macierzysego 35 | close(toParentFD[0]); 36 | write(toParentFD[1], &val2, sizeof(val2)); 37 | 38 | } else { 39 | 40 | val1 = atoi(argv[1]); 41 | 42 | //wyslij val1 potokiem nienazwanym do priocesu potomnego 43 | close(toChildFD[0]); 44 | write(toChildFD[1], &val1, sizeof(val1)); 45 | 46 | sleep(1); 47 | 48 | //odczytaj z potoku nienazwanego wartosc przekazana przez proces potomny i zapisz w zmiennej val3 49 | close(toParentFD[1]); 50 | 51 | read(toParentFD[0], &val3, sizeof(val3)); 52 | 53 | printf("%d square is: %d\n",val1, val3); 54 | } 55 | return 0; 56 | } 57 | -------------------------------------------------------------------------------- /zad kol1_wt_1315/Task3/Makefile: -------------------------------------------------------------------------------- 1 | CFLAGS = -Wall 2 | CC = gcc 3 | 4 | main: main.o 5 | 6 | clean: 7 | rm -f main.o main 8 | 9 | test: main 10 | @echo -e "\n\nTESTING\n\n" 11 | ./main 12 | 13 | all: main test 14 | -------------------------------------------------------------------------------- /zad kol1_wt_1315/Task3/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | int main() { 10 | pid_t pid; 11 | time_t t; 12 | int status; 13 | 14 | if ((pid = fork()) < 0) 15 | perror("fork"); 16 | else if (pid == 0) { 17 | sleep(5); 18 | exit(5); 19 | } 20 | else do { 21 | // sprawdź status procesu potomka, nie zawieszając przy tym procesu macierzystego 22 | // 1) W przypadku błędu zwróć go. 23 | // 2) W przypadku, gdy proces się jeszcze nie zakończył: 24 | // - pobierz aktualną datę. 25 | // - wypisz "Proces potomny nadal pracuje %data%" 26 | // - uśpij proces macierzysty na jedną sekundę 27 | if(waitpid(pid, &status, WNOHANG) < 0) { 28 | perror("waitpid"); 29 | return -1; 30 | } 31 | 32 | if(!WIFEXITED(status)) { 33 | t = time(NULL); 34 | struct tm tm = *localtime(&t); 35 | 36 | printf("Proces potomny nadal pracuje %d-%d-%d", tm.tm_mday, tm.tm_mon, tm.tm_year + 1900); 37 | sleep(3); 38 | } 39 | 40 | // koniec 41 | // ciąg dalszy do while , przypadek, gdy proces potomny się zakończył 42 | if (WIFEXITED(status)) 43 | printf("Proces potomny zakonczył się statusem %d\n", WEXITSTATUS(status)); 44 | else puts("Proces potomny nie zakonczył się prawidłowo"); 45 | } while (pid == 0); 46 | puts("KONIEC"); 47 | return 0; 48 | } 49 | -------------------------------------------------------------------------------- /zad kol1_wt_1500/Task1/Makefile: -------------------------------------------------------------------------------- 1 | CFLAGS = -Wall 2 | CC = gcc 3 | 4 | main: main.o 5 | 6 | clean: 7 | rm -f main.o main 8 | 9 | test: main 10 | @echo -e "\nTESTING\n" 11 | ./main 12 | 13 | all: main test 14 | -------------------------------------------------------------------------------- /zad kol1_wt_1500/Task1/main.c: -------------------------------------------------------------------------------- 1 | #define _XOPEN_SOURCE 700 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | 10 | void handler(int signo, siginfo_t *siginfo, void *ucontext) { 11 | printf("Received signal %d from %d with additional value: %d\n", 12 | signo, siginfo->si_pid, siginfo->si_value.sival_int); 13 | 14 | sigset_t sset; 15 | sigprocmask(SIG_BLOCK, NULL, &sset); 16 | printf("Mask test: %d\n", sigismember(&sset, SIGPIPE)); 17 | } 18 | 19 | /* Funkcja 'set_handler' ma ustawic 'handler' jako funkcje 20 | * obslugujaca sygnal SIGUSR1. W trakcie obslugi sygnalu 21 | * SIGUSR1 ma byc blokowana obsluga sygnalu SIGPIPE. Zwroc 22 | * uwage, ze funkcja 'handler' odczutuje pewna dodatkowa 23 | * informacje wyslana wraz z sygnalem SIGUSR1. 24 | */ 25 | void set_handler(void){ 26 | // Uzupelnij cialo funkcji set_handler zgodnie z 27 | // komentarzem powyzej 28 | 29 | struct sigaction action; 30 | action.sa_sigaction = handler; 31 | action.sa_flags = SA_SIGINFO; 32 | sigemptyset(&action.sa_mask); 33 | sigaddset(&action.sa_mask, SIGPIPE); 34 | 35 | sigaction(SIGUSR1, &action, NULL); 36 | } 37 | 38 | int main(void) { 39 | pid_t child = fork(); 40 | 41 | if(child) { 42 | set_handler(); 43 | waitpid(child, NULL, 0); 44 | } else { 45 | pid_t parent = getppid(); 46 | pid_t me = getpid(); 47 | 48 | sleep(1); 49 | sigqueue(parent, SIGUSR1, (union sigval) me); 50 | } 51 | 52 | return 0; 53 | } 54 | -------------------------------------------------------------------------------- /zad kol1_wt_1500/Task2/Makefile: -------------------------------------------------------------------------------- 1 | CFLAGS = -Wall 2 | CC = gcc 3 | 4 | main: main.o 5 | 6 | clean: 7 | rm -f main.o main 8 | 9 | test: main 10 | @echo -e "\n\nTESTING\n\n" 11 | ./main file1 12 | ./main file1 fsorted 13 | ./main file2 fsorted 14 | 15 | all: main test 16 | -------------------------------------------------------------------------------- /zad kol1_wt_1500/Task2/file1: -------------------------------------------------------------------------------- 1 | Zuzia 2 | Kasia 3 | Ala 4 | Ola 5 | Iza 6 | Marta 7 | Gosia 8 | Zosia 9 | Julia 10 | Aga 11 | Monia 12 | 13 | -------------------------------------------------------------------------------- /zad kol1_wt_1500/Task2/file2: -------------------------------------------------------------------------------- 1 | 6 2 | 5 3 | 3 4 | 4 5 | 1 6 | 2 7 | 8 | -------------------------------------------------------------------------------- /zad kol1_wt_1500/Task2/fsorted: -------------------------------------------------------------------------------- 1 | 2 | 1 3 | 2 4 | 3 5 | 4 6 | 5 7 | 6 8 | a 9 | Iza 10 | Julia 11 | Kasia 12 | Marta 13 | Monia 14 | Ola 15 | Zosia 16 | Zuzia 17 | -------------------------------------------------------------------------------- /zad kol1_wt_1500/Task2/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | int main(int argc, char** argv){ 10 | if (argc == 2){ 11 | char* filename1 = argv[1]; 12 | int fd[2]; 13 | pipe(fd); 14 | pid_t pid = fork(); 15 | if (pid == 0){ 16 | // zamknij deskryptor do zapisu i wykonaj program sort na filename1 17 | // w przypadku błędu zwróć 3 18 | if (close(fd[1]) < 0) { 19 | perror("close pipe"); 20 | exit(-1); 21 | } 22 | int status = execlp("sort", "sort", filename1, NULL); 23 | if(status < 0) { 24 | exit(3); 25 | } 26 | 27 | // koniec 28 | }else{ 29 | close(fd[0]); 30 | } 31 | } 32 | else if (argc==3){ 33 | char* filename1 = argv[1]; 34 | char* filename2 = argv[2]; 35 | int fd[2]; 36 | // otwórz plik filename2 z prawami dostępu rwxr--r--, 37 | // jeśli plik istnieje otwórz go i usuń jego zawartość 38 | 39 | // jesli plik nie bedzie istnial zostanie stworzony 40 | // jesli bedzie istnial to zostanie otworzony i wyyczyszczony (brak O_APPEND) 41 | int fd_filename2 = open(filename2, O_RDWR | O_CREAT, S_IRWXU | S_IRGRP | S_IROTH); 42 | if(fd_filename2 < 0) { 43 | perror("open file"); 44 | return - 1; 45 | } 46 | 47 | // koniec 48 | pipe(fd); 49 | pid_t pid = fork(); 50 | if (pid == 0){ 51 | // zamknij deskryptor do zapisu, 52 | // przekieruj deskryptor standardowego wyjścia na deskryptor pliku filename2 i zamknij plik, 53 | // wykonaj program sort na filename1 54 | // w przypadku błędu zwróć 3. 55 | if(close(fd[1]) < 0) { 56 | perror("close pipe"); 57 | exit(-1); 58 | } 59 | 60 | if(dup2(fd_filename2, STDOUT_FILENO) < 0){ 61 | perror("dup2"); 62 | exit(-1); 63 | } 64 | 65 | close(fd_filename2); 66 | 67 | int status = execlp("sort", "sort", filename1, NULL); 68 | if(status < 0) { 69 | exit(3); 70 | } 71 | // koniec 72 | }else{ 73 | close(fd[0]); 74 | } 75 | } 76 | else 77 | printf("Wrong number of args! \n"); 78 | 79 | return 0; 80 | } -------------------------------------------------------------------------------- /zad kol1_wt_1500/Task3/Makefile: -------------------------------------------------------------------------------- 1 | CFLAGS = -Wall 2 | CC = gcc 3 | 4 | main: main.o 5 | 6 | clean: 7 | rm -f main.o main 8 | 9 | test: main 10 | @echo -e "\nTESTING\n" 11 | ./main 6 + 5 12 | ./main 8 x 12 13 | ./main 144 - 137 14 | ./main 72 / 9 15 | ./main d a r 16 | ./main s + t 17 | all: main 18 | -------------------------------------------------------------------------------- /zad kol1_wt_1500/Task3/calc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lkwinta/Operating_Systems/5b9d19240cb0e3210c794f58022bed579da917c9/zad kol1_wt_1500/Task3/calc -------------------------------------------------------------------------------- /zad kol1_wt_1500/Task3/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | 8 | int main(int argc, char* argv[]) 9 | { 10 | 11 | if(argc !=4){ 12 | printf("Not a suitable number of program parameters\n"); 13 | return(1); 14 | } 15 | 16 | //utworz proces potomny w ktorym wykonasz program ./calc z parametrami argv[1], argv[2] oraz argv[3] 17 | //odpowiednio jako pierwszy operand, operacja (+-x/) i drugi operand 18 | //uzyj tablicowego sposobu przekazywania parametrow do programu 19 | pid_t pid = fork(); 20 | if(pid < 0) { 21 | perror("fork"); 22 | return -1; 23 | } 24 | else if(pid == 0) { 25 | char* args[] = {"calc", argv[1], argv[2], argv[3], NULL}; 26 | 27 | int status = execvp("./calc", args); 28 | exit(status); 29 | } else { 30 | int status; 31 | if(wait(&status) < 0) { 32 | perror("wait"); 33 | return -1; 34 | } 35 | printf("Calc exited with: %d \n", WEXITSTATUS(status)); 36 | } 37 | 38 | return 0; 39 | } 40 | -------------------------------------------------------------------------------- /zad_kol1_sr_1645/Task1/Makefile: -------------------------------------------------------------------------------- 1 | CFLAGS = -Wall 2 | CC = gcc 3 | 4 | main: main.o 5 | 6 | clean: 7 | rm -f main.o main 8 | 9 | test: main 10 | @echo -e "\n\nTESTING\n\n" 11 | ./main 12 | 13 | all: main test 14 | -------------------------------------------------------------------------------- /zad_kol1_sr_1645/Task1/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | int main() { 10 | pid_t pid; 11 | time_t t; 12 | int status; 13 | 14 | if ((pid = fork()) < 0) 15 | perror("fork"); 16 | else if (pid == 0) { 17 | sleep(5); 18 | exit(5); 19 | } 20 | else do { 21 | // sprawdź status procesu potomka, nie zawieszając przy tym procesu macierzystego 22 | // 1) W przypadku błędu zwróć go. 23 | // 2) W przypadku, gdy proces się jeszcze nie zakończył: 24 | // - pobierz aktualną datę. 25 | // - wypisz "Proces potomny nadal pracuje %data%" 26 | // - uśpij proces macierzysty na jedną sekundę 27 | 28 | if(waitpid(pid, &status, WNOHANG) == -1) { 29 | perror("waitpid"); 30 | } 31 | 32 | time_t t = time(NULL); 33 | struct tm tm = *localtime(&t); 34 | 35 | printf("Proces potomny nadal pracuje: %d-%d-%d\n", tm.tm_mday, tm.tm_mon, tm.tm_year + 1900); 36 | sleep(1); 37 | 38 | 39 | 40 | // koniec 41 | // ciąg dalszy do while , przypadek, gdy proces potomny się zakończył 42 | if (WIFEXITED(status)) 43 | printf("Proces potomny zakonczył się statusem %d\n", WEXITSTATUS(status)); 44 | else puts("Proces potomny nie zakonczył się prawidłowo"); 45 | } while (pid == 0); 46 | puts("KONIEC"); 47 | return 0; 48 | } 49 | -------------------------------------------------------------------------------- /zad_kol1_sr_1645/Task2/Makefile: -------------------------------------------------------------------------------- 1 | CFLAGS = -Wall -O0 2 | CC = gcc 3 | 4 | main: main.o 5 | 6 | clean: 7 | rm -f main.o main 8 | 9 | test: main 10 | @echo -e "\n\nTESTING\n\n" 11 | ./main 100 11 12 | ./main 100 10 13 | ./main 100 9 14 | 15 | all: main test 16 | -------------------------------------------------------------------------------- /zad_kol1_sr_1645/Task2/main.c: -------------------------------------------------------------------------------- 1 | #define _XOPEN_SOURCE 700 2 | 3 | #include 4 | #include 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | 13 | void sighandler(int signo, siginfo_t *info, void *extra) { 14 | printf("Wartosc: %d\n", info->si_int); 15 | } 16 | 17 | 18 | int main(int argc, char* argv[]) { 19 | 20 | if(argc != 3){ 21 | printf("Not a suitable number of program parameters\n"); 22 | return 1; 23 | } 24 | 25 | struct sigaction action; 26 | action.sa_sigaction = &sighandler; 27 | action.sa_flags = SA_SIGINFO; 28 | sigemptyset(&action.sa_mask); 29 | 30 | sigaction(SIGUSR1, &action, NULL); 31 | 32 | int child = fork(); 33 | if(child == 0) { 34 | //zablokuj wszystkie sygnaly za wyjatkiem SIGUSR1 35 | //zdefiniuj obsluge SIGUSR1 w taki sposob zeby proces potomny wydrukowal 36 | //na konsole przekazana przez rodzica wraz z sygnalem SIGUSR1 wartosc 37 | 38 | 39 | sigset_t sigset; 40 | sigfillset(&sigset); 41 | sigdelset(&sigset, SIGUSR1); 42 | 43 | sigprocmask(SIG_SETMASK, &sigset, NULL); 44 | } 45 | else { 46 | //wyslij do procesu potomnego sygnal przekazany jako argv[2] 47 | //wraz z wartoscia przekazana jako argv[1] 48 | long signal = strtol(argv[2], NULL, 10); 49 | long arg = strtol(argv[1], NULL, 10); 50 | 51 | union sigval val = {arg}; 52 | 53 | sigqueue(child, signal, val); 54 | 55 | int status; 56 | wait(&status); 57 | printf("exit: %d\n", WEXITSTATUS(status)); 58 | } 59 | 60 | return 0; 61 | } 62 | -------------------------------------------------------------------------------- /zad_kol1_sr_1645/Task3/Makefile: -------------------------------------------------------------------------------- 1 | CFLAGS = -Wall 2 | CC = gcc 3 | 4 | main: main.o 5 | 6 | clean: 7 | rm -f main.o main 8 | 9 | test: main 10 | @echo -e "\n\nTESTING\n\n" 11 | ./main file1 12 | ./main file1 fsorted 13 | ./main file2 fsorted 14 | 15 | all: main test 16 | -------------------------------------------------------------------------------- /zad_kol1_sr_1645/Task3/file1: -------------------------------------------------------------------------------- 1 | Zuzia 2 | Kasia 3 | Ala 4 | Ola 5 | Iza 6 | Marta 7 | Gosia 8 | Zosia 9 | Julia 10 | Aga 11 | Monia 12 | 13 | -------------------------------------------------------------------------------- /zad_kol1_sr_1645/Task3/file2: -------------------------------------------------------------------------------- 1 | 6 2 | 5 3 | 3 4 | 4 5 | 1 6 | 2 7 | 8 | -------------------------------------------------------------------------------- /zad_kol1_sr_1645/Task3/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | int main(int argc, char** argv){ 10 | if (argc == 2){ 11 | char* filename1 = argv[1]; 12 | int fd[2]; 13 | pipe(fd); 14 | pid_t pid = fork(); 15 | if (pid == 0){ 16 | // zamknij deskryptor do zapisu i wykonaj program sort na filename1 17 | // w przypadku błędu zwróć 3 18 | if (close(fd[1]) < 0) { 19 | perror("close pipe"); 20 | exit(-1); 21 | } 22 | int status = execlp("sort", "sort", filename1, NULL); 23 | if(status < 0) { 24 | exit(3); 25 | } 26 | 27 | // koniec 28 | }else{ 29 | close(fd[0]); 30 | } 31 | } 32 | else if (argc==3){ 33 | char* filename1 = argv[1]; 34 | char* filename2 = argv[2]; 35 | int fd[2]; 36 | // otwórz plik filename2 z prawami dostępu rwxr--r--, 37 | // jeśli plik istnieje otwórz go i usuń jego zawartość 38 | 39 | // jesli plik nie bedzie istnial zostanie stworzony 40 | // jesli bedzie istnial to zostanie otworzony i wyyczyszczony (brak O_APPEND) 41 | int fd_filename2 = open(filename2, O_RDWR | O_CREAT, S_IRWXU | S_IRGRP | S_IROTH); 42 | if(fd_filename2 < 0) { 43 | perror("open file"); 44 | return - 1; 45 | } 46 | 47 | // koniec 48 | pipe(fd); 49 | pid_t pid = fork(); 50 | if (pid == 0){ 51 | // zamknij deskryptor do zapisu, 52 | // przekieruj deskryptor standardowego wyjścia na deskryptor pliku filename2 i zamknij plik, 53 | // wykonaj program sort na filename1 54 | // w przypadku błędu zwróć 3. 55 | if(close(fd[1]) < 0) { 56 | perror("close pipe"); 57 | exit(-1); 58 | } 59 | 60 | if(dup2(fd_filename2, STDOUT_FILENO) < 0){ 61 | perror("dup2"); 62 | exit(-1); 63 | } 64 | 65 | close(fd_filename2); 66 | 67 | int status = execlp("sort", "sort", filename1, NULL); 68 | if(status < 0) { 69 | exit(3); 70 | } 71 | // koniec 72 | }else{ 73 | close(fd[0]); 74 | } 75 | } 76 | else 77 | printf("Wrong number of args! \n"); 78 | 79 | return 0; 80 | } -------------------------------------------------------------------------------- /zad_kol2_wt_1315/task1/Makefile: -------------------------------------------------------------------------------- 1 | CFLAGS = -Wall -std=c99 -lpthread 2 | CC = gcc 3 | 4 | main: main.c 5 | $(CC) $^ -o $@ $(CFLAGS) 6 | clean: 7 | rm -f main.o main 8 | 9 | test: main 10 | @echo -e "\n\nTESTING\n\n" 11 | ./main 12 | all: main test 13 | -------------------------------------------------------------------------------- /zad_kol2_wt_1315/task1/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #define THREAD_COUNT 10 6 | 7 | int counter = 0; 8 | pthread_mutex_t counter_lock; 9 | 10 | void *reader_thread(void *arg) { 11 | printf("Start reader.\n"); 12 | 13 | //zablokuj mutex counter_lock 14 | 15 | 16 | int temp = counter; 17 | sleep(1); 18 | 19 | //odblokuj mutex counter_lock 20 | 21 | 22 | printf("Read %d.\n", temp); 23 | return 0; 24 | } 25 | 26 | void *writer_thread(void *arg) { 27 | printf("Start writer.\n"); 28 | 29 | //zablokuj mutex 30 | 31 | 32 | int temp = counter; 33 | temp += 1; 34 | sleep(1); 35 | counter = temp; 36 | 37 | //odblokuj mutex 38 | 39 | 40 | printf("Written %d.\n", temp); 41 | return 0; 42 | } 43 | 44 | int main(int argc, char* argv[]) { 45 | pthread_t threads[THREAD_COUNT]; 46 | 47 | //zainicjuj mutex counter_lock 48 | 49 | 50 | for (int i = 0; i < THREAD_COUNT; i++) { 51 | int result; 52 | if ((i % 3) == 0) { 53 | result = pthread_create(&(threads[i]), NULL, writer_thread, NULL); 54 | } else { 55 | result = pthread_create(&(threads[i]), NULL, reader_thread, NULL); 56 | } 57 | if (result != 0) { 58 | perror("Could not create thread."); 59 | } 60 | } 61 | 62 | for (int i = 0; i < THREAD_COUNT; i++) { 63 | pthread_join(threads[i], NULL); 64 | } 65 | //usuń mutex counter_lock 66 | 67 | printf("\nCounter value after executing %d threads is %d.\n", THREAD_COUNT, counter); 68 | return 0; 69 | } 70 | -------------------------------------------------------------------------------- /zad_kol2_wt_1315/task2/Makefile: -------------------------------------------------------------------------------- 1 | CFLAGS = -Wall -lpthread 2 | CC = gcc 3 | 4 | main: main.c 5 | $(CC) $^ -o $@ $(CFLAGS) 6 | 7 | clean: 8 | rm -f main.o main 9 | 10 | test: main 11 | @echo -e "\nTEST\n" 12 | ./main 2 5 4 13 | ./main 0 5 4 14 | 15 | all: main 16 | -------------------------------------------------------------------------------- /zad_kol2_wt_1315/task2/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | 13 | #define FILE_NAME "common.txt" 14 | #define SEM_NAME "my_kol_sem" 15 | 16 | 17 | void print_sem_value(sem_t *sem){ 18 | int sem_val; 19 | sem_getvalue(sem,&sem_val); 20 | printf("Current sem value: %d\n",sem_val); 21 | } 22 | 23 | int main(int argc, char** args){ 24 | 25 | if(argc !=4){ 26 | printf("Not a suitable number of program parameters\n"); 27 | return(1); 28 | } 29 | 30 | 31 | /************************************ 32 | Utworz semafor posixowy. Ustaw jego wartosc na 1 33 | *************************************/ 34 | 35 | 36 | print_sem_value(sem_id); 37 | 38 | 39 | int fd = open(FILE_NAME, O_WRONLY | O_CREAT | O_TRUNC , 0644); 40 | 41 | int parentLoopCounter = atoi(args[1]); 42 | int childLoopCounter = atoi(args[2]); 43 | 44 | char buf[20]; 45 | pid_t childPid; 46 | int max_sleep_time = atoi(args[3]); 47 | 48 | if(childPid=fork()){ 49 | int status =0; 50 | srand((unsigned)time(0)); 51 | 52 | while(parentLoopCounter--){ 53 | int s = rand()%max_sleep_time+1; 54 | sleep(s); 55 | 56 | print_sem_value(sem_id); 57 | 58 | /****************************************** 59 | Sekcja krytyczna. Zabezpiecz dostep semaforem 60 | ******************************************/ 61 | 62 | print_sem_value(sem_id); 63 | 64 | 65 | sprintf(buf, "Wpis rodzica. Petla %d. Spalem %d\n", parentLoopCounter,s); 66 | write(fd, buf, strlen(buf)); 67 | write(1, buf, strlen(buf)); 68 | 69 | print_sem_value(sem_id); 70 | 71 | /**************************************************** 72 | Koniec sekcji krytycznej 73 | ****************************************************/ 74 | 75 | print_sem_value(sem_id); 76 | 77 | 78 | } 79 | waitpid(childPid,&status,0); 80 | } 81 | else{ 82 | 83 | srand((unsigned)time(0)); 84 | while(childLoopCounter--){ 85 | 86 | int s = rand()%max_sleep_time+1; 87 | sleep(s); 88 | 89 | print_sem_value(sem_id); 90 | 91 | /****************************************** 92 | Sekcja krytyczna. Zabezpiecz dostep semaforem 93 | ******************************************/ 94 | 95 | 96 | print_sem_value(sem_id); 97 | 98 | sprintf(buf, "Wpis dziecka. Petla %d. Spalem %d\n", childLoopCounter,s); 99 | write(fd, buf, strlen(buf)); 100 | write(1, buf, strlen(buf)); 101 | 102 | print_sem_value(sem_id); 103 | 104 | /**************************************************** 105 | Koniec sekcji krytycznej 106 | ****************************************************/ 107 | 108 | 109 | print_sem_value(sem_id); 110 | 111 | } 112 | _exit(0); 113 | } 114 | 115 | /*************************************** 116 | Posprzataj semafor 117 | ***************************************/ 118 | close(fd); 119 | return 0; 120 | } 121 | 122 | 123 | -------------------------------------------------------------------------------- /zad_kol2_wt_1315/task3/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #define MSG_SIZE 20 13 | 14 | /* 15 | * Funkcja 'open_msg_queue' powinna utworzyć POSIX-ową kolejkę komunikatów o nazwie 'name' 16 | * i zwrócić jej identyfikator. Kolejka powinna umożliwiać zarówno wysyłanie jak 17 | * i odbieranie wiadomości. Kolejka powinna działać w trybie nieblokującym, 18 | * tzn. próba odbioru komunikatu z pustej kolejki kończyć winna się natychmiast 19 | * błędem. Maksymalny rozmiar komunikatów w kolejce powinien wynosić 'MSG_SIZE'. 20 | */ 21 | mqd_t open_msg_queue(const char* name) { 22 | 23 | } 24 | 25 | 26 | int main(void) { 27 | char buf[MSG_SIZE]; 28 | mqd_t queue = open_msg_queue("/msgs"); 29 | puts("[Parent] Trying to receive a msg..."); 30 | if (mq_receive(queue, buf, sizeof(buf), NULL) == -1) { 31 | if (errno == EAGAIN) { 32 | puts("[Parent] Ok, no messages yet"); 33 | } else { 34 | perror("[Parent]"); 35 | exit(-1); 36 | } 37 | } 38 | pid_t pid = fork(); 39 | if (pid != 0) { 40 | wait(NULL); 41 | puts("[Parent] Trying to receive a msg once more..."); 42 | int bytes = mq_receive(queue, buf, sizeof(buf), NULL); 43 | printf("[Parent] Received %d bytes: %s\n", bytes, buf); 44 | } else { 45 | const char* data = "Konstantynopolitanczykowianeczka"; 46 | if (mq_send(queue, data, strlen(data) + 1, 0) == -1) { 47 | if (errno == EMSGSIZE) { 48 | printf("[Child] '%s' too long, let's try something else\n", data); 49 | } else { 50 | perror("[Child]"); 51 | exit(-1); 52 | } 53 | } 54 | data = "Ala ma kota"; 55 | mq_send(queue, data, strlen(data) + 1, 0); 56 | } 57 | mq_close(queue); 58 | mq_unlink("/msgs"); 59 | } 60 | -------------------------------------------------------------------------------- /zad_kol2_wt_1315/task3/makefile: -------------------------------------------------------------------------------- 1 | PROG=main 2 | LDLIBS=-lrt 3 | CFLAGS=-g 4 | 5 | test: ${PROG} 6 | @echo -e "\n\nTESTING\n\n" 7 | @./${PROG} 8 | 9 | clean: 10 | rm ${PROG} 11 | 12 | .PHONY: clean test 13 | -------------------------------------------------------------------------------- /zad_kol2_wt_1500/Task2/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | /* 11 | * Funkcja 'create_shared_mem' powinna zaalokować segment pamięci współdzielonej 12 | * o rozmiarze 'size' z nazwą 'name' i zwrócić jego adres. 13 | * Segment ma umożliwiać odczyt i zapis danych. 14 | */ 15 | void* create_shared_mem(const char* name, off_t size) { 16 | int memory_fd = shm_open(name, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR); 17 | if(memory_fd < 0) 18 | perror("shm_open"); 19 | 20 | if(ftruncate(memory_fd, size) < 0) 21 | perror("ftruncate"); 22 | 23 | void* memory_ptr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, memory_fd, 0); 24 | 25 | if(memory_ptr == NULL) 26 | perror("mmap"); 27 | 28 | return memory_ptr; 29 | } 30 | 31 | 32 | int main(void) { 33 | pid_t pid = fork(); 34 | if (pid != 0) { 35 | char* buf = create_shared_mem("/data", 100); 36 | float* fbuf = (float*) buf; 37 | wait(NULL); 38 | printf("Values: %.3f %e %.3f\n", fbuf[0], *(float*)(buf + 37), fbuf[10]); 39 | munmap(buf, 100); 40 | } else { 41 | void* buf = (float*) create_shared_mem("/data", 40); 42 | memset(buf, 0xBF, 40); 43 | munmap(buf, 40); 44 | } 45 | shm_unlink("/data"); 46 | } 47 | -------------------------------------------------------------------------------- /zad_kol2_wt_1500/Task2/makefile: -------------------------------------------------------------------------------- 1 | PROG=main 2 | LDLIBS=-lrt 3 | CFLAGS=-g 4 | 5 | test: ${PROG} 6 | @echo -e "\n\nTESTING\n\n" 7 | @./${PROG} 8 | 9 | clean: 10 | rm ${PROG} 11 | 12 | .PHONY: clean test 13 | -------------------------------------------------------------------------------- /zad_kol2_wt_1500/task1/Makefile: -------------------------------------------------------------------------------- 1 | CFLAGS = -Wall -std=c99 -lpthread 2 | CC = gcc 3 | 4 | main: main.c 5 | $(CC) $^ -o $@ $(CFLAGS) 6 | clean: 7 | rm -f main.o main 8 | 9 | test: main 10 | @echo -e "\n\nTESTING\n\n" 11 | ./main 12 | all: main test 13 | -------------------------------------------------------------------------------- /zad_kol2_wt_1500/task1/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #define THREAD_COUNT 10 6 | 7 | int counter = 0; 8 | pthread_mutex_t counter_lock; 9 | 10 | void *reader_thread(void *arg) { 11 | printf("Start reader.\n"); 12 | 13 | //zablokuj mutex counter_lock 14 | pthread_mutex_lock(&counter_lock); 15 | 16 | int temp = counter; 17 | sleep(1); 18 | 19 | //odblokuj mutex counter_lock 20 | pthread_mutex_unlock(&counter_lock); 21 | 22 | 23 | printf("Read %d.\n", temp); 24 | return 0; 25 | } 26 | 27 | void *writer_thread(void *arg) { 28 | printf("Start writer.\n"); 29 | 30 | //zablokuj mutex 31 | pthread_mutex_lock(&counter_lock); 32 | 33 | 34 | int temp = counter; 35 | temp += 1; 36 | sleep(1); 37 | counter = temp; 38 | 39 | //odblokuj mutex 40 | pthread_mutex_unlock(&counter_lock); 41 | 42 | 43 | printf("Written %d.\n", temp); 44 | return 0; 45 | } 46 | 47 | int main(int argc, char* argv[]) { 48 | pthread_t threads[THREAD_COUNT]; 49 | 50 | //zainicjuj mutex counter_lock 51 | pthread_mutex_init(&counter_lock, NULL); 52 | 53 | 54 | for (int i = 0; i < THREAD_COUNT; i++) { 55 | int result; 56 | if ((i % 3) == 0) { 57 | result = pthread_create(&(threads[i]), NULL, writer_thread, NULL); 58 | } else { 59 | result = pthread_create(&(threads[i]), NULL, reader_thread, NULL); 60 | } 61 | if (result != 0) { 62 | perror("Could not create thread."); 63 | } 64 | } 65 | 66 | for (int i = 0; i < THREAD_COUNT; i++) { 67 | pthread_join(threads[i], NULL); 68 | } 69 | //usuń mutex counter_lock 70 | pthread_mutex_destroy(&counter_lock); 71 | 72 | printf("\nCounter value after executing %d threads is %d.\n", THREAD_COUNT, counter); 73 | return 0; 74 | } 75 | -------------------------------------------------------------------------------- /zad_kol2_wt_1500/task3/Makefile: -------------------------------------------------------------------------------- 1 | CFLAGS = -Wall -std=c99 -lpthread 2 | CC = gcc 3 | 4 | main: main.c 5 | $(CC) $^ -o $@ $(CFLAGS) 6 | 7 | clean: 8 | rm -f main.o main 9 | 10 | test: main 11 | @echo -e "\nTEST\n" 12 | ./main 3 3 13 | 14 | all: main 15 | -------------------------------------------------------------------------------- /zad_kol2_wt_1500/task3/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | void* hello(void* arg){ 11 | 12 | sleep(1); 13 | while(1){ 14 | printf("Hello world from thread number %d\n", *(int*)arg); 15 | /**************************************************** 16 | przerwij dzialanie watku jesli bylo takie zadanie 17 | *****************************************************/ 18 | pthread_testcancel(); 19 | printf("Hello again world from thread number %d\n", *(int*)arg); 20 | sleep(2); 21 | } 22 | return NULL; 23 | } 24 | 25 | 26 | int main(int argc, char** args){ 27 | 28 | if(argc !=3){ 29 | printf("Not a suitable number of program parameters\n"); 30 | return(1); 31 | } 32 | 33 | int n = atoi(args[1]); 34 | 35 | /************************************************** 36 | Utworz n watkow realizujacych funkcje hello 37 | **************************************************/ 38 | pthread_t threads[n]; 39 | int thread_numbers[n]; 40 | for(int i = 0; i < n; i++){ 41 | thread_numbers[i] = i + 1; 42 | pthread_create(&threads[i], NULL, hello, &thread_numbers[i]); 43 | } 44 | 45 | 46 | int i = 0; 47 | while(i++ < atoi(args[2])) { 48 | printf("Hello from main %d\n",i); 49 | sleep(2); 50 | } 51 | 52 | i = 0; 53 | 54 | /******************************************* 55 | "Skasuj" wszystke uruchomione watki i poczekaj na ich zakonczenie 56 | *******************************************/ 57 | for (int i = 0; i < n; i++) { 58 | pthread_cancel(threads[i]); 59 | pthread_join(threads[i], NULL); 60 | } 61 | printf("DONE"); 62 | 63 | 64 | return 0; 65 | } 66 | 67 | --------------------------------------------------------------------------------