├── .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