├── 2022 ├── LICENSE ├── README.md ├── sem01-intro │ ├── Makefile │ ├── README.md │ ├── aplusb.c │ ├── aplusb_asm.S │ ├── aplusb_preprocessed.c │ ├── bad.c │ ├── in.txt │ ├── stack.png │ ├── sum_lib.c │ └── sum_lib.h ├── sem02-shell-regex │ ├── README.md │ ├── greet_friends.sh │ ├── greet_function.sh │ ├── greet_many.sh │ ├── greet_me.sh │ ├── greet_one.sh │ ├── logic.sh │ └── regexp.ipynb ├── sem03-encoding │ ├── Makefile │ ├── README.md │ ├── ascii.png │ ├── overflow.c │ ├── print_float.c │ ├── print_int.c │ └── print_union.c ├── sem04-arm-asm-basics │ ├── Makefile │ ├── README.md │ ├── arr_get.S │ ├── arr_get.c │ ├── sum.c │ ├── sum.out │ ├── sum_fun.S │ ├── sum_main.c │ ├── sum_n.S │ └── sum_n.c ├── sem05-arm-asm-functions │ ├── Makefile │ ├── README.md │ ├── globals_echo.S │ ├── globals_echo.c │ ├── int_echo.S │ ├── sections.c │ ├── structs.c │ └── vmemory.jpg ├── sem06-x86-asm │ ├── Makefile │ ├── README.md │ ├── arr_get.S │ ├── arr_get.c │ ├── int_echo.S │ ├── stack_x64.png │ ├── sum_n.S │ └── sum_n.c ├── sem07-x86-sse │ ├── Makefile │ ├── README.md │ ├── add.S │ ├── add.c │ ├── add_int.c │ ├── exp.S │ └── exp.c ├── sem08-syscalls │ ├── Makefile │ ├── README.md │ ├── brk_demo.c │ ├── hello_nostdlib.S │ ├── hello_stdlib.c │ ├── hello_syscall.c │ └── syscall.S ├── sem09-files │ ├── Makefile │ ├── README.md │ ├── count_filetypes.c │ ├── in.txt │ ├── in_hard.txt │ ├── lseek_demo.c │ └── read_buff.c ├── sem10-mmap │ ├── Makefile │ ├── README.md │ ├── big_file.txt │ ├── caches_demo.c │ ├── gen_big_file.py │ ├── mmap_demo.c │ ├── proc_mem_demo.c │ ├── sections.c │ ├── small_file.txt │ └── vmemory.jpg ├── sem11-fork │ ├── Makefile │ ├── README.md │ ├── fork_hello.c │ ├── mmap_shared.c │ ├── process_attributes.jpg │ └── process_states.png ├── sem12-exec │ ├── Makefile │ ├── README.md │ └── simple_shell.c ├── sem13-pipe │ ├── Makefile │ ├── README.md │ ├── fifo.c │ ├── freopen.c │ ├── pipe.c │ └── redirect.c ├── sem14-signals │ ├── Makefile │ ├── README.md │ ├── alarm_demo.c │ ├── sigaction_demo.c │ ├── signalfd_demo.c │ ├── sigprocmask_demo.c │ ├── sigqueue_demo.c │ └── sigsuspend_demo.c ├── sem15-sockets │ ├── Makefile │ ├── README.md │ ├── dns_request.png │ ├── getaddrinfo_demo.c │ ├── tcp_client.c │ ├── tcp_header.png │ ├── tcp_server.c │ ├── tcp_server.py │ ├── tcp_server_fork.c │ ├── tcp_state_machine.png │ ├── udp_client.c │ ├── udp_header.png │ └── udp_server.c ├── sem17-epoll │ ├── Makefile │ ├── README.md │ ├── nginx_loop.png │ ├── nginx_threadpool.png │ └── server_epoll.c ├── sem18-threads │ ├── Makefile │ ├── README.md │ └── parallel_sum.c ├── sem19-synchronization │ ├── Makefile │ ├── README.md │ ├── atomic.c │ ├── condvar.c │ ├── mutex.c │ └── no_sync.c ├── sem21-dynlib │ ├── Makefile │ ├── README.md │ ├── foo.c │ ├── loader.c │ ├── main.c │ └── mmap_loader.c ├── sem22-http │ ├── Makefile │ ├── README.md │ └── get.c ├── sem23-security │ ├── CMakeLists.txt │ ├── README.md │ ├── assymetric_cipher.png │ ├── block_ciphers.png │ ├── ca.png │ ├── cipher_principles.png │ ├── main.c │ ├── tls.png │ └── tunnel.png ├── sem25-fuse │ ├── README.md │ ├── docker │ │ └── Dockerfile │ ├── fuse.png │ ├── overlayfs.jpg │ ├── task │ │ ├── CMakeLists.txt │ │ ├── kek.txt │ │ └── main.c │ └── vfs.png └── sem26-python │ ├── README.md │ ├── embed │ ├── CMakeLists.txt │ └── main.c │ └── extend │ ├── CMakeLists.txt │ └── main.c ├── README.md ├── sem01-intro ├── Makefile ├── README.md ├── aplusb.c ├── aplusb_asm.S ├── aplusb_preprocessed.c └── bad.c ├── sem02-encoding ├── Makefile ├── README.md ├── overflow.c ├── print_float.c ├── print_int.c └── print_union.c ├── sem03-files ├── Makefile ├── README.md ├── count_filetypes.c ├── fd_table.gif ├── fuse.png ├── fuse │ ├── CMakeLists.txt │ ├── kek.txt │ └── main.c ├── in.txt ├── inode_ext4.jpg ├── list_dir.c ├── lseek_demo.c ├── pread_demo.c ├── read_buff.c ├── time.c └── writev_demo.c ├── sem04-asm ├── Makefile ├── README.md ├── add.S ├── add.c ├── add_int.c ├── arr_get.S ├── arr_get.c ├── exp.S ├── exp.c ├── int_echo.S ├── stack_x64.png ├── sum.c ├── sum_n.S └── sum_n.c ├── sem05-arm ├── Makefile ├── README.md ├── arr_get.S ├── arr_get.c ├── int_echo.S ├── sum_n.S └── sum_n.c ├── sem06-memory ├── Makefile ├── README.md ├── big_file.txt ├── gen_big_file.py ├── mmap_demo.c ├── proc_mem_demo.c ├── sections.c ├── small_file.txt └── vmemory.jpg ├── sem07-processes ├── Makefile ├── README.md ├── elf_layout.png ├── fork_hello.c ├── mmap_shared.c ├── parallel_sum.c ├── process_attributes.jpg ├── process_states.png └── simple_shell.c ├── sem08-thread-sync ├── Makefile ├── README.md ├── atomic.c ├── condvar.c ├── mutex.c └── no_sync.c ├── sem09-ipc ├── Makefile ├── README.md ├── alarm_demo.c ├── eventfd.c ├── fifo.c ├── freopen.c ├── pipe.c ├── redirect.c ├── sigaction_demo.c ├── signalfd_demo.c ├── sigprocmask_demo.c ├── sigqueue_demo.c ├── sigsuspend_demo.c ├── sigwaitinfo_demo.c └── timerfd_demo.c ├── sem10-sockets ├── Makefile ├── README.md ├── dns_request.png ├── getaddrinfo_demo.c ├── icmp_echo.png ├── tcp_client.c ├── tcp_header.png ├── tcp_server.c ├── tcp_server.py ├── tcp_server_fork.c ├── tcp_state_machine.png ├── udp_client.c ├── udp_header.png └── udp_server.c ├── sem11-sockets-advanced ├── Makefile ├── README.md ├── ethernet.c ├── ethernet_header.jpg ├── ip_header.png ├── nginx_loop.png ├── nginx_threadpool.png ├── ping.c └── server_epoll.c ├── sem12-dynlib ├── Makefile ├── README.md ├── foo.c ├── kek.c ├── loader.c ├── main.c └── mmap_loader.c └── sem13-containers ├── README.md ├── cgroups.jpeg ├── containers-vs-virtual-machines.jpg ├── fs_ns.png ├── net_ns.png └── pid_ns.png /2022/README.md: -------------------------------------------------------------------------------- 1 | # АКОС 2022-2023 2 | 3 | В этом репозитории содержатся материалы по курсу АКОС ФПМИ МФТИ. Они использовались на семинарах группы Б05-128. 4 | 5 | При подготовке использованы материалы нескольких курсов по сетям и операционным системам, а также помощь множества людей. Автор особенно благодарен: 6 | 7 | * Александру Андрееву 8 | * Сергею Горбунову 9 | * Дмитрию Купцову 10 | * Андрею Титову 11 | * Асхату Хайруллину 12 | * Виктору Яковлеву 13 | 14 | Горячо приветсвуется добавление деталей и исправление недочётов с помощью Pull Request'ов. 15 | 16 | Связаться с автором можно по контактам из профиля Github. 17 | -------------------------------------------------------------------------------- /2022/sem01-intro/Makefile: -------------------------------------------------------------------------------- 1 | preprocess: 2 | gcc -E aplusb.c -o aplusb_preprocessed.c 3 | compile: preprocess 4 | gcc -S aplusb_preprocessed.c -o aplusb_asm.S 5 | object: compile 6 | gcc -c aplusb_asm.S -o aplusb_object.o 7 | binary: object 8 | gcc aplusb_object.o -o aplusb_executable.out 9 | sum: sum_lib.c sum_lib.h 10 | gcc -g sum_lib.c -o sum.out 11 | bad_gdb: bad.c 12 | gcc -g bad.c -o bad.out 13 | bad_asan: 14 | gcc -fsanitize=address -g bad.c -o bad.out 15 | clean: 16 | rm aplusb_object.o aplusb_executable.out sum.out bad.out coredump 17 | -------------------------------------------------------------------------------- /2022/sem01-intro/aplusb.c: -------------------------------------------------------------------------------- 1 | #define ONE 1 2 | #define TWO 2 3 | 4 | // simple program 5 | 6 | int main() { 7 | int a = ONE; 8 | int b = TWO; 9 | int c = a + b; 10 | return 0; 11 | } -------------------------------------------------------------------------------- /2022/sem01-intro/aplusb_asm.S: -------------------------------------------------------------------------------- 1 | .file "aplusb_preprocessed.c" 2 | .text 3 | .globl main 4 | .type main, @function 5 | main: 6 | .LFB0: 7 | .cfi_startproc 8 | endbr64 9 | pushq %rbp 10 | .cfi_def_cfa_offset 16 11 | .cfi_offset 6, -16 12 | movq %rsp, %rbp 13 | .cfi_def_cfa_register 6 14 | movl $1, -12(%rbp) 15 | movl $2, -8(%rbp) 16 | movl -12(%rbp), %edx 17 | movl -8(%rbp), %eax 18 | addl %edx, %eax 19 | movl %eax, -4(%rbp) 20 | movl $0, %eax 21 | popq %rbp 22 | .cfi_def_cfa 7, 8 23 | ret 24 | .cfi_endproc 25 | .LFE0: 26 | .size main, .-main 27 | .ident "GCC: (Ubuntu 9.4.0-1ubuntu1~20.04.1) 9.4.0" 28 | .section .note.GNU-stack,"",@progbits 29 | .section .note.gnu.property,"a" 30 | .align 8 31 | .long 1f - 0f 32 | .long 4f - 1f 33 | .long 5 34 | 0: 35 | .string "GNU" 36 | 1: 37 | .align 8 38 | .long 0xc0000002 39 | .long 3f - 2f 40 | 2: 41 | .long 0x3 42 | 3: 43 | .align 8 44 | 4: 45 | -------------------------------------------------------------------------------- /2022/sem01-intro/aplusb_preprocessed.c: -------------------------------------------------------------------------------- 1 | # 1 "aplusb.c" 2 | # 1 "" 3 | # 1 "" 4 | # 31 "" 5 | # 1 "/usr/include/stdc-predef.h" 1 3 4 6 | # 32 "" 2 7 | # 1 "aplusb.c" 8 | 9 | 10 | 11 | 12 | 13 | int main() { 14 | int a = 1; 15 | int b = 2; 16 | int c = a + b; 17 | return 0; 18 | } 19 | -------------------------------------------------------------------------------- /2022/sem01-intro/bad.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int get(int *a, int i) { 4 | return a[i]; 5 | } 6 | 7 | int main() { 8 | int a[10]; 9 | printf("%d\n", get(a, 2)); 10 | printf("%d\n", a[27]); 11 | printf("%d\n", a[2007]); 12 | printf("%d\n", get(a, 4007)); 13 | } -------------------------------------------------------------------------------- /2022/sem01-intro/in.txt: -------------------------------------------------------------------------------- 1 | 1 2 2 | -------------------------------------------------------------------------------- /2022/sem01-intro/stack.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Khabutdinov-Arslan/caos-seminars/374b0109aa47de5df35ad1b58506176b47960997/2022/sem01-intro/stack.png -------------------------------------------------------------------------------- /2022/sem01-intro/sum_lib.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "sum_lib.h" 4 | 5 | int main() { 6 | int a; 7 | int b; 8 | scanf("%d", &a); 9 | scanf("%d", &b); 10 | int c = sum(a, b); 11 | printf("%d", c); 12 | } -------------------------------------------------------------------------------- /2022/sem01-intro/sum_lib.h: -------------------------------------------------------------------------------- 1 | int sum(int a, int b) { 2 | return a + b; 3 | } -------------------------------------------------------------------------------- /2022/sem02-shell-regex/greet_friends.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/bash 2 | 3 | friends=("Frodo" "Sam" "Merry" "Pippin") 4 | 5 | echo "Prepare to greet ${#friends[@]} people." 6 | for i in {0..3} 7 | do 8 | echo "Hello, ${friends[$i]}!" 9 | done 10 | -------------------------------------------------------------------------------- /2022/sem02-shell-regex/greet_function.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/bash 2 | 3 | function greet_person() { 4 | if [[ $1 == "Arslan" ]]; then 5 | echo "Again, $1?" 6 | else 7 | echo "Hello, $1!" 8 | fi 9 | echo "Person greeted." 10 | } 11 | 12 | echo "Prepare to greet $# people." 13 | for person in $@ 14 | do 15 | echo $(greet_person $person) 16 | echo $person 17 | greet_person $person 18 | done 19 | -------------------------------------------------------------------------------- /2022/sem02-shell-regex/greet_many.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/bash 2 | 3 | echo "Prepare to greet $# people." 4 | for person in $@ 5 | do 6 | echo "Hello, $person!" 7 | done 8 | 9 | paths="Vasya:Petya:Vanya" 10 | IFS=: 11 | for person in $paths: 12 | do 13 | echo $person 14 | done 15 | -------------------------------------------------------------------------------- /2022/sem02-shell-regex/greet_me.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/bash 2 | 3 | name=$(echo "Arslan") 4 | echo 'Hello, $name!' 5 | echo "Hello, $name!" 6 | -------------------------------------------------------------------------------- /2022/sem02-shell-regex/greet_one.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/bash 2 | 3 | echo "Hello, $1 from $0 arg count $#!" 4 | -------------------------------------------------------------------------------- /2022/sem02-shell-regex/logic.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/bash 2 | 3 | ping 8.8.8.8 -c 1 || echo "8.800.555.3535 not found" 4 | ping 8232324.8.8.8 -c 1 && echo "8.8.8.8 found" 5 | -------------------------------------------------------------------------------- /2022/sem03-encoding/Makefile: -------------------------------------------------------------------------------- 1 | print_int.out: print_int.c 2 | gcc print_int.c -o print_int.out 3 | print_float.out: print_float.c 4 | gcc print_float.c -o print_float.out 5 | overflow.out: overflow.c 6 | gcc -fsanitize=undefined overflow.c -o overflow.out 7 | print_union.out: print_union.c 8 | gcc print_union.c -o print_union.out 9 | clean: 10 | rm *.out -------------------------------------------------------------------------------- /2022/sem03-encoding/README.md: -------------------------------------------------------------------------------- 1 | # Представление данных 2 | 3 | Поговорим о том, как данные хранятся в памяти. 4 | 5 | ## Целые числа 6 | 7 | Минимально адресуемой единицей является байт. Сколько бит в байте? 8 | 9 | Ответ содержится в константе `CHAR_BIT` из `limits.h`. По стандарту гарантируется, что хотя бы 8. Однобайтовый тип данных называется `char`. 10 | 11 | Типы, которыми вы привыкли пользоваться, такие как `int` плохи тем, что на разных системах занимают разное число байт (на 32-битных обычно 4, на 64-битных 8). 12 | 13 | Чтобы избежать всей этой путаницы, далее рекомендуется использовать типы с фиксированным числом бит: `int8_t`, `uint16_t` и так далее. Они определены в `stdint.h`. 14 | 15 | Давайте с помощью небольшой демки (`print_int.c`) посмотрим на то, как эти числа хранятся в памяти. 16 | 17 | Возьмём 8-битное беззнаковое число, как самый простой пример. В нём биты просто задают двоичное представление числа, начиная со старших разрядов. 18 | 19 | На примере 8-битных знаковых мы видим, что первый бит отвечает за знак (0 для положительных, 1 для отрицательных). Далее для положительного числа представление не отличается. Для отрицательного оно получается из соображения, что `(-x)+x=0`. На самом деле происходит переполнение и старший бит отбрасывается. Для чисел с большей разрядностью просто добавляются старшие байты в начало. `x86` поэтому называется `Little-endian` архитектурой. При передаче по сети наоборот принято передавать старшие байты в начале, можно привести число к такому виду с помощью функции `htons` и убедиться, что порядок изменился. 20 | 21 | ## UBSAN 22 | 23 | Переполнение знакового типа является неопределённым поведением по стандарту. Чтобы это отлавливать, можно использовать флаг ``-fsanitize=undefined`` при компиляции (см. ``overflow.c``). 24 | 25 | ## Вещественные числа 26 | 27 | Два основных типа вещественных с плавающей точкой, которые определены стандартом языка Си, - это `float` (используется 4 байта для хранения) и `double` (используется 8 байт). 28 | 29 | Для простоты далее будет рассматриать `float` (см. ``print_float.c``). Видимо, что старший бит вновь признак отрецательности числа. Следующие 8 бит называются экспонентой, а оставшиеся 23 - мантиссой. 30 | 31 | Значение его может быть получено как: 32 | 33 | ``` 34 | Value = (-1)^S * 2^(E-B) * ( 1 + M / (2^(M_bits-1)) ) 35 | ``` 36 | где `S` - бит знака, `E` - значение смещенной экспоненты, `B` - смещение (127 или 1023), а `M` - значение мантиссы, `M_bits` - количество бит в экспоненте. 37 | 38 | Можно разбирать число с помощью ``union`` (см. ``print_union.c``). 39 | 40 | > Красивый визуализатор: https://www.h-schmidt.net/FloatConverter/IEEE754.html 41 | 42 | ## Текстовые кодировки 43 | 44 | ![ASCII](ascii.png) 45 | 46 | В середине 20 века возникла потребность как-то кодировать текст двоичными данными. Тогда решили, что 7 бит достаточно на цифры и латинский алфавит, так и появилась кодировка ASCII. 47 | 48 | Позже один лишний бит позволил хранить в диапазоне от 128 до 255 символы национальных кодировок. В России, например, в 90-е была распространена `KOI8-R`. 49 | 50 | Иметь отдельную кодировку под каждый язык всё ещё было не очень удобно, поэтому появилась `UTF-8`. Чтобы не занимать гарантированно 4 октета (будем называть так группы ), был придуман интересный дизайн. 51 | 52 | ``echo 'C' | hexdump -C`` (`0a` отвечает за перевод строки, по сути есть один значащий байт `43` и код совпадает с ASCII) 53 | 54 | То есть для самых частотных текстов, которые написаны латиницей, она занимает столько же место, сколько ASCII. 55 | 56 | ``echo 'Я' | hexdump -C`` (Байты `d0` `af` отвечают за `Я`) 57 | 58 | Для большинства языков хватает двух байт. 59 | 60 | ``echo '😊' | hexdump -C`` 61 | 62 | 4 байта нужно для каких-то специфических символов. 63 | 64 | В общем случае кодирование подчиняется следующим шаблонам. 65 | 66 | | Количество октетов | Шаблон | 67 | |---|---| 68 | |1|0xxxxxxx| 69 | |2|110xxxxx 10xxxxxx| 70 | |3|1110xxxx 10xxxxxx 10xxxxxx| 71 | |4|11110xxx 10xxxxxx 10xxxxxx 10xxxxxx| 72 | -------------------------------------------------------------------------------- /2022/sem03-encoding/ascii.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Khabutdinov-Arslan/caos-seminars/374b0109aa47de5df35ad1b58506176b47960997/2022/sem03-encoding/ascii.png -------------------------------------------------------------------------------- /2022/sem03-encoding/overflow.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | int main() { 5 | int32_t n = (1 << 31) - 1; 6 | n += 1; 7 | printf("%d\n", n); 8 | } -------------------------------------------------------------------------------- /2022/sem03-encoding/print_float.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | void printBits(size_t size, void* ptr) 6 | { 7 | unsigned char *bytes = (unsigned char*) ptr; 8 | unsigned char byte; 9 | int i, j; 10 | 11 | for (i = 3; i >= 0; i--) { 12 | for (j = 7; j >= 0; j--) { 13 | byte = (bytes[i] >> j) & 1; // get j-th bit 14 | printf("%u", byte); 15 | if ((i >= 2) && (j == 7)) { 16 | printf(" "); 17 | } 18 | } 19 | } 20 | printf("\n"); 21 | } 22 | 23 | int main() { 24 | float op = 1.0; 25 | printf(" 1.0: "); 26 | printBits(sizeof(op), &op); 27 | float on = -1.0; 28 | printf("-1.0: "); 29 | printBits(sizeof(on), &on); 30 | float ep = 8.0; 31 | printf(" 8.0: "); 32 | printBits(sizeof(on), &ep); 33 | float hp = 0.5; 34 | printf(" 0.5: "); 35 | printBits(sizeof(hp), &hp); 36 | float hl = 1.5; 37 | printf(" 1.5: "); 38 | printBits(sizeof(hp), &hl); 39 | float hm = 1.75; 40 | printf("1.75: "); 41 | printBits(sizeof(hm), &hm); 42 | return 0; 43 | } -------------------------------------------------------------------------------- /2022/sem03-encoding/print_int.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | void printBits(size_t size, void* ptr) 6 | { 7 | unsigned char *bytes = (unsigned char*) ptr; 8 | unsigned char byte; 9 | int i, j; 10 | 11 | for (i = size - 1; i >= 0; i--) { 12 | for (j = 7; j >= 0; j--) { 13 | byte = (bytes[i] >> j) & 1; // get j-th bit 14 | printf("%u", byte); 15 | } 16 | printf(" "); 17 | } 18 | printf("\n"); 19 | } 20 | 21 | int main() { 22 | uint8_t up = 27; 23 | printf("uint8_t 27: "); 24 | printBits(sizeof(up), &up); 25 | int8_t sp = 27; 26 | printf("int8_t 27: "); 27 | printBits(sizeof(sp), &sp); 28 | int8_t sn = -27; 29 | printf("int8_t -27: "); 30 | printBits(sizeof(sn), &sn); 31 | uint16_t lp = 27; 32 | printf("uint16_t 27: "); 33 | printBits(sizeof(lp), &lp); 34 | uint16_t no = htons(27); 35 | printf("uint16_t 27: "); 36 | printBits(sizeof(no), &no); 37 | return 0; 38 | } -------------------------------------------------------------------------------- /2022/sem03-encoding/print_union.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | typedef union { 7 | double double_val; 8 | struct { 9 | uint64_t mantissa_val : 52; 10 | uint64_t exp_val : 11; 11 | uint64_t sign_val : 1; 12 | }; 13 | } double_parser_t; 14 | 15 | int main() { 16 | double_parser_t op = {.double_val = -130.0}; 17 | printf("F = %f\n", op.double_val); 18 | printf("S = %d\n", op.sign_val); 19 | printf("E = %d\n", op.exp_val); 20 | printf("M = %lld\n", (long long unsigned int)op.mantissa_val); 21 | return 0; 22 | } -------------------------------------------------------------------------------- /2022/sem04-arm-asm-basics/Makefile: -------------------------------------------------------------------------------- 1 | agcc := /opt/aarch64-gcc/bin/aarch64-linux-gnu-gcc 2 | 3 | sum_demo: 4 | $(agcc) sum.c -o sum.out 5 | 6 | arr_get: 7 | $(agcc) arr_get.c arr_get.S -o arr_get.out 8 | 9 | sum_asm: 10 | $(agcc) sum_main.c sum_fun.S -o sum_ext.out 11 | 12 | sum_n_asm: 13 | $(agcc) sum_n.c sum_n.S -o sum_n.out 14 | 15 | clean: 16 | rm *.out 17 | -------------------------------------------------------------------------------- /2022/sem04-arm-asm-basics/arr_get.S: -------------------------------------------------------------------------------- 1 | .text 2 | .global get 3 | get: 4 | mov x3, 4 // sz = 4 5 | mul x2, x1, x3 // off = idx * sz 6 | ldr x0, [x0, x2] // ret = *(arr + off) 7 | ret 8 | -------------------------------------------------------------------------------- /2022/sem04-arm-asm-basics/arr_get.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | extern int get(int* arr, int idx); 4 | 5 | int main() { 6 | int arr[3] = {1, 7, 9}; 7 | printf("%d\n", get(arr, 1)); 8 | } -------------------------------------------------------------------------------- /2022/sem04-arm-asm-basics/sum.c: -------------------------------------------------------------------------------- 1 | int main(){ 2 | int a = 2, b = 3; 3 | int c = a + b; 4 | return 0; 5 | } -------------------------------------------------------------------------------- /2022/sem04-arm-asm-basics/sum.out: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Khabutdinov-Arslan/caos-seminars/374b0109aa47de5df35ad1b58506176b47960997/2022/sem04-arm-asm-basics/sum.out -------------------------------------------------------------------------------- /2022/sem04-arm-asm-basics/sum_fun.S: -------------------------------------------------------------------------------- 1 | .text // begin section with program scope 2 | .global sum // label is visible outside this source 3 | 4 | sum: 5 | add x2, x1, x0 6 | mov x0, x2 // int return value from function is passed in x0 7 | ret // return to main program 8 | -------------------------------------------------------------------------------- /2022/sem04-arm-asm-basics/sum_main.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | extern int sum(int a, int b); 4 | 5 | int main(){ 6 | int a; 7 | int b; 8 | scanf("%d %d", &a, &b); 9 | int c = sum(a, b); 10 | printf("%d\n", c); 11 | } -------------------------------------------------------------------------------- /2022/sem04-arm-asm-basics/sum_n.S: -------------------------------------------------------------------------------- 1 | .text 2 | .global sum 3 | 4 | sum: 5 | mov x1, x0 // limit = n 6 | mov x2, 0 // i = 0 7 | mov x0, 0 // sum = 0 8 | loop: 9 | add x2, x2, 1 // i += 1 10 | add x0, x0, x2 // sum += i 11 | cmp x2, x1 // i < n 12 | blt loop 13 | ret 14 | -------------------------------------------------------------------------------- /2022/sem04-arm-asm-basics/sum_n.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | extern int sum(int n); 4 | 5 | int main() { 6 | int n; 7 | scanf("%d", &n); 8 | printf("%d\n", sum(n)); 9 | } -------------------------------------------------------------------------------- /2022/sem05-arm-asm-functions/Makefile: -------------------------------------------------------------------------------- 1 | agcc := /opt/aarch64-gcc/bin/aarch64-linux-gnu-gcc 2 | 3 | sections: 4 | $(agcc) sections.c -o sections.out 5 | 6 | globals_echo: 7 | $(agcc) globals_echo.c globals_echo.S -o globals_echo.out 8 | 9 | int_echo: 10 | $(agcc) int_echo.S -o int_echo.out 11 | 12 | structs: 13 | $(agcc) structs.c -o structs.out 14 | 15 | clean: 16 | rm *.out 17 | -------------------------------------------------------------------------------- /2022/sem05-arm-asm-functions/README.md: -------------------------------------------------------------------------------- 1 | # Стек и вызов функций 2 | 3 | ## Секции 4 | 5 | ![Process memory](vmemory.jpg) 6 | 7 | У каждого процесса есть своё виртуальное адресное пространство (подробнее об этом через несколько занятий). В рамках него память разделена на несколько секций. 8 | 9 | * `.text` хранит код программы 10 | * `.data` хранит глобальные инииализрованные переменные 11 | * `.bss` хранит неинициализированные глобальные переменные. 12 | 13 | Посмотреть на содежимое этих секций можно с помощью флага `-t` команды `objdump`. Предлагается погрепать её вывод на примере `sections.c`. 14 | 15 | С кучей и стеком вы уже знакомы из прошлых курсов. Важно про них запомнить, что стек растёт вниз, а куча наверх (по адресам). При этом через `objdump` вы не увидите переменные созданные на стеке и куче, потому что память под них выделяется динамически. 16 | 17 | > Адреса внутри массива всё ещё растут вверх. Почему? (`a[i] = *(a + i)`) 18 | 19 | Когда мы в прошлых программах писали `.text` в начале это означало, что то, что будет дальше, нужно загрузить в соответствующую секцию в памяти. 20 | 21 | Чтобы обращаться к глобальным переменным, нужно воспользоваться секцией `.data`. Пример использования можно посмотреть в `globals_echo.S`. 22 | 23 | ## Макросы 24 | 25 | С обычным стеком мы привыкли взаимодейстововать с помощью операций `push` и `pop`. В AArch64 стек должен быть выровнен по 16 байт, то есть складывать 8-байтные регистры по одному в общем случае не получится. Что с этим можно делать, можно почитать в [статье](https://community.arm.com/arm-community-blogs/b/architectures-and-processors-blog/posts/using-the-stack-in-aarch64-implementing-push-and-pop). 26 | 27 | Как один из вариантов, мы можем реализовать макросы `push` и `pop`, но пользоваться ими аккуратно. Вообще проблемы возникнут, когда мы будем вызывать какие-то функции. А внутри своей функции мы можем двигать `sp` как хотим. 28 | 29 | ``` 30 | .macro push Rn:req 31 | sub sp, sp, 8 32 | str \Rn, [sp] 33 | .endm 34 | 35 | .macro pop Rn:req 36 | ldr \Rn, [sp] 37 | add sp, sp, 8 38 | .endm 39 | ``` 40 | 41 | В частности, на стек обычно сохраняют регистр `x30` (который, напомним, отвечает за адрес возврата), прежде чем вызывать функции через команду `bl`. Пример использования макросов для работы со стеком можно посмотреть в `int_echo.S`. 42 | 43 | ## Выравнивание структур 44 | 45 | Посмотрим на хранение структур в памяти на примере `structs.c`. Как вы думаете, сколько `usual_struct` занимает в памяти? 46 | 47 | Напрашивается ответ `14 + 8 + 1 = 23` байта. Но на самом деле 32. Почему так? 48 | 49 | А если переставить немного члены местами, получится 24. 50 | 51 | Существуют следующие правила выравнивания полей структур: 52 | 53 | * Адрес каждого элемента делится на его требование к выравниванию 54 | * Требование к выравниванию для структуры -- максимум из требований к выравниванию его членов 55 | * Для элементарных типов, как правило, требование к выравниванию это его размер в байтах 56 | * Требование к выравниванию является степенью двойки 57 | 58 | Чтобы соблюсти эти требования, между полями добавляются отступы. 59 | 60 | Требования к выравниванию происходят потому что процессор так эффективнее их считывает (подробнее об этом на лекции про память). Если вы хотите сэкономить память, пожертвовав произодительностью, можно "упаковать структуру". Для этого нужно добавить `__attribute__((__packed__))` в объявление. 61 | -------------------------------------------------------------------------------- /2022/sem05-arm-asm-functions/globals_echo.S: -------------------------------------------------------------------------------- 1 | .data 2 | .global R 3 | R: .space 8 // allocate 8 zeroed bytes 4 | 5 | .text 6 | .global calculate 7 | 8 | calculate: 9 | ldr x1, =A 10 | ldr x2, =R 11 | ldr x1, [x1] // A 12 | str x1, [x2] // *R = A 13 | ret 14 | -------------------------------------------------------------------------------- /2022/sem05-arm-asm-functions/globals_echo.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | uint64_t A; 5 | extern uint64_t R; 6 | 7 | extern void calculate(); 8 | 9 | int main(){ 10 | scanf("%llu", &A); 11 | calculate(); 12 | printf("%llu\n", R); 13 | } 14 | -------------------------------------------------------------------------------- /2022/sem05-arm-asm-functions/int_echo.S: -------------------------------------------------------------------------------- 1 | .section .text 2 | .global main 3 | 4 | .macro push Rn:req 5 | sub sp, sp, 8 6 | str \Rn, [sp] 7 | .endm 8 | 9 | .macro pop Rn:req 10 | ldr \Rn, [sp] 11 | add sp, sp, 8 12 | .endm 13 | 14 | main: 15 | // prepare arguments and call: scanf(x0: "%d", x1: &val1) 16 | adr x0, format_string_scanf 17 | sub sp, sp, 16 // 12 extra bytes to keep stack 16-byte aligned 18 | mov x1, sp 19 | push x1 20 | push x30 // original return adress must be preserved because it will be overwriten by next command 21 | bl scanf 22 | pop x30 23 | pop x1 24 | 25 | ldr x1, [x1] 26 | 27 | // prepare arguments and call: printf(x0: "%d\n", x1: val1) 28 | adr x0, format_string_printf 29 | sub sp, sp, 8 // extra bytes to keep stack 16-byte aligned 30 | push x30 31 | bl printf 32 | pop x30 33 | 34 | // add sp, sp, 32 // return stack pointer to original position 35 | mov x0, 0 36 | ret 37 | 38 | .section .rodata // section for global constants 39 | format_string_printf: 40 | .string "%d\n" 41 | format_string_scanf: 42 | .string "%d" 43 | -------------------------------------------------------------------------------- /2022/sem05-arm-asm-functions/sections.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int global_var; // bss 4 | 5 | int main() { // text 6 | static int static_var = 5; // data 7 | int local_var; // stack 8 | printf("global_var %p\n", &global_var); 9 | printf("static_var %p\n", &static_var); 10 | printf("local_var %p\n", &local_var); 11 | printf("main %p\n", &main); 12 | } -------------------------------------------------------------------------------- /2022/sem05-arm-asm-functions/structs.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | typedef struct usual_struct 6 | { 7 | char name[14]; 8 | uint64_t balance; 9 | uint8_t age; 10 | } usual_struct; 11 | 12 | typedef struct reordered_struct 13 | { 14 | uint64_t balance; 15 | char name[14]; 16 | uint8_t age; 17 | } reordered_struct; 18 | 19 | 20 | typedef struct __attribute__((__packed__)) packed_struct 21 | { 22 | char name[14]; 23 | uint64_t balance; 24 | uint8_t age; 25 | } packed_struct; 26 | 27 | typedef struct outer { 28 | usual_struct man; 29 | uint64_t counter; 30 | } outer; 31 | 32 | 33 | int main() { 34 | usual_struct usual_man; 35 | printf("usual_struct size = %u\n", sizeof(usual_man)); 36 | printf("usual_struct align = %u\n", alignof(usual_man)); 37 | printf("name address = %p\n", &usual_man.name); 38 | printf("balance address = %p\n", &usual_man.balance); 39 | printf("age address = %p\n", &usual_man.age); 40 | reordered_struct reordered_man; 41 | printf("reordered_struct size = %u\n", sizeof(reordered_man)); 42 | printf("reordered_struct align = %u\n", alignof(reordered_man)); 43 | printf("name address = %p\n", &reordered_man.name); 44 | printf("balance address = %p\n", &reordered_man.balance); 45 | printf("age address = %p\n", &reordered_man.age); 46 | packed_struct packed_man; 47 | printf("packed_struct size = %u\n", sizeof(packed_man)); 48 | printf("packed_struct align = %u\n", alignof(packed_man)); 49 | printf("name address = %p\n", &packed_man.name); 50 | printf("balance address = %p\n", &packed_man.balance); 51 | printf("age address = %p\n", &packed_man.age); 52 | return 0; 53 | } -------------------------------------------------------------------------------- /2022/sem05-arm-asm-functions/vmemory.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Khabutdinov-Arslan/caos-seminars/374b0109aa47de5df35ad1b58506176b47960997/2022/sem05-arm-asm-functions/vmemory.jpg -------------------------------------------------------------------------------- /2022/sem06-x86-asm/Makefile: -------------------------------------------------------------------------------- 1 | arr_get: 2 | gcc arr_get.c arr_get.S -o arr_get.out 3 | sum_n: 4 | gcc sum_n.c sum_n.S -o sum_n.out 5 | int_echo: 6 | gcc -g -no-pie int_echo.S -o int_echo.out 7 | clean: 8 | rm *.out 9 | -------------------------------------------------------------------------------- /2022/sem06-x86-asm/arr_get.S: -------------------------------------------------------------------------------- 1 | .intel_syntax noprefix 2 | .text 3 | .global get 4 | 5 | get: // clang made me do it because if you have function ret you may have preprocessing conflict 6 | mov eax, [rdi + rsi * 4] 7 | ret 8 | -------------------------------------------------------------------------------- /2022/sem06-x86-asm/arr_get.c: -------------------------------------------------------------------------------- 1 | #include "stdio.h" 2 | 3 | extern int get(int* arr, int idx); 4 | 5 | int main() { 6 | int arr[3] = {1, -7, 9}; 7 | int res = get(arr, 1); 8 | printf("%d\n", res); 9 | } -------------------------------------------------------------------------------- /2022/sem06-x86-asm/int_echo.S: -------------------------------------------------------------------------------- 1 | .intel_syntax noprefix 2 | .text 3 | .global main 4 | 5 | // stack needs to be 16-byte aligned before `call` 6 | // but it is now aligned because call it pushed old rsp 7 | main: 8 | sub rsp, 16 9 | mov rdi, offset format_string_scanf 10 | mov rsi, rsp 11 | push rsi // need to be 16-aligned before this call 12 | call scanf 13 | pop rsi 14 | mov rsi, [rsi] 15 | add rsp, 8 16 | mov rdi, offset format_string_printf 17 | call printf 18 | add rsp, 8 19 | ret 20 | 21 | 22 | .data // section for global constants 23 | format_string_printf: 24 | .string "%d\n" 25 | format_string_scanf: 26 | .string "%d" 27 | -------------------------------------------------------------------------------- /2022/sem06-x86-asm/stack_x64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Khabutdinov-Arslan/caos-seminars/374b0109aa47de5df35ad1b58506176b47960997/2022/sem06-x86-asm/stack_x64.png -------------------------------------------------------------------------------- /2022/sem06-x86-asm/sum_n.S: -------------------------------------------------------------------------------- 1 | .intel_syntax noprefix 2 | .text 3 | .global sum 4 | 5 | sum: // rdi = n 6 | mov rsi, 0 // i = 0 7 | mov rax, 0 // sum = 0 8 | loop: 9 | add rsi, 1 // i += 1 10 | add rax, rsi 11 | cmp rsi, rdi // i < n 12 | jl loop 13 | ret 14 | -------------------------------------------------------------------------------- /2022/sem06-x86-asm/sum_n.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | extern int sum(int n); 4 | 5 | int main() { 6 | int n; 7 | scanf("%d", &n); 8 | printf("%d\n", sum(n)); 9 | } 10 | -------------------------------------------------------------------------------- /2022/sem07-x86-sse/Makefile: -------------------------------------------------------------------------------- 1 | exp: 2 | gcc exp.c exp.S -o exp.out 3 | add: 4 | gcc add.c add.S -o add.out 5 | add_int: 6 | gcc -mavx add_int.c -o add_int.out 7 | clean: 8 | rm *.out 9 | -------------------------------------------------------------------------------- /2022/sem07-x86-sse/README.md: -------------------------------------------------------------------------------- 1 | # Векторные регистры 2 | 3 | В 80-е годы операции с вещественной точки исполнялись на отдельном процессоре x87. У него был свой стек и набор инструкций. Сейчас же вычисления с плавающей точкой выполняются самим процессором над регистрами семейств SSE (128 бит)/AVX (256 бит)/AVX-512 (512 бит). Называются они `xmm0-15`, `ymm` и `zmm` соответственно. `xmm0` соответсвует младшим 128 битам `ymm0` и так далее. 4 | 5 | ## Скалярные инструкции 6 | 7 | Векторные регистры можно использовать для операций с одиночными вещественными числами. Команды похожи на обычные арифметические. Буква `s` на конце означает, что операция над `float`, буква `d`, что над `double`. Есть также операции для конвертации и сравнения. 8 | 9 | ``` 10 | // Копирование регистр-регистр и регистр-память 11 | movsd DST, SRC // пересылка double 12 | movss DST, SRC // пересылка float 13 | 14 | // Арифметические 15 | addsd DST, SRC // DST += SRC, double 16 | addss DST, SRC // DST += SRC, float 17 | subsd DST, SRC // DST -= SRC, double 18 | subss DST, SRC // DST -= SRC, float 19 | mulsd DST, SRC // DST *= SRC, double 20 | mulss DST, SRC // DST *= SRC, float 21 | divsd DST, SRC // DST /= SRC, double 22 | divss DST, SRC // DST /= SRC, float 23 | sqrtsd DST, SRC // DST = sqrt(SRC), double 24 | sqrtss DST, SRC // DST = sqrt(SRC), float 25 | maxsd DST, SRC // DST = max(DST, SRC), double 26 | maxss DST, SRC // DST = max(DST, SRC), float 27 | minsd DST, SRC // DST = min(DST, SRC), double 28 | minss DST, SRC // DST = min(DST, SRC), float 29 | 30 | // Преобразования 31 | cvtsd2si DST, SRC // double -> int 32 | cvtsi2sd DST, SRC // int -> double 33 | 34 | // Сравнения (операция DST-SRC, которая меняет флаги) 35 | comisd DST, SRC // для double 36 | comiss DST, SRC // для float 37 | ``` 38 | 39 | Вещественные аргументы в функцию передаются в `xmm0-xmm7`, значение возвращается в `xmm0`. 40 | 41 | Пример вычисления `exp(x)` с помощью ряда Тейлора можно посмотреть в `exp.S`. 42 | 43 | ## Векторные инструкции 44 | 45 | Также можно оперировать с векторными регистрами как с массивом из нескольких чисел. В случае `AVX` можно, например, сложить 8 вещественных чисел за одну операцию и, таким образом, получить кратный рост производительности. 46 | 47 | Команды имеют вид `v(operation_name)p[s|d]`: 48 | * `v` означает, что команда оперирует векторным регистром 49 | * `p` означает, что в нём упаковано несколько значений 50 | * `s` означает `float`, `d` означает `double` 51 | 52 | Например: `vaddps dst, src1, src2`. 53 | 54 | Для загрузки значений служит: `vmov[a|u]p[s|d]`. `a` означает, что память выровнена по размеру векторного регистра (выделить такую память можно, например, с помощью `aligned_alloc`). 55 | 56 | Пример сложения двух векторов с помощью `AVX` инструкций можно посмотреть в `add.S`. 57 | 58 | ## Горизонтальная сумма 59 | 60 | Мы научились складывать числа в массиве блоками по несколько штук. В конце у нас в регистре лежат несколько чисел, мы хотим их просуммировать, чтобы получить итоговую сумму. Есть много способов это сделать. Один из наиболее коротких использует инструкции `vperm2f128` и `vhaddps`. 61 | 62 | ``` 63 | // ymm2 = [x7,x6,x5,x4,x3,x2,x1,x0] 64 | vxorps  ymm0, ymm0, ymm0 // clean using XOR method 65 | vperm2f128 ymm0, ymm2, ymm2, 0b10000001 // ymm0 = [0,0,0,0,x4,x7,x6,x5] 66 | vhaddps ymm0, ymm0, ymm2 67 | vhaddps ymm0, ymm0, ymm0 68 | vhaddps ymm0, ymm0, ymm0 69 | ``` 70 | 71 | ## Intrinsics 72 | 73 | Чтобы использовать векторные инструкции в программах на `C`, есть специальные обёртки, называемые `intrinsics`. 74 | 75 | ``` 76 | for (int i = 0; i < n; i+= 8) { 77 | __m256 r1 = _mm256_load_ps(a + i); 78 | __m256 r2 = _mm256_load_ps(b + i); 79 | __m256 r3 = _mm256_add_ps(r1, r2); 80 | _mm256_store_ps(&c[i], r3); 81 | } 82 | ``` 83 | 84 | Здесь `__m256` на самом деле не переменная. Этот код развернётся в загрузку значения в регистр (можно проверить с помощью Godbolt). 85 | 86 | Пример сложения двух векторов с `intrinsics` можно посмотреть в `add_int.c`. 87 | 88 | Справочник по `intrinsics`: [Intel Intrinsics Guide](https://www.laruence.com/sse/#). 89 | -------------------------------------------------------------------------------- /2022/sem07-x86-sse/add.S: -------------------------------------------------------------------------------- 1 | .global very_important_function 2 | .intel_syntax noprefix 3 | .text 4 | 5 | // (N=rdi, *A=rsi, *B=rdx, *R=rcx) 6 | very_important_function: 7 | 8 | mov r8, rdi 9 | 10 | .Loop: 11 | sub rdi, 8 12 | 13 | vmovaps ymm0, [rsi + rdi * 4] 14 | vmovaps ymm1, [rdx + rdi * 4] 15 | vaddps ymm0, ymm0, ymm1 16 | vmovaps [rcx + rdi * 4], ymm0 17 | 18 | cmp rdi, 0 19 | jg .Loop 20 | 21 | ret 22 | -------------------------------------------------------------------------------- /2022/sem07-x86-sse/add.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | extern double 5 | very_important_function(int N, const float *A, const float *B, float *R); 6 | 7 | int main(){ 8 | const size_t n = 8; 9 | float *a = aligned_alloc(32, 32 * n); 10 | float *b = aligned_alloc(32, 32 * n); 11 | float *c = aligned_alloc(32, 32 * n); 12 | for (int i = 0; i < n; i++){ 13 | a[i] = i + 1; 14 | b[i] = 2 * (i + 1); 15 | } 16 | double res = (float)very_important_function(8, a, b, c); 17 | for (int i = 0; i < n; i++) { 18 | printf("%f ", c[i]); 19 | } 20 | printf("\n"); 21 | return 0; 22 | } -------------------------------------------------------------------------------- /2022/sem07-x86-sse/add_int.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | 6 | int main(){ 7 | const size_t n = 8; 8 | float *a = aligned_alloc(32, 32 * n); 9 | float *b = aligned_alloc(32, 32 * n); 10 | float *c = aligned_alloc(32, 32 * n); 11 | for (int i = 0; i < n; i++){ 12 | a[i] = i + 1; 13 | b[i] = 2 * (i + 1); 14 | } 15 | for (int i = 0; i < n; i+= 8) { 16 | __m256 r1 = _mm256_load_ps(a + i); 17 | __m256 r2 = _mm256_load_ps(b + i); 18 | __m256 r3 = _mm256_add_ps(r1, r2); 19 | _mm256_store_ps(&c[i], r3); 20 | } 21 | for (int i = 0; i < n; i++) { 22 | printf("%f ", c[i]); 23 | } 24 | printf("\n"); 25 | return 0; 26 | } 27 | -------------------------------------------------------------------------------- /2022/sem07-x86-sse/exp.S: -------------------------------------------------------------------------------- 1 | .intel_syntax noprefix 2 | .text 3 | .global my_exp 4 | 5 | my_exp: 6 | movsd xmm1, xmm0 // preserve x 7 | vxorpd xmm0, xmm0, xmm0 // clean result 8 | mov rax, 1 9 | cvtsi2sd xmm2, rax // x^n 10 | cvtsi2sd xmm5, rax // n! 11 | mov rax, 0 // i 12 | mov rdi, 10 // iteration_count 13 | .Loop: 14 | vmovsd xmm4, xmm4, xmm2 // x^n 15 | vdivsd xmm4, xmm4, xmm5 // x^n/n! 16 | vaddsd xmm0, xmm0, xmm4 // res += x^n/n! 17 | 18 | vmulsd xmm2, xmm2, xmm1 // x^(n-1)*x 19 | add rax, 1 // i++ 20 | cvtsi2sd xmm6, rax 21 | vmulsd xmm5, xmm5, xmm6 // (n-1)! * n 22 | 23 | cmp rax, rdi 24 | jl .Loop 25 | .Finalize: 26 | ret 27 | -------------------------------------------------------------------------------- /2022/sem07-x86-sse/exp.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | extern double my_exp(); 4 | 5 | int main() { 6 | double x; 7 | scanf("%lf", &x); 8 | printf("%lf\n", my_exp(x)); 9 | return 0; 10 | } -------------------------------------------------------------------------------- /2022/sem08-syscalls/Makefile: -------------------------------------------------------------------------------- 1 | hello_stdlib: 2 | gcc hello_stdlib.c -o hello_stdlib.out 3 | hello_stdlib_static: 4 | gcc -static hello_stdlib.c -o hello_stdlib_static.out 5 | hello_syscall: 6 | gcc -nostdlib hello_syscall.c syscall.S -o hello_syscall.out 7 | hello_nostdlib: 8 | gcc -no-pie -nostdlib hello_nostdlib.S -o hello_nostdlib.out 9 | brk_demo: 10 | gcc -nostdlib syscall.S brk_demo.c -o brk_demo.out 11 | clean: 12 | rm *.out 13 | -------------------------------------------------------------------------------- /2022/sem08-syscalls/README.md: -------------------------------------------------------------------------------- 1 | # Системные вызовы 2 | 3 | Операционная система позволяет писать нам программы, не думая о железе, оперируя высокоуровненивыми абстракциями (файлы). С одной стороны это позволяет исполнять одни и те же программы на разном железе без специальных действий со стороны разработчиков. С другой, обезопашивает пользователя от некорректных программ. 4 | 5 | То есть, если мы хотим как-то обратиться к железу, мы не можем сделать это напрямую. Нам нужен какой-то механизм, который будет позволять делать запросы к операционной системе. Этот механизм называется системными вызовами. 6 | 7 | > Красивая [презентация](https://courses.cs.washington.edu/courses/cse333/20wi/lectures/07/CSE333-L07-posix_20wi.pdf) на тему. 8 | 9 | Обычно код процессором исполняется в непривилигерованном режиме. В тот момент, когда мы совершаем системный вызов, мы переходим в привилегированный режим процессора и начинем исполнять платформозависимый код, который взаимодействует с железом. Потом мы понижаем уровень привилегий и возвращаемся в user-space. 10 | 11 | Напишем Hello World (`hello_stdlib.c`). С помощью команды `ldd` можно посмотреть зависимости программы и увидеть в них `libc` (если скомпилировать с `-static`, зависимостей от динамических библиотек не будет, но вырастет размер исполняемого файла). С помощью `strace` можно увидеть, что "под капотом" вызывается системный вызов `write`. 12 | 13 | Теперь будем обходиться без `glibc`. Точкой входа в программу будет метка `_start`. Для корректного завершения нужно будет воспользоватся системным вызовом `exit`. 14 | 15 | Родным для архитектуры x86-64 соглашением в Linux является использование команды процессора `syscall`, где номер системного вызова передается через `rax` (используйте константы из `sys/syscalls.h` для читабельности), а аргументы передаются через регистры: `rdi`, `rsi`, `rdx`, `r10`, `r8` и `r9`. Обратите внимание, что не все используемые регистры совпадают со стандартным соглашением о вызовах в x86-64, например, вместо регистра `rcx` используется регистр `r10`. 16 | Поэтому когда мы вызываем `write` в C, эта функция правильно перекладывает аргумент и потом совершает системный вызов. Реализовать подобный механизм можно и самому (см. `syscall.S`). Кроме того, использование команды `syscall` может испортить содержимое регистров `rcx` и `r11`. Пример использования можно посмотреть в `hello_nostdlib.c` (и аналогичный пример на ASM `hello_nostdlib.S`). 17 | 18 | Выделять память на куче можно с помощью системного вызова `brk`. 19 | 20 | ``` 21 | int brk(void *addr); 22 | ``` 23 | 24 | Устанавливает указатель на вершину кучи `addr`. Если передать 0, то возвращает указатель на текущую вершину. Поэтому обычно сначала получают текущий адрес, прибавляют к нему размер выделяемое памяти и вызывают `brk` повторно. Пример можно посмотреть в `brk_demo.c`. 25 | -------------------------------------------------------------------------------- /2022/sem08-syscalls/brk_demo.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | long syscall(long number, ...); 4 | 5 | void _start() { 6 | const int size = 8 * 10; 7 | int* data_start = (void*)syscall(SYS_brk, 0); 8 | int* data_end = (void*)syscall(SYS_brk, (int*)data_start + size); 9 | 10 | data_start[0] = 0; 11 | int len = data_end - data_start; 12 | for (int i = 1; i < len; ++i) { 13 | data_start[i] = data_start[i - 1] + 1; 14 | } 15 | char msg = ('0' + (data_start[len - 1]) % 10); 16 | syscall(SYS_write, 1, &msg, 1); 17 | 18 | syscall(SYS_exit, 0); 19 | } 20 | -------------------------------------------------------------------------------- /2022/sem08-syscalls/hello_nostdlib.S: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | .text 4 | .intel_syntax noprefix 5 | .global _start 6 | 7 | .data 8 | string: .ascii "Hello, World!" 9 | 10 | .text 11 | _start: 12 | 13 | output: 14 | mov rax, SYS_write //use syscall write 15 | mov rdi, 1 //with stdout 16 | mov rsi, offset string //given string 17 | mov rdx, 13 //with given length 18 | syscall 19 | 20 | exit: 21 | mov rax,SYS_exit //use exit syscall 22 | mov rdi, 0 //code 0 23 | syscall 24 | -------------------------------------------------------------------------------- /2022/sem08-syscalls/hello_stdlib.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int main(){ 4 | printf("Hello, world!"); 5 | return 0; 6 | } 7 | -------------------------------------------------------------------------------- /2022/sem08-syscalls/hello_syscall.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | long syscall(long number, ...); 4 | 5 | void _start() 6 | { 7 | char* msg = "Hello, World!"; 8 | syscall(/*syscall_number=*/SYS_write, /*fd=*/1, /*string=*/msg, /*length=*/13); 9 | syscall(SYS_exit, 0); 10 | } 11 | -------------------------------------------------------------------------------- /2022/sem08-syscalls/syscall.S: -------------------------------------------------------------------------------- 1 | .intel_syntax noprefix 2 | .global syscall 3 | .text 4 | syscall: 5 | // prologue 6 | push rbx 7 | push rbp 8 | push r12 9 | push r13 10 | push r14 11 | push r15 12 | // also keep r11 and rcx due to 'syscall' on x64 modifies them 13 | push r11 14 | push rcx 15 | // push arguments into memory to reaarange them 16 | push r9 17 | push r8 18 | push rcx 19 | push rdx 20 | push rsi 21 | push rdi 22 | // first argument is syscall number, move it into rax 23 | pop rax 24 | // move arguments into registers to match syscall calling conventions 25 | pop rdi 26 | pop rsi 27 | pop rdx 28 | pop r10 29 | pop r8 30 | // make actual syscall 31 | syscall 32 | // epilogue 33 | pop rcx 34 | pop r11 35 | pop r15 36 | pop r14 37 | pop r13 38 | pop r12 39 | pop rbp 40 | pop rbx 41 | ret 42 | -------------------------------------------------------------------------------- /2022/sem09-files/Makefile: -------------------------------------------------------------------------------- 1 | read_buff: 2 | gcc read_buff.c -o read_buff.out 3 | lseek_demo: 4 | gcc lseek_demo.c -o lseek_demo.out 5 | count_filetypes: 6 | gcc count_filetypes.c -o count_filetypes.out 7 | clean: 8 | rm *.out 9 | -------------------------------------------------------------------------------- /2022/sem09-files/count_filetypes.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | int main() 9 | { 10 | char file_name[PATH_MAX]; 11 | size_t regular_count = 0; 12 | size_t symlink_count = 0; 13 | size_t executable_count = 0; 14 | struct stat file_stats; 15 | while (fgets(file_name, sizeof(file_name), stdin)) { 16 | for (size_t i = 0; i < sizeof(file_name); i++) { 17 | if (file_name[i] == '\n') { 18 | file_name[i] = '\0'; 19 | break; 20 | } 21 | } 22 | if (lstat(file_name, &file_stats) != -1) { 23 | if (S_ISREG(file_stats.st_mode)) { 24 | ++regular_count; 25 | } 26 | if (S_ISLNK(file_stats.st_mode)) { 27 | ++symlink_count; 28 | } 29 | } 30 | if (access(file_name, X_OK)) { 31 | ++executable_count; 32 | } 33 | } 34 | printf("Regular files count = %lu\nSymbolic links count = %lu\nExecutable count = %lu\n", regular_count, symlink_count, executable_count); 35 | return 0; 36 | } 37 | -------------------------------------------------------------------------------- /2022/sem09-files/in.txt: -------------------------------------------------------------------------------- 1 | Voyager1 2 | -------------------------------------------------------------------------------- /2022/sem09-files/in_hard.txt: -------------------------------------------------------------------------------- 1 | Voyager1 2 | -------------------------------------------------------------------------------- /2022/sem09-files/lseek_demo.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | int main() { 8 | int fd = open("in.txt", O_RDONLY); 9 | lseek(fd, 7, SEEK_SET); 10 | char buff[1]; 11 | read(fd, &buff, 1); 12 | write(1, buff, 1); 13 | close(fd); 14 | return 0; 15 | } 16 | 17 | -------------------------------------------------------------------------------- /2022/sem09-files/read_buff.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | int read_buff(int fd, char* buff, size_t buff_size) 8 | { 9 | size_t read_buff_size = 0; 10 | while (read_buff_size < buff_size) { 11 | size_t read_during_iteration = 12 | read(fd, buff + read_buff_size, buff_size - read_buff_size); 13 | read_buff_size += read_during_iteration; 14 | if (read_during_iteration == 0) { 15 | return read_buff_size; 16 | } 17 | if ((read_during_iteration < 0) && (errno != EINTR) && (errno != EAGAIN)) { 18 | return -1; 19 | } 20 | } 21 | return read_buff_size; 22 | } 23 | 24 | int main() { 25 | const size_t BUFF_SIZE = 4096; 26 | char buffer[BUFF_SIZE]; 27 | int read_count = read_buff(0, buffer, BUFF_SIZE); 28 | printf("Read %d", read_count); 29 | return 0; 30 | } 31 | -------------------------------------------------------------------------------- /2022/sem10-mmap/Makefile: -------------------------------------------------------------------------------- 1 | mmap_demo: 2 | gcc mmap_demo.c -o mmap_demo.out 3 | caches_demo: 4 | gcc caches_demo.c -o caches_demo.out 5 | proc_mem_demo: 6 | gcc proc_mem_demo.c -o proc_mem_demo.out 7 | sections: 8 | gcc sections.c -o sections.out 9 | clean: 10 | rm *.out 11 | -------------------------------------------------------------------------------- /2022/sem10-mmap/caches_demo.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | enum { PAD_SIZE = 7 }; 6 | // 150 and 250 practically no difference 7 | // 10 and 30 no difference 8 | enum { EL_COUNT = 10000000 }; 9 | 10 | typedef struct el{ 11 | uint64_t pad[PAD_SIZE]; 12 | uint64_t value; 13 | } el; 14 | 15 | el arr[EL_COUNT]; 16 | 17 | int main(){ 18 | for (uint64_t i = 0; i < EL_COUNT; i++) { 19 | arr[i].value = (i * i - i + 179) % 444; 20 | } 21 | uint64_t sum = 0; 22 | clock_t tbegin = clock(); 23 | for (uint64_t i = 0; i < EL_COUNT; i++) { 24 | sum += arr[i].value; 25 | } 26 | clock_t tend = clock(); 27 | printf("Sum is %lu \n", sum); 28 | double time_spent = (double)(tend - tbegin) / CLOCKS_PER_SEC; 29 | printf("Time spent is %.6f \n", time_spent); 30 | return 0; 31 | } -------------------------------------------------------------------------------- /2022/sem10-mmap/gen_big_file.py: -------------------------------------------------------------------------------- 1 | import random 2 | file_size = 4096 * 179 3 | file_content = '' 4 | for i in range(file_size): 5 | file_content += str(random.randint(0, 9)) 6 | out_file = open('big_file.txt', 'w') 7 | print(file_content, file=out_file) 8 | -------------------------------------------------------------------------------- /2022/sem10-mmap/mmap_demo.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | int main(int argc, char** argv) { 12 | int input_file = open(argv[1], O_RDONLY); 13 | struct stat file_stats; 14 | fstat(input_file, &file_stats); 15 | char* content_ptr = mmap(NULL, file_stats.st_size, PROT_READ, MAP_PRIVATE, input_file, 0); 16 | posix_madvise(content_ptr, file_stats.st_size, POSIX_MADV_RANDOM); // compare POSIX_MADV_RANDOM with POSIX_MADV_SEQUENTIAL 17 | 18 | size_t cnt[10]; 19 | memset(cnt, 0, sizeof(cnt)); 20 | for (size_t i = 0; i < file_stats.st_size; i++) { 21 | cnt[content_ptr[i] - '0']++; 22 | } 23 | 24 | for (int i = 0; i < 10; i++) { 25 | printf("%lu ", cnt[i]); 26 | } 27 | printf("\n"); 28 | 29 | struct rusage resource_usage; 30 | getrusage(RUSAGE_SELF, &resource_usage); 31 | printf("Minor page faults: %ld\n", resource_usage.ru_minflt); 32 | printf("Major page faults: %ld\n", resource_usage.ru_majflt); 33 | 34 | pause(); 35 | 36 | munmap(content_ptr, file_stats.st_size); 37 | close(input_file); 38 | } -------------------------------------------------------------------------------- /2022/sem10-mmap/proc_mem_demo.c: -------------------------------------------------------------------------------- 1 | #define _LARGEFILE64_SOURCE 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | int main(int argc, char** argv) { 13 | const size_t BUFF_SIZE = 4096; 14 | char mem_file_name[BUFF_SIZE]; 15 | int pid = atoi(argv[1]); 16 | uint64_t offset = strtoull(argv[2], NULL, 16); 17 | sprintf(mem_file_name, "/proc/%d/mem", pid); 18 | int mem_fd = open(mem_file_name, O_RDONLY); 19 | if (mem_fd == -1) { 20 | perror("Can't open process memory: "); 21 | return 0; 22 | } 23 | ptrace(PTRACE_ATTACH, pid, NULL, NULL); 24 | waitpid(pid, NULL, 0); 25 | lseek64(mem_fd, offset, SEEK_SET); 26 | char buf[BUFF_SIZE]; 27 | int read_bytes_cnt = read(mem_fd, buf, _SC_PAGE_SIZE); 28 | if (read_bytes_cnt == -1) { 29 | perror("Can't read from memory: "); 30 | return 0; 31 | } 32 | printf("Read bytes: %d\n", read_bytes_cnt); 33 | ptrace(PTRACE_DETACH, pid, NULL, NULL); 34 | printf("%s\n", buf); 35 | return 0; 36 | } -------------------------------------------------------------------------------- /2022/sem10-mmap/sections.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int global_var; // bss 4 | 5 | int main() { // text 6 | static int static_var = 5; // data 7 | int local_var; // stack 8 | printf("global_var %p\n", &global_var); 9 | printf("static_var %p\n", &static_var); 10 | printf("local_var %p\n", &local_var); 11 | printf("main %p\n", &main); 12 | } 13 | -------------------------------------------------------------------------------- /2022/sem10-mmap/small_file.txt: -------------------------------------------------------------------------------- 1 | 1234567890 2 | -------------------------------------------------------------------------------- /2022/sem10-mmap/vmemory.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Khabutdinov-Arslan/caos-seminars/374b0109aa47de5df35ad1b58506176b47960997/2022/sem10-mmap/vmemory.jpg -------------------------------------------------------------------------------- /2022/sem11-fork/Makefile: -------------------------------------------------------------------------------- 1 | fork_hello: 2 | gcc fork_hello.c -o fork_hello.out 3 | mmap_shared: 4 | gcc mmap_shared.c -o mmap_shared.out 5 | 6 | clean: 7 | rm *.out 8 | -------------------------------------------------------------------------------- /2022/sem11-fork/README.md: -------------------------------------------------------------------------------- 1 | # Процессы 2 | 3 | Процесс -- экземпляр исполнения программы. 4 | 5 | ![Process attributes](process_attributes.jpg) 6 | 7 | В ядре операционной системы хранится таблица процессов. Наиболее существенные аттрибуты 8 | 9 | * Для восстановления состояния процесса при переключении, нам нужно сохранить: 10 | * `pc` указатель на следующую инструкцию 11 | * `sp` указатель на стек 12 | * сами регистры можно просто сохранять на стек при преключении или хранить отдельно 13 | * состояние процесса (их диаграмма будет ниже) 14 | * Для контроля за процессами нужно помнить: 15 | * `pid` идентификатор процесса 16 | * `pid` родителя, чтобы оповещать его об изменении статусов ребёнка 17 | * `uid` и `gid` для квотирования ресурсов 18 | * Планировщику нужно знать 19 | * приоритет 20 | * время исполнения 21 | * Для работы с файлами нужно хранить: 22 | * таблицу фаловых дескрипторов 23 | * текущую директорию, от которой считаются относительные пути (напомню, что это причина, по которой нет утилиты `cd`) 24 | * корневую директорию, от которой считаются абсолютные пути. Это, в частности, позволяет изолировать процессы друг от друга. 25 | 26 | ![Process states](process_states.png) 27 | 28 | Процесс может находится в разных состояниях: 29 | * Выполняется 30 | * Остановлен (до получения сигнала `SIGCONT`, о них поговорим позже) 31 | * Сон (например, ждёт ввода) 32 | * Зомби (процесс завершился, но ещё не удалён из таблицы процессов) 33 | 34 | ## Создание процессов. fork 35 | 36 | Системный вызов `fork` создаёт почти полную копию текущего процесса за исключением: 37 | 38 | * `pid` 39 | * маски сигналов, ожидающих обработки 40 | * блокировок памяти и файлов 41 | * таймеров 42 | * операция неблокирующего чтения 43 | 44 | `pid_t fork(void)` 45 | 46 | В родителе при успешном он возвращает `pid` ребёнка, а в самом ребёнке `0`. Чтобы получить `pid` в самом ребёнке можно, например, использовать системный вызов `getpid`. 47 | 48 | Но раз вы создали некоторый ресурс, нужно не забыть его удалить (иначе в таблице процессов кончится место). Для этого служит семейство семейных вызовов `wait`. Наиболее часто для этого используют `waitpid`. 49 | 50 | `pid_t waitpid(pid_t pid, int *wstatus, int options);` 51 | 52 | Он позволяет дождаться процесса по `pid` и получить его статус возврата. С помощью `WEXITSTATUS(wstatus)` можно вытащить из него код возврата. По-умолчанию `waitpid` ждёт перехода в зомби, но можно с помощью опцию переопределить это поведение на какие-то другие состояния процесса. 53 | 54 | Пример использования этих конструкций можно найти в `fork_hello.c`. 55 | 56 | С помощью флага `MAP_SHARED` можно передать от ребёнка какую-то информацию родителю, пример можно увидеть в `mmap_shared.c`. -------------------------------------------------------------------------------- /2022/sem11-fork/fork_hello.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | int main() { 7 | pid_t pid = fork(); 8 | if (pid == 0) { 9 | printf("Hello from child with pid %d!\n", getpid()); 10 | return 179; 11 | } else { 12 | int status; 13 | waitpid(pid, &status, 0); 14 | printf("Child exited with code %d\n", WEXITSTATUS(status)); 15 | printf("Hello from parent!\n"); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /2022/sem11-fork/mmap_shared.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | int main() { 9 | char* content_ptr = mmap(NULL, 10, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0); 10 | strcpy(content_ptr, "Hello"); 11 | pid_t pid = fork(); 12 | if (pid == 0) { 13 | printf("Parent says: %s!\n", content_ptr); 14 | strcpy(content_ptr, "Hola"); 15 | } else { 16 | int status; 17 | waitpid(pid, &status, 0); 18 | printf("Child says: %s!\n", content_ptr); 19 | } 20 | return 0; 21 | } 22 | -------------------------------------------------------------------------------- /2022/sem11-fork/process_attributes.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Khabutdinov-Arslan/caos-seminars/374b0109aa47de5df35ad1b58506176b47960997/2022/sem11-fork/process_attributes.jpg -------------------------------------------------------------------------------- /2022/sem11-fork/process_states.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Khabutdinov-Arslan/caos-seminars/374b0109aa47de5df35ad1b58506176b47960997/2022/sem11-fork/process_states.png -------------------------------------------------------------------------------- /2022/sem12-exec/Makefile: -------------------------------------------------------------------------------- 1 | simple_shell: 2 | gcc simple_shell.c -o simple_shell.out 3 | clean: 4 | rm *.out 5 | -------------------------------------------------------------------------------- /2022/sem12-exec/README.md: -------------------------------------------------------------------------------- 1 | # Запуск программ. Exec 2 | 3 | Обычно системный вызов `fork` используется в комбинации с другим системным вызовом `exec`, который запускает программу. Виртуальное адресное пространство при этом перезаписывается. Большинство других атрибутов процесса, например, таблица файловых дескрипторов, сохраняются. Подробнее можно почитать в `man 2 execve`. 4 | 5 | Поверх этого системногого вызова реализованы в библиотеке разные интерфейсы: 6 | 7 | ``` 8 | int execve(const char *filename, 9 | char *const argv[], 10 | char *const envp[]); 11 | int execvpe(.....) // параметры аналогично execve 12 | 13 | int execv(const char *filename, char *const argv[]) 14 | int execvp(......) // параметры аналогично execv 15 | 16 | int execle(const char *filename, 17 | const char arg0, ..., /* NULL */, 18 | const char env0, ..., /* NULL */); 19 | 20 | int execl(const char *filename, 21 | const char arg0, ..., /* NULL */); 22 | int execlp(......) // параметры аналогично execl 23 | ``` 24 | 25 | Что означают суффиксы? 26 | * `v` или `l` - параметры передаются в виде массивов (`v`), заканчивающихся элементом `NULL`, либо в виде переменного количества аргументов (`l`), где признаком конца перечисления аргументов является значение `NULL`. 27 | * `e` - кроме аргументов программы передаются переменные окружения в виде строк `КЛЮЧ=ЗНАЧЕНИЕ`. 28 | * `p` - именем программы может быть не только имя файла, но и имя, которое нужно найти в одном из каталогов, перечисленных в переменной окружения `PATH`. 29 | 30 | Вооружившись `fork+exec` можно, например, написать свой простенький shell (см. `simple_shell.c`). 31 | -------------------------------------------------------------------------------- /2022/sem12-exec/simple_shell.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | int main() { 9 | size_t BUFF_SIZE = 4096; 10 | char* shell_intro = "my_shell> "; 11 | printf("%s", shell_intro); 12 | 13 | char* buff = malloc(BUFF_SIZE); 14 | size_t read; 15 | while((read = getline(&buff, &BUFF_SIZE, stdin)) != -1){ 16 | size_t MAX_ARGS = 10; 17 | char* args[MAX_ARGS]; 18 | for (int i = 0; i < MAX_ARGS; i++) { 19 | args[i] = NULL; 20 | } 21 | char* pos; 22 | pos = strtok(buff, " \n"); 23 | int argc = 0; 24 | while (pos != NULL) { 25 | args[argc] = pos; 26 | ++argc; 27 | pos = strtok(NULL, " \n"); 28 | } 29 | 30 | pid_t pid = fork(); 31 | if (pid == 0) { 32 | execvp(args[0], args); 33 | perror("Can't spawn child: "); 34 | } else { 35 | waitpid(pid, NULL, 0); 36 | printf("%s", shell_intro); 37 | } 38 | } 39 | 40 | return 0; 41 | } 42 | -------------------------------------------------------------------------------- /2022/sem13-pipe/Makefile: -------------------------------------------------------------------------------- 1 | freopen: 2 | gcc freopen.c -o freopen.out 3 | redirect: 4 | gcc redirect.c -o redirect.out 5 | pipe: 6 | gcc pipe.c -o pipe.out 7 | fifo: 8 | gcc fifo.c -o fifo.out 9 | clean: 10 | rm *.out 11 | 12 | -------------------------------------------------------------------------------- /2022/sem13-pipe/README.md: -------------------------------------------------------------------------------- 1 | # Дублирование файловых дескрипторов. Каналы 2 | 3 | ## Дублирование файловых дескрипторов 4 | 5 | Для того, чтобы копировать файловые дескрипторы, есть 2 системных вызова: `dup` и `dup2`. 6 | 7 | ``` 8 | /* Возвращает копию нового файлового дескриптора, при этом, по аналогии 9 | с open, численное значение нового файлового дескриптора - минимальный 10 | не занятый номер. */ 11 | int dup(int old_fd); 12 | 13 | /* Создаёт копию нового файлового дескриптора с явно указанным номером new_fd и в случае успеха его возвращает. 14 | Если ранее файловый дескриптор new_fd был открыт, то закрывает его. */ 15 | int dup2(int old_fd, int new_fd); 16 | ``` 17 | 18 | С помощью `dup2` можно сделать, например, перенаправление вывода нашей программы в файл (см. `freopen.c`). 19 | 20 | А если скомбинировать его с `fork` и `exec`, можно перенаправлять вывод произольной программ в файл (см. `redirect.c`). 21 | 22 | ## Неименованные каналы 23 | 24 | Канал -- это пара связанных между собой файловых дескрипторов, один из которых предназначен для только для чтения, а другой -- только для записи. 25 | ``` 26 | /* Создаёт канал и записывает файловый дескриптор читающего конца в 0 элемент массива, а пишущего -- в 1. */ 27 | int pipe(int pipefd[2]); 28 | ``` 29 | 30 | Канал буферизован, размер буффера, как правило, 65K. Запись происходит в буфер, если в нём не хватает места, то блокируемся. Если при этом читающий конец закрыт, `write` завершится с ошибкой `Broken pipe`. При чтении если буфер непуст, читаем из него, иначе блокируемся. Если пишущий конец закрыт, `read` вернёт 0. 31 | 32 | Используя `pipe` можно перенаправить вывод произвольной программы себе на ввод (см. `pipe.c`). Если закомментировать в этом примере 23 строку, то программа зависнет. Почему? 33 | 34 | ## Именованные каналы 35 | 36 | Для взаимодействия неродственных процессов можно использовать именованные каналы `FIFO`. 37 | 38 | ``` 39 | /* Создаёт файл канала по заданому пути и с задаными флагами доступа */ 40 | int mkfifo(const char *pathname, mode_t mode); 41 | ``` 42 | 43 | После создания такой канал можно открыть как обычный файл, но с режимом только на чтение или только на запись. Перед началом использования именного канала и пишущий, и читающий концы должны быть открыты. В остальном отличий от обычных каналов нет (пример использования можно увидеть в `fifo.c`). 44 | -------------------------------------------------------------------------------- /2022/sem13-pipe/fifo.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 pid = fork(); 11 | 12 | if (pid == 0) { 13 | mkfifo("./fifo_demo", 0644); 14 | int fd = open("./fifo_demo", O_WRONLY); 15 | dup2(fd, 1); 16 | close(fd); 17 | execlp(argv[1], argv[1], NULL); 18 | } else { 19 | // parent will read 20 | sleep(1); // not the best way to sync, but 21 | int fd = open("./fifo_demo", O_RDONLY); 22 | dup2(fd, 0); 23 | close(fd); 24 | 25 | int total_read = 0; 26 | char buffer[4096]; 27 | int current_read; 28 | while ((current_read = read(0, buffer, sizeof(buffer))) > 0) { 29 | total_read += current_read; 30 | } 31 | printf("%d\n", total_read); 32 | waitpid(pid, NULL, 0); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /2022/sem13-pipe/freopen.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | int main() { 7 | int fd = open("out.txt", O_WRONLY | O_CREAT | O_TRUNC, 0664); 8 | dup2(fd, 1); 9 | close(fd); // now we can close original descriptor 10 | printf("Hello, world!"); 11 | return 0; 12 | } 13 | -------------------------------------------------------------------------------- /2022/sem13-pipe/pipe.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | int main(int argc, char* argv[]) { 9 | int pipe_out[2]; 10 | pipe(pipe_out); 11 | 12 | pid_t pid = fork(); 13 | 14 | if (pid == 0) { 15 | // child will write 16 | close(pipe_out[0]); 17 | dup2(pipe_out[1], 1); 18 | close(pipe_out[1]); 19 | 20 | execlp(argv[1], argv[1], NULL); 21 | } else { 22 | // parent will read 23 | close(pipe_out[1]); 24 | dup2(pipe_out[0], 0); 25 | close(pipe_out[0]); 26 | 27 | int total_read = 0; 28 | char buffer[4096]; 29 | int current_read; 30 | while ((current_read = read(0, buffer, sizeof(buffer))) > 0) { 31 | total_read += current_read; 32 | } 33 | printf("%d\n", total_read); 34 | waitpid(pid, NULL, 0); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /2022/sem13-pipe/redirect.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | int main(int argc, char* argv[]) { 7 | int fd = open("out.txt", O_WRONLY | O_CREAT | O_TRUNC, 0664); 8 | dup2(fd, 1); 9 | close(fd); // now we can close original descriptor 10 | execvp(argv[1], argv + 1); 11 | } 12 | -------------------------------------------------------------------------------- /2022/sem14-signals/Makefile: -------------------------------------------------------------------------------- 1 | sigaction_demo: 2 | gcc sigaction_demo.c -o sigaction_demo.out 3 | sigprocmask_demo: 4 | gcc sigprocmask_demo.c -o sigprocmask_demo.out 5 | sigsuspend_demo: 6 | gcc sigsuspend_demo.c -o sigsuspend_demo.out 7 | signalfd_demo: 8 | gcc signalfd_demo.c -o signalfd_demo.out 9 | sigqueue_demo: 10 | gcc sigqueue_demo.c -o sigqueue_demo.out 11 | alarm_demo: 12 | gcc alarm_demo.c -o alarm_demo.out 13 | sigwaitinfo_demo: 14 | gcc sigwaitinfo_demo.c -o sigwaitinfo_demo.out 15 | timerfd_demo: 16 | gcc timerfd_demo.c -o timerfd_demo.out 17 | clean: 18 | rm *.out 19 | -------------------------------------------------------------------------------- /2022/sem14-signals/alarm_demo.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | int main() { 5 | alarm(3); 6 | pause(); 7 | return 1; 8 | } -------------------------------------------------------------------------------- /2022/sem14-signals/sigaction_demo.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | volatile sig_atomic_t received_sigint = 0; 7 | 8 | void sigint_handler(int signum) 9 | { 10 | received_sigint = 1; 11 | } 12 | 13 | void set_handler(int signum, void* handler, struct sigaction* action) 14 | { 15 | action->sa_handler = handler; 16 | action->sa_flags = SA_RESTART; 17 | sigaction(signum, action, NULL); 18 | } 19 | 20 | int main() { 21 | struct sigaction sigint_action; 22 | memset(&sigint_action, 0, sizeof(sigint_action)); 23 | set_handler(SIGINT, sigint_handler, &sigint_action); 24 | 25 | printf("%d\n", getpid()); 26 | fflush(stdout); 27 | 28 | while (1) { 29 | pause(); 30 | if (received_sigint) { 31 | printf("%s\n", "SIGINT received"); 32 | fflush(stdout); 33 | received_sigint = 0; 34 | } 35 | } 36 | return 0; 37 | } 38 | -------------------------------------------------------------------------------- /2022/sem14-signals/signalfd_demo.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | int main() { 10 | sigset_t mask; 11 | sigfillset(&mask); 12 | sigdelset(&mask, SIGCONT); 13 | sigprocmask(SIG_BLOCK, &mask, NULL); 14 | 15 | printf("%d\n", getpid()); 16 | fflush(stdout); 17 | 18 | int fd = signalfd(-1, &mask, 0); 19 | struct signalfd_siginfo fdsi; 20 | while (true) { 21 | read(fd, &fdsi, sizeof(struct signalfd_siginfo)); 22 | printf("Got signal %d\n", fdsi.ssi_signo); 23 | } 24 | close(fd); 25 | return 0; 26 | } 27 | -------------------------------------------------------------------------------- /2022/sem14-signals/sigprocmask_demo.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | volatile sig_atomic_t received_sigint = 0; 7 | 8 | void sigint_handler(int signum) 9 | { 10 | received_sigint = 1; 11 | } 12 | 13 | void set_handler(int signum, void* handler, struct sigaction* action) 14 | { 15 | action->sa_handler = handler; 16 | action->sa_flags = SA_RESTART; 17 | sigaction(signum, action, NULL); 18 | } 19 | 20 | int main() { 21 | struct sigaction sigint_action; 22 | memset(&sigint_action, 0, sizeof(sigint_action)); 23 | set_handler(SIGINT, sigint_handler, &sigint_action); 24 | 25 | printf("%d\n", getpid()); 26 | fflush(stdout); 27 | 28 | sigset_t mask; 29 | sigemptyset(&mask); 30 | sigaddset(&mask, SIGINT); 31 | 32 | while (1) { 33 | sigprocmask(SIG_BLOCK, &mask, NULL); 34 | sleep(5); 35 | sigprocmask(SIG_UNBLOCK, &mask, NULL); 36 | if (received_sigint) { 37 | printf("%s\n", "SIGINT received"); 38 | fflush(stdout); 39 | received_sigint = 0; 40 | } 41 | } 42 | return 0; 43 | } 44 | -------------------------------------------------------------------------------- /2022/sem14-signals/sigqueue_demo.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | volatile sig_atomic_t last_signal = 0; 10 | volatile sig_atomic_t last_value = 0; 11 | 12 | static void handler(int signum, siginfo_t *info, void *context) { 13 | last_signal = signum; 14 | last_value = info->si_int; 15 | } 16 | 17 | int main() { 18 | for (int i = SIGRTMIN; i <= SIGRTMAX; i++) { 19 | sigaction(i, &(struct sigaction){.sa_sigaction=handler, .sa_flags=SA_SIGINFO}, NULL); 20 | } 21 | 22 | pid_t pid = fork(); 23 | if (pid == 0) { 24 | sigset_t mask; 25 | sigemptyset(&mask); 26 | while (1) { 27 | sigsuspend(&mask); 28 | printf("Got signal %d with value %d \n", last_signal, last_value); 29 | fflush(stdout); 30 | } 31 | } else { 32 | for (int i = SIGRTMIN; i <= SIGRTMIN + 5; i++) { 33 | sigqueue(pid, i, (union sigval){i * i}); 34 | sleep(1); 35 | } 36 | kill(pid, SIGTERM); 37 | waitpid(pid, NULL, 0); 38 | } 39 | return 0; 40 | } -------------------------------------------------------------------------------- /2022/sem14-signals/sigsuspend_demo.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | volatile sig_atomic_t received_sigint = 0; 7 | 8 | void sigint_handler(int signum) 9 | { 10 | received_sigint = 1; 11 | } 12 | 13 | void set_handler(int signum, void* handler, struct sigaction* action) 14 | { 15 | action->sa_handler = handler; 16 | action->sa_flags = SA_RESTART; 17 | sigaction(signum, action, NULL); 18 | } 19 | 20 | int main() { 21 | struct sigaction sigint_action; 22 | memset(&sigint_action, 0, sizeof(sigint_action)); 23 | set_handler(SIGINT, sigint_handler, &sigint_action); 24 | 25 | printf("%d\n", getpid()); 26 | fflush(stdout); 27 | 28 | sigset_t mask_without_sigint; 29 | sigfillset(&mask_without_sigint); 30 | sigdelset(&mask_without_sigint, SIGINT); 31 | 32 | while (1) { 33 | sigsuspend(&mask_without_sigint); 34 | if (received_sigint) { 35 | printf("%s\n", "SIGINT received"); 36 | fflush(stdout); 37 | received_sigint = 0; 38 | } 39 | } 40 | return 0; 41 | } 42 | -------------------------------------------------------------------------------- /2022/sem15-sockets/Makefile: -------------------------------------------------------------------------------- 1 | udp_client: 2 | gcc udp_client.c -o udp_client.out 3 | udp_server: 4 | gcc udp_server.c -o udp_server.out 5 | tcp_client: 6 | gcc tcp_client.c -o tcp_client.out 7 | tcp_server: 8 | gcc tcp_server.c -o tcp_server.out 9 | tcp_server_fork: 10 | gcc tcp_server_fork.c -o tcp_server_fork.out 11 | getaddrinfo_demo: 12 | gcc getaddrinfo_demo.c -o getaddrinfo_demo.out 13 | clean: 14 | rm *.out 15 | -------------------------------------------------------------------------------- /2022/sem15-sockets/dns_request.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Khabutdinov-Arslan/caos-seminars/374b0109aa47de5df35ad1b58506176b47960997/2022/sem15-sockets/dns_request.png -------------------------------------------------------------------------------- /2022/sem15-sockets/getaddrinfo_demo.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | int main(int argc, char* argv[]) 8 | { 9 | struct addrinfo* res = NULL; 10 | getaddrinfo(argv[1], "80", 0, &res); 11 | 12 | struct addrinfo* i; 13 | 14 | for(i=res; i!=NULL; i=i->ai_next) 15 | { 16 | char str[INET6_ADDRSTRLEN]; 17 | if (i->ai_addr->sa_family == AF_INET) { 18 | struct sockaddr_in *p = (struct sockaddr_in *)i->ai_addr; 19 | printf("%s\n", inet_ntop(AF_INET, &p->sin_addr, str, sizeof(str))); 20 | } 21 | } 22 | 23 | freeaddrinfo(res); 24 | 25 | return 0; 26 | } 27 | -------------------------------------------------------------------------------- /2022/sem15-sockets/tcp_client.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | int main(int argc, char* argv[]) 12 | { 13 | int fd = socket(AF_INET, SOCK_STREAM, 0); 14 | struct in_addr input_address; 15 | input_address.s_addr = inet_addr(argv[1]); 16 | struct sockaddr_in server_address; 17 | server_address.sin_family = AF_INET; 18 | server_address.sin_addr = input_address; 19 | server_address.sin_port = htons(atoi(argv[2])); 20 | int connection_status = 21 | connect(fd, (struct sockaddr*)&server_address, sizeof(server_address)); 22 | if (connection_status) { 23 | printf("%s", strerror(errno)); 24 | return 1; 25 | } 26 | const int BUFF_SIZE = 4096; 27 | char buff[BUFF_SIZE]; 28 | while (scanf("%s", buff) > 0) { 29 | write(fd, buff, strlen(buff)); 30 | if (read(fd, &buff, sizeof(buff)) == 0) { 31 | break; 32 | } 33 | printf("%s\n", buff); 34 | } 35 | shutdown(fd, SHUT_RDWR); 36 | close(fd); 37 | return 0; 38 | } 39 | -------------------------------------------------------------------------------- /2022/sem15-sockets/tcp_header.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Khabutdinov-Arslan/caos-seminars/374b0109aa47de5df35ad1b58506176b47960997/2022/sem15-sockets/tcp_header.png -------------------------------------------------------------------------------- /2022/sem15-sockets/tcp_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 | #include 14 | 15 | const int BUFF_SIZE = 4096; 16 | 17 | int server_socket_init(uint16_t port) 18 | { 19 | int server_fd; 20 | server_fd = socket(AF_INET, SOCK_STREAM, 0); 21 | struct in_addr input_address; 22 | input_address.s_addr = inet_addr("127.0.0.1"); 23 | struct sockaddr_in server_address; 24 | server_address.sin_family = AF_INET; 25 | server_address.sin_addr = input_address; 26 | server_address.sin_port = htons(port); 27 | 28 | int connection_status = bind( 29 | server_fd, (struct sockaddr*)&server_address, sizeof(server_address)); 30 | if (connection_status) { 31 | perror("Bind failed: "); 32 | exit(1); 33 | } 34 | 35 | int listening_status = listen(server_fd, SOMAXCONN); 36 | if (listening_status) { 37 | perror("Listen failed: "); 38 | exit(1); 39 | } 40 | return server_fd; 41 | } 42 | 43 | void read_message(int client_fd, char* buff){ 44 | read(client_fd, buff, BUFF_SIZE); 45 | } 46 | 47 | void write_message(int client_fd, char* buff){ 48 | write(client_fd, buff, strlen(buff)); 49 | } 50 | 51 | int main(int argc, char* argv[]) 52 | { 53 | int server_fd = server_socket_init(atoi(argv[1])); 54 | while (true) { 55 | int client_fd = accept(server_fd, NULL, NULL); 56 | char msg[BUFF_SIZE]; 57 | while (1) { 58 | read_message(client_fd, msg); 59 | if (msg[0] == 'q') { 60 | break; 61 | } 62 | write_message(client_fd, msg); 63 | } 64 | shutdown(client_fd, SHUT_RDWR); 65 | close(client_fd); 66 | } 67 | shutdown(server_fd, SHUT_RDWR); 68 | close(server_fd); 69 | return 0; 70 | } 71 | -------------------------------------------------------------------------------- /2022/sem15-sockets/tcp_server.py: -------------------------------------------------------------------------------- 1 | import socket 2 | 3 | HOST = "127.0.0.1" 4 | PORT = 3003 5 | 6 | with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: 7 | s.bind((HOST, PORT)) 8 | s.listen() 9 | conn, addr = s.accept() 10 | with conn: 11 | print(f"Connected by {addr}") 12 | while True: 13 | data = conn.recv(1024) 14 | if not data: 15 | break 16 | conn.sendall(data) 17 | -------------------------------------------------------------------------------- /2022/sem15-sockets/tcp_server_fork.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 | #include 14 | 15 | const int BUFF_SIZE = 4096; 16 | 17 | int server_socket_init(uint16_t port) 18 | { 19 | int server_fd; 20 | server_fd = socket(AF_INET, SOCK_STREAM, 0); 21 | struct in_addr input_address; 22 | input_address.s_addr = inet_addr("127.0.0.1"); 23 | struct sockaddr_in server_address; 24 | server_address.sin_family = AF_INET; 25 | server_address.sin_addr = input_address; 26 | server_address.sin_port = htons(port); 27 | 28 | int connection_status = bind( 29 | server_fd, (struct sockaddr*)&server_address, sizeof(server_address)); 30 | if (connection_status) { 31 | perror("Connection failed: "); 32 | exit(1); 33 | } 34 | 35 | int listening_status = listen(server_fd, SOMAXCONN); 36 | if (listening_status) { 37 | perror("Listen failed: "); 38 | exit(1); 39 | } 40 | return server_fd; 41 | } 42 | 43 | void read_message(int client_fd, char* buff){ 44 | read(client_fd, buff, BUFF_SIZE); 45 | } 46 | 47 | void write_message(int client_fd, char* buff){ 48 | write(client_fd, buff, strlen(buff)); 49 | } 50 | 51 | int main(int argc, char* argv[]) 52 | { 53 | struct sigaction zombie_killer = { 54 | .sa_handler = SIG_IGN, 55 | .sa_flags = SA_NOCLDWAIT 56 | }; 57 | sigaction(SIGCHLD, &zombie_killer, NULL); 58 | 59 | 60 | int server_fd = server_socket_init(atoi(argv[1])); 61 | while (true) { 62 | int client_fd = accept(server_fd, NULL, NULL); 63 | int pid = fork(); 64 | if (pid == 0){ 65 | char msg[BUFF_SIZE]; 66 | while (1) { 67 | read_message(client_fd, msg); 68 | if (msg[0] == 'q') { 69 | break; 70 | } 71 | write_message(client_fd, msg); 72 | } 73 | shutdown(client_fd, SHUT_RDWR); 74 | close(client_fd); 75 | break; 76 | } else { 77 | close(client_fd); 78 | } 79 | } 80 | shutdown(server_fd, SHUT_RDWR); 81 | close(server_fd); 82 | return 0; 83 | } 84 | -------------------------------------------------------------------------------- /2022/sem15-sockets/tcp_state_machine.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Khabutdinov-Arslan/caos-seminars/374b0109aa47de5df35ad1b58506176b47960997/2022/sem15-sockets/tcp_state_machine.png -------------------------------------------------------------------------------- /2022/sem15-sockets/udp_client.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | int main(int argc, char* argv[]) 12 | { 13 | int fd = socket(AF_INET, SOCK_DGRAM, 0); 14 | struct in_addr input_address; 15 | input_address.s_addr = inet_addr(argv[1]); 16 | struct sockaddr_in server_address; 17 | server_address.sin_family = AF_INET; 18 | server_address.sin_addr = input_address; 19 | server_address.sin_port = htons(atoi(argv[2])); 20 | 21 | const int BUFF_SIZE = 4096; 22 | char buff[BUFF_SIZE]; 23 | while (scanf("%s", buff) > 0) { 24 | sendto(fd, buff, strlen(buff), 0, (const struct sockaddr*)&server_address, 25 | sizeof(server_address)); 26 | if (recvfrom(fd, buff, sizeof(buff), 0, NULL, NULL) == 0) { 27 | break; 28 | } 29 | fflush(stdout); 30 | printf("%s\n", buff); 31 | } 32 | 33 | close(fd); 34 | return 0; 35 | } 36 | -------------------------------------------------------------------------------- /2022/sem15-sockets/udp_header.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Khabutdinov-Arslan/caos-seminars/374b0109aa47de5df35ad1b58506176b47960997/2022/sem15-sockets/udp_header.png -------------------------------------------------------------------------------- /2022/sem15-sockets/udp_server.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | int main(int argc, char* argv[]) 12 | { 13 | int fd = socket(AF_INET, SOCK_DGRAM, 0); 14 | struct in_addr input_address; 15 | input_address.s_addr = inet_addr(argv[1]); 16 | struct sockaddr_in server_address; 17 | server_address.sin_family = AF_INET; 18 | server_address.sin_addr = input_address; 19 | server_address.sin_port = htons(atoi(argv[2])); 20 | 21 | int connection_status = bind(fd, (struct sockaddr*)&server_address, sizeof(server_address)); 22 | if (connection_status) { 23 | perror("Bind failed: "); 24 | exit(1); 25 | } 26 | 27 | const int BUFF_SIZE = 4096; 28 | char buff[BUFF_SIZE]; 29 | while (1) { 30 | struct in_addr client_address; 31 | socklen_t client_address_length; 32 | if (recvfrom(fd, buff, sizeof(buff), 0, (struct sockaddr*)&client_address, 33 | &client_address_length) == 0) { 34 | break; 35 | } 36 | sendto(fd, buff, strlen(buff), 0, (const struct sockaddr*)&client_address, 37 | client_address_length); 38 | fflush(stdout); 39 | printf("%s\n", buff); 40 | } 41 | 42 | close(fd); 43 | return 0; 44 | } 45 | -------------------------------------------------------------------------------- /2022/sem17-epoll/Makefile: -------------------------------------------------------------------------------- 1 | server_epoll: 2 | gcc server_epoll.c -o server_epoll.out 3 | 4 | clean: 5 | rm *.out 6 | -------------------------------------------------------------------------------- /2022/sem17-epoll/README.md: -------------------------------------------------------------------------------- 1 | # epoll 2 | 3 | На прошлом семинаре мы научились писать TCP-сервер. Проблема была в том, что он обслуживал только одного клиента за раз. Мы решили это тем, что создавали по процессу на каждого клиента. Но это было очень ресурсозатратно. В популярном веб-сервере Apache в какой-то момент решили заранее создавать фиксированной количество процессов под обработку соединений (prefork model). Далее процессы заменили на потоки и сэкономили ещё какое-то количество ресурсов. 4 | 5 | Большую часть времени процесс всё ещё находится в ожидании ответа от клиента. А мог бы заниматься какой-то полезной работой. Мы хотим научиться обрабатывать несколько сокетов одним потоком. Именно эту задачу решали разработчики NGINX (подробнее можно почитать про это здесь: https://www.aosabook.org/en/nginx.html). 6 | 7 | ## Неблокирующий ввод/вывод 8 | 9 | В обычном режиме системные вызовы `read/write` блокируются, пока данные не считаются/запишутся. Если вы пытаетесь одновременно обработать несколько сокетов, это может стать узким местом. Можно установить файловому дескриптору специальный неблокирующий режим. Тогда, при попытке из него считать вернётся `-1` и ошибка `EAGAIN` в случае отсутсвия данных. 10 | 11 | ``` 12 | int current_descriptor_flags = fcntl(fd, F_GETFL); 13 | fcntl(fd, F_SETFL, current_descriptor_flags | O_NONBLOCK); 14 | ``` 15 | 16 | Тогда можно вернуться к обработке этого файлового дескриптора позже, а пока обработать другие. 17 | 18 | Этот механизм не работает, к сожалению, для диска. А это могло бы быть полезно при считывании больших файлов. Для этого недавно в Linux появился новый механизм [IO_URING](https://habr.com/ru/post/589389/). 19 | 20 | ## Поллинг 21 | 22 | Как понять, что в файловом дескрипторе появились данные, которые можно прочитать? Для этого в ядре Linux (обратите внимание, в отличии от большинства конструкций курса, эта не работает на MAC, там есть `kqueue`) есть возможность создать специальную очередь событий. 23 | 24 | Сначала её нужно создать: 25 | 26 | ``` 27 | int epoll_create(int size); 28 | // size = max number of events 29 | // returns fd 30 | ``` 31 | 32 | События в очереди описываются специальной структурой: 33 | 34 | ``` 35 | #include 36 | 37 | typedef union { 38 | void* ptr; 39 | int fd; 40 | uint32_t u32; 41 | uint64_t u64; 42 | } epoll_data_t; 43 | 44 | struct epoll_event { 45 | uint32_t events; // маска событий (EPOLLIN | EPOLLOUT | EPOLLHUP | EPOLLERR) 46 | epoll_data_t data; // произвольное поле, заполненное при регистрации 47 | }; 48 | ``` 49 | 50 | Чтобы начать следить за файловым дескриптором, нужно настроить интересные типы событий. 51 | 52 | ``` 53 | epoll_ctl( 54 | int epoll_fd, 55 | int op, // (EPOLL_CTL_ADD | EPOLL_CTL_MOD | EPOLL_CTL_DEL) 56 | int fd, // interesting fd 57 | struct epoll_event *event 58 | ); 59 | ``` 60 | 61 | После этого в цикле можно считывать события из очереди и обрабатывать: 62 | 63 | ``` 64 | struct epoll_event events[MaxEventsToRead]; 65 | int N = epoll_wait( 66 | int epoll_fd, 67 | struct epoll_event *events, // array to store events 68 | int MaxEventsToRead, // max event count 69 | int timeout // timeout or -1 70 | ); 71 | // returns number of events 72 | ``` 73 | 74 | Пример Echo-сервера на epoll можно найти в `server_epoll.c`. 75 | 76 | ## NGINX 77 | 78 | Более современный веб-сервер NGINX использует epoll в worker процессах, которые обрабатывают клиентов. Это позволяет ему тратить в 100 раз меньше памяти на клиентов чем Apache и легко выдерживать 10K соединений. 79 | 80 | ![nginx loop](nginx_loop.png) 81 | 82 | Далее авторы придумали ещё одну оптимизацию. Завести фиксированный пул потоков для обработки операций ввода-вывода. Задачи складываются в очередь, которую потом разбирают потоки. За счёт этого конкретный медленный клиент не тормозит всех остальных. Подробнее можно почитать в блоге: https://www.nginx.com/blog/thread-pools-boost-performance-9x/ 83 | 84 | ![nginx threadpoll](nginx_threadpool.png) 85 | 86 | ## Outro 87 | 88 | В жизни `epoll` напрямую вы использовать, скорее всего, не будете. Однако, именно он работает под капотом популярных библиотек. Для примера можно посмотреть на echo-сервер на [Asio](https://github.com/chriskohlhoff/asio/blob/master/asio/src/examples/cpp14/echo/async_tcp_echo_server.cpp). 89 | 90 | Хорошие конспект с картинками, чтобы закрепить знания: https://copyconstruct.medium.com/the-method-to-epolls-madness-d9d2d6378642. -------------------------------------------------------------------------------- /2022/sem17-epoll/nginx_loop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Khabutdinov-Arslan/caos-seminars/374b0109aa47de5df35ad1b58506176b47960997/2022/sem17-epoll/nginx_loop.png -------------------------------------------------------------------------------- /2022/sem17-epoll/nginx_threadpool.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Khabutdinov-Arslan/caos-seminars/374b0109aa47de5df35ad1b58506176b47960997/2022/sem17-epoll/nginx_threadpool.png -------------------------------------------------------------------------------- /2022/sem17-epoll/server_epoll.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 | #include 14 | #include 15 | 16 | #define BUFF_SIZE 4096 17 | 18 | typedef struct { 19 | int fd; 20 | char buffer[BUFF_SIZE]; 21 | int buffer_begin; 22 | int buffer_end; 23 | } fd_data; 24 | 25 | int server_socket_init(uint16_t port) 26 | { 27 | int server_fd; 28 | server_fd = socket(AF_INET, SOCK_STREAM, 0); 29 | struct in_addr input_address; 30 | input_address.s_addr = inet_addr("127.0.0.1"); 31 | struct sockaddr_in server_address; 32 | server_address.sin_family = AF_INET; 33 | server_address.sin_addr = input_address; 34 | server_address.sin_port = htons(port); 35 | 36 | int connection_status = bind( 37 | server_fd, (struct sockaddr*)&server_address, sizeof(server_address)); 38 | if (connection_status) { 39 | perror("Connection failed: "); 40 | exit(1); 41 | } 42 | 43 | int listening_status = listen(server_fd, SOMAXCONN); 44 | if (listening_status) { 45 | perror("Listen failed: "); 46 | exit(1); 47 | } 48 | return server_fd; 49 | } 50 | 51 | void disable_io_block(int fd) 52 | { 53 | int current_descriptor_flags = fcntl(fd, F_GETFL); 54 | fcntl(fd, F_SETFL, current_descriptor_flags | O_NONBLOCK); 55 | } 56 | 57 | void read_buff(fd_data* data) { 58 | data->buffer_end = read(data->fd, data->buffer, BUFF_SIZE); 59 | } 60 | 61 | void write_buff(fd_data* data) { 62 | data->buffer_begin += write(data->fd, data->buffer + data->buffer_begin, data->buffer_end - data->buffer_begin); 63 | if (data->buffer_begin == data->buffer_end) { 64 | data->buffer_begin = data->buffer_end = 0; 65 | } 66 | } 67 | 68 | void process_epoll_event(struct epoll_event* event, int server_fd, int epoll_fd) 69 | { 70 | if (event->data.fd == server_fd) { 71 | int client_fd = accept(server_fd, NULL, NULL); 72 | disable_io_block(client_fd); 73 | 74 | struct epoll_event client_event; 75 | fd_data* add_data = calloc(1, sizeof(*add_data)); 76 | add_data->fd = client_fd; 77 | add_data->buffer_begin = 0; 78 | add_data->buffer_end = 0; 79 | client_event.data.ptr = add_data; 80 | client_event.events = EPOLLIN | EPOLLOUT | EPOLLHUP; 81 | 82 | if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, client_fd, &client_event) < 0) { 83 | perror("Add client to epoll failed: "); 84 | } 85 | 86 | } else { 87 | const uint32_t mask = event->events; 88 | fd_data* data = event->data.ptr; 89 | if ((mask & EPOLLIN) && (data->buffer_end == 0)) { 90 | read_buff(data); 91 | } 92 | if ((mask & EPOLLOUT) && (data->buffer_end != 0)) { 93 | write_buff(data); 94 | } 95 | if (mask & EPOLLHUP) { 96 | free(data); 97 | epoll_ctl(epoll_fd, EPOLL_CTL_DEL, event->data.fd, NULL); 98 | } 99 | } 100 | } 101 | 102 | int main(int argc, char* argv[]) 103 | { 104 | int server_fd = server_socket_init(atoi(argv[1])); 105 | 106 | disable_io_block(server_fd); 107 | 108 | struct epoll_event listen_event; 109 | int epoll_fd = epoll_create1(0); 110 | 111 | listen_event.events = EPOLLIN; 112 | listen_event.data.fd = server_fd; 113 | if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, server_fd, &listen_event) < 0) { 114 | perror("Adding server socket to epoll failed: "); 115 | exit(1); 116 | } 117 | 118 | const int MAX_PENDING_EVENTS_COUNT = 64000; 119 | struct epoll_event pending_events[MAX_PENDING_EVENTS_COUNT]; 120 | 121 | while (1) { 122 | int pending_events_count = 123 | epoll_wait(epoll_fd, pending_events, MAX_PENDING_EVENTS_COUNT, -1); 124 | for (size_t i = 0; i < pending_events_count; i++) { 125 | process_epoll_event(&pending_events[i], server_fd, epoll_fd); 126 | } 127 | } 128 | 129 | close(server_fd); 130 | return 0; 131 | } 132 | -------------------------------------------------------------------------------- /2022/sem18-threads/Makefile: -------------------------------------------------------------------------------- 1 | parallel_sum: 2 | gcc -pthread parallel_sum.c -o parallel_sum.out 3 | clean: 4 | rm *.out 5 | -------------------------------------------------------------------------------- /2022/sem18-threads/README.md: -------------------------------------------------------------------------------- 1 | # Потоки 2 | 3 | Зачастую нам бывает нужно что-нибудь параллельно посчитать. В этом случае нам удобно иметь общую память, а для локальных вычислений достаточно своего стека и регистров (и своей обработки сигналов). В таком случае можно говорить о потоках. 4 | 5 | Чтобы использовать потоки, нужно при компиляции добавить опцию `-pthread`. 6 | 7 | ## Использование POSIX потоков 8 | 9 | Запуск потоков осуществляется с помощью системного вызова `pthread_create`. 10 | 11 | ``` 12 | int pthread_create( 13 | // указатель на переменную-результат 14 | pthread_t *thread, 15 | 16 | // опционально: параметры нового потока, 17 | // например размер стека 18 | // может быть NULL 19 | const pthread_attr_t *attr, 20 | 21 | // функция, которая будет выполняться 22 | (void*)(*function)(void*), 23 | 24 | // аргумент, который передается в функцию 25 | void *arg 26 | ); 27 | ``` 28 | 29 | Дождаться завершения можно с помощью `pthread_join`. 30 | 31 | ``` 32 | int pthread_join( 33 | // поток, который нужно ждать 34 | pthread_t thread, 35 | 36 | // указатель на результат работы функции, 37 | // либо NULL, если он не интересен 38 | (void*) *retval 39 | ); 40 | ``` 41 | 42 | При это `retval` должен быть на куче, потому что стек потока уничтожится после завершения функции. 43 | 44 | Измерить ускорение от распараллеливания можно на примере `parallel_sum.c`. Заодно можно поговорить о том, что измеряет команда `time`. 45 | * `real` считает астрономическое время с момента запуска до завершения программы 46 | * `user` считает, сколько времени ваша программа исполнялась на CPU в обычном режиме 47 | * `sys` считает, сколько времени ваша программа исполнялась на CPU в привелигированном режиме (например, в системных вызовах) 48 | 49 | Можно позапускать пример на разном числе потоков и заметить, что по `real` достигается почти линейное ускорение в начале. А две других цифры не меняются, потому что объём вычислений остаётся неизменным, они просто распределяются по разным ядрам процессора. Ускорение остановится примерно в тот момент, когда число потоков сравнится с числом потоков процессора. 50 | 51 | ## Аттрибуты потоков 52 | 53 | Если результат работы потока вам больше не нужен, можно завершить его с помощью `pthread_cancel`. 54 | 55 | ``` 56 | int pthread_cancel( 57 | // поток, который нужно прибить 58 | pthread_t thread 59 | ); 60 | ``` 61 | 62 | Манипуляция аттрибутами потока производится путём создания структуры и вызова сеттеров. 63 | ``` 64 | pthread_attr_t thread_attr; 65 | pthread_attr_init(&thread_attr); 66 | pthread_attr_setstacksize(&thread_attr, MY_STACK_SIZE); 67 | pthread_attr_destroy(&thread_attr); 68 | ``` 69 | 70 | В контестах вам понадобится уменьшить размер стека (по-умолчанию он 8ГБ). 71 | 72 | ## Дополнительно 73 | 74 | Можно заметить, например с помощью Godbolt, что если создать в функции мало локальных переменных, стек-пойнтер не двигается. Это потому, что под стековым кадром в функциях, которые не вызывают другие функции, под Linux есть 128 байт красной зоны, которыми можно безопасно пользоваться. Это позволяет экономить целых две ассемблерных инструкции! 75 | -------------------------------------------------------------------------------- /2022/sem18-threads/parallel_sum.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | typedef struct { 7 | size_t l; 8 | size_t r; 9 | } task_args; 10 | 11 | typedef struct { 12 | size_t sum; 13 | } task_res; 14 | 15 | void* thread_sum(void *p_arg) { 16 | task_args* arg = (task_args*)p_arg; 17 | task_res* res = malloc(sizeof(task_res)); 18 | res->sum = 0; 19 | for (size_t i = arg->l; i < arg->r; i++) { 20 | res->sum += i; 21 | } 22 | return res; 23 | } 24 | 25 | int main(int argc, char **argv) { 26 | const size_t MAXN = 1000000000; 27 | const size_t THREAD_COUNT = atoi(argv[1]); 28 | pthread_t threads[THREAD_COUNT]; 29 | task_args thread_args[THREAD_COUNT]; 30 | for (size_t i = 0; i < THREAD_COUNT; ++i) { 31 | thread_args[i].l = (MAXN * i) / THREAD_COUNT; 32 | thread_args[i].r = (MAXN * (i + 1)) / THREAD_COUNT; 33 | pthread_create(&threads[i], NULL, thread_sum, &thread_args[i]); 34 | } 35 | 36 | size_t sum = 0; 37 | for (size_t i = 0; i < THREAD_COUNT; i++) { 38 | void* res_p; 39 | pthread_join(threads[i], &res_p); 40 | task_res* res = (task_res*)res_p; 41 | sum = (sum + res->sum); 42 | free(res); 43 | } 44 | 45 | printf("Calculated sum %lu\n", sum); 46 | return 0; 47 | } 48 | -------------------------------------------------------------------------------- /2022/sem19-synchronization/Makefile: -------------------------------------------------------------------------------- 1 | no_sync: 2 | gcc -pthread no_sync.c -o no_sync.out 3 | mutex: 4 | gcc -pthread mutex.c -o mutex.out 5 | condvar: 6 | gcc -pthread condvar.c -o condvar.out 7 | atomic: 8 | gcc -pthread atomic.c -o atomic.out 9 | clean: 10 | rm *.out 11 | -------------------------------------------------------------------------------- /2022/sem19-synchronization/atomic.c: -------------------------------------------------------------------------------- 1 | // solution by Alexey Zertsalov 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | typedef struct { 11 | pthread_t thread; 12 | int* counter; 13 | } thread_data; 14 | 15 | static void* thread_func(void* arg) { 16 | thread_data* data_ptr = (thread_data*)arg; 17 | for (int i = 0; i < 10000000; i++) { 18 | atomic_fetch_add(data_ptr->counter, 1); 19 | } 20 | return NULL; 21 | } 22 | 23 | int main() { 24 | const int THREADS_COUNT = 10; 25 | thread_data threads[THREADS_COUNT]; 26 | int shared_counter = 0; 27 | for (int i = 0; i < THREADS_COUNT; i++) { 28 | threads[i].counter = &shared_counter; 29 | pthread_create(&threads[i].thread, NULL, thread_func, (void*)&threads[i]); 30 | } 31 | for (int i = 0; i < THREADS_COUNT; i++) { 32 | pthread_join(threads[i].thread, NULL); 33 | } 34 | printf("%d\n", shared_counter); 35 | return 0; 36 | } 37 | -------------------------------------------------------------------------------- /2022/sem19-synchronization/condvar.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | typedef struct { 8 | int64_t current_num; 9 | int32_t num_updated; 10 | pthread_mutex_t* syncer; 11 | pthread_cond_t* prime_ready; 12 | 13 | } thread_data; 14 | 15 | void* worker(void* arg) 16 | { 17 | 18 | thread_data* data_ptr = (thread_data*)arg; 19 | for (int64_t i = 0; i < 10; i++) { 20 | pthread_mutex_lock(data_ptr->syncer); 21 | while (data_ptr->num_updated == 1) { 22 | pthread_cond_wait(data_ptr->prime_ready, data_ptr->syncer); 23 | } 24 | data_ptr->current_num = i * i; 25 | data_ptr->num_updated = 1; 26 | pthread_cond_signal(data_ptr->prime_ready); 27 | pthread_mutex_unlock(data_ptr->syncer); 28 | } 29 | return NULL; 30 | } 31 | 32 | int main(int argc, char* argv[]) 33 | { 34 | pthread_mutex_t syncer; 35 | pthread_mutex_init(&syncer, NULL); 36 | pthread_cond_t prime_ready; 37 | pthread_cond_init(&prime_ready, NULL); 38 | thread_data data; 39 | data.syncer = &syncer; 40 | data.prime_ready = &prime_ready; 41 | data.num_updated = 0; 42 | data.current_num = 0; 43 | 44 | pthread_t finder; 45 | 46 | pthread_create(&finder, NULL, worker, (void*)&data); 47 | for (int i = 0; i < 10; i++) { 48 | pthread_mutex_lock(&syncer); 49 | while (data.num_updated == 0) { 50 | pthread_cond_wait(&prime_ready, &syncer); 51 | } 52 | printf("%ld ", data.current_num); 53 | fflush(stdout); 54 | pthread_cond_signal(data.prime_ready); 55 | data.num_updated = 0; 56 | pthread_mutex_unlock(&syncer); 57 | } 58 | printf("\n"); 59 | 60 | pthread_join(finder, NULL); 61 | 62 | return 0; 63 | } 64 | -------------------------------------------------------------------------------- /2022/sem19-synchronization/mutex.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | typedef struct { 8 | pthread_t thread; 9 | int* counter; 10 | pthread_mutex_t* syncer; 11 | } thread_data; 12 | 13 | static void* thread_func(void* arg) { 14 | thread_data* data_ptr = (thread_data*)arg; 15 | int local_counter = 0; 16 | for (int i = 0; i < 100000000; i++) { 17 | local_counter++; 18 | } 19 | pthread_mutex_lock(data_ptr->syncer); 20 | (*(data_ptr->counter)) += local_counter; 21 | pthread_mutex_unlock(data_ptr->syncer); 22 | return NULL; 23 | } 24 | 25 | int main() { 26 | pthread_mutex_t syncer; 27 | pthread_mutex_init(&syncer, NULL); 28 | 29 | const int THREADS_COUNT = 10; 30 | thread_data threads[THREADS_COUNT]; 31 | int shared_counter = 0; 32 | for (int i = 0; i < THREADS_COUNT; i++) { 33 | threads[i].counter = &shared_counter; 34 | threads[i].syncer = &syncer; 35 | pthread_create(&threads[i].thread, NULL, thread_func, (void*)&threads[i]); 36 | } 37 | for (int i = 0; i < THREADS_COUNT; i++) { 38 | pthread_join(threads[i].thread, NULL); 39 | } 40 | printf("%d\n", shared_counter); 41 | return 0; 42 | } -------------------------------------------------------------------------------- /2022/sem19-synchronization/no_sync.c: -------------------------------------------------------------------------------- 1 | // solution by Alexey Zertsalov 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | typedef struct { 10 | pthread_t thread; 11 | int* counter; 12 | } thread_data; 13 | 14 | static void* thread_func(void* arg) { 15 | thread_data* data_ptr = (thread_data*)arg; 16 | for (int i = 0; i < 100000000; i++) { 17 | (*(data_ptr->counter))++; 18 | } 19 | return NULL; 20 | } 21 | 22 | int main() { 23 | const int THREADS_COUNT = 10; 24 | thread_data threads[THREADS_COUNT]; 25 | int shared_counter = 0; 26 | for (int i = 0; i < THREADS_COUNT; i++) { 27 | threads[i].counter = &shared_counter; 28 | pthread_create(&threads[i].thread, NULL, thread_func, (void*)&threads[i]); 29 | } 30 | for (int i = 0; i < THREADS_COUNT; i++) { 31 | pthread_join(threads[i].thread, NULL); 32 | } 33 | printf("%d\n", shared_counter); 34 | return 0; 35 | } 36 | -------------------------------------------------------------------------------- /2022/sem21-dynlib/Makefile: -------------------------------------------------------------------------------- 1 | static: 2 | gcc foo.c main.c -o static.out 3 | 4 | shared_lib: 5 | gcc -fPIC -shared foo.c -o libfoo.so 6 | 7 | dynamic: shared_lib 8 | gcc -L . main.c -lfoo -o dynamic.out 9 | 10 | dynamic_rpath: shared_lib 11 | gcc -L. main.c -lfoo -Wl,-rpath -Wl,'$$ORIGIN/.' -o dynamic_rpath.out 12 | 13 | loader: dynamic 14 | gcc loader.c -ldl -o loader.out 15 | 16 | mmap_loader: dynamic 17 | gcc mmap_loader.c -ldl -o mmap_loader.out 18 | 19 | clean: 20 | rm *.out, *.so 21 | -------------------------------------------------------------------------------- /2022/sem21-dynlib/README.md: -------------------------------------------------------------------------------- 1 | # Динамические библиотеки 2 | 3 | Наша программа, как правило, существует не сама по себе, а использует некоторые библиотеки. А значит, их надо как-то слинковать с нашим кодом. 4 | 5 | ## Статическая/динамическая линковка 6 | 7 | При статической линковке библиотечный код вставляется в исполняемый файл (см. `make static`). 8 | 9 | Можно проверить, что код функции `foo` действительно попал в `static.out` с помощью `objdump -d static.out | grep foo --after 9`. 10 | 11 | При динамической линковке (см. `make dynamic`) код функции подгружается в момент выполнения специальной программой `ld`. Поскольку код библиотеки может быть загружен в произвольное место в адресном пространстве, он должен быть позиционно-независимым (`fPIC`). Можно убедиться, что `dynamic.out` не содержит `foo`. 12 | 13 | При запуске нужно указать путь для поиска динамических библиотек `LD_LIBRARY_PATH=. ./dynamic.out`. 14 | 15 | В качестве альтернативы можно зашить путь для поиска библиотек в исполняемый файл (см. цель `dynamic_rpath`). 16 | 17 | |Статическая линковка|Динамическая линковка| 18 | |---|---| 19 | |Происходит во время компиляции|Происходит во время исполнения| 20 | |Код библиотеки содержится в исполняемом файле|Код библиотеки подгружается во время исполнения| 21 | |Больше исполняемый файл|Меньше исполняемый файл| 22 | |Медленнее загрузка|Быстрее загрузка| 23 | |Тяжело поддерживать|Легко обновлять| 24 | 25 | ## Трамплины 26 | 27 | Видно, что в `dynamic.out` содержится некая функция `foo@plt`. Она указывает на некоторый адрес. Он называется `relocation` и указывает на то, что при вызове функции нужно её загрузить и адрес записать адрес функции. С помощью `readelf -r dynamic.out` можно увидеть релокацию для функции `foo`. При первом вызове из `plt` вызовется динамический линковщик, который загрузит функцию и проставит адрес в `got`. 28 | 29 | ## Загрузка библиотеки с помощью dl 30 | 31 | `void *dlopen(const char *filename, int flags)` загружает файл с библиотекой и `handle`. Наиболее важные флаги: 32 | 33 | * `RTLD_NOW` загружает все символы и только потом выходит из `dlopen` 34 | * `RTLD_LAZY` подгружает символы по мере вызова 35 | 36 | `void *dlsym(void *handle, const char *symbol)` возвращает арес символа 37 | 38 | `int dlclose(void *handle)` уменьшает счётчик использований динамического объекта. Если он становится нулём, выгружает его из памяти. 39 | 40 | `char *dlerror()` возвращает текст ошибки, связанной с динамической загрузкой библиотек. 41 | 42 | Пример использования см. в `loader.c`. 43 | 44 | ## Загрузка библиотеки с помощью mmap 45 | 46 | Как мы помним из прошлых занятий, можно делать маппинги произвольных файлов в память. В частности, можно загрузить код библиотеки с правами на исполнение и вызвать его. Пример чисто игрушечный (так в жизни делать не стоит) можно найти в `mmap_loader.c`. 47 | -------------------------------------------------------------------------------- /2022/sem21-dynlib/foo.c: -------------------------------------------------------------------------------- 1 | double foo(double x) { 2 | return x * x; 3 | } 4 | -------------------------------------------------------------------------------- /2022/sem21-dynlib/loader.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | typedef double (*func_t)(double); 6 | 7 | int main(int argc, char* argv[]) 8 | { 9 | if (argc != 3){ 10 | fprintf(stderr, "There must be 2 arguments: library file and fucntion name"); 11 | exit(1); 12 | } 13 | void* lib = dlopen(argv[1], RTLD_NOW); 14 | if (! lib) { 15 | fprintf(stderr, "dlopen error: %s\n", dlerror()); 16 | exit(1); 17 | } 18 | void * entry = dlsym(lib, argv[2]); 19 | if (! entry) { 20 | fprintf(stderr, "dlsym error: %s\n", dlerror()); 21 | exit(1); 22 | } 23 | func_t func = entry; 24 | double argument; 25 | while (scanf("%lf", &argument) != EOF) { 26 | printf("%.3f\n", func(argument)); 27 | } 28 | dlclose(lib); 29 | } 30 | -------------------------------------------------------------------------------- /2022/sem21-dynlib/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | double foo(double x); 4 | 5 | int main() { 6 | printf("%lf\n", foo(1.3)); 7 | return 0; 8 | } 9 | -------------------------------------------------------------------------------- /2022/sem21-dynlib/mmap_loader.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | typedef double (*func_t)(double); 12 | 13 | int main(int argc, char** argv) { 14 | int input_file = open("libfoo.so", O_RDONLY); 15 | struct stat file_stats; 16 | fstat(input_file, &file_stats); 17 | char* content_ptr = mmap(NULL, file_stats.st_size, PROT_EXEC, MAP_PRIVATE, input_file, 0); 18 | func_t func = (func_t)(content_ptr + 0x10f9); 19 | double argument; 20 | while (scanf("%lf", &argument) != EOF) { 21 | printf("%.3f\n", func(argument)); 22 | } 23 | munmap(content_ptr, file_stats.st_size); 24 | close(input_file); 25 | } 26 | -------------------------------------------------------------------------------- /2022/sem22-http/Makefile: -------------------------------------------------------------------------------- 1 | get: 2 | gcc get.c -o get.out 3 | 4 | clean: 5 | rm *.out -------------------------------------------------------------------------------- /2022/sem22-http/get.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | int main(int argc, char* argv[]) 10 | { 11 | signal(SIGPIPE, SIG_IGN); 12 | char* hostname = argv[1]; 13 | char* path = argv[2]; 14 | 15 | struct addrinfo addr_hints = {.ai_family = AF_INET, 16 | .ai_socktype = SOCK_STREAM}; 17 | struct addrinfo* addr_res = NULL; 18 | getaddrinfo(hostname, "http", &addr_hints, &addr_res); 19 | int sock_fd = socket(AF_INET, SOCK_STREAM, 0); 20 | if (connect(sock_fd, addr_res->ai_addr, addr_res->ai_addrlen)) { 21 | perror("Couldn't connect to server"); 22 | exit(1); 23 | } 24 | 25 | const int BUFF_SIZE = 1000000; 26 | char buff[BUFF_SIZE]; 27 | memset(buff, 0, BUFF_SIZE); 28 | snprintf( 29 | buff, 30 | sizeof(buff), 31 | "GET %s HTTP/1.1\r\nHost: %s\r\nConnection: close\r\n\r\n", 32 | path, 33 | hostname); 34 | if (send(sock_fd, buff, strlen(buff), 0) == -1) { 35 | perror("Couldn't send request"); 36 | exit(1); 37 | } 38 | memset(buff, 0, BUFF_SIZE); 39 | 40 | FILE *sock_f = fdopen(sock_fd, "r"); 41 | bool headers_found = 0; 42 | while (fgets(buff, sizeof(buff), sock_f)){ 43 | if ((strcmp(buff, "\n") == 0) || (strcmp(buff, "\r\n")) == 0){ 44 | headers_found = 1; 45 | continue; 46 | } 47 | if (headers_found){ 48 | printf("%s", buff); 49 | } 50 | } 51 | 52 | fclose(sock_f); 53 | close(sock_fd); 54 | return 0; 55 | } 56 | -------------------------------------------------------------------------------- /2022/sem23-security/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.14) 2 | project(inf22_0 C) 3 | 4 | find_package(OpenSSL REQUIRED) 5 | 6 | set(CMAKE_C_STANDARD 99) 7 | 8 | include_directories(${OPENSSL_INCLUDE_DIR}) 9 | 10 | add_executable(main main.c) 11 | 12 | target_link_libraries(main ${OPENSSL_CRYPTO_LIBRARY}) 13 | -------------------------------------------------------------------------------- /2022/sem23-security/README.md: -------------------------------------------------------------------------------- 1 | # Безопасность 2 | 3 | ## Задачи которые мы решаем 4 | 5 | * Конфидециальность -- прочесть сообщение могут только получатель и отправитель 6 | * Ауентификация -- получатель и отправитель могут подтвердить источник сообщения 7 | * Целостность -- получать и отправитель могут понять, что сообщение не было изменено в процессе передачи 8 | 9 | ## Принципы шифрования 10 | 11 | ![Cipher scheme](cipher_principles.png) 12 | 13 | Отправитель применяет некоторую функцию (шифр) к исходному тексту, обратить которую может только получатель. Поскольку придумывать каждый раз новые эффективные алгоритмы шифрования тяжело, ещё в 19 веке криптографы решили, что результат шифра должен зависеть не только от входа, но и от ключа. Таким образом алгоритмы публично известны и проверены годами, а для конкретного взаимодействия нужно менять только ключ. 14 | 15 | Большинство современных шифров -- блочный. Они используют ключ небольшого размера (как правило, сотни бит) и шифрует с его помощью блоки соответствующего размера. У такого подхода есть минус, что одинаковые блоки исходного текста переходят в одинаковые после шифрования. Чтобы такого не было, при шифровании учитывается контекст, высчитанный на основе предыдущих блоков. Изначально он равен Initialization Vector. 16 | 17 | ![Block ciphers](block_ciphers.png) 18 | 19 | ## Симметричное шифрование 20 | 21 | Симметричное шифрование называется так, потому что для шифрования и расшифровки используется один и тот же ключ. В домашнем задании вы будете использовать популярный алгоритм AES. Подробнее про него можно почитать тут: https://habr.com/ru/post/112733/. 22 | 23 | Проблема симметричного шифрования в том, что вам нужно как-то заранее обменяться ключом. Обычно для этого используют ассиметричное шифрование. 24 | 25 | ## Ассиметричное шифрование 26 | 27 | ![Assymetric cipher](assymetric_cipher.png) 28 | 29 | В ассиметричном шифровании есть два ключа: публичный для шифрования и приватный для расшифровки. Наиболее популярный алгоритм RSA использует факторизацию больших чисел. Почитать подробнее: https://neerc.ifmo.ru/wiki/index.php?title=RSA 30 | 31 | Поскольку он вычислительно значительно более сложный, применяется обычно для обмена ключами. 32 | 33 | А можно наоборот зашифровать сообщение приватным ключом, а расшифровывать публичным. С точки зрения обмена секретами сообщения, которые может расшифровать кто угодно, не очень полезны. Зато именно так работает электронная подпись. 34 | 35 | Проблема целостности решается тем, что вместе с сообщением передаётся хэш-функция (например семейства SHA-2) от конкатенация исходного текста с секретом. 36 | 37 | ## Сертификаты 38 | 39 | Проблема, которая сразу всплывает, как понять, что публичный ключ действительно принадлежит нужному агенту. Например, если вы передаёте сайту банка данные банковской карты, вы хотите убедиться, что это действительно он. 40 | 41 | ![CA](ca.png) 42 | 43 | Один из вариантов решения -- подписывать публичный ключ сертификатом. Сертификат подписан сертификатом более высокого уровня и так далее. Так называемые корневые сертификаты вшиваются, например, в браузеры. 44 | 45 | ## TLS 46 | 47 | Транспортный уровень решает проблему доставки сообщений от отправителя для адресата. На следующем уровне мы хотим решить проблему безопасной доставки, потому что если при перехвате вскроются данные вашей кредитной карты, будет неприятно. 48 | 49 | Механизм установки соединения напоминает TCP. 50 | 51 | ![TLS](tls.png) 52 | 53 | Сначала клиент и сервер договаривается об алгоритмах, которые дальше будут использовать, например AES с SHA-256. После этого сервер может послать свой публичный ключ ключ клиенту. Далее на основе него и времён отправки генерируются секреты (по-одному на каждое направление связи). 54 | 55 | ## IPSec 56 | 57 | IPSec это семейство протоколов для безопасной передачи данных по сети. 58 | 59 | Первая часть Authentication Header и Encapsulating Security Payload для передачи данных по установленному соединению. 60 | 61 | Вторая часть ISAKMP для обмена ключами и установки соединения. 62 | 63 | ESP это протокол для безопасной передачи данных через SA. Заголовок ESP находится после IP заголовка. В тунельном режиме оригинальный IP пакет упаковывается в ESP целиком и шифруется. 64 | 65 | ![ESP tunnel](tunnel.png) 66 | 67 | Ровно так построен VPN. Подробнее можно почитать в СДСМ: https://linkmeup.ru/blog/1197/ -------------------------------------------------------------------------------- /2022/sem23-security/assymetric_cipher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Khabutdinov-Arslan/caos-seminars/374b0109aa47de5df35ad1b58506176b47960997/2022/sem23-security/assymetric_cipher.png -------------------------------------------------------------------------------- /2022/sem23-security/block_ciphers.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Khabutdinov-Arslan/caos-seminars/374b0109aa47de5df35ad1b58506176b47960997/2022/sem23-security/block_ciphers.png -------------------------------------------------------------------------------- /2022/sem23-security/ca.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Khabutdinov-Arslan/caos-seminars/374b0109aa47de5df35ad1b58506176b47960997/2022/sem23-security/ca.png -------------------------------------------------------------------------------- /2022/sem23-security/cipher_principles.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Khabutdinov-Arslan/caos-seminars/374b0109aa47de5df35ad1b58506176b47960997/2022/sem23-security/cipher_principles.png -------------------------------------------------------------------------------- /2022/sem23-security/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | int main() 8 | { 9 | const int HASH_SIZE = 512 / CHAR_BIT; 10 | SHA512_CTX context; 11 | SHA512_Init(&context); 12 | 13 | char buff[HASH_SIZE]; 14 | while (1) { 15 | size_t read_during_iteration = read(0, buff, sizeof(buff)); 16 | if (read_during_iteration == 0){ 17 | break; 18 | }else{ 19 | SHA512_Update(&context, buff, read_during_iteration); 20 | } 21 | } 22 | 23 | unsigned char hash[HASH_SIZE]; 24 | SHA512_Final(hash, &context); 25 | printf("0x"); 26 | for (size_t i = 0; i < sizeof(hash); i++){ 27 | printf("%02"PRIx32, hash[i]&0xffU); 28 | } 29 | printf("\n"); 30 | 31 | return 0; 32 | } -------------------------------------------------------------------------------- /2022/sem23-security/tls.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Khabutdinov-Arslan/caos-seminars/374b0109aa47de5df35ad1b58506176b47960997/2022/sem23-security/tls.png -------------------------------------------------------------------------------- /2022/sem23-security/tunnel.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Khabutdinov-Arslan/caos-seminars/374b0109aa47de5df35ad1b58506176b47960997/2022/sem23-security/tunnel.png -------------------------------------------------------------------------------- /2022/sem25-fuse/docker/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM busybox 2 | RUN echo 'kek' > kek.txt 3 | RUN echo 'lol' > lol.txt 4 | -------------------------------------------------------------------------------- /2022/sem25-fuse/fuse.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Khabutdinov-Arslan/caos-seminars/374b0109aa47de5df35ad1b58506176b47960997/2022/sem25-fuse/fuse.png -------------------------------------------------------------------------------- /2022/sem25-fuse/overlayfs.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Khabutdinov-Arslan/caos-seminars/374b0109aa47de5df35ad1b58506176b47960997/2022/sem25-fuse/overlayfs.jpg -------------------------------------------------------------------------------- /2022/sem25-fuse/task/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.13) 2 | project(fuse_demo C) 3 | 4 | find_package(PkgConfig REQUIRED) 5 | pkg_check_modules(FUSE REQUIRED fuse3) 6 | 7 | link_libraries(${FUSE_LIBRARIES}) # -lfuse3 -lpthread 8 | include_directories(${FUSE_INCLUDE_DIRS}) # -I/usr/include/fuse3 9 | # compile_options(${FUSE_CFLAGS_OTHER}) # empty since fuse 3.0 10 | 11 | add_executable(fuse_demo main.c) -------------------------------------------------------------------------------- /2022/sem25-fuse/task/kek.txt: -------------------------------------------------------------------------------- 1 | 1 2 | kek.txt 3 3 | 4 | kek 5 | -------------------------------------------------------------------------------- /2022/sem25-fuse/vfs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Khabutdinov-Arslan/caos-seminars/374b0109aa47de5df35ad1b58506176b47960997/2022/sem25-fuse/vfs.png -------------------------------------------------------------------------------- /2022/sem26-python/README.md: -------------------------------------------------------------------------------- 1 | # Python extending and embedding 2 | 3 | Python гораздо больше распространён, чем C. Он используется в вебе, для расчётов, автоматизации процессов и многих других вещей. 4 | 5 | Зачем может понадобиться писать библиотеки для него на C? 6 | * Есть большое количество библиотек для C, для которых проще написать адаптер 7 | * Хочется делать что-то низкоуровневое 8 | * Хочется ускорить программу. В частности, на Python из-за GIL тяжело параллелить вычисления. 9 | 10 | ## Python/C API 11 | 12 | Python из C подключается как динамическая библиотека. Она содержит множество методов, подробнее можно почитать в официальной [документации](https://docs.python.org/3/c-api/index.html). 13 | 14 | Создание примера базового модуля также хорошо описано в [документации](https://docs.python.org/3/extending/extending.html). Остановимся на основных полезных конструкциях, которые используются в `extend`. 15 | 16 | Базовым классом в Python является `object`, поэтому все функции в `C` работают с `PyObject`. Конкретные классы и методы именуются `PyClass_Method`, указатель на объект класса передаётся первым аргументом. `args` распаковываются с помощью `PyArg_ParseTuple`, подробнее про её аргументы можно почитать в [документации](https://docs.python.org/3/c-api/arg.html). Чтобы преобразовать примитивный тип из `Python` в `C` используются конструкции вида `PyTypeAsType`, обратно -- `PyTypeFromType`. Исключения прокидываются с помощью `PyErr_SetString` и возвращения `NULL` из функции. 17 | 18 | Для объявления модуля используется `PyInit_Module` (название должно совпадать с названием результирующей разделяемой библиотеки). В нём перечисляется метаинформация о модуле и ссылки на методы. Также модуль нужно добавить в таблицу модулей с помощью `PyImport_AppendInittab`. 19 | 20 | ## Вызов интерпретатора Python из С 21 | 22 | Если по какой-то причине вам понадобилось вызвать интерпретатор `Python` из `C`, нужно всё также подключить `Python.h` в программе. Отправить текст на запуск можно с помощью `PyRun_SimpleFile` или `PyRun_SimpleString`. Перед их использованием нужно не забыть сделать `Py_Initialize`, а после `Py_Finalize`. Пример можно найти в `embed`. 23 | -------------------------------------------------------------------------------- /2022/sem26-python/embed/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.13) 2 | project(main C) 3 | 4 | add_executable(main main.c) 5 | 6 | find_package(PythonLibs 3.6 REQUIRED) 7 | include_directories(${PYTHON_INCLUDE_DIRS}) 8 | target_link_libraries(main ${PYTHON_LIBRARIES}) 9 | 10 | -------------------------------------------------------------------------------- /2022/sem26-python/embed/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | int main(int argc, char *argv[]) 5 | { 6 | int a; 7 | scanf("%d", &a); 8 | char buff[50]; 9 | sprintf(buff, "a=%d\nprint(a ** 2)", a); 10 | Py_Initialize(); 11 | PyRun_SimpleString(buff); 12 | Py_Finalize(); 13 | } 14 | -------------------------------------------------------------------------------- /2022/sem26-python/extend/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | find_package(PythonLibs 3.6 REQUIRED) 2 | include_directories(${PYTHON_INCLUDE_DIRS}) 3 | 4 | python_add_module(matrix main.c) 5 | -------------------------------------------------------------------------------- /2022/sem26-python/extend/main.c: -------------------------------------------------------------------------------- 1 | #define PY_SSIZE_T_CLEAN 2 | #include 3 | #include 4 | 5 | static PyObject* 6 | dot(PyObject *self, PyObject *args); 7 | 8 | PyObject* 9 | PyInit_matrix(); 10 | 11 | static PyObject* 12 | create_module(); 13 | 14 | int 15 | main() { 16 | PyImport_AppendInittab("matrix", create_module); 17 | 18 | Py_Initialize(); 19 | } 20 | 21 | static PyObject* 22 | create_module() { 23 | PyErr_SetString(PyExc_RuntimeError, "Not implemented yet"); 24 | return NULL; 25 | } 26 | 27 | static PyObject* 28 | dot(PyObject *self, PyObject *args) { 29 | int32_t matrix_size; 30 | PyObject* A; 31 | PyObject* B; 32 | if (!PyArg_ParseTuple(args, "i|O|O", &matrix_size, &A, &B)) { 33 | // not valid arguments 34 | } 35 | PyObject* result = PyList_New(0); 36 | for (int32_t i = 0; i < matrix_size; ++i) { 37 | PyObject* current_line = PyList_New(0); 38 | for (int32_t j = 0; j < matrix_size; ++j) { 39 | int64_t current_result = 0; 40 | for (int32_t k = 0; k < matrix_size; ++k){ 41 | int64_t element_from_A = 0; 42 | if (i < PyList_Size(A) && k < PyList_Size(A)) 43 | element_from_A = PyLong_AsLong(PyList_GetItem(PyList_GetItem(A, i), k)); 44 | int64_t element_from_B = 0; 45 | if (j < PyList_Size(B) && k < PyList_Size(B)) 46 | element_from_B = PyLong_AsLong(PyList_GetItem(PyList_GetItem(B, k), j)); 47 | current_result += element_from_A * element_from_B; 48 | } 49 | PyList_Append(current_line, PyLong_FromLong(current_result)); 50 | } 51 | PyList_Append(result, current_line); 52 | } 53 | return result; 54 | } 55 | 56 | PyObject* PyInit_matrix(void) { 57 | 58 | static PyMethodDef methods[] = { 59 | { 60 | // name of the method 61 | .ml_name = "dot", 62 | // pointer 63 | .ml_meth = dot, 64 | // flags for using C 65 | .ml_flags = METH_VARARGS, 66 | // description 67 | .ml_doc = "Do something very useful" 68 | }, 69 | // end 70 | {NULL, NULL, 0, NULL} 71 | }; 72 | 73 | static PyModuleDef moduleDef = { 74 | .m_base = PyModuleDef_HEAD_INIT, 75 | // module name 76 | .m_name = "matrix", 77 | // additional memory 78 | .m_size = -1, 79 | // methods 80 | .m_methods = methods, 81 | }; 82 | return PyModule_Create(&moduleDef); 83 | } 84 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # АКОС 2023-2024 2 | 3 | В этом репозитории содержатся материалы по осенней части курса АКОС ФПМИ МФТИ. Они используются на семинарах группы Б05-227. 4 | 5 | ## Список тем 6 | 7 | 1. [Инструменты разработчика](sem01-intro) 8 | 2. [Представление данных](sem02-encoding) 9 | 3. [Файлы](sem03-files) 10 | 4. [Ассемблер x86](sem04-asm) 11 | 5. [Ассемблер ARM](sem05-arm) 12 | 6. [Виртуальная память](sem06-memory) 13 | 7. [Процессы и потоки](sem07-processes) 14 | 8. [Многопоточная синхронизация](sem08-thread-sync) 15 | 9. [Межпроцессное взаимодействие](sem09-ipc) 16 | 10. [Сокеты](sem10-sockets) 17 | 11. [Продвинутые сокеты](sem11-sockets-advanced) 18 | 12. [Линковка](sem12-dynlib) 19 | 13. [Контейнеры](sem13-containers) 20 | 21 | ## Благодарности 22 | 23 | При подготовке использованы материалы нескольких курсов по сетям и операционным системам, а также помощь множества людей. Автор особенно благодарен: 24 | 25 | * Александру Андрееву 26 | * Сергею Горбунову 27 | * Дмитрию Купцову 28 | * Андрею Титову 29 | * Асхату Хайруллину 30 | * Виктору Яковлеву 31 | 32 | ## Участие 33 | 34 | Горячо приветсвуется добавление деталей и исправление недочётов с помощью Pull Request'ов. 35 | 36 | Связаться с автором можно по контактам из профиля Github. 37 | -------------------------------------------------------------------------------- /sem01-intro/Makefile: -------------------------------------------------------------------------------- 1 | preprocess: aplusb.c 2 | gcc -E aplusb.c -o aplusb_preprocessed.c 3 | compile: preprocess 4 | gcc -S aplusb_preprocessed.c -o aplusb_asm.S 5 | object: compile 6 | gcc -c aplusb_asm.S -o aplusb_object.o 7 | binary: object 8 | gcc aplusb_object.o -o aplusb_executable.out 9 | debug: aplusb.c 10 | gcc -g aplusb.c -o aplusb_debug.out 11 | bad_gdb: bad.c 12 | gcc -g bad.c -o bad.out 13 | bad_asan: 14 | gcc -fsanitize=address -g bad.c -o bad.out 15 | clean: 16 | rm aplusb_object.o aplusb_executable.out aplusb_debug.out bad.out coredump 17 | -------------------------------------------------------------------------------- /sem01-intro/aplusb.c: -------------------------------------------------------------------------------- 1 | #define ONE 1 2 | #define TWO 2 3 | 4 | // simple program 5 | 6 | int main() { 7 | int a = ONE; 8 | int b = TWO; 9 | int c = a + b; 10 | return 0; 11 | } -------------------------------------------------------------------------------- /sem01-intro/aplusb_asm.S: -------------------------------------------------------------------------------- 1 | .file "aplusb_preprocessed.c" 2 | .text 3 | .globl main 4 | .type main, @function 5 | main: 6 | .LFB0: 7 | .cfi_startproc 8 | endbr64 9 | pushq %rbp 10 | .cfi_def_cfa_offset 16 11 | .cfi_offset 6, -16 12 | movq %rsp, %rbp 13 | .cfi_def_cfa_register 6 14 | movl $1, -12(%rbp) 15 | movl $2, -8(%rbp) 16 | movl -12(%rbp), %edx 17 | movl -8(%rbp), %eax 18 | addl %edx, %eax 19 | movl %eax, -4(%rbp) 20 | movl $0, %eax 21 | popq %rbp 22 | .cfi_def_cfa 7, 8 23 | ret 24 | .cfi_endproc 25 | .LFE0: 26 | .size main, .-main 27 | .ident "GCC: (Ubuntu 12.3.0-1ubuntu1~23.04) 12.3.0" 28 | .section .note.GNU-stack,"",@progbits 29 | .section .note.gnu.property,"a" 30 | .align 8 31 | .long 1f - 0f 32 | .long 4f - 1f 33 | .long 5 34 | 0: 35 | .string "GNU" 36 | 1: 37 | .align 8 38 | .long 0xc0000002 39 | .long 3f - 2f 40 | 2: 41 | .long 0x3 42 | 3: 43 | .align 8 44 | 4: 45 | -------------------------------------------------------------------------------- /sem01-intro/aplusb_preprocessed.c: -------------------------------------------------------------------------------- 1 | # 0 "aplusb.c" 2 | # 0 "" 3 | # 0 "" 4 | # 1 "/usr/include/stdc-predef.h" 1 3 4 5 | # 0 "" 2 6 | # 1 "aplusb.c" 7 | 8 | 9 | 10 | 11 | 12 | int main() { 13 | int a = 1; 14 | int b = 2; 15 | int c = a + b; 16 | return 0; 17 | } 18 | -------------------------------------------------------------------------------- /sem01-intro/bad.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | int get(int *a, int i) { 5 | return a[i]; 6 | } 7 | 8 | int main() { 9 | int a[10]; 10 | printf("%d\n", get(a, 2)); 11 | printf("%d\n", a[27]); 12 | printf("%d\n", a[2007]); 13 | printf("%d\n", get(a, 4000007)); 14 | } 15 | -------------------------------------------------------------------------------- /sem02-encoding/Makefile: -------------------------------------------------------------------------------- 1 | print_int.out: print_int.c 2 | gcc print_int.c -o print_int.out 3 | print_float.out: print_float.c 4 | gcc print_float.c -o print_float.out 5 | overflow.out: overflow.c 6 | gcc -fsanitize=undefined overflow.c -o overflow.out 7 | print_union.out: print_union.c 8 | gcc print_union.c -o print_union.out 9 | clean: 10 | rm *.out -------------------------------------------------------------------------------- /sem02-encoding/README.md: -------------------------------------------------------------------------------- 1 | # Представление данных 2 | 3 | Поговорим о том, как данные хранятся в памяти. 4 | 5 | ## Целые числа 6 | 7 | Минимально адресуемой единицей является байт. Сколько бит в байте? 8 | 9 | Ответ содержится в константе `CHAR_BIT` из `limits.h`. По стандарту гарантируется, что хотя бы 8. Однобайтовый тип данных называется `char`. 10 | 11 | Типы, которыми вы привыкли пользоваться, такие как `int` плохи тем, что на разных системах занимают разное число байт (на 32-битных обычно 4, на 64-битных 8). 12 | 13 | Чтобы избежать всей этой путаницы, далее рекомендуется использовать типы с фиксированным числом бит: `int8_t`, `uint16_t` и так далее. Они определены в `stdint.h`. 14 | 15 | Давайте с помощью небольшой демки (`print_int.c`) посмотрим на то, как эти числа хранятся в памяти. 16 | 17 | Возьмём 8-битное беззнаковое число, как самый простой пример. В нём биты просто задают двоичное представление числа, начиная со старших разрядов. 18 | 19 | На примере 8-битных знаковых мы видим, что первый бит отвечает за знак (0 для положительных и нуля, 1 для отрицательных).Для чисел от $0$ до $2^{n-1}$ (где $n$ это число знаков в двоичной записи) представление совпадает с беззнаковыми. Для отрицательных представление получается из соображения, что $x+(-x)=2^n$ (старший бит отбрасывается из-за переполнения и получается просто 0, то есть мы как бы работаем в кольце вычетов по модулю $2^n$). 20 | 21 | Для чисел с большей разрядностью просто добавляются старшие байты в начало. `x86` поэтому называется `Little-endian` архитектурой. При передаче по сети наоборот принято передавать старшие байты в начале, можно привести число к такому виду с помощью функции `htons` и убедиться, что порядок изменился. 22 | 23 | ## UBSAN 24 | 25 | Переполнение знакового типа является неопределённым поведением по стандарту. Чтобы это отлавливать, можно использовать флаг ``-fsanitize=undefined`` при компиляции (см. ``overflow.c``). 26 | 27 | ## Вещественные числа 28 | 29 | Два основных типа вещественных с плавающей точкой, которые определены стандартом языка Си, - это `float` (используется 4 байта для хранения) и `double` (используется 8 байт). 30 | 31 | Для простоты далее будет рассматриать `float` (см. ``print_float.c``). Видимо, что старший бит вновь признак отрецательности числа. Следующие 8 бит называются экспонентой, а оставшиеся 23 - мантиссой. 32 | 33 | Значение для обычных чисел (не денормализованных) может быть получено как: 34 | 35 | ``` 36 | Value = (-1)^S * 2^(E-B) * ( 1 + M / (2^(M_bits-1)) ) 37 | ``` 38 | где `S` - бит знака, `E` - значение смещенной экспоненты, `B` - смещение (127 или 1023), а `M` - значение мантиссы, `M_bits` - количество бит в экспоненте. 39 | 40 | Можно разбирать число с помощью ``union`` (см. ``print_union.c``). 41 | 42 | > Визуализатор конвертация: https://www.h-schmidt.net/FloatConverter/IEEE754.html 43 | > Визуализатор арифметики: http://weitz.de/ieee/ 44 | > Как производить арифметические действия: https://www.rfwireless-world.com/Tutorials/floating-point-tutorial.html 45 | 46 | 47 | ## Текстовые кодировки 48 | 49 | ![ASCII](ascii.png) 50 | 51 | В середине 20 века возникла потребность как-то кодировать текст двоичными данными. Тогда решили, что 7 бит достаточно на цифры и латинский алфавит, так и появилась кодировка ASCII. 52 | 53 | Позже один лишний бит позволил хранить в диапазоне от 128 до 255 символы национальных кодировок. В России, например, в 90-е была распространена `KOI8-R`. 54 | 55 | Иметь отдельную кодировку под каждый язык всё ещё было не очень удобно, поэтому появилась `UTF-8`. Чтобы не занимать гарантированно 4 октета (будем называть так группы ), был придуман интересный дизайн. 56 | 57 | ``echo 'C' | hexdump -C`` (`0a` отвечает за перевод строки, по сути есть один значащий байт `43` и код совпадает с ASCII) 58 | 59 | То есть для самых частотных текстов, которые написаны латиницей, она занимает столько же место, сколько ASCII. 60 | 61 | ``echo 'Я' | hexdump -C`` (Байты `d0` `af` отвечают за `Я`) 62 | 63 | Для большинства языков хватает двух байт. 64 | 65 | ``echo '😊' | hexdump -C`` 66 | 67 | 4 байта нужно для каких-то специфических символов. 68 | 69 | В общем случае кодирование подчиняется следующим шаблонам. 70 | 71 | | Количество октетов | Шаблон | 72 | |---|---| 73 | |1|0xxxxxxx| 74 | |2|110xxxxx 10xxxxxx| 75 | |3|1110xxxx 10xxxxxx 10xxxxxx| 76 | |4|11110xxx 10xxxxxx 10xxxxxx 10xxxxxx| 77 | -------------------------------------------------------------------------------- /sem02-encoding/overflow.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | int main() { 5 | int32_t n = (1 << 31) - 1; 6 | n += 1; 7 | printf("%d\n", n); 8 | } -------------------------------------------------------------------------------- /sem02-encoding/print_float.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | void printBits(size_t size, void* ptr) 6 | { 7 | unsigned char *bytes = (unsigned char*) ptr; 8 | unsigned char byte; 9 | int i, j; 10 | 11 | for (i = 3; i >= 0; i--) { 12 | for (j = 7; j >= 0; j--) { 13 | byte = (bytes[i] >> j) & 1; // get j-th bit 14 | printf("%u", byte); 15 | if ((i >= 2) && (j == 7)) { 16 | printf(" "); 17 | } 18 | } 19 | } 20 | printf("\n"); 21 | } 22 | 23 | int main() { 24 | float op = 1.0; 25 | printf(" 1.0: "); 26 | printBits(sizeof(op), &op); 27 | float on = -1.0; 28 | printf("-1.0: "); 29 | printBits(sizeof(on), &on); 30 | float ep = 8.0; 31 | printf(" 8.0: "); 32 | printBits(sizeof(on), &ep); 33 | float hp = 0.5; 34 | printf(" 0.5: "); 35 | printBits(sizeof(hp), &hp); 36 | float hl = 1.5; 37 | printf(" 1.5: "); 38 | printBits(sizeof(hp), &hl); 39 | float hm = 1.75; 40 | printf("1.75: "); 41 | printBits(sizeof(hm), &hm); 42 | return 0; 43 | } -------------------------------------------------------------------------------- /sem02-encoding/print_int.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | void printBits(size_t size, void* ptr) 6 | { 7 | unsigned char *bytes = (unsigned char*) ptr; 8 | unsigned char byte; 9 | int i, j; 10 | 11 | for (i = size - 1; i >= 0; i--) { 12 | for (j = 7; j >= 0; j--) { 13 | byte = (bytes[i] >> j) & 1; 14 | printf("%u", byte); 15 | } 16 | printf(" "); 17 | } 18 | printf("\n"); 19 | } 20 | 21 | int main() { 22 | uint8_t up = 27; 23 | printf("uint8_t 27: "); 24 | printBits(sizeof(up), &up); 25 | int8_t sp = 27; 26 | printf("int8_t 27: "); 27 | printBits(sizeof(sp), &sp); 28 | int8_t sn = -27; 29 | printf("int8_t -27: "); 30 | printBits(sizeof(sn), &sn); 31 | uint16_t lp = 27; 32 | printf("uint16_t 27 (little endian): "); 33 | printBits(sizeof(lp), &lp); 34 | uint16_t no = htons(27); 35 | printf("uint16_t 27 (big endian): "); 36 | printBits(sizeof(no), &no); 37 | return 0; 38 | } -------------------------------------------------------------------------------- /sem02-encoding/print_union.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | typedef union { 7 | double double_val; 8 | struct { 9 | uint64_t mantissa_val : 52; 10 | uint64_t exp_val : 11; 11 | uint64_t sign_val : 1; 12 | }; 13 | } double_parser_t; 14 | 15 | int main() { 16 | double_parser_t op = {.double_val = -130.0}; 17 | printf("F = %f\n", op.double_val); 18 | printf("S = %d\n", op.sign_val); 19 | printf("E = %d\n", op.exp_val); 20 | printf("M = %lld\n", (long long unsigned int)op.mantissa_val); 21 | return 0; 22 | } -------------------------------------------------------------------------------- /sem03-files/Makefile: -------------------------------------------------------------------------------- 1 | read_buff.out: read_buff.c 2 | gcc read_buff.c -o read_buff.out 3 | lseek_demo.out: lseek_demo.c 4 | gcc lseek_demo.c -o lseek_demo.out 5 | pread_demo.out: pread_demo.c 6 | gcc pread_demo.c -o pread_demo.out 7 | writev_demo.out: writev_demo.c 8 | gcc writev_demo.c -o writev_demo.out 9 | count_filetypes.out: count_filetypes.c 10 | gcc count_filetypes.c -o count_filetypes.out 11 | list_dir.out: list_dir.c 12 | gcc list_dir.c -o list_dir.out 13 | time.out: time.c 14 | gcc time.c -o time.out 15 | clean: 16 | rm *.out 17 | -------------------------------------------------------------------------------- /sem03-files/count_filetypes.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | int main() 9 | { 10 | char file_name[PATH_MAX]; 11 | size_t regular_count = 0; 12 | size_t symlink_count = 0; 13 | size_t executable_count = 0; 14 | struct stat file_stats; 15 | while (fgets(file_name, sizeof(file_name), stdin)) { 16 | for (size_t i = 0; i < sizeof(file_name); i++) { 17 | if (file_name[i] == '\n') { 18 | file_name[i] = '\0'; 19 | break; 20 | } 21 | } 22 | if (lstat(file_name, &file_stats) != -1) { 23 | if (S_ISREG(file_stats.st_mode)) { 24 | ++regular_count; 25 | } 26 | if (S_ISLNK(file_stats.st_mode)) { 27 | ++symlink_count; 28 | } 29 | } 30 | if (access(file_name, X_OK)) { 31 | ++executable_count; 32 | } 33 | } 34 | printf("Regular files count = %lu\nSymbolic links count = %lu\nExecutable count = %lu\n", regular_count, symlink_count, executable_count); 35 | return 0; 36 | } 37 | -------------------------------------------------------------------------------- /sem03-files/fd_table.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Khabutdinov-Arslan/caos-seminars/374b0109aa47de5df35ad1b58506176b47960997/sem03-files/fd_table.gif -------------------------------------------------------------------------------- /sem03-files/fuse.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Khabutdinov-Arslan/caos-seminars/374b0109aa47de5df35ad1b58506176b47960997/sem03-files/fuse.png -------------------------------------------------------------------------------- /sem03-files/fuse/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.13) 2 | project(fuse_demo C) 3 | 4 | find_package(PkgConfig REQUIRED) 5 | pkg_check_modules(FUSE REQUIRED fuse3) # apt install libfuse3-dev pkg-config fuse3 6 | 7 | link_libraries(${FUSE_LIBRARIES}) # -lfuse3 -lpthread 8 | include_directories(${FUSE_INCLUDE_DIRS}) # -I/usr/include/fuse3 9 | # compile_options(${FUSE_CFLAGS_OTHER}) # empty since fuse 3.0 10 | 11 | add_executable(fuse_demo main.c) 12 | -------------------------------------------------------------------------------- /sem03-files/fuse/kek.txt: -------------------------------------------------------------------------------- 1 | 1 2 | kek.txt 3 3 | 4 | kek 5 | -------------------------------------------------------------------------------- /sem03-files/in.txt: -------------------------------------------------------------------------------- 1 | Voyager1 2 | -------------------------------------------------------------------------------- /sem03-files/inode_ext4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Khabutdinov-Arslan/caos-seminars/374b0109aa47de5df35ad1b58506176b47960997/sem03-files/inode_ext4.jpg -------------------------------------------------------------------------------- /sem03-files/list_dir.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | int main(int argc, char **argv) { 8 | char *dir_name = (argc > 1) ? argv[1] : getcwd(malloc(PATH_MAX), PATH_MAX); 9 | DIR* dir = opendir(dir_name); 10 | for (struct dirent* cur_entry = readdir(dir); cur_entry; cur_entry = readdir(dir)){ 11 | printf("%s\n", cur_entry->d_name); 12 | } 13 | closedir(dir); 14 | return 0; 15 | } 16 | -------------------------------------------------------------------------------- /sem03-files/lseek_demo.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | int main() { 8 | int fd = open("in.txt", O_RDONLY); 9 | lseek(fd, 7, SEEK_SET); 10 | char buff[1]; 11 | read(fd, &buff, sizeof(buff)); 12 | write(1, buff, sizeof(buff)); 13 | close(fd); 14 | return 0; 15 | } 16 | 17 | -------------------------------------------------------------------------------- /sem03-files/pread_demo.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | int main() { 8 | int fd = open("in.txt", O_RDONLY); 9 | char buff[1]; 10 | pread(fd, &buff, sizeof(buff), 7); 11 | write(1, buff, sizeof(buff)); 12 | close(fd); 13 | return 0; 14 | } 15 | 16 | -------------------------------------------------------------------------------- /sem03-files/read_buff.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | int read_buff(int fd, char* buff, size_t buff_size) 8 | { 9 | size_t read_buff_size = 0; 10 | while (read_buff_size < buff_size) { 11 | size_t read_during_iteration = 12 | read(fd, buff + read_buff_size, buff_size - read_buff_size); 13 | read_buff_size += read_during_iteration; 14 | if (read_during_iteration == 0) { 15 | return read_buff_size; 16 | } 17 | if ((read_during_iteration < 0) && (errno != EINTR) && (errno != EAGAIN)) { 18 | return -1; 19 | } 20 | } 21 | return read_buff_size; 22 | } 23 | 24 | int main() { 25 | const size_t BUFF_SIZE = 4096; 26 | char buffer[BUFF_SIZE]; 27 | int read_count = read_buff(0, buffer, BUFF_SIZE); 28 | printf("Read %d", read_count); 29 | return 0; 30 | } 31 | -------------------------------------------------------------------------------- /sem03-files/time.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | int main() { 8 | struct timespec spec = {0}; 9 | clock_gettime(CLOCK_REALTIME, &spec); 10 | struct tm local_tm = {0}; 11 | localtime_r(&spec.tv_sec, &local_tm); 12 | char time_str[100]; 13 | size_t time_len = strftime(time_str, sizeof(time_str), "%Y.%m.%d %H:%M:%S", &local_tm); 14 | printf("%s\n", time_str); 15 | } 16 | -------------------------------------------------------------------------------- /sem03-files/writev_demo.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | 6 | int main() 7 | { 8 | char buff1[] = "buff1 ", buff2[] = "buff2\n"; 9 | struct iovec buffs[] = { 10 | {buff1, strlen(buff1)}, 11 | {buff2, strlen(buff2)}, 12 | }; 13 | writev(1, buffs, 2); 14 | return 0; 15 | } 16 | -------------------------------------------------------------------------------- /sem04-asm/Makefile: -------------------------------------------------------------------------------- 1 | sum.out: sum.c 2 | gcc sum.c -o sum.out 3 | arr_get.out: arr_get.c arr_get.S 4 | gcc arr_get.c arr_get.S -o arr_get.out 5 | sum_n.out: sum_n.c sum_n.S 6 | gcc sum_n.c sum_n.S -o sum_n.out 7 | int_echo.out: int_echo.S 8 | gcc -g -no-pie int_echo.S -o int_echo.out 9 | exp.out: exp.c exp.S 10 | gcc exp.c exp.S -o exp.out 11 | add.out: add.c add.S 12 | gcc add.c add.S -o add.out 13 | add_int.out: add_int.c 14 | gcc -mavx add_int.c -o add_int.out 15 | clean: 16 | rm *.out 17 | -------------------------------------------------------------------------------- /sem04-asm/add.S: -------------------------------------------------------------------------------- 1 | .global very_important_function 2 | .intel_syntax noprefix 3 | .text 4 | 5 | // (N=rdi, *A=rsi, *B=rdx, *R=rcx) 6 | very_important_function: 7 | 8 | mov r8, rdi 9 | 10 | .Loop: 11 | sub rdi, 8 12 | 13 | vmovaps ymm0, [rsi + rdi * 4] 14 | vmovaps ymm1, [rdx + rdi * 4] 15 | vaddps ymm0, ymm0, ymm1 16 | vmovaps [rcx + rdi * 4], ymm0 17 | 18 | cmp rdi, 0 19 | jg .Loop 20 | 21 | ret 22 | -------------------------------------------------------------------------------- /sem04-asm/add.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | extern double 7 | very_important_function(int N, const float *A, const float *B, float *R); 8 | 9 | int main(){ 10 | const size_t n = 8; 11 | const size_t alig = alignof(float) * CHAR_BIT; 12 | const size_t sz = alig * n; 13 | float *a = aligned_alloc(alig , sz); 14 | float *b = aligned_alloc(alig, sz); 15 | float *c = aligned_alloc(alig, sz); 16 | for (int i = 0; i < n; i++){ 17 | a[i] = i + 1; 18 | b[i] = 2 * (i + 1); 19 | } 20 | double res = (float)very_important_function(8, a, b, c); 21 | for (int i = 0; i < n; i++) { 22 | printf("%f ", c[i]); 23 | } 24 | printf("\n"); 25 | return 0; 26 | } 27 | -------------------------------------------------------------------------------- /sem04-asm/add_int.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include 7 | 8 | int main(){ 9 | const size_t n = 8; 10 | const size_t alig = alignof(float) * CHAR_BIT; 11 | const size_t sz = alig * n; 12 | float *a = aligned_alloc(alig , sz); 13 | float *b = aligned_alloc(alig, sz); 14 | float *c = aligned_alloc(alig, sz); 15 | for (int i = 0; i < n; i++){ 16 | a[i] = i + 1; 17 | b[i] = 2 * (i + 1); 18 | } 19 | for (int i = 0; i < n; i+= 8) { 20 | __m256 r1 = _mm256_load_ps(a + i); 21 | __m256 r2 = _mm256_load_ps(b + i); 22 | __m256 r3 = _mm256_add_ps(r1, r2); 23 | _mm256_store_ps(&c[i], r3); 24 | } 25 | for (int i = 0; i < n; i++) { 26 | printf("%f ", c[i]); 27 | } 28 | printf("\n"); 29 | return 0; 30 | } 31 | -------------------------------------------------------------------------------- /sem04-asm/arr_get.S: -------------------------------------------------------------------------------- 1 | .intel_syntax noprefix 2 | .text 3 | .global get 4 | 5 | get: 6 | mov eax, [rdi + rsi * 4] 7 | ret 8 | -------------------------------------------------------------------------------- /sem04-asm/arr_get.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | extern int get(int* arr, int idx); 4 | 5 | int main() { 6 | int arr[3] = {1, 7, 9}; 7 | printf("%d\n", get(arr, 1)); 8 | } 9 | -------------------------------------------------------------------------------- /sem04-asm/exp.S: -------------------------------------------------------------------------------- 1 | .intel_syntax noprefix 2 | .text 3 | .global my_exp 4 | 5 | my_exp: 6 | movsd xmm1, xmm0 // preserve x 7 | vxorpd xmm0, xmm0, xmm0 // clean result 8 | mov rax, 1 9 | cvtsi2sd xmm2, rax // x^n 10 | cvtsi2sd xmm5, rax // n! 11 | mov rax, 0 // i 12 | mov rdi, 10 // iteration_count 13 | .Loop: 14 | vmovsd xmm4, xmm4, xmm2 // x^n 15 | vdivsd xmm4, xmm4, xmm5 // x^n/n! 16 | vaddsd xmm0, xmm0, xmm4 // res += x^n/n! 17 | 18 | vmulsd xmm2, xmm2, xmm1 // x^(n-1)*x 19 | add rax, 1 // i++ 20 | cvtsi2sd xmm6, rax 21 | vmulsd xmm5, xmm5, xmm6 // (n-1)! * n 22 | 23 | cmp rax, rdi 24 | jl .Loop 25 | .Finalize: 26 | ret 27 | -------------------------------------------------------------------------------- /sem04-asm/exp.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | extern double my_exp(); 4 | 5 | int main() { 6 | double x; 7 | scanf("%lf", &x); 8 | printf("%lf\n", my_exp(x)); 9 | return 0; 10 | } 11 | -------------------------------------------------------------------------------- /sem04-asm/int_echo.S: -------------------------------------------------------------------------------- 1 | .intel_syntax noprefix 2 | .text 3 | .global main 4 | 5 | // stack needs to be 16-byte aligned before `call` 6 | // 8 bytes are taken by rtuen address 7 | main: 8 | sub rsp, 16 // allocate space for int 9 | mov rdi, offset format_string_scanf 10 | mov rsi, rsp 11 | push rsi // save scratch register on stack 12 | call scanf 13 | pop rsi 14 | mov rsi, [rsi] 15 | add rsp, 8 16 | mov rdi, offset format_string_printf 17 | call printf 18 | add rsp, 8 19 | ret 20 | 21 | 22 | .data // section for global constants 23 | format_string_printf: 24 | .string "%d\n" 25 | format_string_scanf: 26 | .string "%d" 27 | -------------------------------------------------------------------------------- /sem04-asm/stack_x64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Khabutdinov-Arslan/caos-seminars/374b0109aa47de5df35ad1b58506176b47960997/sem04-asm/stack_x64.png -------------------------------------------------------------------------------- /sem04-asm/sum.c: -------------------------------------------------------------------------------- 1 | int main(){ 2 | int a = 2, b = 3; 3 | int c = a + b; 4 | return 0; 5 | } 6 | -------------------------------------------------------------------------------- /sem04-asm/sum_n.S: -------------------------------------------------------------------------------- 1 | .intel_syntax noprefix 2 | .text 3 | .global sum 4 | 5 | sum: // rdi = n 6 | mov rsi, 0 // i = 0 7 | mov rax, 0 // sum = 0 8 | loop: 9 | add rsi, 1 // i += 1 10 | add rax, rsi 11 | cmp rsi, rdi // i < n 12 | jl loop 13 | ret 14 | -------------------------------------------------------------------------------- /sem04-asm/sum_n.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | extern int sum(int n); 4 | 5 | int main() { 6 | int n; 7 | scanf("%d", &n); 8 | printf("%d\n", sum(n)); 9 | } 10 | -------------------------------------------------------------------------------- /sem05-arm/Makefile: -------------------------------------------------------------------------------- 1 | agcc := /opt/aarch64-gcc/bin/aarch64-linux-gnu-gcc 2 | 3 | arr_get.out: arr_get.c arr_get.S 4 | $(agcc) arr_get.c arr_get.S -o arr_get.out 5 | sum_n.out: sum_n.c sum_n.S 6 | $(agcc) sum_n.c sum_n.S -o sum_n.out 7 | int_echo.out: int_echo.S 8 | $(agcc) int_echo.S -o int_echo.out 9 | clean: 10 | rm *.out 11 | -------------------------------------------------------------------------------- /sem05-arm/README.md: -------------------------------------------------------------------------------- 1 | # ARM ассемблер 2 | 3 | Теперь поговорим про ARM ассемблер (конкретнее, про AArch64). Процессоры на базе архитектуры ARM устанавливаются в компьютеры Apple, а также большинство портативной электроники (смартфонов, планшетов и так далее). Архитектура ощутимо эффективнее x86, поэтому многие считают, что за ним будущее. 4 | 5 | ## Настройка окружения 6 | 7 | Необходимо скачать компилятор и библиотеки для другой платформы из проекта [Linaro](http://releases.linaro.org/components/toolchain/binaries/7.5-2019.12/aarch64-linux-gnu/). Полезно в `~/.bashrc` дописать алиасы наподобие: 8 | 9 | ``` 10 | alias agcc='/opt/aarch64-gcc/bin/aarch64-linux-gnu-gcc' 11 | alias agdb='/opt/aarch64-gcc/bin/aarch64-linux-gnu-gdb' 12 | alias aobjdump='/opt/aarch64-gcc/bin/aarch64-linux-gnu-objdump' 13 | alias arun='qemu-aarch64 -L /opt/aarch64-sysroot' 14 | ``` 15 | 16 | ## Регистры и соглашения о вызовах 17 | 18 | В `armv8` есть 31 64-битный регистр общего назначения, доступных программно: `x0`, `x1`, ... , `x30`. Через `w0`, `w1`, ..., `w30` можно обращаться к младшим 32 битам этих регистров. 19 | 20 | В системе Linux предусмотрены следующие соглашения об использовании регистров: 21 | 22 | | Регистры | Назначение | 23 | | --------------- | ------------------------------------------------------------ | 24 | | `x0` ... `x7` | аргументы функции и возвращаемое значение (`x0`) | 25 | | `x8` ... `x18` | временные регистры, для которые не гарантируется сохранение результата, если вызывать какую-либо функцию | 26 | | `x19` ... `x28` | регистры, для которых гарантируется, что вызываемая функция их не будет портить | 27 | | `x29` | указатель на границу фрейма функции, обычно используется отладчиком | 28 | | `x30` | адрес возврата из функции | 29 | | `sp` | указатель на вершину стека | 30 | 31 | Команда `mov a b` копирует в регистр `a` данные из регистра `b`. Для работы с памятью, в отличии от `x86_64`, предусмотрены специальные инструкции `ldr/str register [address]`. Первая загружает данные в регистр, а вторая в память. Как и в случае `mov` в `x86_64`, для оперирования числами меньшего размера есть специальные суффиксы: `ldrb` загружает `uint8_t`, `ldrsb` загружает `int8_t` и так далее (суффикс `h` используется для 2 байтов, `w` для 4 байтов). Пример использования можно посмотреть в `arr_get.S`. 32 | 33 | ## Арифметические команды 34 | 35 | Ещё в нашем минимальном примере можно увидеть некоторые арифметические команды. У большинства из них вид `command res, left, right`. Этот синтаксис означает `res = left + right` где `+` -- произвольная операция: `add`, `sub`, `mul`, `and`, `orr` и так далее. Довольно полезная инструкция `madd res, mleft, mright, anum` делает `mres=mleft*mright+anum`. 36 | 37 | ## Метки и переходы 38 | 39 | Аналогично `x86_64` можно создавать метки и переходить по ним. Для безусловного перехода предназначена команда `b label`. Команда `cmp` сравнивает числа и выставляет специальные флаги. На основе этих флагов работают суффиксы, которые можно дописать к команде `b`: 40 | 41 | * `eq`, `ne` (проверка на равенство) 42 | * `lt`, `le`, `gt`, `ge` (сравнение знаковых чисел) 43 | * `hi`,`ls` (> и <= для беззнаковых ) 44 | 45 | Пример использования можно посмотреть в `sum_n.S`. 46 | 47 | ## Вызов функций 48 | 49 | Для вызова функций есть команда `bl label`. Она кладёт в `x30` адрес следующей команды. При этом нужно не забыть сохранить исходный адрес возврата (например на стек). Для выхода из функции предназначена команда `ret`. Обратите внимание, что встроенных команд `push/pop` в `aarch64` нет, но можно реализовать их самим с помощью макросов. Пример можно посмотреть в `int_echo.S`. 50 | -------------------------------------------------------------------------------- /sem05-arm/arr_get.S: -------------------------------------------------------------------------------- 1 | .text 2 | .global get 3 | get: 4 | mov x3, 4 // sz = 4 5 | mul x2, x1, x3 // off = idx * sz 6 | ldr x0, [x0, x2] // ret = *(arr + off) 7 | ret 8 | -------------------------------------------------------------------------------- /sem05-arm/arr_get.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | extern int get(int* arr, int idx); 4 | 5 | int main() { 6 | int arr[3] = {1, 7, 9}; 7 | printf("%d\n", get(arr, 1)); 8 | } 9 | -------------------------------------------------------------------------------- /sem05-arm/int_echo.S: -------------------------------------------------------------------------------- 1 | .text 2 | .global main 3 | 4 | .macro push Rn:req 5 | sub sp, sp, 8 6 | str \Rn, [sp] 7 | .endm 8 | 9 | .macro pop Rn:req 10 | ldr \Rn, [sp] 11 | add sp, sp, 8 12 | .endm 13 | 14 | main: 15 | sub sp, sp, 16 // 12 extra bytes to keep stack 16-byte aligned 16 | adr x0, format_string_scanf 17 | mov x1, sp 18 | push x1 19 | push x30 // save return address, because bl overwrites it 20 | bl scanf 21 | pop x30 22 | pop x1 23 | 24 | ldr x1, [x1] 25 | 26 | adr x0, format_string_printf 27 | add sp, sp, 8 // extra bytes to keep stack 16-byte aligned 28 | push x30 29 | bl printf 30 | pop x30 31 | 32 | add sp, sp, 8 33 | mov x0, 0 34 | ret 35 | 36 | .data // section for global constants 37 | format_string_printf: 38 | .string "%d\n" 39 | format_string_scanf: 40 | .string "%d" 41 | -------------------------------------------------------------------------------- /sem05-arm/sum_n.S: -------------------------------------------------------------------------------- 1 | .text 2 | .global sum 3 | 4 | sum: 5 | mov x1, x0 // limit = n 6 | mov x2, 0 // i = 0 7 | mov x0, 0 // sum = 0 8 | loop: 9 | add x2, x2, 1 // i += 1 10 | add x0, x0, x2 // sum += i 11 | cmp x2, x1 // i < n 12 | blt loop 13 | ret 14 | -------------------------------------------------------------------------------- /sem05-arm/sum_n.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | extern int sum(int n); 4 | 5 | int main() { 6 | int n; 7 | scanf("%d", &n); 8 | printf("%d\n", sum(n)); 9 | } 10 | -------------------------------------------------------------------------------- /sem06-memory/Makefile: -------------------------------------------------------------------------------- 1 | mmap_demo.out: mmap_demo.c 2 | gcc mmap_demo.c -o mmap_demo.out 3 | proc_mem_demo.out: proc_mem_demo.c 4 | gcc proc_mem_demo.c -o proc_mem_demo.out 5 | sections.out: sections.c 6 | gcc sections.c -o sections.out 7 | clean: 8 | rm *.out 9 | -------------------------------------------------------------------------------- /sem06-memory/gen_big_file.py: -------------------------------------------------------------------------------- 1 | import random 2 | file_size = 4096 * 179 3 | file_content = '' 4 | for i in range(file_size): 5 | file_content += str(random.randint(0, 9)) 6 | out_file = open('big_file.txt', 'w') 7 | print(file_content, file=out_file) 8 | -------------------------------------------------------------------------------- /sem06-memory/mmap_demo.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | int main(int argc, char** argv) { 12 | int input_file = open(argv[1], O_RDONLY); 13 | struct stat file_stats; 14 | fstat(input_file, &file_stats); 15 | char* content_ptr = mmap(NULL, file_stats.st_size, PROT_READ, MAP_PRIVATE, input_file, 0); 16 | posix_madvise(content_ptr, file_stats.st_size, POSIX_MADV_SEQUENTIAL); // compare POSIX_MADV_RANDOM with POSIX_MADV_SEQUENTIAL 17 | 18 | size_t cnt[10]; 19 | memset(cnt, 0, sizeof(cnt)); 20 | for (size_t i = 0; i < file_stats.st_size; i++) { 21 | cnt[content_ptr[i] - '0']++; 22 | } 23 | 24 | for (int i = 0; i < 10; i++) { 25 | printf("%lu ", cnt[i]); 26 | } 27 | printf("\n"); 28 | 29 | struct rusage resource_usage; 30 | getrusage(RUSAGE_SELF, &resource_usage); 31 | printf("Minor page faults: %ld\n", resource_usage.ru_minflt); 32 | printf("Major page faults: %ld\n", resource_usage.ru_majflt); 33 | 34 | pause(); 35 | 36 | munmap(content_ptr, file_stats.st_size); 37 | close(input_file); 38 | } 39 | -------------------------------------------------------------------------------- /sem06-memory/proc_mem_demo.c: -------------------------------------------------------------------------------- 1 | #define _LARGEFILE64_SOURCE 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | int main(int argc, char** argv) { 13 | char mem_file_name[_SC_PAGE_SIZE]; 14 | int pid = atoi(argv[1]); 15 | uint64_t offset = strtoull(argv[2], NULL, 16); 16 | sprintf(mem_file_name, "/proc/%d/mem", pid); 17 | 18 | int mem_fd = open(mem_file_name, O_RDONLY); 19 | if (mem_fd == -1) { 20 | perror("Can't open process memory: "); 21 | return 0; 22 | } 23 | ptrace(PTRACE_ATTACH, pid, NULL, NULL); 24 | waitpid(pid, NULL, 0); 25 | 26 | lseek64(mem_fd, offset, SEEK_SET); 27 | char buf[_SC_PAGE_SIZE]; 28 | int read_bytes_cnt = read(mem_fd, buf, _SC_PAGE_SIZE); 29 | if (read_bytes_cnt == -1) { 30 | perror("Can't read from memory: "); 31 | return 0; 32 | } 33 | 34 | printf("Read bytes: %d\n", read_bytes_cnt); 35 | ptrace(PTRACE_DETACH, pid, NULL, NULL); 36 | printf("%s\n", buf); 37 | 38 | return 0; 39 | } 40 | -------------------------------------------------------------------------------- /sem06-memory/sections.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int global_var; // bss 4 | 5 | int main() { // text 6 | static int static_var = 5; // data 7 | int local_var; // stack 8 | printf("global_var %p\n", &global_var); 9 | printf("static_var %p\n", &static_var); 10 | printf("local_var %p\n", &local_var); 11 | printf("main %p\n", &main); 12 | } 13 | -------------------------------------------------------------------------------- /sem06-memory/small_file.txt: -------------------------------------------------------------------------------- 1 | 1234567890 2 | -------------------------------------------------------------------------------- /sem06-memory/vmemory.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Khabutdinov-Arslan/caos-seminars/374b0109aa47de5df35ad1b58506176b47960997/sem06-memory/vmemory.jpg -------------------------------------------------------------------------------- /sem07-processes/Makefile: -------------------------------------------------------------------------------- 1 | fork_hello.out: fork_hello.c 2 | gcc fork_hello.c -o fork_hello.out 3 | mmap_shared.out: mmap_shared.c 4 | gcc mmap_shared.c -o mmap_shared.out 5 | simple_shell.out: simple_shell.c 6 | gcc simple_shell.c -o simple_shell.out 7 | parallel_sum.out: parallel_sum.c 8 | gcc parallel_sum.c -o parallel_sum.out 9 | clean: 10 | rm *.out 11 | -------------------------------------------------------------------------------- /sem07-processes/elf_layout.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Khabutdinov-Arslan/caos-seminars/374b0109aa47de5df35ad1b58506176b47960997/sem07-processes/elf_layout.png -------------------------------------------------------------------------------- /sem07-processes/fork_hello.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | int main() { 7 | pid_t pid = fork(); 8 | if (pid == 0) { 9 | printf("Hello from child with pid %d!\n", getpid()); 10 | return 179; 11 | } else { 12 | int status; 13 | waitpid(pid, &status, 0); 14 | printf("Child exited with code %d\n", WEXITSTATUS(status)); 15 | printf("Hello from parent!\n"); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /sem07-processes/mmap_shared.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | int main() { 9 | char* content_ptr = mmap(NULL, 10, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0); 10 | strcpy(content_ptr, "Hello"); 11 | pid_t pid = fork(); 12 | if (pid == 0) { 13 | printf("Parent says: %s!\n", content_ptr); 14 | strcpy(content_ptr, "Hola"); 15 | } else { 16 | int status; 17 | waitpid(pid, &status, 0); 18 | printf("Child says: %s!\n", content_ptr); 19 | } 20 | return 0; 21 | } 22 | -------------------------------------------------------------------------------- /sem07-processes/parallel_sum.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | typedef struct { 7 | size_t l; 8 | size_t r; 9 | } task_args; 10 | 11 | typedef struct { 12 | size_t sum; 13 | } task_res; 14 | 15 | void* thread_sum(void *p_arg) { 16 | task_args* arg = (task_args*)p_arg; 17 | task_res* res = malloc(sizeof(task_res)); 18 | res->sum = 0; 19 | for (size_t i = arg->l; i < arg->r; i++) { 20 | res->sum += i; 21 | } 22 | return res; 23 | } 24 | 25 | int main(int argc, char **argv) { 26 | const size_t MAXN = 1000000000; 27 | const size_t THREAD_COUNT = atoi(argv[1]); 28 | pthread_t threads[THREAD_COUNT]; 29 | task_args thread_args[THREAD_COUNT]; 30 | for (size_t i = 0; i < THREAD_COUNT; ++i) { 31 | thread_args[i].l = (MAXN * i) / THREAD_COUNT; 32 | thread_args[i].r = (MAXN * (i + 1)) / THREAD_COUNT; 33 | pthread_create(&threads[i], NULL, thread_sum, &thread_args[i]); 34 | } 35 | 36 | size_t sum = 0; 37 | for (size_t i = 0; i < THREAD_COUNT; i++) { 38 | void* res_p; 39 | pthread_join(threads[i], &res_p); 40 | task_res* res = (task_res*)res_p; 41 | sum = (sum + res->sum); 42 | free(res); 43 | } 44 | 45 | printf("Calculated sum %lu\n", sum); 46 | return 0; 47 | } 48 | -------------------------------------------------------------------------------- /sem07-processes/process_attributes.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Khabutdinov-Arslan/caos-seminars/374b0109aa47de5df35ad1b58506176b47960997/sem07-processes/process_attributes.jpg -------------------------------------------------------------------------------- /sem07-processes/process_states.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Khabutdinov-Arslan/caos-seminars/374b0109aa47de5df35ad1b58506176b47960997/sem07-processes/process_states.png -------------------------------------------------------------------------------- /sem07-processes/simple_shell.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | int main() { 9 | size_t BUFF_SIZE = 4096; 10 | char* shell_intro = "my_shell> "; 11 | printf("%s", shell_intro); 12 | 13 | char* buff = malloc(BUFF_SIZE); 14 | size_t read; 15 | while((read = getline(&buff, &BUFF_SIZE, stdin)) != -1){ 16 | size_t MAX_ARGS = 10; 17 | char* args[MAX_ARGS]; 18 | for (int i = 0; i < MAX_ARGS; i++) { 19 | args[i] = NULL; 20 | } 21 | char* pos; 22 | pos = strtok(buff, " \n"); 23 | int argc = 0; 24 | while (pos != NULL) { 25 | args[argc] = pos; 26 | ++argc; 27 | pos = strtok(NULL, " \n"); 28 | } 29 | 30 | pid_t pid = fork(); 31 | if (pid == 0) { 32 | execvp(args[0], args); 33 | perror("Can't spawn child: "); 34 | } else { 35 | waitpid(pid, NULL, 0); 36 | printf("%s", shell_intro); 37 | } 38 | } 39 | 40 | return 0; 41 | } 42 | -------------------------------------------------------------------------------- /sem08-thread-sync/Makefile: -------------------------------------------------------------------------------- 1 | no_sync.out: no_sync.c 2 | gcc -pthread no_sync.c -o no_sync.out 3 | mutex.out: mutex.c 4 | gcc -pthread mutex.c -o mutex.out 5 | condvar.out: condvar.c 6 | gcc -pthread condvar.c -o condvar.out 7 | atomic.out: atomic.c 8 | gcc -pthread atomic.c -o atomic.out 9 | clean: 10 | rm *.out 11 | -------------------------------------------------------------------------------- /sem08-thread-sync/atomic.c: -------------------------------------------------------------------------------- 1 | // solution by Alexey Zertsalov 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | typedef struct { 11 | pthread_t thread; 12 | _Atomic volatile int* counter; 13 | } thread_data; 14 | 15 | static void* thread_func(void* arg) { 16 | thread_data* data_ptr = (thread_data*)arg; 17 | for (int i = 0; i < 10000000; i++) { 18 | atomic_fetch_add(data_ptr->counter, 1); 19 | } 20 | return NULL; 21 | } 22 | 23 | int main() { 24 | const int THREADS_COUNT = 10; 25 | thread_data threads[THREADS_COUNT]; 26 | _Atomic volatile int shared_counter = 0; 27 | for (int i = 0; i < THREADS_COUNT; i++) { 28 | threads[i].counter = &shared_counter; 29 | pthread_create(&threads[i].thread, NULL, thread_func, (void*)&threads[i]); 30 | } 31 | for (int i = 0; i < THREADS_COUNT; i++) { 32 | pthread_join(threads[i].thread, NULL); 33 | } 34 | printf("%d\n", shared_counter); 35 | return 0; 36 | } 37 | -------------------------------------------------------------------------------- /sem08-thread-sync/condvar.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | typedef struct { 8 | int64_t current_num; 9 | int32_t num_updated; 10 | pthread_mutex_t* syncer; 11 | pthread_cond_t* prime_ready; 12 | 13 | } thread_data; 14 | 15 | void* worker(void* arg) 16 | { 17 | 18 | thread_data* data_ptr = (thread_data*)arg; 19 | for (int64_t i = 0; i < 10; i++) { 20 | pthread_mutex_lock(data_ptr->syncer); 21 | while (data_ptr->num_updated == 1) { 22 | pthread_cond_wait(data_ptr->prime_ready, data_ptr->syncer); 23 | } 24 | data_ptr->current_num = i * i; 25 | data_ptr->num_updated = 1; 26 | pthread_cond_signal(data_ptr->prime_ready); 27 | pthread_mutex_unlock(data_ptr->syncer); 28 | } 29 | return NULL; 30 | } 31 | 32 | int main(int argc, char* argv[]) 33 | { 34 | pthread_mutex_t syncer; 35 | pthread_mutex_init(&syncer, NULL); 36 | pthread_cond_t prime_ready; 37 | pthread_cond_init(&prime_ready, NULL); 38 | thread_data data; 39 | data.syncer = &syncer; 40 | data.prime_ready = &prime_ready; 41 | data.num_updated = 0; 42 | data.current_num = 0; 43 | 44 | pthread_t finder; 45 | 46 | pthread_create(&finder, NULL, worker, (void*)&data); 47 | for (int i = 0; i < 10; i++) { 48 | pthread_mutex_lock(&syncer); 49 | while (data.num_updated == 0) { 50 | pthread_cond_wait(&prime_ready, &syncer); 51 | } 52 | printf("%ld ", data.current_num); 53 | fflush(stdout); 54 | pthread_cond_signal(data.prime_ready); 55 | data.num_updated = 0; 56 | pthread_mutex_unlock(&syncer); 57 | } 58 | printf("\n"); 59 | 60 | pthread_join(finder, NULL); 61 | 62 | return 0; 63 | } 64 | -------------------------------------------------------------------------------- /sem08-thread-sync/mutex.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | typedef struct { 8 | pthread_t thread; 9 | int* counter; 10 | pthread_mutex_t* syncer; 11 | } thread_data; 12 | 13 | static void* thread_func(void* arg) { 14 | thread_data* data_ptr = (thread_data*)arg; 15 | int local_counter = 0; 16 | for (int i = 0; i < 100000000; i++) { 17 | local_counter++; 18 | } 19 | pthread_mutex_lock(data_ptr->syncer); 20 | (*(data_ptr->counter)) += local_counter; 21 | pthread_mutex_unlock(data_ptr->syncer); 22 | return NULL; 23 | } 24 | 25 | int main() { 26 | pthread_mutex_t syncer; 27 | pthread_mutex_init(&syncer, NULL); 28 | 29 | const int THREADS_COUNT = 10; 30 | thread_data threads[THREADS_COUNT]; 31 | int shared_counter = 0; 32 | for (int i = 0; i < THREADS_COUNT; i++) { 33 | threads[i].counter = &shared_counter; 34 | threads[i].syncer = &syncer; 35 | pthread_create(&threads[i].thread, NULL, thread_func, (void*)&threads[i]); 36 | } 37 | for (int i = 0; i < THREADS_COUNT; i++) { 38 | pthread_join(threads[i].thread, NULL); 39 | } 40 | printf("%d\n", shared_counter); 41 | return 0; 42 | } -------------------------------------------------------------------------------- /sem08-thread-sync/no_sync.c: -------------------------------------------------------------------------------- 1 | // solution by Alexey Zertsalov 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | typedef struct { 10 | pthread_t thread; 11 | int* counter; 12 | } thread_data; 13 | 14 | static void* thread_func(void* arg) { 15 | thread_data* data_ptr = (thread_data*)arg; 16 | for (int i = 0; i < 30000; i++) { 17 | ++(*(data_ptr->counter)); 18 | } 19 | return NULL; 20 | } 21 | 22 | int main() { 23 | const int THREADS_COUNT = 10; 24 | thread_data threads[THREADS_COUNT]; 25 | int shared_counter = 0; 26 | for (int i = 0; i < THREADS_COUNT; i++) { 27 | threads[i].counter = &shared_counter; 28 | pthread_create(&threads[i].thread, NULL, thread_func, (void*)&threads[i]); 29 | } 30 | for (int i = 0; i < THREADS_COUNT; i++) { 31 | pthread_join(threads[i].thread, NULL); 32 | } 33 | printf("%d\n", shared_counter); 34 | return 0; 35 | } 36 | -------------------------------------------------------------------------------- /sem09-ipc/Makefile: -------------------------------------------------------------------------------- 1 | freopen.out: freopen.c 2 | gcc freopen.c -o freopen.out 3 | redirect.out: redirect.c 4 | gcc redirect.c -o redirect.out 5 | pipe.out: pipe.c 6 | gcc pipe.c -o pipe.out 7 | fifo.out: fifo.c 8 | gcc fifo.c -o fifo.out 9 | sigaction_demo.out: sigaction_demo.c 10 | gcc sigaction_demo.c -o sigaction_demo.out 11 | sigprocmask_demo.out: sigprocmask_demo.c 12 | gcc sigprocmask_demo.c -o sigprocmask_demo.out 13 | sigsuspend_demo.out: sigsuspend_demo.c 14 | gcc sigsuspend_demo.c -o sigsuspend_demo.out 15 | signalfd_demo.out: signalfd_demo.c 16 | gcc signalfd_demo.c -o signalfd_demo.out 17 | sigqueue_demo.out: sigqueue_demo.c 18 | gcc sigqueue_demo.c -o sigqueue_demo.out 19 | alarm_demo.out: alarm_demo.c 20 | gcc alarm_demo.c -o alarm_demo.out 21 | sigwaitinfo_demo.out: sigwaitinfo_demo.c 22 | gcc sigwaitinfo_demo.c -o sigwaitinfo_demo.out 23 | timerfd_demo.out: timerfd_demo.c 24 | gcc timerfd_demo.c -o timerfd_demo.out 25 | eventfd.out: eventfd.c 26 | gcc eventfd.c -o eventfd.out 27 | clean: 28 | rm *.out 29 | 30 | -------------------------------------------------------------------------------- /sem09-ipc/alarm_demo.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | int main() { 5 | alarm(3); 6 | pause(); 7 | return 1; 8 | } -------------------------------------------------------------------------------- /sem09-ipc/eventfd.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | 6 | int main() { 7 | uint64_t terminate_num = 42; 8 | int efd = eventfd(0, 0); 9 | if (efd == -1) { 10 | perror("Can't create eventfd"); 11 | } 12 | pid_t pid = fork(); 13 | if (pid == 0) { 14 | for (int i = 1; i <= 10; i++) { 15 | uint64_t num = i * i; 16 | write(efd, &num, sizeof(num)); 17 | sleep(1); 18 | } 19 | write(efd, &terminate_num, sizeof(terminate_num)); 20 | } else { 21 | while (1) { 22 | uint64_t num; 23 | int read_bytes = read(efd, &num, sizeof(num)); 24 | if (read_bytes < 0) { 25 | perror("Can't read from eventfd"); 26 | return -1; 27 | } 28 | if ((num == terminate_num) || (read_bytes == 0)) { 29 | break; 30 | } 31 | printf("%lu\n", num); 32 | } 33 | } 34 | return 0; 35 | } -------------------------------------------------------------------------------- /sem09-ipc/fifo.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 pid = fork(); 11 | 12 | if (pid == 0) { 13 | mkfifo("./fifo_demo", 0644); 14 | int fd = open("./fifo_demo", O_WRONLY); 15 | dup2(fd, 1); 16 | close(fd); 17 | execlp(argv[1], argv[1], NULL); 18 | } else { 19 | // parent will read 20 | sleep(1); // not the best way to sync, but 21 | int fd = open("./fifo_demo", O_RDONLY); 22 | dup2(fd, 0); 23 | close(fd); 24 | 25 | int total_read = 0; 26 | char buffer[4096]; 27 | int current_read; 28 | while ((current_read = read(0, buffer, sizeof(buffer))) > 0) { 29 | total_read += current_read; 30 | } 31 | printf("%d\n", total_read); 32 | waitpid(pid, NULL, 0); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /sem09-ipc/freopen.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | int main() { 7 | int fd = open("out.txt", O_WRONLY | O_CREAT | O_TRUNC, 0664); 8 | dup2(fd, 1); 9 | close(fd); // now we can close original descriptor 10 | printf("Hello, world!"); 11 | return 0; 12 | } 13 | -------------------------------------------------------------------------------- /sem09-ipc/pipe.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | int main(int argc, char* argv[]) { 9 | int pipe_out[2]; 10 | pipe(pipe_out); 11 | 12 | pid_t pid = fork(); 13 | 14 | if (pid == 0) { 15 | // child will write 16 | close(pipe_out[0]); 17 | dup2(pipe_out[1], 1); 18 | close(pipe_out[1]); 19 | 20 | execlp(argv[1], argv[1], NULL); 21 | } else { 22 | // parent will read 23 | close(pipe_out[1]); 24 | dup2(pipe_out[0], 0); 25 | close(pipe_out[0]); 26 | 27 | int total_read = 0; 28 | char buffer[4096]; 29 | int current_read; 30 | while ((current_read = read(0, buffer, sizeof(buffer))) > 0) { 31 | total_read += current_read; 32 | } 33 | printf("%d\n", total_read); 34 | waitpid(pid, NULL, 0); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /sem09-ipc/redirect.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | int main(int argc, char* argv[]) { 7 | int fd = open("out.txt", O_WRONLY | O_CREAT | O_TRUNC, 0664); 8 | dup2(fd, 1); 9 | close(fd); // now we can close original descriptor 10 | execvp(argv[1], argv + 1); 11 | } 12 | -------------------------------------------------------------------------------- /sem09-ipc/sigaction_demo.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | volatile sig_atomic_t received_sigint = 0; 7 | 8 | void sigint_handler(int signum) 9 | { 10 | received_sigint = 1; 11 | } 12 | 13 | void set_handler(int signum, void* handler, struct sigaction* action) 14 | { 15 | action->sa_handler = handler; 16 | action->sa_flags = SA_RESTART; 17 | sigaction(signum, action, NULL); 18 | } 19 | 20 | int main() { 21 | struct sigaction sigint_action; 22 | memset(&sigint_action, 0, sizeof(sigint_action)); 23 | set_handler(SIGINT, sigint_handler, &sigint_action); 24 | 25 | printf("%d\n", getpid()); 26 | fflush(stdout); 27 | 28 | while (1) { 29 | pause(); 30 | if (received_sigint) { 31 | printf("%s\n", "SIGINT received"); 32 | fflush(stdout); 33 | received_sigint = 0; 34 | } 35 | } 36 | return 0; 37 | } 38 | -------------------------------------------------------------------------------- /sem09-ipc/signalfd_demo.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | int main() { 10 | sigset_t mask; 11 | sigfillset(&mask); 12 | sigdelset(&mask, SIGCONT); 13 | sigprocmask(SIG_BLOCK, &mask, NULL); 14 | 15 | printf("%d\n", getpid()); 16 | fflush(stdout); 17 | 18 | int fd = signalfd(-1, &mask, 0); 19 | struct signalfd_siginfo fdsi; 20 | while (true) { 21 | read(fd, &fdsi, sizeof(struct signalfd_siginfo)); 22 | printf("Got signal %d\n", fdsi.ssi_signo); 23 | } 24 | close(fd); 25 | return 0; 26 | } 27 | -------------------------------------------------------------------------------- /sem09-ipc/sigprocmask_demo.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | volatile sig_atomic_t received_sigint = 0; 7 | 8 | void sigint_handler(int signum) 9 | { 10 | received_sigint = 1; 11 | } 12 | 13 | void set_handler(int signum, void* handler, struct sigaction* action) 14 | { 15 | action->sa_handler = handler; 16 | action->sa_flags = SA_RESTART; 17 | sigaction(signum, action, NULL); 18 | } 19 | 20 | int main() { 21 | struct sigaction sigint_action; 22 | memset(&sigint_action, 0, sizeof(sigint_action)); 23 | set_handler(SIGINT, sigint_handler, &sigint_action); 24 | 25 | printf("%d\n", getpid()); 26 | fflush(stdout); 27 | 28 | sigset_t mask; 29 | sigemptyset(&mask); 30 | sigaddset(&mask, SIGINT); 31 | 32 | while (1) { 33 | sigprocmask(SIG_BLOCK, &mask, NULL); 34 | sleep(5); 35 | sigprocmask(SIG_UNBLOCK, &mask, NULL); 36 | if (received_sigint) { 37 | printf("%s\n", "SIGINT received"); 38 | fflush(stdout); 39 | received_sigint = 0; 40 | } 41 | } 42 | return 0; 43 | } 44 | -------------------------------------------------------------------------------- /sem09-ipc/sigqueue_demo.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | volatile sig_atomic_t last_signal = 0; 10 | volatile sig_atomic_t last_value = 0; 11 | 12 | static void handler(int signum, siginfo_t *info, void *context) { 13 | last_signal = signum; 14 | last_value = info->si_int; 15 | } 16 | 17 | int main() { 18 | for (int i = SIGRTMIN; i <= SIGRTMAX; i++) { 19 | sigaction(i, &(struct sigaction){.sa_sigaction=handler, .sa_flags=SA_SIGINFO}, NULL); 20 | } 21 | 22 | pid_t pid = fork(); 23 | if (pid == 0) { 24 | sigset_t mask; 25 | sigemptyset(&mask); 26 | while (1) { 27 | sigsuspend(&mask); 28 | printf("Got signal %d with value %d \n", last_signal, last_value); 29 | fflush(stdout); 30 | } 31 | } else { 32 | for (int i = SIGRTMIN; i <= SIGRTMIN + 5; i++) { 33 | sigqueue(pid, i, (union sigval){i * i}); 34 | sleep(1); 35 | } 36 | kill(pid, SIGTERM); 37 | waitpid(pid, NULL, 0); 38 | } 39 | return 0; 40 | } -------------------------------------------------------------------------------- /sem09-ipc/sigsuspend_demo.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | volatile sig_atomic_t received_sigint = 0; 7 | 8 | void sigint_handler(int signum) 9 | { 10 | received_sigint = 1; 11 | } 12 | 13 | void set_handler(int signum, void* handler, struct sigaction* action) 14 | { 15 | action->sa_handler = handler; 16 | action->sa_flags = SA_RESTART; 17 | sigaction(signum, action, NULL); 18 | } 19 | 20 | int main() { 21 | struct sigaction sigint_action; 22 | memset(&sigint_action, 0, sizeof(sigint_action)); 23 | set_handler(SIGINT, sigint_handler, &sigint_action); 24 | 25 | printf("%d\n", getpid()); 26 | fflush(stdout); 27 | 28 | sigset_t mask_without_sigint; 29 | sigfillset(&mask_without_sigint); 30 | sigdelset(&mask_without_sigint, SIGINT); 31 | 32 | while (1) { 33 | sigsuspend(&mask_without_sigint); 34 | if (received_sigint) { 35 | printf("%s\n", "SIGINT received"); 36 | fflush(stdout); 37 | received_sigint = 0; 38 | } 39 | } 40 | return 0; 41 | } 42 | -------------------------------------------------------------------------------- /sem09-ipc/sigwaitinfo_demo.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | int main() { 7 | sigset_t mask; 8 | sigemptyset(&mask); 9 | sigaddset(&mask, SIGINT); 10 | 11 | sigprocmask(SIG_BLOCK, &mask, NULL); 12 | 13 | siginfo_t info; 14 | 15 | while (1) { 16 | int signum = sigwaitinfo(&mask, &info); 17 | printf("%s siginfo=%d, signum=%d\n", "Received signal", info.si_signo, signum); 18 | fflush(stdout); 19 | } 20 | 21 | return 0; 22 | } 23 | -------------------------------------------------------------------------------- /sem09-ipc/timerfd_demo.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | int main() { 9 | int fd = timerfd_create(CLOCK_MONOTONIC, 0); 10 | struct itimerspec itval; 11 | itval.it_interval.tv_sec = 1; 12 | itval.it_interval.tv_nsec = 0; 13 | itval.it_value.tv_sec = 1; 14 | itval.it_value.tv_nsec = 0; 15 | int st = timerfd_settime(fd, 0, &itval, NULL); 16 | 17 | unsigned long long missed; 18 | while (1) { 19 | int ret = read(fd, &missed, sizeof(missed)); 20 | printf("%s %llu\n", "Interval signal ", missed); 21 | fflush(stdout); 22 | } 23 | 24 | return 0; 25 | } 26 | -------------------------------------------------------------------------------- /sem10-sockets/Makefile: -------------------------------------------------------------------------------- 1 | udp_client.out: udp_client.c 2 | gcc udp_client.c -o udp_client.out 3 | udp_server.out: 4 | gcc udp_server.c -o udp_server.out 5 | tcp_client.out: 6 | gcc tcp_client.c -o tcp_client.out 7 | tcp_server.out: 8 | gcc tcp_server.c -o tcp_server.out 9 | tcp_server_fork.out: 10 | gcc tcp_server_fork.c -o tcp_server_fork.out 11 | getaddrinfo_demo.out: 12 | gcc getaddrinfo_demo.c -o getaddrinfo_demo.out 13 | clean: 14 | rm *.out 15 | -------------------------------------------------------------------------------- /sem10-sockets/dns_request.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Khabutdinov-Arslan/caos-seminars/374b0109aa47de5df35ad1b58506176b47960997/sem10-sockets/dns_request.png -------------------------------------------------------------------------------- /sem10-sockets/getaddrinfo_demo.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | int main(int argc, char* argv[]) 8 | { 9 | struct addrinfo* res = NULL; 10 | getaddrinfo(argv[1], "80", 0, &res); 11 | 12 | struct addrinfo* i; 13 | 14 | for(i=res; i!=NULL; i=i->ai_next) 15 | { 16 | char str[INET6_ADDRSTRLEN]; 17 | if (i->ai_addr->sa_family == AF_INET) { 18 | struct sockaddr_in *p = (struct sockaddr_in *)i->ai_addr; 19 | printf("%s\n", inet_ntop(AF_INET, &p->sin_addr, str, sizeof(str))); 20 | } 21 | } 22 | 23 | freeaddrinfo(res); 24 | 25 | return 0; 26 | } 27 | -------------------------------------------------------------------------------- /sem10-sockets/icmp_echo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Khabutdinov-Arslan/caos-seminars/374b0109aa47de5df35ad1b58506176b47960997/sem10-sockets/icmp_echo.png -------------------------------------------------------------------------------- /sem10-sockets/tcp_client.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | int main(int argc, char* argv[]) 12 | { 13 | int fd = socket(AF_INET, SOCK_STREAM, 0); 14 | struct in_addr input_address; 15 | input_address.s_addr = inet_addr(argv[1]); 16 | struct sockaddr_in server_address; 17 | server_address.sin_family = AF_INET; 18 | server_address.sin_addr = input_address; 19 | server_address.sin_port = htons(atoi(argv[2])); 20 | int connection_status = 21 | connect(fd, (struct sockaddr*)&server_address, sizeof(server_address)); 22 | if (connection_status) { 23 | printf("%s", strerror(errno)); 24 | return 1; 25 | } 26 | const int BUFF_SIZE = 4096; 27 | char buff[BUFF_SIZE]; 28 | while (scanf("%s", buff) > 0) { 29 | write(fd, buff, strlen(buff)); 30 | if (read(fd, &buff, sizeof(buff)) == 0) { 31 | break; 32 | } 33 | printf("%s\n", buff); 34 | } 35 | shutdown(fd, SHUT_RDWR); 36 | close(fd); 37 | return 0; 38 | } 39 | -------------------------------------------------------------------------------- /sem10-sockets/tcp_header.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Khabutdinov-Arslan/caos-seminars/374b0109aa47de5df35ad1b58506176b47960997/sem10-sockets/tcp_header.png -------------------------------------------------------------------------------- /sem10-sockets/tcp_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 | #include 14 | 15 | const int BUFF_SIZE = 4096; 16 | 17 | int server_socket_init(uint16_t port) 18 | { 19 | int server_fd; 20 | server_fd = socket(AF_INET, SOCK_STREAM, 0); 21 | struct in_addr input_address; 22 | input_address.s_addr = inet_addr("127.0.0.1"); 23 | struct sockaddr_in server_address; 24 | server_address.sin_family = AF_INET; 25 | server_address.sin_addr = input_address; 26 | server_address.sin_port = htons(port); 27 | 28 | int connection_status = bind( 29 | server_fd, (struct sockaddr*)&server_address, sizeof(server_address)); 30 | if (connection_status) { 31 | perror("Bind failed: "); 32 | exit(1); 33 | } 34 | 35 | int listening_status = listen(server_fd, SOMAXCONN); 36 | if (listening_status) { 37 | perror("Listen failed: "); 38 | exit(1); 39 | } 40 | return server_fd; 41 | } 42 | 43 | void read_message(int client_fd, char* buff){ 44 | read(client_fd, buff, BUFF_SIZE); 45 | } 46 | 47 | void write_message(int client_fd, char* buff){ 48 | write(client_fd, buff, strlen(buff)); 49 | } 50 | 51 | int main(int argc, char* argv[]) 52 | { 53 | int server_fd = server_socket_init(atoi(argv[1])); 54 | while (true) { 55 | int client_fd = accept(server_fd, NULL, NULL); 56 | char msg[BUFF_SIZE]; 57 | while (1) { 58 | read_message(client_fd, msg); 59 | if (msg[0] == 'q') { 60 | break; 61 | } 62 | write_message(client_fd, msg); 63 | } 64 | shutdown(client_fd, SHUT_RDWR); 65 | close(client_fd); 66 | } 67 | shutdown(server_fd, SHUT_RDWR); 68 | close(server_fd); 69 | return 0; 70 | } 71 | -------------------------------------------------------------------------------- /sem10-sockets/tcp_server.py: -------------------------------------------------------------------------------- 1 | import socket 2 | 3 | HOST = "127.0.0.1" 4 | PORT = 3003 5 | 6 | with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: 7 | s.bind((HOST, PORT)) 8 | s.listen() 9 | conn, addr = s.accept() 10 | with conn: 11 | print(f"Connected by {addr}") 12 | while True: 13 | data = conn.recv(1024) 14 | if not data: 15 | break 16 | conn.sendall(data) 17 | -------------------------------------------------------------------------------- /sem10-sockets/tcp_server_fork.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 | #include 14 | 15 | const int BUFF_SIZE = 4096; 16 | 17 | int server_socket_init(uint16_t port) 18 | { 19 | int server_fd; 20 | server_fd = socket(AF_INET, SOCK_STREAM, 0); 21 | struct in_addr input_address; 22 | input_address.s_addr = inet_addr("127.0.0.1"); 23 | struct sockaddr_in server_address; 24 | server_address.sin_family = AF_INET; 25 | server_address.sin_addr = input_address; 26 | server_address.sin_port = htons(port); 27 | 28 | int connection_status = bind( 29 | server_fd, (struct sockaddr*)&server_address, sizeof(server_address)); 30 | if (connection_status) { 31 | perror("Connection failed: "); 32 | exit(1); 33 | } 34 | 35 | int listening_status = listen(server_fd, SOMAXCONN); 36 | if (listening_status) { 37 | perror("Listen failed: "); 38 | exit(1); 39 | } 40 | return server_fd; 41 | } 42 | 43 | void read_message(int client_fd, char* buff){ 44 | read(client_fd, buff, BUFF_SIZE); 45 | } 46 | 47 | void write_message(int client_fd, char* buff){ 48 | write(client_fd, buff, strlen(buff)); 49 | } 50 | 51 | int main(int argc, char* argv[]) 52 | { 53 | struct sigaction zombie_killer = { 54 | .sa_handler = SIG_IGN, 55 | .sa_flags = SA_NOCLDWAIT 56 | }; 57 | sigaction(SIGCHLD, &zombie_killer, NULL); 58 | 59 | 60 | int server_fd = server_socket_init(atoi(argv[1])); 61 | while (true) { 62 | int client_fd = accept(server_fd, NULL, NULL); 63 | int pid = fork(); 64 | if (pid == 0){ 65 | char msg[BUFF_SIZE]; 66 | while (1) { 67 | read_message(client_fd, msg); 68 | if (msg[0] == 'q') { 69 | break; 70 | } 71 | write_message(client_fd, msg); 72 | } 73 | shutdown(client_fd, SHUT_RDWR); 74 | close(client_fd); 75 | break; 76 | } else { 77 | close(client_fd); 78 | } 79 | } 80 | shutdown(server_fd, SHUT_RDWR); 81 | close(server_fd); 82 | return 0; 83 | } 84 | -------------------------------------------------------------------------------- /sem10-sockets/tcp_state_machine.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Khabutdinov-Arslan/caos-seminars/374b0109aa47de5df35ad1b58506176b47960997/sem10-sockets/tcp_state_machine.png -------------------------------------------------------------------------------- /sem10-sockets/udp_client.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | int main(int argc, char* argv[]) 12 | { 13 | int fd = socket(AF_INET, SOCK_DGRAM, 0); 14 | struct in_addr input_address; 15 | input_address.s_addr = inet_addr(argv[1]); 16 | struct sockaddr_in server_address; 17 | server_address.sin_family = AF_INET; 18 | server_address.sin_addr = input_address; 19 | server_address.sin_port = htons(atoi(argv[2])); 20 | 21 | const int BUFF_SIZE = 4096; 22 | char buff[BUFF_SIZE]; 23 | while (scanf("%s", buff) > 0) { 24 | sendto(fd, buff, strlen(buff), 0, (const struct sockaddr*)&server_address, 25 | sizeof(server_address)); 26 | if (recvfrom(fd, buff, sizeof(buff), 0, NULL, NULL) == 0) { 27 | break; 28 | } 29 | fflush(stdout); 30 | printf("%s\n", buff); 31 | } 32 | 33 | close(fd); 34 | return 0; 35 | } 36 | -------------------------------------------------------------------------------- /sem10-sockets/udp_header.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Khabutdinov-Arslan/caos-seminars/374b0109aa47de5df35ad1b58506176b47960997/sem10-sockets/udp_header.png -------------------------------------------------------------------------------- /sem10-sockets/udp_server.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | int main(int argc, char* argv[]) 12 | { 13 | int fd = socket(AF_INET, SOCK_DGRAM, 0); 14 | struct in_addr input_address; 15 | input_address.s_addr = inet_addr(argv[1]); 16 | struct sockaddr_in server_address; 17 | server_address.sin_family = AF_INET; 18 | server_address.sin_addr = input_address; 19 | server_address.sin_port = htons(atoi(argv[2])); 20 | 21 | int connection_status = bind(fd, (struct sockaddr*)&server_address, sizeof(server_address)); 22 | if (connection_status) { 23 | perror("Bind failed: "); 24 | exit(1); 25 | } 26 | 27 | const int BUFF_SIZE = 4096; 28 | char buff[BUFF_SIZE]; 29 | while (1) { 30 | struct in_addr client_address; 31 | socklen_t client_address_length; 32 | if (recvfrom(fd, buff, sizeof(buff), 0, (struct sockaddr*)&client_address, 33 | &client_address_length) == 0) { 34 | break; 35 | } 36 | sendto(fd, buff, strlen(buff), 0, (const struct sockaddr*)&client_address, 37 | client_address_length); 38 | fflush(stdout); 39 | printf("%s\n", buff); 40 | } 41 | 42 | close(fd); 43 | return 0; 44 | } 45 | -------------------------------------------------------------------------------- /sem11-sockets-advanced/Makefile: -------------------------------------------------------------------------------- 1 | server_epoll.out: 2 | gcc server_epoll.c -o server_epoll.out 3 | ping.out: 4 | gcc ping.c -o ping.out 5 | ethernet.out: 6 | gcc ethernet.c -o ethernet.out 7 | clean: 8 | rm *.out 9 | -------------------------------------------------------------------------------- /sem11-sockets-advanced/ethernet.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | int main() { 9 | int sock_fd = socket(AF_PACKET,SOCK_RAW, htons(ETH_P_ALL)); 10 | if (sock_fd < 0) { 11 | perror("Couldn't open socket: "); 12 | return -1; 13 | } 14 | const int BUFF_SIZE = 65536; 15 | char buff[BUFF_SIZE]; 16 | struct sockaddr saddr; 17 | int saddr_len = sizeof (saddr); 18 | memset(buff, 0, BUFF_SIZE); 19 | int packet_length = recvfrom(sock_fd,buff,BUFF_SIZE,0,&saddr,(socklen_t *)&saddr_len); 20 | if (packet_length < 0) { 21 | perror("Couldn't read from socket"); 22 | return -1; 23 | } 24 | struct ethhdr *eth = (struct ethhdr *)(buff); 25 | printf("Source Address: %.2X-%.2X-%.2X-%.2X-%.2X-%.2X\n", eth->h_source[0],eth->h_source[1],eth->h_source[2],eth->h_source[3],eth->h_source[4],eth->h_source[5]); 26 | printf("“Destination Address: %.2X-%.2X-%.2X-%.2X-%.2X-%.2X\n", eth->h_dest[0],eth->h_dest[1],eth->h_dest[2],eth->h_dest[3],eth->h_dest[4],eth->h_dest[5]); 27 | printf("Protocol: %d\n”",eth->h_proto); 28 | return 0; 29 | } -------------------------------------------------------------------------------- /sem11-sockets-advanced/ethernet_header.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Khabutdinov-Arslan/caos-seminars/374b0109aa47de5df35ad1b58506176b47960997/sem11-sockets-advanced/ethernet_header.jpg -------------------------------------------------------------------------------- /sem11-sockets-advanced/ip_header.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Khabutdinov-Arslan/caos-seminars/374b0109aa47de5df35ad1b58506176b47960997/sem11-sockets-advanced/ip_header.png -------------------------------------------------------------------------------- /sem11-sockets-advanced/nginx_loop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Khabutdinov-Arslan/caos-seminars/374b0109aa47de5df35ad1b58506176b47960997/sem11-sockets-advanced/nginx_loop.png -------------------------------------------------------------------------------- /sem11-sockets-advanced/nginx_threadpool.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Khabutdinov-Arslan/caos-seminars/374b0109aa47de5df35ad1b58506176b47960997/sem11-sockets-advanced/nginx_threadpool.png -------------------------------------------------------------------------------- /sem11-sockets-advanced/ping.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 | struct my_ping { 15 | struct icmphdr hdr; 16 | char buffer[64 - sizeof(struct icmphdr)]; 17 | }; 18 | 19 | unsigned short in_cksum(unsigned short *ptr, int nbytes) { 20 | register long sum; 21 | u_short oddbyte; 22 | register u_short answer; 23 | 24 | sum = 0; 25 | while (nbytes > 1) { 26 | sum += *ptr++; 27 | nbytes -= 2; 28 | } 29 | 30 | if (nbytes == 1) { 31 | oddbyte = 0; 32 | *((u_char *) & oddbyte) = *(u_char *) ptr; 33 | sum += oddbyte; 34 | } 35 | 36 | sum = (sum >> 16) + (sum & 0xffff); 37 | sum += (sum >> 16); 38 | answer = ~sum; 39 | 40 | return (answer); 41 | } 42 | 43 | int wake = 0; 44 | 45 | void handler(int i) { 46 | wake = 1; 47 | } 48 | 49 | 50 | // args: server addr, total timeout, sleep between attempts 51 | int main(int argc, char* argv[]) { 52 | int interval = atoi(argv[3]); 53 | 54 | int client_fd = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP); 55 | struct sockaddr_in server_addr; 56 | 57 | server_addr.sin_family = AF_INET; 58 | server_addr.sin_port = htons(80); 59 | server_addr.sin_addr.s_addr = inet_addr(argv[1]); 60 | 61 | signal(SIGALRM, handler); 62 | alarm(atoi(argv[2])); 63 | 64 | 65 | int out = 0; 66 | 67 | while(!wake) { 68 | struct my_ping paket = { 69 | .hdr.type = 8, 70 | .hdr.code = 0, 71 | .hdr.un.echo.id = getpid(), 72 | .hdr.un.echo.sequence = out, 73 | }; 74 | 75 | paket.hdr.checksum = in_cksum((unsigned short*)&paket, sizeof(paket)); 76 | 77 | sendto(client_fd, (const char *)&paket, sizeof(paket), 78 | 0, (const struct sockaddr *) &server_addr, 79 | sizeof(server_addr)); 80 | socklen_t len; 81 | size_t length = recvfrom(client_fd, (char *)&paket, sizeof(paket), 82 | 0, (struct sockaddr *) &server_addr, &len); 83 | ++ out; 84 | usleep(interval); 85 | } 86 | close(client_fd); 87 | printf("%i\n", out); 88 | } -------------------------------------------------------------------------------- /sem11-sockets-advanced/server_epoll.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 | #include 14 | #include 15 | 16 | #define BUFF_SIZE 4096 17 | 18 | typedef struct { 19 | int fd; 20 | char buffer[BUFF_SIZE]; 21 | int buffer_begin; 22 | int buffer_end; 23 | } fd_data; 24 | 25 | int server_socket_init(uint16_t port) 26 | { 27 | int server_fd; 28 | server_fd = socket(AF_INET, SOCK_STREAM, 0); 29 | struct in_addr input_address; 30 | input_address.s_addr = inet_addr("127.0.0.1"); 31 | struct sockaddr_in server_address; 32 | server_address.sin_family = AF_INET; 33 | server_address.sin_addr = input_address; 34 | server_address.sin_port = htons(port); 35 | 36 | int connection_status = bind( 37 | server_fd, (struct sockaddr*)&server_address, sizeof(server_address)); 38 | if (connection_status) { 39 | perror("Connection failed: "); 40 | exit(1); 41 | } 42 | 43 | int listening_status = listen(server_fd, SOMAXCONN); 44 | if (listening_status) { 45 | perror("Listen failed: "); 46 | exit(1); 47 | } 48 | return server_fd; 49 | } 50 | 51 | void disable_io_block(int fd) 52 | { 53 | int current_descriptor_flags = fcntl(fd, F_GETFL); 54 | fcntl(fd, F_SETFL, current_descriptor_flags | O_NONBLOCK); 55 | } 56 | 57 | void read_buff(fd_data* data) { 58 | data->buffer_end = read(data->fd, data->buffer, BUFF_SIZE); 59 | } 60 | 61 | void write_buff(fd_data* data) { 62 | data->buffer_begin += write(data->fd, data->buffer + data->buffer_begin, data->buffer_end - data->buffer_begin); 63 | if (data->buffer_begin == data->buffer_end) { 64 | data->buffer_begin = data->buffer_end = 0; 65 | } 66 | } 67 | 68 | void process_epoll_event(struct epoll_event* event, int server_fd, int epoll_fd) 69 | { 70 | if (event->data.fd == server_fd) { 71 | int client_fd = accept(server_fd, NULL, NULL); 72 | disable_io_block(client_fd); 73 | 74 | struct epoll_event client_event; 75 | fd_data* add_data = calloc(1, sizeof(*add_data)); 76 | add_data->fd = client_fd; 77 | add_data->buffer_begin = 0; 78 | add_data->buffer_end = 0; 79 | client_event.data.ptr = add_data; 80 | client_event.events = EPOLLIN | EPOLLOUT | EPOLLHUP; 81 | 82 | if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, client_fd, &client_event) < 0) { 83 | perror("Add client to epoll failed: "); 84 | } 85 | 86 | } else { 87 | const uint32_t mask = event->events; 88 | fd_data* data = event->data.ptr; 89 | if ((mask & EPOLLIN) && (data->buffer_end == 0)) { 90 | read_buff(data); 91 | } 92 | if ((mask & EPOLLOUT) && (data->buffer_end != 0)) { 93 | write_buff(data); 94 | } 95 | if (mask & EPOLLHUP) { 96 | free(data); 97 | epoll_ctl(epoll_fd, EPOLL_CTL_DEL, event->data.fd, NULL); 98 | } 99 | } 100 | } 101 | 102 | int main(int argc, char* argv[]) 103 | { 104 | int server_fd = server_socket_init(atoi(argv[1])); 105 | 106 | disable_io_block(server_fd); 107 | 108 | struct epoll_event listen_event; 109 | int epoll_fd = epoll_create1(0); 110 | 111 | listen_event.events = EPOLLIN; 112 | listen_event.data.fd = server_fd; 113 | if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, server_fd, &listen_event) < 0) { 114 | perror("Adding server socket to epoll failed: "); 115 | exit(1); 116 | } 117 | 118 | const int MAX_PENDING_EVENTS_COUNT = 64000; 119 | struct epoll_event pending_events[MAX_PENDING_EVENTS_COUNT]; 120 | 121 | while (1) { 122 | int pending_events_count = 123 | epoll_wait(epoll_fd, pending_events, MAX_PENDING_EVENTS_COUNT, -1); 124 | for (size_t i = 0; i < pending_events_count; i++) { 125 | process_epoll_event(&pending_events[i], server_fd, epoll_fd); 126 | } 127 | } 128 | 129 | close(epoll_fd); 130 | close(server_fd); 131 | return 0; 132 | } 133 | -------------------------------------------------------------------------------- /sem12-dynlib/Makefile: -------------------------------------------------------------------------------- 1 | static.out: 2 | gcc foo.c main.c -o static.out 3 | 4 | libfoo.so: 5 | gcc -fPIC -shared foo.c -o libfoo.so 6 | 7 | dynamic.out: libfoo.so 8 | gcc -L . main.c -lfoo -o dynamic.out 9 | 10 | dynamic_rpath.out: libfoo.so 11 | gcc -L. main.c -lfoo -Wl,-rpath -Wl,'$$ORIGIN/.' -o dynamic_rpath.out 12 | 13 | loader.out: dynamic.out 14 | gcc loader.c -ldl -o loader.out 15 | 16 | mmap_loader.out: dynamic.out 17 | gcc mmap_loader.c -ldl -o mmap_loader.out 18 | 19 | clean: 20 | rm *.out 21 | rm *.so 22 | -------------------------------------------------------------------------------- /sem12-dynlib/README.md: -------------------------------------------------------------------------------- 1 | # Динамические библиотеки 2 | 3 | Наша программа, как правило, существует не сама по себе, а использует некоторые библиотеки. А значит, их надо как-то слинковать с нашим кодом. 4 | 5 | ## Статическая/динамическая линковка 6 | 7 | При статической линковке библиотечный код вставляется в исполняемый файл (см. `make static.out`). 8 | 9 | Можно проверить, что код функции `foo` действительно попал в `static.out` с помощью `objdump -d static.out | grep foo --after 9`. 10 | 11 | При динамической линковке (см. `make dynamic.out`) код функции подгружается в момент выполнения специальной программой `ld`. Поскольку код библиотеки может быть загружен в произвольное место в адресном пространстве, он должен быть позиционно-независимым (`fPIC`). Можно убедиться, что `dynamic.out` не содержит `foo`. 12 | 13 | При запуске нужно указать путь для поиска динамических библиотек `LD_LIBRARY_PATH=. ./dynamic.out`. 14 | 15 | В качестве альтернативы можно зашить путь для поиска библиотек в исполняемый файл (см. цель `dynamic_rpath`). 16 | 17 | |Статическая линковка|Динамическая линковка| 18 | |---|---| 19 | |Происходит во время компиляции|Происходит во время исполнения| 20 | |Код библиотеки содержится в исполняемом файле|Код библиотеки подгружается во время исполнения| 21 | |Больше исполняемый файл|Меньше исполняемый файл| 22 | |Медленнее загрузка|Быстрее загрузка| 23 | |Тяжело поддерживать|Легко обновлять| 24 | 25 | ## Трамплины 26 | 27 | Видно, что в `dynamic.out` содержится некая функция `foo@plt`. Она указывает на некоторый адрес. Он называется `relocation` и указывает на то, что при вызове функции нужно её загрузить и адрес записать адрес функции. С помощью `readelf -r dynamic.out` можно увидеть релокацию для функции `foo`. При первом вызове из `plt` вызовется динамический линковщик, который загрузит функцию и проставит адрес в `got`. 28 | 29 | ## Загрузка библиотеки с помощью dl 30 | 31 | `void *dlopen(const char *filename, int flags)` загружает файл с библиотекой и `handle`. Наиболее важные флаги: 32 | 33 | * `RTLD_NOW` загружает все символы и только потом выходит из `dlopen` 34 | * `RTLD_LAZY` подгружает символы по мере вызова 35 | 36 | `void *dlsym(void *handle, const char *symbol)` возвращает арес символа 37 | 38 | `int dlclose(void *handle)` уменьшает счётчик использований динамического объекта. Если он становится нулём, выгружает его из памяти. 39 | 40 | `char *dlerror()` возвращает текст ошибки, связанной с динамической загрузкой библиотек. 41 | 42 | Пример использования см. в `loader.c`. 43 | 44 | ## Загрузка библиотеки с помощью mmap 45 | 46 | Как мы помним из прошлых занятий, можно делать маппинги произвольных файлов в память. В частности, можно загрузить код библиотеки с правами на исполнение и вызвать его. Пример чисто игрушечный (так в жизни делать не стоит) можно найти в `mmap_loader.c`. 47 | -------------------------------------------------------------------------------- /sem12-dynlib/foo.c: -------------------------------------------------------------------------------- 1 | double foo(double x) { 2 | return x * x; 3 | } 4 | -------------------------------------------------------------------------------- /sem12-dynlib/kek.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | int main() { 5 | printf("pid: %d\n", getpid()); 6 | } 7 | -------------------------------------------------------------------------------- /sem12-dynlib/loader.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | typedef double (*func_t)(double); 6 | 7 | int main(int argc, char* argv[]) 8 | { 9 | if (argc != 3){ 10 | fprintf(stderr, "There must be 2 arguments: library file and fucntion name"); 11 | exit(1); 12 | } 13 | void* lib = dlopen(argv[1], RTLD_NOW); 14 | if (! lib) { 15 | fprintf(stderr, "dlopen error: %s\n", dlerror()); 16 | exit(1); 17 | } 18 | void * entry = dlsym(lib, argv[2]); 19 | if (! entry) { 20 | fprintf(stderr, "dlsym error: %s\n", dlerror()); 21 | exit(1); 22 | } 23 | func_t func = entry; 24 | double argument; 25 | while (scanf("%lf", &argument) != EOF) { 26 | printf("%.3f\n", func(argument)); 27 | } 28 | dlclose(lib); 29 | } 30 | -------------------------------------------------------------------------------- /sem12-dynlib/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | double foo(double x); 4 | 5 | int main() { 6 | printf("%lf\n", foo(1.3)); 7 | return 0; 8 | } 9 | -------------------------------------------------------------------------------- /sem12-dynlib/mmap_loader.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | typedef double (*func_t)(double); 12 | 13 | int main(int argc, char** argv) { 14 | int input_file = open("libfoo.so", O_RDONLY); 15 | struct stat file_stats; 16 | fstat(input_file, &file_stats); 17 | char* content_ptr = mmap(NULL, file_stats.st_size, PROT_EXEC, MAP_PRIVATE, input_file, 0); 18 | func_t func = (func_t)(content_ptr + 0x10f9); 19 | double argument; 20 | while (scanf("%lf", &argument) != EOF) { 21 | printf("%.3f\n", func(argument)); 22 | } 23 | munmap(content_ptr, file_stats.st_size); 24 | close(input_file); 25 | } 26 | -------------------------------------------------------------------------------- /sem13-containers/README.md: -------------------------------------------------------------------------------- 1 | # Контейнеры 2 | 3 | Контейнер это набор изолированных процессов. Изоляция происходит на уровне файловой системы, сети, процессов и других ресурсов. 4 | 5 | ![Отличия контейнеризации от виртуализации](containers-vs-virtual-machines.jpg) 6 | 7 | В отличии от виртуальных машин, в которых поверх гипервизора запускается гостевая система целиком, включая ядро, контейнеры используют ядро хостовой ОС. Это требует меньше ресурсов, но взамен мы жертвуем возможностью эмулировать другие ОС (например Linux на Mac) и безопасностью. 8 | 9 | ## namespaces 10 | 11 | Namespaces это компонент ядра Linux, который позволяет изолировать ресурсы. Категории ресурсов можно посмотреть в ``/proc/pid/ns``. Отделение части ресурсов процесса в новую группу происходит с помощью системного вызова ``unshare``. 12 | 13 | ### pid 14 | 15 | ![PID namespaces](pid_ns.png) 16 | 17 | Внутри контейнера видны только его процессы. 18 | 19 | ``` 20 | $ docker run -it --rm busybox 21 | / # ps aux 22 | PID USER TIME COMMAND 23 | 1 root 0:00 sh 24 | 7 root 0:00 ps aux 25 | ``` 26 | 27 | ### mount 28 | 29 | ![Mount namespaces](fs_ns.png) 30 | 31 | ``` 32 | / # mount 33 | overlay on / type overlay (rw,relatime,lowerdir=/var/lib/docker/overlay2/l/FT5QA66BOLCNWLHESUCX7GXQAF:/var/lib/docker/overlay2/l/QGKLLJVDLJEAQEPKFI5TNULEA7,upperdir=/var/lib/docker/overlay2/fd9a065e6c797fda3f02a9a7abca98cbce567d93698cb5d2fdd977c5f7c88b57/diff,workdir=/var/lib/docker/overlay2/fd9a065e6c797fda3f02a9a7abca98cbce567d93698cb5d2fdd977c5f7c88b57/work,nouserxattr) 34 | ... 35 | ``` 36 | 37 | Видно, что в корень контейнера смонтирована некая ``overlayfs``. Этот вид ФС использует ``Copy-on-write`` идиому. ``lower_dir`` это ``readonly`` слои ``image`` (команда ``RUN`` порождает новый слой, поэтому, по возможности, объединяйте команды в цепочки), которые можно переиспользовать для разных контейнеров. Когда вы меняете файл внутри контейнера, он записывается в ``upper_dir`` (в частности, если удалить файл из нижнего слоя, в верхнем появится файл-заглушка). ``Overlayfs`` на лету склеивает из слоев ``merged_dir``, именно ее вы и видите внутри контейнера. 38 | 39 | Подробнее можно почитать в официальной документации: https://docs.docker.com/storage/storagedriver/overlayfs-driver/ 40 | 41 | ### network 42 | 43 | ![Network namespaces](net_ns.png) 44 | 45 | По-умолчанию ``docker`` создает виртуальный свитч ``docker0``, который объединяет контейнеры и хост между собой. 46 | 47 | ``` 48 | $ ip addr 49 | ... 50 | 5: docker0: mtu 1500 qdisc noqueue state UP group default 51 | link/ether 02:42:90:66:2a:b9 brd ff:ff:ff:ff:ff:ff 52 | inet 172.17.0.1/16 brd 172.17.255.255 scope global docker0 53 | valid_lft forever preferred_lft forever 54 | ... 55 | ``` 56 | 57 | Можно проверить, что контейнеры пингуются друг из друга. Если выключить интерфейс на хосте, внутри контейнера пропадет доступ в есть. 58 | 59 | ``` 60 | $ sudo ip link set docker0 down 61 | ``` 62 | 63 | Можно создавать отдельные свитчи для групп контейнеров: ``docker network create network_name`` (чтобы сеть появилась в контейнере, нужно ее передать через флаг ``--network network_name``). Контейнеры из разных сетей не видят друг друга. 64 | 65 | Еще один способ выключить сеть в контейнере это удалить маршрут со стороны хоста. 66 | 67 | ``` 68 | $ sudo ip route del 172.19.0.0/16 dev br-68bd2dd5acc8 69 | ``` 70 | 71 | И, наконец, можно дропать все пакеты исходящие от контейнера с помощью ``iptables``. 72 | 73 | ``` 74 | $ sudo iptables -I FORWARD -s 172.19.0.2 -j DROP 75 | ``` 76 | 77 | Неплохой гайд по разным вариантам настройки сети в контейнерах: https://k21academy.com/docker-kubernetes/docker-networking-different-types-of-networking-overview-for-beginners/ 78 | 79 | 80 | ## cgroups 81 | 82 | ![Cgroups](cgroups.jpeg) 83 | 84 | Cgroups задают ограничения на ресурсы. Их иерархию можно посмотреть с помощью ``systemd-cgls``. Далее через ``sysfs`` можно смотреть информацию конкретной группе. Например: 85 | 86 | ``` 87 | $ cat /sys/fs/cgroup/system.slice/containerd.service/pids.current 88 | 36 89 | ``` 90 | 91 | ## Toy containers 92 | 93 | Пример минимальной реализации контейнеризации с помощью вышеописанных механизмов можно посмотреть по ссылке: https://github.com/carzil/containers/tree/master 94 | -------------------------------------------------------------------------------- /sem13-containers/cgroups.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Khabutdinov-Arslan/caos-seminars/374b0109aa47de5df35ad1b58506176b47960997/sem13-containers/cgroups.jpeg -------------------------------------------------------------------------------- /sem13-containers/containers-vs-virtual-machines.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Khabutdinov-Arslan/caos-seminars/374b0109aa47de5df35ad1b58506176b47960997/sem13-containers/containers-vs-virtual-machines.jpg -------------------------------------------------------------------------------- /sem13-containers/fs_ns.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Khabutdinov-Arslan/caos-seminars/374b0109aa47de5df35ad1b58506176b47960997/sem13-containers/fs_ns.png -------------------------------------------------------------------------------- /sem13-containers/net_ns.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Khabutdinov-Arslan/caos-seminars/374b0109aa47de5df35ad1b58506176b47960997/sem13-containers/net_ns.png -------------------------------------------------------------------------------- /sem13-containers/pid_ns.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Khabutdinov-Arslan/caos-seminars/374b0109aa47de5df35ad1b58506176b47960997/sem13-containers/pid_ns.png --------------------------------------------------------------------------------