[
  {
    "path": ".gitignore",
    "content": ".vscode/settings.json\n"
  },
  {
    "path": "2018-2019.md",
    "content": "# АКОС - архив\n\nГруппы 793, 794, 795, 796, 797, 798 и 7910 ФИВТ МФТИ.\n\n * [Coding Standards для используемого в курсе языка Си](practice/codestyle.md)\n\n * [Семестровый проект](projects/)\n\n## Лекции\n\n### Презентации лекций в формате PDF\n\n * [Осенний семестр 2018](lectures/fall-2018)\n * [Весенний семестр 2018](lectures/spring-2019)\n\n### Видео лекций\n\n * Записываются силами студсовета и публикуются на [YouTube](https://www.youtube.com/playlist?list=PL4_hYwCyhAva4dDOnyddyvkAs_jWVr624)\n\n## Семинарские занятия\n\n 1. [Введение в Linux и инструменты разработки](practice/linux_basics/)\n 2. [Целочисленная арифметика](practice/integers/)\n 3. [Часть 1: Инструменты для ARM](practice/arm/)\n [Часть 2: Ассемблер ARM](practice/asm/arm_basics/)\n 4. [Адресация памяти](practice/asm/arm_load_store/)\n 5. [Глобальные переменные, константы и библиотечные функции Си](practice/asm/arm_globals_plt/)\n 6. [Ассемблер x86](practice/asm/x86_basics/)\n 7. [Вещественные операции и SSE](practice/asm/x86_fpmath/)\n 8. [Системные вызовы](practice/asm/nostdlib_baremetal/)\n 9. [Низкоуровневый файловый ввод и вывод](practice/file_io/)\n 10. [Аттрибуты файлов и файловых дескрипторов](practice/stat_fcntl/)\n 11. [POSIX API для работы с файловой системой и временем](practice/posix_dirent_time/)\n 12. [Отображение файлов на память](practice/mmap/)\n 13. [Запуск и завершение работы процессов](practice/fork/)\n 14. [Запуск программ через fork-exec](practice/exec-rlimit-ptrace/)\n 15. [Указатели на функции и динамические библиотеки](practice/function-pointers/)\n 16. [Копии файловых дескрипторов и неименованные каналы](practice/fdup-pipe/)\n 17. [Сигналы. Часть 1](practice/signal-1/)\n 18. [Сигналы. Часть 2](practice/signal-2/)\n 19. [Разделяемая память и семафоры POSIX](practice/posix_ipc/)\n 20. [Сокеты TCP/IP](practice/sockets-tcp/)\n 21. [Сокеты UDP](practice/sockets-udp/)\n 22. [Мультиплексирование ввода-вывода](practice/epoll/)\n 23. [Многопоточность POSIX Threads](practice/pthread/)\n 24. [Многопоточная синхронизация](practice/mutex-condvar-atomic/)\n 25. [Протокол HTTP/1.1. Сборка с помощью CMake](practice/http-cmake/)\n 26. [Шифрование с использованием OpenSSL/LibreSSL](practice/openssl/)\n"
  },
  {
    "path": "2019-2020-informatics.md",
    "content": "# АКОС на ПМИ+ИВТ\n\n### Осень 2019\n\n 1. [Введение в Linux](practice/linux_basics/intro.md)\n  2. [Инструменты разработки в UNIX](practice/linux_basics/devtools.md)\n 3. [Часть 1: Целочисленная арифметика](practice/integers/)\n [Часть 2: Вещественная арифметика](practice/ieee754/)\n 4. [Часть 1: Инструменты для ARM](practice/arm/)\n [Часть 2: Ассемблер ARM](practice/asm/arm_basics/)\n 5. [Адресация данных в памяти и использование библиотечных функций](practice/arm_globals_plt/)\n 6. [Ассемблер x86](practice/asm/x86_basics/)\n 7. [Вещественные операции и SSE](practice/asm/x86_fpmath/)\n 8. [Системные вызовы](practice/asm/nostdlib_baremetal/)\n 9. [Низкоуровневый файловый ввод и вывод](practice/file_io/)\n 10. [Аттрибуты файлов и файловых дескрипторов](practice/stat_fcntl/)\n 11. [Отображение файлов на память](practice/mmap/)\n 12. [Запуск и завершение работы процессов](practice/fork/)\n 13. [Сигналы. Часть 1](practice/signal-1/)\n 14. [Сигналы. Часть 2](practice/signal-2/)\n 15. [Запуск программ через fork-exec](practice/exec-rlimit-ptrace/)\n\n\n### Весна 2020\n 1. [Копии файловых дескрипторов и неименованные каналы](practice/fdup-pipe/)\n 2. [Сокеты TCP/IP](practice/sockets-tcp/)\n 3. [Мультиплексирование ввода-вывода](practice/epoll/)\n 4. [Многопоточность POSIX Threads](practice/pthread/)\n 5. [Многопоточная синхронизация](practice/mutex-condvar-atomic/)\n 6. [Разделяемая память и семафоры POSIX](practice/posix_ipc/)\n 7. [Указатели на функции и динамические библиотеки](practice/function-pointers/)\n 8. [Сокеты UDP и AF_PACKET](practice/sockets-udp/)\n 9. [Berkley Packet Filter](practice/bpf/)\n 10. Неделя с 6 по 11 апреля: [Часть 1: Протокол HTTP/1.1 и cURL](practice/http-curl/) [Часть 2: Сборка с помощью CMake](practice/linux_basics/cmake.md)\n 11. Неделя с 13 по 18 апреля: [Шифрование с использованием OpenSSL/LibreSSL](practice/openssl/)\n 12. Неделя с 20 по 25 апреля: [Часть 1: Работа с каталогами в POSIX](practice/posix_dirent_time). [Часть 2: Файловые системы FUSE](practice/fuse/)\n 13. Неделя с 27 апреля по 9 мая: **новых тем не будет, только прием задач**. Дополнительный семинар: [Время в UNIX](practice/time/)\n 14. Неделя с 11 по 16 мая: [Python Extending and Embedding](practice/python/)\n"
  },
  {
    "path": "2019-2020-physics.md",
    "content": "# АКОС на ПМФ\r\n\r\n1. [Введение в Linux и инструменты разработки](practice/linux_basics/)\r\n2. [Часть 1: Целочисленная арифметика](practice/integers/)\r\n[Часть 2: Вещественная арифметика](practice/ieee754/)\r\n3. [Часть 1: Инструменты для ARM](practice/arm/)\r\n[Часть 2: Ассемблер ARM](practice/asm/arm_basics/)\r\n[Часть 3: Адресация памяти в ARM-системах](practice/asm/arm_load_store/)\r\n4. [Глобальные переменные, константы и библиотечные функции Си](practice/arm_globals_plt/)\r\n5. [Часть 1. Ассемблер x86](practice/asm/x86_basics/)\r\n[Часть 2. Вещественные операции и SSE](practice/asm/x86_fpmath/)\r\n6. [Системные вызовы](practice/asm/nostdlib_baremetal/)\r\n7. [Часть 1. Низкоуровневый файловый ввод и вывод](practice/file_io/)\r\n[Часть 2. Аттрибуты файлов и файловых дескрипторов](practice/stat_fcntl/)\r\n8. [POSIX API для работы с файловой системой и временем](practice/posix_dirent_time/)\r\n9. [Отображение файлов на память](practice/mmap/)\r\n10. [Часть 1. Запуск и завершение работы процессов](practice/fork/)\r\n[Часть 2. Запуск программ через fork-exec](practice/exec-rlimit-ptrace/)\r\n11. [Копии файловых дескрипторов и неименованные каналы](practice/fdup-pipe/)\r\n12. [Сигналы. Часть 1](practice/signal-1/)\r\n[Сигналы. Часть 2](practice/signal-2/)\r\n13. [Часть 1. Сокеты TCP/IP](practice/sockets-tcp/)\r\n[Часть 2. Сокеты UDP](practice/sockets-udp/)\r\n14. [Мультиплексирование ввода-вывода](practice/epoll/)\r\n"
  },
  {
    "path": "2019-2020.md",
    "content": "# АКОС - архив\n\n * [English Version for Harbour Space Joint Program](harbour/)\n * [English Version for Foreigners MIPT Program](en-mipt/)\n\n------\n\n * [2019-2020: направления ПМИ и ИВТ](2019-2020-informatics.md)\n * [2019-2020: направление ПМФ и матгруппа](2019-2020-physics.md)"
  },
  {
    "path": "2020-2021-informatics.md",
    "content": "# АКОС на ПМИ\n\n### Осень 2020\n\n  1. [Введение в Linux](practice/linux_basics/intro.md)\n  2. [Базовые инструменты разработки в UNIX](practice/linux_basics/devtools.md)\n  3. [Часть 1: Сборка с помощью CMake](practice/linux_basics/cmake.md) \n     [Часть 2: Python Extending and Embedding](practice/python/)\n  4. [Часть 1: Целочисленная арифметика](practice/integers/)\n     [Часть 2: Вещественная арифметика](practice/ieee754/)\n  5. [Часть 1: Инструменты для ARM](practice/arm/)\n     [Часть 2: Ассемблер ARM](practice/asm/arm_basics/)\n  6. [Адресация данных в памяти и использование библиотечных функций](practice/arm_globals_plt/)\n  7. [Ассемблер x86](practice/asm/x86_basics/)\n  8. [Вещественные операции и SSE](practice/asm/x86_fpmath/)\n  9. [Системные вызовы](practice/asm/nostdlib_baremetal/)\n  10. [Низкоуровневый файловый ввод и вывод](practice/file_io/)\n  11. [Аттрибуты файлов и файловых дескрипторов](practice/stat_fcntl/)\n  12. [Отображение файлов на память](practice/mmap/)\n  13. [Запуск и завершение работы процессов](practice/fork/)\n  14. [Запуск программ через fork-exec](practice/exec-rlimit-ptrace/)\n\n\n### Весна 2021  \n  1. [Сигналы. Часть 1](practice/signal-1/)\n  2. [Сигналы. Часть 2](practice/signal-2/)\n  3. [Копии файловых дескрипторов и неименованные каналы](practice/fdup-pipe/)\n  4. [Сокеты TCP/IP](practice/sockets-tcp/)\n  5. [Мультиплексирование ввода-вывода](practice/epoll/)\n  6. [Многопоточность POSIX Threads](practice/pthread/)\n  7. [Мьютексы, условные переменные, атомарные переменные](practice/mutex-condvar-atomic/)\n  8. [Разделяемая память и семафоры POSIX](practice/posix_ipc/)\n  9. [Указатели на функции и динамические библиотеки](practice/function-pointers/)\n  10. [Сокеты UDP и AF_PACKET](practice/sockets-udp/)\n  11. [Berkley Packet Filter](practice/bpf/)\n  12. [Протокол HTTP/1.1 и cURL](practice/http-curl/) \n  13. [Шифрование с использованием OpenSSL/LibreSSL](practice/openssl/)\n  14. [Часть 1: Работа с каталогами в POSIX](practice/posix_dirent_time). [Часть 2: Файловые системы FUSE](practice/fuse/)\n  15. [Время в UNIX](practice/time/)\n"
  },
  {
    "path": "2020-2021-os-course.md",
    "content": "# Операционные системы на ИВТ\n\n### Осень 2020\n\n   1. [Часть 1: Введение в Linux](practice/linux_basics/intro.md)\n      [Часть 2: Базовые инструменты разработки в UNIX](practice/linux_basics/devtools.md)\n   2. [Системные вызовы](practice/asm/nostdlib_baremetal/)\n   3. [Низкоуровневый файловый ввод и вывод](practice/file_io/)\n   4. [Аттрибуты файлов и файловых дескрипторов](practice/stat_fcntl/)\n   5. [Отображение файлов на память](practice/mmap/)\n   6. [Запуск и завершение работы процессов](practice/fork/)\n   7. [Запуск программ через fork-exec](practice/exec-rlimit-ptrace/)\n   8. [Копии файловых дескрипторов и неименованные каналы](practice/fdup-pipe/)\n   9. [Сигналы. Часть 1](practice/signal-1/) [Сигналы. Часть 2](practice/signal-2/)\n   10. [Сокеты TCP/IP](practice/sockets-tcp/)\n   11. [Мультиплексирование ввода-вывода](practice/epoll/)\n   12. [Сокеты UDP и AF_PACKET](practice/sockets-udp/)\n       [Berkley Packet Filter](practice/bpf/)\n   13. [Часть 1: Работа с каталогами в POSIX](practice/posix_dirent_time). [Часть 2: Файловые системы FUSE](practice/fuse/)\n   14. [Шифрование с использованием OpenSSL/LibreSSL](practice/openssl/)\n   15. [Время в UNIX](practice/time/)\n"
  },
  {
    "path": "2020-2021.md",
    "content": "# АКОС - Operating Systems Course\n\n![Лицензия Creative Commons](https://i.creativecommons.org/l/by-sa/4.0/88x31.png)\n\nВсе материалы доступны по лицензии [Creative Commons «Attribution-ShareAlike» («Атрибуция-СохранениеУсловий») 4.0 Всемирная](http://creativecommons.org/licenses/by-sa/4.0/).\n\n[Coding Standards для используемого в курсе языка Си](practice/codestyle.md)\n\n[Конфиг для clang-format](practice/.clang-format)\n\n* [Направление ПМФ](2019-2020-physics.md)\n* [Направление ИВТ](2020-2021-os-course.md)\n* [Направление ПМИ](2020-2021-informatics.md)\n\n----\n\n * [Архив: 2019-2020 учебный год](2019-2020.md)\n\n * [Архив: 2018-2019 учебный год](2018-2019.md)\n\n"
  },
  {
    "path": "2021-2022-full-year-course.md",
    "content": "# АКОС на ПМИ\n\n### Модуль 1 (осень 2021)\n\n1. [Введение в Linux и базовые инструменты разработки](practice/linux_basics/)\n2. [Командный интерпретатор bash и утилита sed](practice/bash-grep-sed)\n3. [Целочисленная и вещественная арифметика](practice/math)\n4. [Архитектура AArch64 и язык ассемблера](practice/aarch64)\n5. [Архитектура AArch64 - стек и вызов функций](practice/aarch64-functions)\n6. [Архитектура x86-64 и язык ассемблера](practice/x86-64)\n\n### Модуль 2 (осень 2021)\n\n1. [Системные вызовы](practice/asm/nostdlib_baremetal/)\n2. [Низкоуровневый файловый ввод и вывод](practice/file_io/)\n3. [Аттрибуты файлов и файловых дескрипторов](practice/stat_fcntl/)\n4. [Отображение файлов на память](practice/mmap/)\n5. [Запуск и завершение работы процессов](practice/fork/)\n6. [Запуск программ через fork-exec](practice/exec-rlimit-ptrace/)\n\n\n### Модуль 3 (весна 2022)\n1. [Копии файловых дескрипторов и неименованные каналы](practice/fdup-pipe/)\n2. [Сигналы](practice/signals/)\n3. [Сокеты TCP/IP](practice/sockets-tcp/)\n4. [Мультиплексирование ввода-вывода](practice/epoll/)\n5. [Многопоточность POSIX Threads](practice/pthread/)\n6. [Мьютексы, условные переменные, атомарные переменные](practice/mutex-condvar-atomic/)\n7. [Низкоуровневое сетевое взаимодействие](practice/sockets-udp/)\n\n### Модуль 4 (весна 2022)\n\n1. [Библиотеки функций и их загрузка](practice/function-pointers)\n2. [Часть 1: Система сборки CMake](practice/linux_basics/cmake.md). [Часть 2: Протокол HTTP/1.1 и cURL](practice/http-curl/) \n3. [Шифрование с использованием OpenSSL/LibreSSL](practice/openssl/)\n4. [Часть 1: Работа с каталогами в POSIX](practice/posix_dirent_time). [Часть 2: Файловые системы FUSE](practice/fuse/)\n5. [Python Extending and Embedding](practice/python)\n\n"
  },
  {
    "path": "2021-2022-half-year-course.md",
    "content": "# Операционные системы на ИВТ\n\n### Осень 2020\n\n1. [Введение в Linux и базовые инструменты разработки](practice/linux_basics/)\n2. [Командный интерпретатор bash и обработка текстов](practice/bash-grep-sed/)\n3. [Системные вызовы](practice/asm/nostdlib_baremetal/)\n4. [Низкоуровневый файловый ввод и вывод](practice/file_io/)\n5. [Аттрибуты файлов и файловых дескрипторов](practice/stat_fcntl/)\n6. [Отображение файлов на память](practice/mmap/)\n7. [Запуск и завершение работы процессов](practice/fork/)\n8. [Запуск программ через fork-exec](practice/exec-rlimit-ptrace/)\n9. [Копии файловых дескрипторов и неименованные каналы](practice/fdup-pipe/)\n10. [Сигналы. Часть 1](practice/signal-1/) [Сигналы. Часть 2](practice/signal-2/)\n11. [Сокеты TCP/IP](practice/sockets-tcp/)\n12. [Мультиплексирование ввода-вывода](practice/epoll/)\n13. [Сокеты UDP и AF_PACKET](practice/sockets-udp/) [Berkley Packet Filter](practice/bpf/)\n14. [Часть 1: Работа с каталогами в POSIX](practice/posix_dirent_time). [Часть 2: Файловые системы FUSE](practice/fuse/)\n15. [Шифрование с использованием OpenSSL/LibreSSL](practice/openssl/)\n"
  },
  {
    "path": "2021-2022.md",
    "content": "# АКОС - Operating Systems Course\n\n![Лицензия Creative Commons](https://i.creativecommons.org/l/by-sa/4.0/88x31.png)\n\nВсе материалы доступны по лицензии [Creative Commons «Attribution-ShareAlike» («Атрибуция-СохранениеУсловий») 4.0 Всемирная](http://creativecommons.org/licenses/by-sa/4.0/).\n\n\n\n## Материалы курса 2021/2022 учебного года\n\n* [Направления ПМФ, ИВТ в бакалавриате ФПМИ, и магистратура Блокчейн на ЛФИ](2021-2022-half-year-course.md)\n* [Направление ПМИ в бакалавриате ФПМИ](2021-2022-full-year-course.md)\n\n\n\n## Конфиги\n\nВ курсе используется проверка корректности стиля форматирования, и в случае несоответствия, решения задач не принимаются. Предварительно необходимо выполнять форматирование кода с помощью `clang-format`.\n\n[Конфиг для clang-format](practice/.clang-format)\n\n\n\n## Материалы предыдущих лет\n\n * [Архив: 2020-2021 учебный год](2020-2021.md)\n * [Архив: 2019-2020 учебный год](2019-2020.md)\n * [Архив: 2018-2019 учебный год](2018-2019.md)\n\n"
  },
  {
    "path": "2022-2023-full-year-course.md",
    "content": "# АКОС на ПМИ\n\n### Модуль 1 (осень 2022)\n\n1. [Введение в Linux и базовые инструменты разработки](practice/linux_basics/)\n2. [Командный интерпретатор bash и утилита sed](practice/bash-grep-sed)\n3. [Целочисленная и вещественная арифметика](practice/math)\n4. [Архитектура AArch64 и язык ассемблера](practice/aarch64)\n5. [Архитектура AArch64 - стек и вызов функций](practice/aarch64-functions)\n6. [Архитектура x86-64 и язык ассемблера](practice/x86-64)\n\n### Модуль 2 (осень 2022)\n\n1. [Системные вызовы](practice/asm/nostdlib_baremetal/)\n2. [Низкоуровневый файловый ввод и вывод](practice/file_io/)\n3. [Аттрибуты файлов и файловых дескрипторов](practice/stat_fcntl/)\n4. [Отображение файлов на память](practice/mmap/)\n5. [Запуск и завершение работы процессов](practice/fork/)\n6. [Запуск программ через fork-exec](practice/exec-rlimit-ptrace/)\n\n\n### Модуль 3 (весна 2023)\n1. [Многопоточность POSIX Threads](practice/pthread/)\n2. [Мьютексы, условные переменные, атомарные переменные](practice/mutex-condvar-atomic/)\n3. [Копии файловых дескрипторов и неименованные каналы](practice/fdup-pipe/)\n4. [Сигналы](practice/signals/)\n5. [Сокеты TCP/IP](practice/sockets-tcp/)\n6. [Низкоуровневое сетевое взаимодействие](practice/sockets-udp/)\n7. [Мультиплексирование ввода-вывода](practice/epoll/)\n\n\n### Модуль 4 (весна 2023)\n\n1. [Библиотеки функций и их загрузка](practice/function-pointers)\n2. [Часть 1: Система сборки CMake](practice/linux_basics/cmake.md). [Часть 2: Протокол HTTP/1.1 и cURL](practice/http-curl/) \n3. [Шифрование с использованием OpenSSL/LibreSSL](practice/openssl/)\n4. [Часть 1: Работа с каталогами в POSIX](practice/posix_dirent_time). [Часть 2: Файловые системы FUSE](practice/fuse/)\n5. [Python Extending and Embedding](practice/python)\n\n"
  },
  {
    "path": "2022-2023.md",
    "content": "# АКОС - Operating Systems Course\n\n![Лицензия Creative Commons](https://i.creativecommons.org/l/by-sa/4.0/88x31.png)\n\nВсе материалы доступны по лицензии [Creative Commons «Attribution-ShareAlike» («Атрибуция-СохранениеУсловий») 4.0 Всемирная](http://creativecommons.org/licenses/by-sa/4.0/).\n\n\n\n## Материалы курса 2022/2023 учебного года\n\n* [Направления ПМФ, ИВТ в бакалавриате ФПМИ](2021-2022-half-year-course.md)\n* [Направление ПМИ в бакалавриате ФПМИ](2022-2023-full-year-course.md)\n\n\n\n## Конфиги\n\nВ курсе используется проверка корректности стиля форматирования, и в случае несоответствия, решения задач не принимаются. Предварительно необходимо выполнять форматирование кода с помощью `clang-format`.\n\n[Конфиг для clang-format](practice/.clang-format)\n\n\n\n## Материалы предыдущих лет\n\n * [Архив: 2021-2022 учебный год](2021-2022.md)\n * [Архив: 2020-2021 учебный год](2020-2021.md)\n * [Архив: 2019-2020 учебный год](2019-2020.md)\n * [Архив: 2018-2019 учебный год](2018-2019.md)\n"
  },
  {
    "path": "LICENSE",
    "content": "Attribution-ShareAlike 4.0 International\n\n=======================================================================\n\nCreative Commons Corporation (\"Creative Commons\") is not a law firm and\ndoes not provide legal services or legal advice. Distribution of\nCreative Commons public licenses does not create a lawyer-client or\nother relationship. Creative Commons makes its licenses and related\ninformation available on an \"as-is\" basis. Creative Commons gives no\nwarranties regarding its licenses, any material licensed under their\nterms and conditions, or any related information. Creative Commons\ndisclaims all liability for damages resulting from their use to the\nfullest extent possible.\n\nUsing Creative Commons Public Licenses\n\nCreative Commons public licenses provide a standard set of terms and\nconditions that creators and other rights holders may use to share\noriginal works of authorship and other material subject to copyright\nand certain other rights specified in the public license below. The\nfollowing considerations are for informational purposes only, are not\nexhaustive, and do not form part of our licenses.\n\n     Considerations for licensors: Our public licenses are\n     intended for use by those authorized to give the public\n     permission to use material in ways otherwise restricted by\n     copyright and certain other rights. Our licenses are\n     irrevocable. Licensors should read and understand the terms\n     and conditions of the license they choose before applying it.\n     Licensors should also secure all rights necessary before\n     applying our licenses so that the public can reuse the\n     material as expected. Licensors should clearly mark any\n     material not subject to the license. This includes other CC-\n     licensed material, or material used under an exception or\n     limitation to copyright. More considerations for licensors:\n    wiki.creativecommons.org/Considerations_for_licensors\n\n     Considerations for the public: By using one of our public\n     licenses, a licensor grants the public permission to use the\n     licensed material under specified terms and conditions. If\n     the licensor's permission is not necessary for any reason--for\n     example, because of any applicable exception or limitation to\n     copyright--then that use is not regulated by the license. Our\n     licenses grant only permissions under copyright and certain\n     other rights that a licensor has authority to grant. Use of\n     the licensed material may still be restricted for other\n     reasons, including because others have copyright or other\n     rights in the material. A licensor may make special requests,\n     such as asking that all changes be marked or described.\n     Although not required by our licenses, you are encouraged to\n     respect those requests where reasonable. More considerations\n     for the public:\n    wiki.creativecommons.org/Considerations_for_licensees\n\n=======================================================================\n\nCreative Commons Attribution-ShareAlike 4.0 International Public\nLicense\n\nBy exercising the Licensed Rights (defined below), You accept and agree\nto be bound by the terms and conditions of this Creative Commons\nAttribution-ShareAlike 4.0 International Public License (\"Public\nLicense\"). To the extent this Public License may be interpreted as a\ncontract, You are granted the Licensed Rights in consideration of Your\nacceptance of these terms and conditions, and the Licensor grants You\nsuch rights in consideration of benefits the Licensor receives from\nmaking the Licensed Material available under these terms and\nconditions.\n\n\nSection 1 -- Definitions.\n\n  a. Adapted Material means material subject to Copyright and Similar\n     Rights that is derived from or based upon the Licensed Material\n     and in which the Licensed Material is translated, altered,\n     arranged, transformed, or otherwise modified in a manner requiring\n     permission under the Copyright and Similar Rights held by the\n     Licensor. For purposes of this Public License, where the Licensed\n     Material is a musical work, performance, or sound recording,\n     Adapted Material is always produced where the Licensed Material is\n     synched in timed relation with a moving image.\n\n  b. Adapter's License means the license You apply to Your Copyright\n     and Similar Rights in Your contributions to Adapted Material in\n     accordance with the terms and conditions of this Public License.\n\n  c. BY-SA Compatible License means a license listed at\n     creativecommons.org/compatiblelicenses, approved by Creative\n     Commons as essentially the equivalent of this Public License.\n\n  d. Copyright and Similar Rights means copyright and/or similar rights\n     closely related to copyright including, without limitation,\n     performance, broadcast, sound recording, and Sui Generis Database\n     Rights, without regard to how the rights are labeled or\n     categorized. For purposes of this Public License, the rights\n     specified in Section 2(b)(1)-(2) are not Copyright and Similar\n     Rights.\n\n  e. Effective Technological Measures means those measures that, in the\n     absence of proper authority, may not be circumvented under laws\n     fulfilling obligations under Article 11 of the WIPO Copyright\n     Treaty adopted on December 20, 1996, and/or similar international\n     agreements.\n\n  f. Exceptions and Limitations means fair use, fair dealing, and/or\n     any other exception or limitation to Copyright and Similar Rights\n     that applies to Your use of the Licensed Material.\n\n  g. License Elements means the license attributes listed in the name\n     of a Creative Commons Public License. The License Elements of this\n     Public License are Attribution and ShareAlike.\n\n  h. Licensed Material means the artistic or literary work, database,\n     or other material to which the Licensor applied this Public\n     License.\n\n  i. Licensed Rights means the rights granted to You subject to the\n     terms and conditions of this Public License, which are limited to\n     all Copyright and Similar Rights that apply to Your use of the\n     Licensed Material and that the Licensor has authority to license.\n\n  j. Licensor means the individual(s) or entity(ies) granting rights\n     under this Public License.\n\n  k. Share means to provide material to the public by any means or\n     process that requires permission under the Licensed Rights, such\n     as reproduction, public display, public performance, distribution,\n     dissemination, communication, or importation, and to make material\n     available to the public including in ways that members of the\n     public may access the material from a place and at a time\n     individually chosen by them.\n\n  l. Sui Generis Database Rights means rights other than copyright\n     resulting from Directive 96/9/EC of the European Parliament and of\n     the Council of 11 March 1996 on the legal protection of databases,\n     as amended and/or succeeded, as well as other essentially\n     equivalent rights anywhere in the world.\n\n  m. You means the individual or entity exercising the Licensed Rights\n     under this Public License. Your has a corresponding meaning.\n\n\nSection 2 -- Scope.\n\n  a. License grant.\n\n       1. Subject to the terms and conditions of this Public License,\n          the Licensor hereby grants You a worldwide, royalty-free,\n          non-sublicensable, non-exclusive, irrevocable license to\n          exercise the Licensed Rights in the Licensed Material to:\n\n            a. reproduce and Share the Licensed Material, in whole or\n               in part; and\n\n            b. produce, reproduce, and Share Adapted Material.\n\n       2. Exceptions and Limitations. For the avoidance of doubt, where\n          Exceptions and Limitations apply to Your use, this Public\n          License does not apply, and You do not need to comply with\n          its terms and conditions.\n\n       3. Term. The term of this Public License is specified in Section\n          6(a).\n\n       4. Media and formats; technical modifications allowed. The\n          Licensor authorizes You to exercise the Licensed Rights in\n          all media and formats whether now known or hereafter created,\n          and to make technical modifications necessary to do so. The\n          Licensor waives and/or agrees not to assert any right or\n          authority to forbid You from making technical modifications\n          necessary to exercise the Licensed Rights, including\n          technical modifications necessary to circumvent Effective\n          Technological Measures. For purposes of this Public License,\n          simply making modifications authorized by this Section 2(a)\n          (4) never produces Adapted Material.\n\n       5. Downstream recipients.\n\n            a. Offer from the Licensor -- Licensed Material. Every\n               recipient of the Licensed Material automatically\n               receives an offer from the Licensor to exercise the\n               Licensed Rights under the terms and conditions of this\n               Public License.\n\n            b. Additional offer from the Licensor -- Adapted Material.\n               Every recipient of Adapted Material from You\n               automatically receives an offer from the Licensor to\n               exercise the Licensed Rights in the Adapted Material\n               under the conditions of the Adapter's License You apply.\n\n            c. No downstream restrictions. You may not offer or impose\n               any additional or different terms or conditions on, or\n               apply any Effective Technological Measures to, the\n               Licensed Material if doing so restricts exercise of the\n               Licensed Rights by any recipient of the Licensed\n               Material.\n\n       6. No endorsement. Nothing in this Public License constitutes or\n          may be construed as permission to assert or imply that You\n          are, or that Your use of the Licensed Material is, connected\n          with, or sponsored, endorsed, or granted official status by,\n          the Licensor or others designated to receive attribution as\n          provided in Section 3(a)(1)(A)(i).\n\n  b. Other rights.\n\n       1. Moral rights, such as the right of integrity, are not\n          licensed under this Public License, nor are publicity,\n          privacy, and/or other similar personality rights; however, to\n          the extent possible, the Licensor waives and/or agrees not to\n          assert any such rights held by the Licensor to the limited\n          extent necessary to allow You to exercise the Licensed\n          Rights, but not otherwise.\n\n       2. Patent and trademark rights are not licensed under this\n          Public License.\n\n       3. To the extent possible, the Licensor waives any right to\n          collect royalties from You for the exercise of the Licensed\n          Rights, whether directly or through a collecting society\n          under any voluntary or waivable statutory or compulsory\n          licensing scheme. In all other cases the Licensor expressly\n          reserves any right to collect such royalties.\n\n\nSection 3 -- License Conditions.\n\nYour exercise of the Licensed Rights is expressly made subject to the\nfollowing conditions.\n\n  a. Attribution.\n\n       1. If You Share the Licensed Material (including in modified\n          form), You must:\n\n            a. retain the following if it is supplied by the Licensor\n               with the Licensed Material:\n\n                 i. identification of the creator(s) of the Licensed\n                    Material and any others designated to receive\n                    attribution, in any reasonable manner requested by\n                    the Licensor (including by pseudonym if\n                    designated);\n\n                ii. a copyright notice;\n\n               iii. a notice that refers to this Public License;\n\n                iv. a notice that refers to the disclaimer of\n                    warranties;\n\n                 v. a URI or hyperlink to the Licensed Material to the\n                    extent reasonably practicable;\n\n            b. indicate if You modified the Licensed Material and\n               retain an indication of any previous modifications; and\n\n            c. indicate the Licensed Material is licensed under this\n               Public License, and include the text of, or the URI or\n               hyperlink to, this Public License.\n\n       2. You may satisfy the conditions in Section 3(a)(1) in any\n          reasonable manner based on the medium, means, and context in\n          which You Share the Licensed Material. For example, it may be\n          reasonable to satisfy the conditions by providing a URI or\n          hyperlink to a resource that includes the required\n          information.\n\n       3. If requested by the Licensor, You must remove any of the\n          information required by Section 3(a)(1)(A) to the extent\n          reasonably practicable.\n\n  b. ShareAlike.\n\n     In addition to the conditions in Section 3(a), if You Share\n     Adapted Material You produce, the following conditions also apply.\n\n       1. The Adapter's License You apply must be a Creative Commons\n          license with the same License Elements, this version or\n          later, or a BY-SA Compatible License.\n\n       2. You must include the text of, or the URI or hyperlink to, the\n          Adapter's License You apply. You may satisfy this condition\n          in any reasonable manner based on the medium, means, and\n          context in which You Share Adapted Material.\n\n       3. You may not offer or impose any additional or different terms\n          or conditions on, or apply any Effective Technological\n          Measures to, Adapted Material that restrict exercise of the\n          rights granted under the Adapter's License You apply.\n\n\nSection 4 -- Sui Generis Database Rights.\n\nWhere the Licensed Rights include Sui Generis Database Rights that\napply to Your use of the Licensed Material:\n\n  a. for the avoidance of doubt, Section 2(a)(1) grants You the right\n     to extract, reuse, reproduce, and Share all or a substantial\n     portion of the contents of the database;\n\n  b. if You include all or a substantial portion of the database\n     contents in a database in which You have Sui Generis Database\n     Rights, then the database in which You have Sui Generis Database\n     Rights (but not its individual contents) is Adapted Material,\n\n     including for purposes of Section 3(b); and\n  c. You must comply with the conditions in Section 3(a) if You Share\n     all or a substantial portion of the contents of the database.\n\nFor the avoidance of doubt, this Section 4 supplements and does not\nreplace Your obligations under this Public License where the Licensed\nRights include other Copyright and Similar Rights.\n\n\nSection 5 -- Disclaimer of Warranties and Limitation of Liability.\n\n  a. UNLESS OTHERWISE SEPARATELY UNDERTAKEN BY THE LICENSOR, TO THE\n     EXTENT POSSIBLE, THE LICENSOR OFFERS THE LICENSED MATERIAL AS-IS\n     AND AS-AVAILABLE, AND MAKES NO REPRESENTATIONS OR WARRANTIES OF\n     ANY KIND CONCERNING THE LICENSED MATERIAL, WHETHER EXPRESS,\n     IMPLIED, STATUTORY, OR OTHER. THIS INCLUDES, WITHOUT LIMITATION,\n     WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR\n     PURPOSE, NON-INFRINGEMENT, ABSENCE OF LATENT OR OTHER DEFECTS,\n     ACCURACY, OR THE PRESENCE OR ABSENCE OF ERRORS, WHETHER OR NOT\n     KNOWN OR DISCOVERABLE. WHERE DISCLAIMERS OF WARRANTIES ARE NOT\n     ALLOWED IN FULL OR IN PART, THIS DISCLAIMER MAY NOT APPLY TO YOU.\n\n  b. TO THE EXTENT POSSIBLE, IN NO EVENT WILL THE LICENSOR BE LIABLE\n     TO YOU ON ANY LEGAL THEORY (INCLUDING, WITHOUT LIMITATION,\n     NEGLIGENCE) OR OTHERWISE FOR ANY DIRECT, SPECIAL, INDIRECT,\n     INCIDENTAL, CONSEQUENTIAL, PUNITIVE, EXEMPLARY, OR OTHER LOSSES,\n     COSTS, EXPENSES, OR DAMAGES ARISING OUT OF THIS PUBLIC LICENSE OR\n     USE OF THE LICENSED MATERIAL, EVEN IF THE LICENSOR HAS BEEN\n     ADVISED OF THE POSSIBILITY OF SUCH LOSSES, COSTS, EXPENSES, OR\n     DAMAGES. WHERE A LIMITATION OF LIABILITY IS NOT ALLOWED IN FULL OR\n     IN PART, THIS LIMITATION MAY NOT APPLY TO YOU.\n\n  c. The disclaimer of warranties and limitation of liability provided\n     above shall be interpreted in a manner that, to the extent\n     possible, most closely approximates an absolute disclaimer and\n     waiver of all liability.\n\n\nSection 6 -- Term and Termination.\n\n  a. This Public License applies for the term of the Copyright and\n     Similar Rights licensed here. However, if You fail to comply with\n     this Public License, then Your rights under this Public License\n     terminate automatically.\n\n  b. Where Your right to use the Licensed Material has terminated under\n     Section 6(a), it reinstates:\n\n       1. automatically as of the date the violation is cured, provided\n          it is cured within 30 days of Your discovery of the\n          violation; or\n\n       2. upon express reinstatement by the Licensor.\n\n     For the avoidance of doubt, this Section 6(b) does not affect any\n     right the Licensor may have to seek remedies for Your violations\n     of this Public License.\n\n  c. For the avoidance of doubt, the Licensor may also offer the\n     Licensed Material under separate terms or conditions or stop\n     distributing the Licensed Material at any time; however, doing so\n     will not terminate this Public License.\n\n  d. Sections 1, 5, 6, 7, and 8 survive termination of this Public\n     License.\n\n\nSection 7 -- Other Terms and Conditions.\n\n  a. The Licensor shall not be bound by any additional or different\n     terms or conditions communicated by You unless expressly agreed.\n\n  b. Any arrangements, understandings, or agreements regarding the\n     Licensed Material not stated herein are separate from and\n     independent of the terms and conditions of this Public License.\n\n\nSection 8 -- Interpretation.\n\n  a. For the avoidance of doubt, this Public License does not, and\n     shall not be interpreted to, reduce, limit, restrict, or impose\n     conditions on any use of the Licensed Material that could lawfully\n     be made without permission under this Public License.\n\n  b. To the extent possible, if any provision of this Public License is\n     deemed unenforceable, it shall be automatically reformed to the\n     minimum extent necessary to make it enforceable. If the provision\n     cannot be reformed, it shall be severed from this Public License\n     without affecting the enforceability of the remaining terms and\n     conditions.\n\n  c. No term or condition of this Public License will be waived and no\n     failure to comply consented to unless expressly agreed to by the\n     Licensor.\n\n  d. Nothing in this Public License constitutes or may be interpreted\n     as a limitation upon, or waiver of, any privileges and immunities\n     that apply to the Licensor or You, including from the legal\n     processes of any jurisdiction or authority.\n\n\n=======================================================================\n\nCreative Commons is not a party to its public\nlicenses. Notwithstanding, Creative Commons may elect to apply one of\nits public licenses to material it publishes and in those instances\nwill be considered the “Licensor.” The text of the Creative Commons\npublic licenses is dedicated to the public domain under the CC0 Public\nDomain Dedication. Except for the limited purpose of indicating that\nmaterial is shared under a Creative Commons public license or as\notherwise permitted by the Creative Commons policies published at\ncreativecommons.org/policies, Creative Commons does not authorize the\nuse of the trademark \"Creative Commons\" or any other trademark or logo\nof Creative Commons without its prior written consent including,\nwithout limitation, in connection with any unauthorized modifications\nto any of its public licenses or any other arrangements,\nunderstandings, or agreements concerning use of licensed material. For\nthe avoidance of doubt, this paragraph does not form part of the\npublic licenses.\n\nCreative Commons may be contacted at creativecommons.org.\n"
  },
  {
    "path": "en-mipt/Exam-Fall.md",
    "content": " 1. What is an Operating System, describe it's purpose. \r\n Linux basic concepts: services, users, sessions and processes. \r\n 2. Software for Linux. Packages and repositories. Building from the source code.\r\n Describe differences between various approaches.\r\n 3. Source code compilation. Describe the stages of compilation. \r\n Differences between C and C++ languages. The way to use both C and C++ in the same\r\n project.\r\n 4. Integer numbers representation and bitwise operations. Signed and unsigned\r\n integers. Integer overflows and the ways to detect them.\r\n 5. Floating point implementation. The real number types. Special IEEE754 values.\r\n 6. Assembly languages and processor instruction set architectures. Difference between\r\n RISC and CISC. Program layout in memory.\r\n 7. Interrupts and interrupt handling. Software interrupts. BIOS and Kernel purpose. \r\n System calls and calling conventions for Linux/x86.\r\n 8. UNIX Virtual File System. File system organization and file types. Describe the\r\n difference between hard links and symbolic links. Physical file systems.\r\n 9. File descriptors: purpose and relationship to virtual file system. Low-level file\r\n operations provided by The Kernel.\r\n 10. Memory layout. Describe static memory, stack and heap.\r\n  Physical address space and processe's virtual address space. Memory paging on x86.\r\n 11. Processes in UNIX systems. Process attributes and lifetime states. Zombie process\r\n  problem. \r\n 12. UNIX signals. Standard signals purpose and default behaviour.\r\n  Singal handling for System-V-style and BSD-style systems. Describe \r\n  Async-Safety problem.\r\n 13. UNIX signals delivering. Pending signals mask and signal mask for process.\r\n POSIX Extended signals (\"realtime extension\").\r\n 14. Compiled software libraries. Static and dynamic libraries. Position-Independent \r\n Code. Libraries linkage and loading.\r\n 15. Function replace techniques. Link-stage wrapping and library forced preloading. \r\n Describe differences in implementation and appliсation area.\r\n"
  },
  {
    "path": "en-mipt/README.md",
    "content": "# Operating Systems Course for Foreigners Program\n\n**Important!** The Fall Midterm exam will take place:\n* Dec, 19 (Thursday) - Early Exam at Timka Building in Moscow\n* TBA: from Dec, 23 to Dec, 26 - The Main Exam at Dolgoprudny MIPT campus\n\nThe exam program is [available here](Exam-Fall.md).\n\n---\nOur primary operating system is the Linux. You can use\n[this VirtualBox\nimage](https://drive.google.com/file/d/19pvmNOhqSQG_ZGx6kZ2hbhcuVefShDmI/view?usp=sharing).\n\nRegular user name for this image is `student`, password `qwerty`. The root\nuser password is the same.\n\nThe contest for your homeworks is here: [http://ejudge64.atp-fivt.org](http://ejudge64.atp-fivt.org)\n\n## Part I. Fall 2019\n\n 1. [Lesson 01. Introduction to UNIX-like systems and Linux](linux-basics/linux-intro.md)\n 2. [Lesson 02. Administration Basics](admin-basics/)\n 3. [Lesson 03. Developer Tools for C/C++ Lanuages](dev-tools/dev-tools.md)\n 4. [Lesson 04. Numbers And Structures Representation](numbers/)\n 5. [Lesson 05. Assembly Language. Memory Access, Stack and Heap](arm/arm.md)\n 6. [Lesson 06. System Calls versus Functions](syscalls/)\n 7. [Lesson 07. File Descriptors and Low-Level File Operations](fds/)\n 8. [Lesson 08. File Attributes](stat/)\n 9. [Lesson 09. Posix Time Representation](time/)\n 10. [Lesson 10. Memory Mapping](mmap/)\n 11. [Lesson 11. Processes Creation and Lifecycle](processes-1/)\n 12. [Lesson 12. Process Spawning and Restriction](processes-2/)\n 13. [Lesson 13. Pointers to Functions. Runtime Libraries Loading](dlopen/)\n 14. Midterm Exam\n"
  },
  {
    "path": "en-mipt/admin-basics/README.md",
    "content": "# Linux Administration\n\nThere is no special topics to cover.\n\nJust read [this full reference](https://www.tldp.org/LDP/sag/html/sag.html) when you'll face a problem.\n"
  },
  {
    "path": "en-mipt/arm/arm.md",
    "content": "# ARM assembler basics\n\n## Writing and compiling programs\n\nAssembly language programs for the GNU compiler are saved in a file whose name ends in `.s` or `.S`. In the case of `.S` it is assumed that the text of the program can be processed by the preprocessor.\n\nOne of the commands is used to compile:\n`arm-linux-gnueabi-as` or `arm-linux-gnueabi-gcc`. In the first case, the text is only compiled into an object file, in the second – into an executable program, linked with the standard C library, from which you can use I/O functions.\n\nARM processors support two sets of commands: the main 32-bit `arm`, and the compacted 16-bit `thumb`. And the processor is able to switch between them. In this workshop, we will use a 32-bit instruction set, so the texts should be compiled with the `-marm` option.\n\n## General syntax\n\n```\n// This is a comment (like in C++)\n\n    .text      // the beginning of the section .text with program code\n    .global f  // indicates that the f label\n               // is externally accessible (similar to extern)\nf:             // label (ends with a colon)\n\n     // series of commands\n     mul   r0, r0, r3\n     mul   r0, r0, r3\n     mul   r1, r1, r3\n     add   r0, r0, r1\n     add   r0, r0, r2  \n     mov   r1, r0\n     bx    lr\n```\n\n## Registers\n\nThe processor can only perform operations on *registers* - 32-bit memory cells in the processor core. ARM has 16 registers available programmatically: `r0`, `r1`, ... ,`r15`.\n\nRegisters `r13`...`r15` has special assignments and extra names:\n\n * `r15` = `pc`: Program Counter - pointer to the currently executing instruction\n * `r14` = `lr`: Link Register - stores the return address from the function\n * `r13` = `sp`: Stack Pointer - pointer to the top of the stack.\n\n## Flags\n\nCommands execution may lead to some additional information that is stored in the *flag register*. Flags refer to the last command executed. The main flags are:\n\n * `C`: Carry - an unsigned overflow occurred\n * `V`: oVerflow - a signed overflow occurred\n * `N`: Negative - negative result\n * `Z`: Zero - zeroing the result.\n\n## Commands\n\nFor a complete list of 32-bit commands, see [this reference](/practice/asm/arm_basics/arm_reference.pdf), starting at page 151.\n\nThe ARM-32 architecture implies that almost all commands can have *conditional execution*. The condition is encoded with 4 bits in the command itself, and in terms of Assembly syntax, commands can have suffixes.\nThus, each command consists of two parts (without spaces): the command itself and its suffix.\n\n## Basic arithmetic operations\n\n* `AND regd, rega, argb`  // regd ← rega & argb\n* `EOR regd, rega, argb`  // regd ← rega ^ argb\n* `SUB regd, rega, argb`  // regd ← rega − argb\n* `RSB regd, rega, argb`  // regd ← argb - rega\n* `ADD regd, rega, argb`  // regd ← rega + argb\n* `ADC regd, rega, argb`  // regd ← rega + argb + carry\n* `SBC regd, rega, argb`  // regd ← rega − argb − !carry\n* `RSC regd, rega, argb`  // regd ← argb − rega − !carry\n* `TST rega, argb`        // set flags for rega & argb\n* `TEQ rega, argb`        // set flags for rega ^ argb\n* `CMP rega, argb`        // set flags for rega − argb\n* `CMN rega, argb`        // set flags for rega + argb\n* `ORR regd, rega, argb`  // regd ← rega | argb\n* `MOV regd, arg`         // regd ← arg\n* `BIC regd, rega, argb`  // regd ← rega & ~argb\n* `MVN regd, arg`         // regd ← ~argb\n\n## Suffixes-conditions\n\n```\nEQ        equal  (Z)\nNE        not equal  (!Z)\nCS or HS  carry set / unsigned higher or same  (C)\nCC or LO  carry clear / unsigned lower  (!C)\nMI        minus / negative  (N)\nPL        plus / positive or zero  (!N)\nVS        overflow set  (V)\nVC        overflow clear  (!V)\nHI        unsigned higher  (C && !Z)\nLS        unsigned lower or same  (!C || Z)\nGE        signed greater than or equal  (N == V)\nLT        signed less than  (N != V)\nGT        signed greater than  (!Z && (N == V))\nLE        signed less than or equal  (Z || (N != V))\n```\n\n## Transitions\n\nThe `pc` counter is automatically incremented by 4 when executed another instruction. Commands are used to branch programs:\n\n * `B label` - the transition to the label; is used inside of functions for branches associated with loops or conditions\n * `BL label` - save current `pc` to `lr` and switch to `label`; usually used to call functions\n * `BX register` - go to the address specified in the register; usually used to exit functions.\n \n## Memory operation\n\nThe processor can only perform operations on registers. Special register loading/saving instructions are used to interact with the memory.\n\n* `LDR regd, [regaddr]` – loads the machine word from memory from the address stored in regaddr and stores it in the regd register\n* `STR reds, [regaddr]` – stores the machine word in memory at the address, specified in the regaddr register.\n\n\n# Development for ARM architecture\n\n## Cross-compilation\n\nThe process of building programs for a different processor architecture or operating system is called cross-compilation.\n\nThis requires a special version of the `gcc` compiler,\ndesigned for a different platform. Many distributions have separate compiler packages for other platforms, including ARM.\n\nIn addition, you can download an all-in-one delivery for the ARM architecture from the Linaro project: [http://releases.linaro.org/components/toolchain/binaries/7.3-2018.05/arm-linux-gnueabi/](http://releases.linaro.org/components/toolchain/binaries/7.3-2018.05/arm-linux-gnueabi/).\n\nFull `gcc` command names have the *triplet* form:\n```\nARCH-OS[-VENDOR]-gcc\nARCH-OS[-VENDOR]-g++\nARCH-OS[-VENDOR]-gdb\n\netc.\n```\nwhere `ARCH` is the architecture name: `i686`, `x86_64`, `arm`, `ppc`, etc.; `OS` -- the operating system, e.g. `linux`, `win32` or `darwin`; `VENDOR` (optional triplet fragment) -- binary interface agreements (if there are several of them for the platform, for example for ARM this can be a `gnueabi` (standard Linux agreement) or `none-eabi` (no OS, just bare hardware).\n\nThe name of the architecture for ARM is often distinguished between `arm` (soft float) and `armhf` (hard float). In the first case, the absence of a floating-point block is implied, so all operations are emulated by software, in the second case they are performed by hardware.\n\n\n## Running programs for non-native architectures\n\nExecution of programs designed for other architectures is possible only by interpretation of a foreign set of commands. *Emulators* -- special programs intended for this purpose.\n\nARM architecture, like many other architectures, is supported by the [QEMU emulator](https://www.qemu.org/).\n\nYou can emulate either a computer system as a whole, similar to VirtualBox, or only a set of processor commands, using the environment of the Linux host system.\n\n### Running ARM binaries in the native environment\n\nThis emulator is included in all common distributions. QEMU commands are like:\n```\nqemu-ARCH\nqemu-system-ARCH\n```\n\nwhere `ARCH` is the name of the architecture to be emulated. Commands, that have `system` in their names, start emulation of a computer system, and you must install an operating system to use them.\n\nCommands without`system` in their names require an executable file name as a mandatory argument in Linux, and emulate only a set of processor commands in *user mode*, executing a \"foreign\" executable file as if it were a normal program.\n\nSince most programs compiled for ARM Linux use the standard C library, it is necessary to use the ARM version of glibc. A minimal environment with the necessary libraries can be taken from the Linaro project (see link above), and passed to qemu using the `-L PATH_K_SYSROOT` option.\n\nCompile and run example:\n```\n# assuming the compiler is unpacked in /opt/arm-gcc,\n# and sysroot -- in /opt/arm-sysroot\n\n# Compile\n> /opt/arm-gcc/bin/arm-linux-gnueabi-gcc -o program hello.c\n\n# The output is an executable file that cannot be executed\n> ./program\nbash: ./program: cannot execute binary file: Exec format error\n\n# But we can run it with qemu-arm\n> qemu-arm -L /opt/arm-sysroot ./program\nHello, World!\n\n```\n\n### Running ARM programs in Raspberry Pi environment emulation\n\nThe ideal option for testing and debugging is to use real hardware, such as Raspberry Pi.\n\nIf you do not have a computer with an ARM-processor, you can\nperform PC emulation with Raspbian system installed.\n\nYou can download the image from here: [Google Drive](https://drive.google.com/open?id=11lc_f-_crhP-CJi_FEYb4DE0u9TMViT4)\n"
  },
  {
    "path": "en-mipt/dev-tools/dev-tools.md",
    "content": "\r\n# Developer tools\r\n\r\n## Compilers: `gcc` and `clang`\r\n\r\nThe standard delivery of modern UNIX systems includes one of the compilers: either `gcc` or `clang`. By default, `gcc` is used in Linux and `clang` -- in BSD-systems. Working with `gcc` compiler will be described below. But keep in mind that working with `clang` is very similar. Both compilers have a lot in common, including command-line options.\r\n\r\nIn addition, there is the `cc` command, which is a symbolic link to the default C compiler (`gcc` or `clang`), and the `c++` command, which is a symbolic link to the default C++ compiler.\r\n\r\nLet's consider a simple program in C++:\r\n```\r\n// file hello.cpp\r\n#include <iostream>\r\n\r\nint\r\nmain() {\r\n  std::cout << \"Hello, World!\" << std::endl;\r\n  return 0;\r\n}\r\n```\r\n\r\nYou can compile this program by using the command:\r\n```\r\n> c++ -o program.jpg hello.cpp\r\n```\r\n\r\nThe compiler option `-o FILENAME` specifies the name of the output file to be created. The default name is `a.out`. Pay attention that `program.jpg` is an executable file!\r\n\r\n### Stages of compiling a С or C++ program\r\n\r\nWhen you run `c++ -o program.jpg hello.cpp` command, a quite complex chain of actions is performed:\r\n\r\n 1. *Preprocessing* of text file `hello.cpp`. At this stage, the *preprocessor directives* are processed (that begin with the `#` character), and after that the new program text is obtained. If you run the compiler with the `-E` option, only this step will be executed, and the converted text of the program will be output to standard output stream (stdout).\r\n\r\n 2. *Translating* one or more C/C++ texts into object modules that contain machine code. If you specify the `-c` option, the build of the program will be stopped at this stage and object files with the suffix `o` will be created. Object files contain *binary* executable code that corresponds exactly to some Assembly language text. This text can be obtained using the `-S` option. In this case, text files with the suffix `.s` will be created instead of object files.\r\n\r\n 3. *Linking* one or more object files into an executable file and linking it to the C/C++ standard library (or other libraries, if required). The compiler calls the third-party program `ld` to perform the linking.\r\n\r\n### C programs v.s. C++ programs\r\n\r\nThe `gcc` compiler has the `-x LANGUAGE` option to specify the source language of the program: C (`c`), C++ (`c++`) or Fortran (`fortran`). By default, the source language is defined according to the filename: `.c` is a program in C language, and the file with the name ending in `.cc`, `.cpp` or `.cxx` -- is a C++ text. Therefore, the filename is significant.\r\n\r\nThis applies to the preprocessing and translation stages, but it can cause problems in the build stage, too. For example, when using the `gcc` command instead of `g++` (or `cc` instead of `c++`) you can successfully compile a program's source code in C++, but you will encounter errors during the linking phase because the options passed to the `ld` linker bind only to the C standard library, not to C++ one. Therefore, when building programs in C++, you need to use the command `c++` or `g++`.\r\n\r\n### Standard specifying\r\n\r\nThe compiler option `-std=NAME ' allows you to explicitly specify the language standard to be used. It is recommended to specify explicitly the standard to use because the default behavior depends on the Linux distribution you are using. Valid names:\r\n * `c89`, `c99`, `c11`, `gnu99`, `gnu11` for C;\r\n * `c++03`, `c++11`, `c++14`, `c++17`, `gnu++11`, `gnu++14`, `gnu++17` for C++.\r\n\r\nA double-digit number in the name of standard indicates its year. If `gnu` is in the standard name, GNU compiler extensions are implied (specific to UNIX-like systems), and the `#define _DEFAULT_SOURCE` macro is also considered to be defined, that in some cases changes the behaviour of individual functions of the standard library.\r\n\r\nIn the future, we will focus on the standard `c11`, and in some tasks, where it will be explicitly stated -- on its extension `gnu11`.\r\n\r\n## Object files, libraries and executable files\r\n\r\n### Python interpreter module `ctypes` \r\n\r\nConsider a [program](my-first-program.c) in C:\r\n```\r\n/* my-first-program.c */\r\n#include <stdio.h>\r\n\r\nstatic void\r\ndo_something()\r\n{\r\n    printf(\"Hello, World!\\n\");\r\n}\r\n\r\nextern void\r\ndo_something_else(int value)\r\n{\r\n    printf(\"Number is %d\\n\", value);\r\n}\r\n\r\nint\r\nmain()\r\n{\r\n    do_something();\r\n}\r\n```\r\n\r\nLet's compile this program into an object file, and then get from it: (1) an executable program; (2) a shared library. pay attention to the `-fPIC` option for generating position-independent code, which will be discussed in a later seminar.\r\n\r\n```\r\n> gcc -c -fPIC my-first-program.c\r\n> gcc -o program.jpg my-first-program.o\r\n> gcc -shared -o library.so my-first-program.o\r\n```\r\n\r\nAs a result, we get the program `program.jpg`, which outputs the line `Hello, World!` and a *library* with the name `library.so` that can be both used from C/C++ programs, and dynamic loaded for using by the Python interpreter:\r\n\r\n```\r\n> python3\r\nPython 3.6.5 (default, Mar 31 2018, 19:45:04) [GCC] on linux\r\nType \"help\", \"copyright\", \"credits\" or \"license\" for more information.\r\n>>> from ctypes import cdll\r\n>>> lib = cdll.LoadLibrary(\"./library.so\")\r\n>>> lib.do_something_else(123)\r\n>>> retval = lib.do_something_else(123)\r\nNumber is 123\r\n>>> print(retval)\r\n14\r\n```\r\n\r\nNote that the result of  `do_something_else` function is a mysterious number `14` (it may be other when you try to reproduce this experiment), although the function returns `void`.\r\n\r\nIt is so because shared libraries store only the **names** of the functions, not their signatures (parameter and return value types).\r\n\r\nAttempt to call the `do_something` function will fail:\r\n```\r\n>>> lib.do_something()\r\nTraceback (most recent call last):\r\n  File \"<stdin>\", line 1, in <module>\r\n  File \"/usr/lib64/python3.6/ctypes/__init__.py\", line 361, in __getattr__\r\n    func = self.__getitem__(name)\r\n  File \"/usr/lib64/python3.6/ctypes/__init__.py\", line 366, in __getitem__\r\n    func = self._FuncPtr((name_or_ordinal, self))\r\nAttributeError: ./library.so: undefined symbol: do_something\r\n```\r\n\r\nIn this case, the name `do_something` is not found because in the C source text, the `static` modifier before the function name explicitly prohibits the usage of the function anywhere outside the current source text.\r\n\r\n### Display the symbol-table\r\n\r\nTo examine object files, including linked ones, you can use the `objdump` utility.\r\n\r\nThe `--syms` or `-t` options display the separate sections of the executable file that are named with *characters*.\r\n\r\nSome names are marked as '*UND*', which means that the name is used in the object file, but its location is unknown. The task of the linker is just to find the required names in different object files or dynamic libraries, and then - to substitute the correct address.\r\n\r\nSome characters are marked as global (the `g` character in the second column of the output) and some are marked as local (the `l` character). Non-global characters are considered *non-exportable*, that means (theoretically) they should not be accessible from the outside.\r\n\r\n\r\n## Debugger\r\n\r\nIf you compile a program with the `-g ` option, the size of the program will increase because it has now additional sections that contain *debugging information*.\r\n\r\nDebug information contains information about the correspondence of individual fragments of the program to the source code, and includes line numbers, source file names, type names, functions and variables.\r\n\r\nThis information is used only by the debugger, and has almost no effect on the behavior of the program. Thus, debugging information can be combined with optimization, even with the quite aggressive one (compiler option `-O3`).\r\n\r\nFor debugging use the `gdb` command with the name of the executable file or command as an argument.\r\n\r\nBasic `gdb` commands:\r\n * `run` -- run the program, you can pass arguments after `run`;\r\n * `break` -- set a break-point, the parameters for this command may be either a function name or a couple of `FILENAME:LINENUMBER`;\r\n * `ni`, `si` -- step over the function or step into respectively;\r\n * `return` -- step out of the function;\r\n * `continue` -- continue to the next breakpoint or exception;\r\n * `print` -- prints the value of a given expression for a current context.\r\n\r\nInteraction with the debugger is performed in the command line mode. Various integrated development environments  (CLion, CodeBlocks, QtCreator) are just graphical shells that use this particular debugger and visualise the interaction with it.\r\n\r\nA more detailed list of commands can be found in [CheatSheet](https://www.cheatography.com/fristle/cheat-sheets/closed-source-debugging-with-gdb/).\r\n"
  },
  {
    "path": "en-mipt/dev-tools/my-first-program.c",
    "content": "/* my-first-program.c */\n#include <stdio.h>\n\nstatic void\ndo_something()\n{\n    printf(\"Hello, World!\\n\");\n}\n\nextern void\ndo_something_else(int value)\n{\n    printf(\"Number is %d\\n\", value);\n}\n\nint\nmain()\n{\n    do_something();\n}\n"
  },
  {
    "path": "en-mipt/fds/README.md",
    "content": "# File input-output\n\n## File descriptors\n\nFile descriptors are integers that uniquely identify open files within a single program.\n\nTypically, when a process starts, descriptors `0`, `1`, and `2` are already occupied by the standard input stream (`stdin`), the standard output stream (`stdout`), and the standard error stream (`stderr`).\n\nFile descriptors can be created using the `create file` or `open file` operation.\n\n\n## open/close system calls\n\nThe `open` system call is intended to create a file descriptor from an existing file, and has a following signature:\n\n```\nint open(const char *path, int oflag, ... /* mode_t mode */);\n```\n\nThe first parameter is the file name (full, or relative to the current directory). The second parameter — the options for opening the file. The third (optional) is the access rights to the file when it is created.\n\nMain options for opening files:\n * `O_RDONLY` - read only;\n * `O_WRONLY` - write only;\n * `O_RDWR` - read and write;\n * `O_APPEND` - write to end of file (appending);\n * `O_TRUNC` - truncating to length 0;\n * `O_CREAT` - create a file if it does not exist;\n * `O_EXCL` - create a file only if it does not exist.\n\nIf successful, a non - negative descriptor number is returned, and if an error occurs, the value `-1` is returned.\n\n\n## POSIX error handling\n\nThe error code of the last operation is stored in the global integer \"variable\" `errno` (in fact, in modern implementations it is a macro). The values of the codes can be found from the `man` pages, or you can print the error text using the `perror` function.\n\n\n## POSIX file attributes\n\nWhen creating a file, the required parameter is a set of POSIX attributes of file access. Basically, they are encoded in octal calculus as `0ugo`, where `u` (user) - access rights for the owner of the file, `g` (group) - access rights for all users of the file group, `o` (others) - for users who are neither the file's owner nor members of the file's group.\n\nIn octal notation values from 0 to 7 correspond to a combination of three bits:\n```\n00: ---\n01: --x\n02: -w-\n03: -wx\n04: r--\n05: r-x\n06: rw-\n07: rwx\n```\n\n\n## POSIX read and write\n\nRead and write operations are done using system calls:\n```\nssize_t read(int fd, void *buf, size_t count);\nssize_t write(int fd, const void *buf, size_t count);\n```\n`buf` is a pointer to the data buffer and `count` is the maximum number of bytes to read / write.\n\nTypically, `count` specifies the size of the data buffer when reading, or the amount of data when writing.\n\nThe return type `ssize_t` is an integer defined in the range `[-1...SIZE_MAX]`, where `SSIZE_MAX` is usually the same as `SSIZE_MAX/2`. The value `-1` is used as an error indication; non-negative values are the number of bytes written / read, which may be less than `count`.\n\nIf the `read` system call returns `0`, the end of the file has been reached or the input channel has been closed.\n\n## File navigation in POSIX\n\nYou can move the current position in the file for ordinary files.\n```\noff_t lseek(int fd, off_t offset, int whence);\n```\nThis system call is intended to move the current pointer to a file.\n\nThe third parameter `whence` is one of the three standard ways to move:\n * `SEEK_SET` - explicitly specify a position in the file;\n * `SEEK_CUR` - move the pointer to a certain offset relative to the current position;\n * `SEEK_END` - move the pointer to a certain offset relative to the end of the file.\n\nThe `lseek` system call returns the current position in the file, or `-1` if an error occurs.\n\nThe type `off_t` is signed and is 32-bit, by default . In order to be able to work with files larger than 2 gigabytes, the value of the preprocessor variable **is determined before connecting the header files**:\n```\n#define _FILE_OFFSET_BITS 64\n```\n\nIn this case, the data type `off_t` becomes 64-bit. You can determine the value of preprocessor variables without changing the source code of the program by passing the `-DKEY=VALUE` option to the compiler:\n```\n# Compile a program with support for large files\ngcc -D_FILE_OFFSET_BITS=64 legacy_source.c\n```\n\n## Compiling and running Windows programs from Linux\n\nFor cross-compilation, the GCC compiler is used with the target system `w64-mingw`. Can be installed from the package:\n * `mingw32-gcc` - for Fedora\n * `gcc-mingw-w64` - for Ubuntu\n * `mingw32-cross-gcc` - for openSUSE.\n\nYou can compile a program for Windows with the command:\n```\n$ i686-w64-mingw-gcc -m32 program.c\n# The output is a.exe file, not a.out\n```\n\nNote that the Linux system, unlike Windows, is case-sensitive (distinguishes case of letters in the file system), so you need to use the standard WinAPI header files in lowercase:\n```\n#include <windows.h> // correct\n#include <Windows.h> // compiles in Windows, but not in Linux\n```\n\nYou can run the resulting file using WINE:\n```\n$ WINEDEBUG=-all wine a.exe\n```\n\nSetting the environment variable `WINEDEBUG` to `- all` causes the console to not display debug information related to the `wine` subsystem, which is mixed with the output of the program itself.\n\n## File descriptors and other data types in WinAPI\n\nFor file descriptors in Windows `HANDLE` type is used.\n\nFor single-byte strings `LPCSTR` is used, for multi-byte strings `LPCWSTR` is used.\n\nWinAPI functions that have different string support options and work with single-byte functions - end with the letter `A`, and functions that work with multi-byte files - end with the letter `W`.\n\nType `DWORD` is for an unsigned 32-bit number used for flags.\n\nFor a full list of data types, check [Microsoft documentation](https://docs.microsoft.com/en-us/windows/desktop/winprog/windows-data-types).\n\n## WinAPI functions for working with files\n\nThe file can be opened using the [CreateFile function](https://docs.microsoft.com/ru-ru/windows/desktop/api/fileapi/nf-fileapi-createfilea).\n\nRead and write — using the [ReadFile](https://docs.microsoft.com/en-us/windows/desktop/api/fileapi/nf-fileapi-readfile) and [WriteFile](https://docs.microsoft.com/en-us/windows/desktop/api/fileapi/nf-fileapi-writefile).\n\nFile navigation-using the [SetFilePointerEx function](https://docs.microsoft.com/ru-ru/windows/desktop/api/fileapi/nf-fileapi-setfilepointerex).\n"
  },
  {
    "path": "en-mipt/linux-basics/linux-intro.md",
    "content": "\n# Introduction to Linux OS \n\n## It is not Windows. Forget everything you know.\n\n### Terminology\n\n * File system is a hierarchy of files and *directories*. Do not call directories – \"folders\".\n * Unlike Windows, all files in UNIX are equal, regardless of their name. The concept of \"file extension\" does not exist, but there are *name suffixes*, separated from the main name by a period, for readability. The filename may have several suffixes, for example `.tar.gz`.\n * The system runs a huge number of *processes*, not \"tasks\". Processes can be started either directly by the user or by one of the *daemons* that are launched at system startup. Daemons are processes in themselves.\n\n\n### Common notations of keyboard shortcuts\n\nThe following notations of keyboard shortcuts are commonly used for working with UNIX console programs:\n * `C-Letter` - simultaneous pressing `Ctrl` and the letter key. Attention to MacOS users:  `Ctrl` - is exactly the `Ctrl` key, not the `Command`.\n * `M-Letter` - simultaneous pressing `Alt` and the letter key. \"M\" stands for\n \"Meta\". There was such a key button on old workstations Sun и SGI.\n * `C-Letter1 Letter2` - simultaneously press `Ctrl` and `Letter1`, then release\n `Ctrl` и press `Letter2`. The same is for `Alt`. Shortcut `C-Letter1` is called *prefix* of a keyboard shortcut. Usually keyboard shortcuts are grouped under the same prefixes for actions of the same nature.\n * `C-Letter1 C-Letter2`. Press `Ctrl`, after that press and release `Letter1`, then  press and release `Letter2`. After that you can release `Ctrl` key.\n * Keys `F13`...`F15`. They are missing on the PC-keyboard.  Pressing can be done with `Shift` and one of the functional keys with number `F...` 10 or 12 less, depending on the terminal. For example, in many graphical terminals `Shift+F5` is for pressing `F15`.\n\n\n## Start working\n\nLinux, like any other operating system of the UNIX family, is **multiuser** operating system. To get started it is necessary to know your username and password.\n\nThere are different ways to login to the system depending on the purposes of using the system.\n\n\n### Local login with GUI (Graphical User Interface)\nThis option is typically used for installing Linux as a Desktop.\nUsually most Linux distributions provide automatic login if only one user-human was specified during installation (there is also another kind of users –system-users). In case of multiple users, login to the system is pretty similar to the one in Windows or Mac.\n\nAfter login — a graphical shell (GNOME, Unity or KDE) is displayed.\nThe command line, that we will mainly work with, is available in Terminal app.\n\n### Local login without GUI\nThis option is typically used for initial server setup (the graphics stack is a potential \"security hole\" and usually is not installed) and also for working with embedded systems.\n\nAfter the system is loaded or the terminal is connected, text message will ask for a user name and a password. And after login to system the control is transferred to the **command interpreter**.\n\n### Remote SSH login\nTo connect via SSH, you need to use the following command (for Linux/Mac)\n\n```\nssh USERNAME@HOSTNAME\n```\n\nThere are special programs for SSH connection in Windows, for example [PuTTY](http://www.chiark.greenend.org.uk/~sgtatham/putty/download.html).\n\nAfter the connection you need to enter your password for login. In some cases\nthere is no need to enter a password, for example, if authorization is configured with using SSH keys.\n\nAfter login to system the control is transferred to the **command interpreter**.\n\n\n## Command line working basics\n### File system navigation\n\nThe command-line prompt usually has an appearance dependent on status:\n\n```\nUSERNAME@HOSTNAME:WORKING_DIRECTORY>\n```\n\nRoot directory in file system hierarchy -- is `/`. After system login the working directory is a *home directory* of current user -- this is a directory that is both readable and writable.\n\nThe home directories of regular users are located:\n * `/home/` -- for Linux\n * `/usr/local/home` -- for FreeBSD\n * `/Users` -- for MacOS\n\nRegardless of the operating system, the name `~` (\"tilde\" symbol) is synonymous with home directory of the *current user*.\n\nTokens `.` and `..`  mean respectively the current directory and the directory\none level higher in the hierarchy.\n\nTo navigate through the directories, use the command `cd`. Examples:\n\n```\ncd ..          # Navigate one level up\ncd ../..       # Navigate two levels up\ncd ../src      # Navigate one level up, and then to subdirectory src\ncd /           # Navigate to the root directory\ncd /usr/lib64  # Navigate to directory /usr/lib64\ncd ~/projects  # Navigate to directory /home/NAME/projects\n```\n\n**Note**. When entering file or directory names, press `TAB` for autocomplete function.\n\n### Run executable files\n\nExecutable file is any file (including a text file) that has special attribute.\n\nThere are two ways to run an executable file:\n * Enter the name of this file, if the file is located in one\n of the directories listed in the *environmental variable* `PATH`. All standard UNIX system programs are run in this way.\n * Enter the *full name* of the file. The full name can be either absolute (starts with `/`) or relative (starts with `.`). Programs in the home directory are usually run in this way.\n\n\n### Default file management programs\n\n * `cp` -- copy file or directory (with option `-R`)\n * `mv` -- rename (move) file or directory (with option `-R`)\n * `rm` -- remove file or directory (with option `-r`)\n * `ls` -- list working directory contents\n\nAll these commands are normal programs that are located in the directory `/usr/bin`.\n\n**Question**. Why isn't there a program called `cd`?\n<!--\nCorrect answer: current directory is the state of interpreter. Every command is run in a separate process that cannot change the state of the parent process.\n-->\n\n\n### Executable file formats\n\n * Binary file starts with a byte sequence `0x7F 0x45 0x4C 0x46`. This format is called ELF (Executable and Linkable Format).\n * An arbitrary file (including a text file) that starts with a string `#!INTERPRETER_NAME\\n`. In this case system starts the specified interpreter and passes the executable file to it as an argument.\n\n Executable file example:\n\n ```\n #!/usr/bin/python\n\n print(\"Hello, UNIX!\")\n ```\n\n### Midnight Commander\n\nUsing the command line is not always convenient to navigate the file system.\n\n**Note**. When using the provided VM image, the file system is also\navailable via FTP: [ftp://student@192.168.56.105/](ftp://student@192.168.56.105/).\n\n*Midnight Commander* -- two-panel file manager available for almost every UNIX-like operating system (including MacOS). It is run with the command `mc`. Working with it is similar to working with FAR Manager or Total Commander. Some keyboard shortcuts are an exception.\n\nMain operations:\n * `F3` -- file view\n * `F4` -- file edit\n * `Shift+F4` -- create and edit new file\n * `F5` -- copy\n * `F6` -- move\n * `F7` -- create directory\n * `F8` -- remove\n * `F10` -- exit Midnight Commander\n * `C-x c` -- edit file permissions\n * `C-x o` -- edit file's user\n * `C-x s` -- create symbolic link for file\n\n**Note.** To exit view or edit mode press `Esc` **twice**. It is so because `Esc` key in classic terminals is intended for prefix input of control characters.\n\n\n### File system hierarchy\n\nUnlike Windows, where each physical disk or partition on the disk corresponds to a specific letter, for example, `C:\\`, the file system tree on UNIX systems shares the root `/`. Separate disks or partitions are *mounted* into subdirectories of the main file system.\n\nThe file system of all Linux distributions has the following hierarchy:\n * `/bin` -- executable programs that provide the essential commands\n * `/boot` -- files required to boot the operating system\n * `/dev` -- pseudo-device files\n * `/etc` -- system configuration text files\n * `/home` -- user home directories\n * `/lib` or `/lib64`, or both of them -- the minimum set of shared libraries required for system availability. The `/lib64` directory is present on 64-bit systems and contains variants of libraries for\nx86_64, while the `/lib` contains their analogues for i386.\n * `/lost+found` -- files that are out of any directory for some reason (such as an incorrect shutdown of the computer, or disk failure), but their content is available.\n * `/media` -- directory for mounting replaceable media available to all users\n * `/mnt` -- directory for mounting network file systems or foreign sections\n * `/opt` -- directory for installing third-party applications not from the distribution repository, such as Google Chrome\n * `/proc` -- a virtual file system with information about the processes running in the system is mounted here\n * `/root` -- the home directory for the `root` user\n * `/run` -- contains *sockets* and text files with *process IDs* for running daemons\n * `/sbin` -- executable files to be run by `root` user; other users do not have this directory enabled in the environment variable `PATH`, and to run them, you need to specify the full path\n * `/srv` -- files with data for services provided by the system\n * `/sys` -- virtual file system for viewing and modifying kernel settings\n * `/tmp` -- Directory for temporary files\n * `/usr` -- contains a hierarchy similar to the root hierarchy; it contains the files of most of the programs that are installed from distribution *repositories*\n * `/usr/local` -- similar to `/usr`, but is for installing programs yourself from source\n * `/var` -- contains data from various daemons, such as database.\n\n## Console text editors\n\n### Midnight Commander internal editor\n\nCalled by pressing `F4` from the file manager or by command `mcedit FILENAME` as an independent program.\n\nMain keys:\n * `F2` -- save file\n * `Esc Esc` -- exit\n * `F3` -- start/end of text selection\n * `F5` -- copy selected text to current position\n * `F6` -- move selected text to current position\n * `F8` -- delete selected text; if no text is selected, -- delete the current line\n\n### VI editor\n\nBecause Midnight Commander, and accordingly, the `mcedit` editor are not always installed by default, sometimes there is a need to use the editor `vi`, taht is included in the basic set of almost all Linux distributions.\n\nThe editor is started with the command `vi FILENAME` or as a result of some action that requires text editting (for example, `git commit` command -- to edit a commit message).\n\nThe editor `vi` can be identified by the black screen, and symbol `~` in all empty lines at the end of the text in the left column of the terminal.\n\nAfter launching the editor is in **command mode**. No need to press alphanumeric keys to enter text in this mode. If this happens, press `C-[` to return to command mode.\n\nIn command mode the text is navigated by the arrow keys, as well as the `h`, `j`, `k` and `l` keys.\nIn addition to the `vi` text editor, many development environments, such as QtCreator и IntelliJ IDEA, as well as Chrome and Firefox browsers, have plugins that allow you to use VIM-style navigation. So it is better to remember these keys.\n\nTo switch to **insert mode**, similar to GUI-editors, you need to press the `i` key. To exit this mode, use the shortcut `C-[`. To switch to **replacement mode** -- there is the `o` key, output is similar.\n\nThe basic commands that you need to remember:\n * `:w` -- save file\n * `:e FILENAME` -- open or create a file with the specified name\n * `:q` -- exiting the editor is possible only if there are no unsaved changes\n * `:q!` -- force exit from editor without saving\n * `!COMMAND` -- run a UNIX command without exiting the editor\n\nYou can get a more detailed guide to `vi` by running `vimtutor`\n\n### Nano editor\n\nSome distributions, such as Ubuntu, have the `nano` editor installed by default instead of `vi`.\nIt is an easy to use text editor. It can be identified by the text `GNU nano` in the header at the top of the terminal, and hints about keyboard shortcuts like `^G Get Help` in the basement. The `^` symbol stands for  `Ctrl` key.\n"
  },
  {
    "path": "en-mipt/numbers/README.md",
    "content": "# Numbers Representaion\r\n\r\n## Integer data types\r\n\r\nThe minimum addressable data size is \"typically\" one byte (8 bits). \"Typically\" -- it means that it is not always, and there are different exotic architectures, where \"byte\" is 9 bits (PDP-10), or specialized signal processors with a minimum addressable data size of 16 bits (TMS32F28xx).\r\n\r\nThe C standard defines the constant `CHAR_BIT` (in the header file `<limits.h>`), for which it is guaranteed that `CHAR_BIT >= 8`.\r\n\r\nA data type representing one byte is historically called a \"character\" -- `char`, which contains exactly `CHAR_BITS` bits.\r\n\r\nThe sign of the type `char` is not defined by the standard. For example, it is a signed data type for the x86 architecture, but unsigned -- for ARM. The gcc compiler options `-fsigned-char` and `-funsigned-char` define this behavior.\r\n\r\nFor other integer data types: `short`, `int`, `long`, `long long`, the C language standard defines the minimum bit size:\r\n\r\n| Data type  | Size                              |\r\n| -----------| ----------------------------------|\r\n| `short`    | at least 16 bits                  |\r\n| `int`      | at least 16 bits, usually 32 bits |\r\n| `long`     | at least 32 bits                  |\r\n| `long long`| at least 64 bits, usually 64 bits |\r\n\r\nTherefore, you cannot rely on the number of bits in primitive data types, and you should check it with the help of `sizeof` operator, which returns the `number of bytes`, that is, in most cases, how many blocks of size `CHAR_BIT` fit in the data type.\r\n\r\nThe `long` data type should be treated with extreme caution: on a 64-bit Unix system it is 64-bit, and, for example, on 64-bit Windows it is 32-bit. Therefore, to avoid confusion, this type of data is not allowed.\r\n\r\n## Signed and unsigned data types\r\n\r\nInteger data types can be preceded by modifiers `unsigned` or `signed`, which indicate the possibility of negative numbers.\r\n\r\nFor signed types, the high-order bit defines the sign of a number: the value `1` is for negative sign.\r\n\r\nThe method of internal representation of negative numbers is not regulated by the [C standard](http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570), but all modern computers use the reverse additional code. Moreover, paragraph 6.3.1.3.2 of the C language standard defines a method for converting types from signed to unsigned in such a way that leads to coding with an additional reverse code.\r\n\r\nThus, the value `-1` is represented as an integer, all bits of which are equal to one.\r\n\r\nFrom the point of view of low-level programming, and the C language in particular, the sign of data types determines only the way of applying various operations.\r\n\r\n## Data types with a fixed number of bits\r\n\r\nData types that are guaranteed to have a fixed number of digits:\r\n`int8_t`, `int16_t`, `int32_t`, `int64_t` — for signed, and\r\n`uint8_t`, `uint16_t`, `uint32_t`, `uint64_t` — for unsigned,\r\nare defined in header files: `<stdint.h>` (for C99+) and `<cstdint>` (for C++11 and later).\r\n\r\n## Integer overflow\r\n\r\nAn integer overflow situation occurs when the result data type does not have enough digits to store the final result. For example, if you add the unsigned 8-bit integers: 255 and 1, you get a result that cannot be represented as an 8-bit value.\r\n\r\nFor **unsigned** numbers the overflow situation is normal, and is equivalent to the operation \"addition modulo\".\r\n\r\nFor **signed** data types -- it leads to a situation of *Undefined behaviour*. Such situations cannot occur in correct programs.\r\n\r\nExample:\r\n```\r\nint some_func(int x) {\r\n    return x+1 > x;\r\n}\r\n```\r\n\r\nIt makes sense that such a program should always return a value of `1` (or `true`), since we know that `x+1` is always greater than `x`. The compiler can use this fact to optimize the code, and always return a true value. Thus, the behavior of the program depends on the optimization options that were used.\r\n\r\n## Undefined behaviour control\r\n\r\nThe latest versions of the compilers `clang` and `gcc` (since 6th version) are able to control situations of undefined behavior.\r\n\r\nYou can enable the generation of *managed* program code that uses additional run-time checks. Certainly, it comes at the cost of some performance degradation.\r\n\r\nSuch tools are called *sanitizers*, designed for different purposes.\r\n\r\nThe `-fsanitize=undefined` option is used to enable a sanitizer to monitor the undefined behaviour.\r\n\r\n## Overflow control, regardless of sign\r\n\r\nInteger overflow means the shift of high-order bit, and many processors, including the x86 family, can diagnose this. C and C++ standards do not provide this capability, but the gcc compiler (since the 5th version) provides **non-standard** built-in functions for performing operations with overflow control.\r\n\r\n```\r\n// Addition operation\r\nbool __builtin_sadd_overflow (int a, int b, int *res);\r\nbool __builtin_saddll_overflow (long long int a, long long int b, long long int *res);\r\nbool __builtin_uadd_overflow (unsigned int a, unsigned int b, unsigned int *res);\r\nbool __builtin_uaddl_overflow (unsigned long int a, unsigned long int b, unsigned long int *res);\r\nbool __builtin_uaddll_overflow (unsigned long long int a, unsigned long long int b, unsigned long long int *res);\r\n\r\n// Subtraction operation\r\nbool __builtin_ssub_overflow (int a, int b, int *res)\r\nbool __builtin_ssubl_overflow (long int a, long int b, long int *res)\r\nbool __builtin_ssubll_overflow (long long int a, long long int b, long long int *res)\r\nbool __builtin_usub_overflow (unsigned int a, unsigned int b, unsigned int *res)\r\nbool __builtin_usubl_overflow (unsigned long int a, unsigned long int b, unsigned long int *res)\r\nbool __builtin_usubll_overflow (unsigned long long int a, unsigned long long int b, unsigned long long int *res)\r\n\r\n// Multiplication operation\r\nbool __builtin_smul_overflow (int a, int b, int *res)\r\nbool __builtin_smull_overflow (long int a, long int b, long int *res)\r\nbool __builtin_smulll_overflow (long long int a, long long int b, long long int *res)\r\nbool __builtin_umul_overflow (unsigned int a, unsigned int b, unsigned int *res)\r\nbool __builtin_umull_overflow (unsigned long int a, unsigned long int b, unsigned long int *res)\r\nbool __builtin_umulll_overflow (unsigned long long int a, unsigned long long int b, unsigned long long int *res)\r\n\r\n```\r\n\r\n## Real data types\r\n\r\nThere are two ways to represent real numbers: with a fixed number of digits for the fractional part (fixed-point), and with a variable number of digits (floating-point).\r\n\r\nFixed-point representation is often used where guaranteed accuracy to a certain digit is required, such as in Finance.\r\n\r\nFloating-point representation is more common, and all modern processor architectures operate with this format.\r\n\r\nThere are two main types of floating-point objects that are defined by the C standard: `float` (uses 4 bytes for storage) and `double` (uses 8 bytes).\r\n\r\nThe most significant bit (MSB, also called the high-order bit)\r\nThe high-order bit in the number representation indicates the sign of a number. Next, in order of bits, the value of *biased exponent* is stored (8 bits for `float` or 11 bits for `double`), followed by the *mantissa* value (23 or 52 bits).\r\n\r\nThe biased exponent is necessary in order to be able to store values with a negative exponent in such a representation. The offset for type `float` is `127`, for type `double` -- `1023`.\r\n\r\nSo, the result value can be calculated like\r\n\r\n```\r\nValue = (-1)^S * 2^(E-B) * ( 1 + M / (2^M_bits - 1) )\r\n```\r\n\r\nwhere `S` is the sign bit, `E` is the biased exponent, `B` is the bias offset (127 or 1023), and `M` is the mantissa value, `M_bits` is the number of bits in the exponent.\r\n\r\n\r\n## How to get the individual bits of a real number\r\n\r\nBitwise operations refer to integer arithmetic, and are not provided for types `float` и `double`. Thus, you need to store a real number in memory, and then read it, interpreting it as an integer. In case of C++, the `reinterpret_cast`operator is used for this. For the C language there are two ways: use pointer casting -- the analog of 'reinterpret_cast', or use the type 'union'.\r\n\r\n### Pointers casting\r\n```\r\n// We have some real number that is stored in memory\r\ndouble a = 3.14159;\r\n\r\n// Get a pointer to this number\r\ndouble* a_ptr_as_double = &a;\r\n\r\n// Lose type information by casting it to void*\r\nvoid* a_ptr_as_void = a_ptr_as_void;\r\n\r\n// Void* pointer in C can be assigned to any pointer\r\nuint64_t* a_ptr_as_uint = a_ptr_as_void;\r\n\r\n// Well, then just dereferenced pointer\r\nuint64_t b = *a_as_uint;\r\n```\r\n\r\n### The use of a type `union`\r\n\r\nThe `union` type is a data type that is syntactically very similar to the `struct` typeю It means that you can list there several named fields, but conceptually they are completely different data types! If a structure or class has a separate storage space in memoty for each field, this does not happen for `union`, and all fields overlap when placed in memory.\r\n\r\nTypically, the `union` type is used as a variant data type (in C++ since the 17th standard `std::variant` is provided for this), but as a side effect -- it is convenient to use type casts in a manner of `reinterpret_cast` ь, without using pointers.\r\n\r\n```\r\n// We have some real number that is stored in memory\r\ndouble a = 3.14159;\r\n\r\n// Use union type\r\ntypedef union {\r\n    double     real_value;\r\n    uint64_t   uint_value;\r\n} real_or_uint;\r\n\r\nreal_or_uint u;\r\nu.real_value = a;\r\nuint64_t b = u.uint_value;\r\n```\r\n\r\n## Special values in IEEE 754 format\r\n\r\n * Infinity: `E=0xFF...FF`, `M=0`\r\n * Minus zero (the result of dividing 1 by minus infinity): `S=1`, `E=0`, `M=0`\r\n * NaN: `S=[any]`, `E=0xFF...FF`, `M <> 0`\r\n\r\nSome processors, such as the x86 architecture, support an extension of the standard that allows you to more efficiently represent a set of numbers whose values are close to zero. Such numbers are called *denormalized*.\r\n\r\nThe feature of a denormalized number is the value of the offset exponent `E=0`. In this case, the numerical value is calculated like:\r\n\r\n```\r\nValue = (-1)^S * ( M / (2^M_bits - 1) )\r\n```\r\n"
  },
  {
    "path": "en-mipt/syscalls/README.md",
    "content": "# x86 assembler and System Calls\n\nThe main reference for the set of commands [(converted to HTML)](https://www.felixcloutier.com/x86/).\n\nReference for the MMX, SSE, and AVX command sets [on the Intel website](https://software.intel.com/sites/landingpage/IntrinsicsGuide/).\n\nGood tutorial on x86 Assembly [on WikiBooks](https://en.wikibooks.org/wiki/X86_Assembly)\n\n## 32-bit assembler on 64-bit systems\n\nWe will use a 32-bit instruction set. On 64-bit architectures, the GCC compiler option `-m32` is used for this.\n\nIt is also necessary to install the 32-bit library stack. On Ubuntu it is done with only one command:\n```\nsudo apt-get install gcc-multilib\n```\n\n## Intel and At&T syntax\n\nHistorically, there are two x86 Assembly language syntaxes: AT&T syntax – used in UNIX systems, and Intel syntax – used in DOS/Windows.\n\nThe difference is primarily in the order of command arguments.\n\nThe gcc compiler uses AT&T syntax by default, but can switch to Intel syntax with `-masm=intel` option.\n\nYou can also specify the syntax to use in the first line in the text\nof the program itself:\n```nasm\n.intel_syntax noprefix\n```\n\nThe parameter `noprefix` after `.intel_syntax` indicates here that in addition to the order of the arguments corresponding to the Intel syntax, register names should not begin with the `%` character, and constants should not begin with the `$` character, as it is customary to do in the AT&T syntax.\n\nWe will use this syntax because it is the syntax that most of the available documentation and examples are written with, including documentation from processor manufacturers.\n\n## General purpose processor registers\n\nHistorically, the x86 processor family inherited a set of 8-bit General-purpose registers of the 8080/8085 family called `a`, `b`, `c` and `d`. But since the 8086 processor became 16-bit, the registers were named `ax`, `bx`, `cx`and `dx`.\nIn 32-bit processors they are called `eax`, `ebx`, `ecx` and `edx`, in 64-bit `rax`, `rbx`, `rcx` and `rdx`.\n\nIn addition, x86 has \"dual-purpose\" registers, which can be used, among other things, as General-purpose registers, if you use a limited subset of processor commands:\n\n * `ebp` - the upper boundary of the stack;\n * `esi` - index of the array element that is a source for copy operation;\n * `edi` - index of the array element that is a destination for copy operation.\n\nThe `esp` register contains a pointer to the lower boundary of the stack, so it is not recommended to use it arbitrarily.\n\n### x86-64 registers\n\n64-bit registers for the x86-64 architecture are named starting with the letter `r`. In addition to the registers `rax`...`rsi`,`rdi` general purpose registers`r9`...`r15` can be used. The stack pointer is stored in `rsp`, the upper bound of the stack frame is stored in `rbp`.\n\nThe lower 32-bit parts of the `rax`...`rsi`,`rdi`,`rsp`,`rbp`  registers can be addressed by the names `eax`...`esi`,`edi`,`esp`, `ebp`. When writing values to 32-bit register names, the highest 32 digits are zeroed, which is acceptable for operations on 32-bit unsigned values.\n\nTo work with signed 32-bit values, such as the `int` type, you must first perform the *sign extension* operations with the `movslq` command.\n\n## Some instructions\n\n**For Intel syntax**, the first argument of the command is the one whose value will be modified, and the second – the one which remains unchanged.\n\n```nasm\nadd     DST, SRC        /* DST += SRC */\nsub     DST, SRC        /* DST -= SRC */\ninc     DST             /* ++DST */\ndec     DST             /* --DST */\nneg     DST             /* DST = -DST */\nmov     DST, SRC        /* DST = SRC */\nimul    SRC             /* (eax,edx) = eax * SRC - signed */\nmul     SRC             /* (eax,edx) = eax * SRC - unsigned */\nand     DST, SRC        /* DST &= SRC */\nor      DST, SRC        /* DST |= SRC */\nxor     DST, SRC        /* DST ^= SRC */\nnot     DST             /* DST = ~DST */\ncmp     DST, SRC        /* DST - SRC, the result is not saved, */\ntest    DST, SRC        /* DST & SRC, the result is not saved  */\nadc     DST, SRC        /* DST += SRC + CF */\nsbb     DST, SRC        /* DST -= SRC - CF */\n```\n\n**For AT&T syntax** the order of arguments is the opposite. That is, the command\n`add %eax, %ebx` will calculate the sum of `%eax` and `%ebx` , then save the result\nin register `%ebx`, which is specified as the second argument.\n\n## Processor flags\n\nUnlike ARM processors, where the flag register is updated\nonly if there is a special flag in the command, denoted by a suffix\n`s`, in Intel: processors flags are always updated by most instructions.\n\nThe `ZF` flag is set if the result of operation is zero.\n\nThe `SF` flag is set if the result of the operation is a\nnegative number.\n\nThe `CF` flag is set if the operation results in a transfer from the highest bit of the result. For example, `CF` is set for addition operation if the result of addition of two unsigned numbers cannot be represented by a 32-bit unsigned number.\n\nThe `OF` flag is set if the operation results in an overflow of the signed result. For example, when adding, `OF` is set if the result of adding two signed numbers cannot be represented by a 32-bit signed number.\n\nNote that both addition `add` and subtraction `sub` operations set both the `CF` and the `OF` flags. Addition and subtraction of signed and unsigned numbers are executed exactly in the same way, and so only one instruction is used for both signed and unsigned operations.\n\nThe `test` and `cmp` instructions do not save the result but only change the flags.\n\n## Control the execution order of the program\n\nUnconditional jump is performed using the `jmp` statement\n```nasm\njmp label\n```\n\nConditional jumps check combinations of arithmetic flags:\n```nasm\njz      label   /* jump, if equal to (zero), ZF == 1 */\njnz     label   /* jump, if not equal (not zero), ZF == 0 */\njc      label   /* jump, if CF == 1 */\njnc     label   /* jump, if CF == 0 */\njo      label   /* jump, if OF == 1 */\njno     label   /* jump, if OF == 0 */\njg      label   /* jump, if greater for signed numbers */\njge     label   /* jump, if >= for signed numbers */\njl      label   /* jump, if < for signed numbers */\njle     label   /* jump, if <= for signed numbers */\nja      label   /* jump, if > for unsigned numbers */\njae     label   /* jump, if >= (unsigned) */\njb      label   /* jump, if < (unsigned) */\njbe     label   /* jump, if <= (unsigned) */\n```\n\nFunction call and return are performed by `call` and `ret` commands\n```nasm\ncall    label   /* pushes the return address to the stack, and jumps to label */\nret             /* pulls the return address from the stack and navigates to it */\n```\n\nBesides that there is a complex command for loops organization, which implies that the `ecx` register contains a loop counter:\n```nasm\nloop    label   /* decreases the ecx value by 1; if ecx==0, then\n                   jump to the next instruction, otherwise\n                   jump to label */\n```\n\n## Memory addressing\n\nUnlike RISC processors, x86 use **one of the command arguments** as an address in memory.\n\n**In AT&T syntax** this addressing is written as: `OFFSET(BASE, INDEX, SCALE)`, where `OFFSET` – is a constant, `BASE` and `INDEX` – are registers, and `SCALE` -  is one of the values: `1`,  `2`,  `4` or `8`.\n\nThe memory address is calculated as `OFFSET+BASE+INDEX*SCALE`. `OFFSET`,\n`INDEX` and `SCALE` parameters are optional. In their absence it is implied,\nthat `OFFSET=0`, `INDEX=0`, `SCALE` is equal to the size of the machine word.\n\n**Intel syntax** uses a more obvious notation: `[BASE + INDEX * SCALE + OFFSET]`.\n\n\n## Calling conventions for 32-bit architecture\n\nThe return value of the 32-bit function type is written to the register `eax`. The pair `eax` and `edx` is used to return the 64-bit value.\n\nThe called function must store the values of the general-purpose registers `ebx`, `ebp`, `esi` and `edi` on the stack.\n\nArguments can be passed to a function in different ways, depending on the conventions of the ABI.\n\n\n### Cdecl and stdcall conventions\n\nArgument passing conventions used on 32-bit x86 systems. All function arguments are stacked right-to-left, then a function is called that addresses the arguments through a `ebp` or `esp` pointer with some positive offset.\n\nExample:\n```c\nchar * s = \"Name\";\nint value1 = 123;\ndouble value2 = 3.14159;\nprintf(\"Hello, %s! Val1 = %d, val2 = %g\\n\", s, value1, value2);\n```\n\nHere, before calling `printf`, the values of the variables will be stacked before the function is called:\n```nasm\npush    value2\npush    value1\npush    s\npush    .FormatString\ncall    printf\n```\n\nIf the `stdcall` convention is used, **called** function must remove its arguments from the stack after they were used.\n\nIf the `cdecl` convention is used, the **calling** function must remove from the stack those variables that were passed to the called function.\n\nIn C/C++, the conventions that are used can be specified in functions specifiers, for example:\n```\nvoid __cdecl regular_function(int arg1, int arg2);\n#define WINAPI __stdcall\nvoid WINAPI  winapi_function(int arg1, int arg2);\n```\n\nThe `stdcall` convention is now used primarily in the Windows operating system to refer to WinAPI functions. In all other cases on 32-bit systems  `cdecl` convention is used.\n\n### fastcall convention\n\nIf you want to pass some integer arguments to a function, you can use registers, as in the ARM architecture. This agreement is called `fastcall`.\n\n\nThe `fastcall` convention is used to call kernel functions (system calls) on UNIX-like systems. In particular, Linux uses the `eax` register to pass the system call number, and the `ebx`, `ecx`, and `edx` registers – to pass integer arguments.\n\nA similar approach is used in the x86-64 architecture, where there are more registers available than in the 32-bit x86 architecture.\n\n## Calling conventions for 64-bit architecture AMD64 SystemV ABI\n\nInteger arguments are passed sequentially in registers: `rdi`, `rsi`, `rdx`, `rcx`, `r8`, `r9`. If more than 6 arguments are passed, the remaining arguments are passed through the stack.\n\nReal arguments are passed through registers `xmm0`...`xmm7`.\n\nThe return value of the integer type must be stored in `rax`, for real type – in `xmm0`.\n\nThe called function must store the values of general-purpose registers `rbx`, `rby`, and registers `r12`...`r15` on the stack.\n\nIn addition, when calling a function for a 64-bit architecture, there is an additional requirement – before calling the function, the stack must be aligned to the boundary of 16 bytes, that is, you must reduce the value of `rsp` so it was a multiple of 16. If a stack is used to pass parameters apart from registers, these parameters must be pinned to the bottom aligned edge of the stack.\n\nFunctions are guaranteed a 128-byte \"red zone\" in the stack below the `rsp` register - an area that will not be affected by any external event, such as a signal handler. Thus, it is possible to use memory up to `rsp-128` for addressing local variables.\n\n## Interacting with the outside world in Linux via system calls\n\nThe Linux operating system implements system calls via interrupt with the number `0x80`. The `eax` register stores the system call number, arguments are passed through the `ebx`, `ecx`, `edx`, `esi`, `edi` registers, and the return value is passed through `eax`.\n\nSystem call numbers on x86 are listed in the file `/usr/include/asm/unistd_32.h`.\n\nSome useful system calls:\n * `exit` (`_exit` in C notation) = `1` - program exit;\n * `read` = `3` - read from file descriptor;\n * `write` = `4` - write to file descriptor;\n * `brk` (`sbrk` in C notation) = `45` - moves the boundary of the program data segment.\n\nExample (output `Hello` string):\n```asm\n    .text\n    ......\n    mov   eax, 4  // 4 - number for write\n    mov   ebx, 1  // 1 - stdout file descriptor\n    mov   ecx, hello_ptr // pointer to hello\n    mov   edx, 5  // number of bytes in output\n    int   0x80    // Linux system call\n    ......\n    .data\nhello:\n    .string \"Hello\"\nhello_ptr:\n    .long   hello\n```\n"
  },
  {
    "path": "harbour/README.md",
    "content": "# Operating Systems Course for Harbour Space Program\r\n\r\nOur primary operating system is the Linux. You can use\r\n[this VirtualBox\r\nimage](https://drive.google.com/file/d/19pvmNOhqSQG_ZGx6kZ2hbhcuVefShDmI/view?usp=sharing).\r\n\r\nRegular user name for this image is `student`, password `qwerty`. The root\r\nuser password is the same.\r\n\r\nThe contest for your homeworks is here: [http://ejudge64.atp-fivt.org](http://ejudge64.atp-fivt.org)\r\n\r\n##\r\nPlan is approximate, it may slightly change during the course.\r\n\r\n 1. [Lesson 01. Introduction to Unix and developer tools]()\r\n 2. [Lesson 02. Data representation. Integer arithmetic](ints/)\r\n 3. [Lesson 03. Floating point numbers representation](ieee754/)\r\n 4. [Lesson 04. ARM tools. ARM assembly. ARM memory.](arm/arm.md)\r\n 5. [Lesson 05. Global variables, constants and C libraries](arm/memory_addressing.md)\r\n 6. [Lesson 06. Assembly x86](asm-x86/)\r\n 7. [Lesson 07. System calls](../en-mipt/syscalls/)\r\n 8. [Lesson 08. Low-level input and output.](../en-mipt/fds/)\r\n [File attributes]()\r\n 9. [Lesson 09. Low-level file operations](files/)\r\n 10. [Lesson 10. Posix Time Representation.](time/) [Sanitizers, memory mapping](mmap/)\r\n 11. [Lesson 11. Processes Creation and Lifecycle]()\r\n [Process Spawning and Restriction]()\r\n 12. [Lesson 12. Pipes.](pipes/) [Signals](signals/)\r\n 13. [Lesson 13. Sockets TCP/IP and UDP](scokets/)\r\n 14. [Lesson 14. Pointers to Functions. Runtime Libraries Loading](libs/)\r\n 15. [Lesson 15. Encryption with OpenSSL](openssl/)\r\n"
  },
  {
    "path": "harbour/arm/arm.md",
    "content": "# ARM assembler basics\n\n## Writing and compiling programs\n\nAssembly language programs for the GNU compiler are saved in a file whose name ends in `.s` or `.S`. In the case of `.S` it is assumed that the text of the program can be processed by the preprocessor.\n\nOne of the commands is used to compile:\n`arm-linux-gnueabi-as` or `arm-linux-gnueabi-gcc`. In the first case, the text is only compiled into an object file, in the second – into an executable program, linked with the standard C library, from which you can use I/O functions.\n\nARM processors support two sets of commands: the main 32-bit `arm`, and the compacted 16-bit `thumb`. And the processor is able to switch between them. In this workshop, we will use a 32-bit instruction set, so the texts should be compiled with the `-marm` option.\n\n## General syntax\n\n```\n// This is a comment (like in C++)\n\n    .text      // the beginning of the section .text with program code\n    .global f  // indicates that the f label\n               // is externally accessible (similar to extern)\nf:             // label (ends with a colon)\n\n     // series of commands\n     mul   r0, r0, r3\n     mul   r0, r0, r3\n     mul   r1, r1, r3\n     add   r0, r0, r1\n     add   r0, r0, r2  \n     mov   r1, r0\n     bx    lr\n```\n\n## Registers\n\nThe processor can only perform operations on *registers* - 32-bit memory cells in the processor core. ARM has 16 registers available programmatically: `r0`, `r1`, ... ,`r15`.\n\nRegisters `r13`...`r15` has special assignments and extra names:\n\n * `r15` = `pc`: Program Counter - pointer to the currently executing instruction\n * `r14` = `lr`: Link Register - stores the return address from the function\n * `r13` = `sp`: Stack Pointer - pointer to the top of the stack.\n\n## Flags\n\nCommands execution may lead to some additional information that is stored in the *flag register*. Flags refer to the last command executed. The main flags are:\n\n * `C`: Carry - an unsigned overflow occurred\n * `V`: oVerflow - a signed overflow occurred\n * `N`: Negative - negative result\n * `Z`: Zero - zeroing the result.\n\n## Commands\n\nFor a complete list of 32-bit commands, see [this reference](/practice/asm/arm_basics/arm_reference.pdf), starting at page 151.\n\nThe ARM-32 architecture implies that almost all commands can have *conditional execution*. The condition is encoded with 4 bits in the command itself, and in terms of Assembly syntax, commands can have suffixes.\nThus, each command consists of two parts (without spaces): the command itself and its suffix.\n\n## Basic arithmetic operations\n\n* `AND regd, rega, argb`  // regd ← rega & argb\n* `EOR regd, rega, argb`  // regd ← rega ^ argb\n* `SUB regd, rega, argb`  // regd ← rega − argb\n* `RSB regd, rega, argb`  // regd ← argb - rega\n* `ADD regd, rega, argb`  // regd ← rega + argb\n* `ADC regd, rega, argb`  // regd ← rega + argb + carry\n* `SBC regd, rega, argb`  // regd ← rega − argb − !carry\n* `RSC regd, rega, argb`  // regd ← argb − rega − !carry\n* `TST rega, argb`        // set flags for rega & argb\n* `TEQ rega, argb`        // set flags for rega ^ argb\n* `CMP rega, argb`        // set flags for rega − argb\n* `CMN rega, argb`        // set flags for rega + argb\n* `ORR regd, rega, argb`  // regd ← rega | argb\n* `MOV regd, arg`         // regd ← arg\n* `BIC regd, rega, argb`  // regd ← rega & ~argb\n* `MVN regd, arg`         // regd ← ~argb\n\n## Suffixes-conditions\n\n```\nEQ        equal  (Z)\nNE        not equal  (!Z)\nCS or HS  carry set / unsigned higher or same  (C)\nCC or LO  carry clear / unsigned lower  (!C)\nMI        minus / negative  (N)\nPL        plus / positive or zero  (!N)\nVS        overflow set  (V)\nVC        overflow clear  (!V)\nHI        unsigned higher  (C && !Z)\nLS        unsigned lower or same  (!C || Z)\nGE        signed greater than or equal  (N == V)\nLT        signed less than  (N != V)\nGT        signed greater than  (!Z && (N == V))\nLE        signed less than or equal  (Z || (N != V))\n```\n\n## Transitions\n\nThe `pc` counter is automatically incremented by 4 when executed another instruction. Commands are used to branch programs:\n\n * `B label` - the transition to the label; is used inside of functions for branches associated with loops or conditions\n * `BL label` - save current `pc` to `lr` and switch to `label`; usually used to call functions\n * `BX register` - go to the address specified in the register; usually used to exit functions.\n \n## Memory operation\n\nThe processor can only perform operations on registers. Special register loading/saving instructions are used to interact with the memory.\n\n* `LDR regd, [regaddr]` – loads the machine word from memory from the address stored in regaddr and stores it in the regd register\n* `STR reds, [regaddr]` – stores the machine word in memory at the address, specified in the regaddr register.\n\n\n# Development for ARM architecture\n\n## Cross-compilation\n\nThe process of building programs for a different processor architecture or operating system is called cross-compilation.\n\nThis requires a special version of the `gcc` compiler,\ndesigned for a different platform. Many distributions have separate compiler packages for other platforms, including ARM.\n\nIn addition, you can download an all-in-one delivery for the ARM architecture from the Linaro project: [http://releases.linaro.org/components/toolchain/binaries/7.3-2018.05/arm-linux-gnueabi/](http://releases.linaro.org/components/toolchain/binaries/7.3-2018.05/arm-linux-gnueabi/).\n\nFull `gcc` command names have the *triplet* form:\n```\nARCH-OS[-VENDOR]-gcc\nARCH-OS[-VENDOR]-g++\nARCH-OS[-VENDOR]-gdb\n\netc.\n```\nwhere `ARCH` is the architecture name: `i686`, `x86_64`, `arm`, `ppc`, etc.; `OS` -- the operating system, e.g. `linux`, `win32` or `darwin`; `VENDOR` (optional triplet fragment) -- binary interface agreements (if there are several of them for the platform, for example for ARM this can be a `gnueabi` (standard Linux agreement) or `none-eabi` (no OS, just bare hardware).\n\nThe name of the architecture for ARM is often distinguished between `arm` (soft float) and `armhf` (hard float). In the first case, the absence of a floating-point block is implied, so all operations are emulated by software, in the second case they are performed by hardware.\n\n\n## Running programs for non-native architectures\n\nExecution of programs designed for other architectures is possible only by interpretation of a foreign set of commands. *Emulators* -- special programs intended for this purpose.\n\nARM architecture, like many other architectures, is supported by the [QEMU emulator](https://www.qemu.org/).\n\nYou can emulate either a computer system as a whole, similar to VirtualBox, or only a set of processor commands, using the environment of the Linux host system.\n\n### Running ARM binaries in the native environment\n\nThis emulator is included in all common distributions. QEMU commands are like:\n```\nqemu-ARCH\nqemu-system-ARCH\n```\n\nwhere `ARCH` is the name of the architecture to be emulated. Commands, that have `system` in their names, start emulation of a computer system, and you must install an operating system to use them.\n\nCommands without`system` in their names require an executable file name as a mandatory argument in Linux, and emulate only a set of processor commands in *user mode*, executing a \"foreign\" executable file as if it were a normal program.\n\nSince most programs compiled for ARM Linux use the standard C library, it is necessary to use the ARM version of glibc. A minimal environment with the necessary libraries can be taken from the Linaro project (see link above), and passed to qemu using the `-L PATH_K_SYSROOT` option.\n\nCompile and run example:\n```\n# assuming the compiler is unpacked in /opt/arm-gcc,\n# and sysroot -- in /opt/arm-sysroot\n\n# Compile\n> /opt/arm-gcc/bin/arm-linux-gnueabi-gcc -o program hello.c\n\n# The output is an executable file that cannot be executed\n> ./program\nbash: ./program: cannot execute binary file: Exec format error\n\n# But we can run it with qemu-arm\n> qemu-arm -L /opt/arm-sysroot ./program\nHello, World!\n\n```\n\n### Running ARM programs in Raspberry Pi environment emulation\n\nThe ideal option for testing and debugging is to use real hardware, such as Raspberry Pi.\n\nIf you do not have a computer with an ARM-processor, you can\nperform PC emulation with Raspbian system installed.\n\nYou can download the image from here: [Google Drive](https://drive.google.com/open?id=11lc_f-_crhP-CJi_FEYb4DE0u9TMViT4)\n"
  },
  {
    "path": "harbour/arm/memory_addressing.md",
    "content": "# Addressing data in memory and using library functions\n\n* [ARM reference](/practice/asm/arm_basics/arm_reference.pdf)\n\n## Basic commands\nAs is typical for classical RISC architecture, the ARM processor can only perform operations on registers. Separate commands *load* (`ldr`) and *save* (`str`) are used to access memory.\n\nGeneral form of commands:\n```\nLDR{condition}{type} Register, Address\nSTR{condition}{type} Register, Address\n```\nwhere `{condition}` - this is a condition of command execution, maybe blank (see previous workshop);`{type}` - data type:\n * `B` - unsigned byte\n * `SB` - signed byte\n * `H` - half-word (16 bit)\n * `SB`- significant half-word\n * `D` - double word.\n\nIf type is not specified in the command name, so the common word is implied. Note that to perform data load/save operations that are smaller than the machine word, the signed commands are singled out separately, which make the careful bit extension with zeros, while retaining the senior signed bit.\n\nIn case of register loading/saving operations of a pair of registers  (double word), the register must have an even number. The second machine word is implied to be in the neighbouring register numbered `Rn+1`.\n\n## Addressing\n\nThe address looks like: `[R_base {, offset}]`, where `R_base` – the name of the register that contains the base address in memory and the optional parameter `offset` – is the offset from the address. The resulting address is defined as `*R_base + offset`.\n\nThe offset can be either a register name or a numeric constant encoded into a command. Registers are typically used to index array elements, and constants -- to access structure fields or local variables and arguments relative to `[sp]`.\n\n## Addressing fields of C-structures\n\n\nAccording to the C standard, fields in the memory of structures are placed according to the following rules:\n * the order of the fields in memory corresponds to the order of the fields in the structure description\n * the size of the structure must be a multiple of the size of the machine word\n * data inside machine words are placed in such a way as to be pinned to their boundaries.\n\nThus, the size of the structure does not always match the sum of the sizes of the individual fields. For example:\n\n```\nstruct A {\n  char  f1; // 1 byte\n  int   f2; // 4 bytes\n  char  f3; // 1 byte\n};\n// 1 + 4 + 1      = 6 bytes\n// size(struct A) = 12 bytes\n```\n\nIn this example, the field `f1` uses the part of the machine word, the field `f2` - has a size of 4 bytes, so it takes the next machine word, and for the field `f3`  you have to use another one. A simple rearrangement of the fields saves 4 bytes:\n\n```\nstruct A {\n  char  f1; // 1 byte\n  char  f3; // 1 byte\n  int   f2; // 4 bytes  \n};\n// 1 + 1 + 4      = 6 bytes\n// size(struct A) = 8 bytes\n```\nIn this case, the `f1` and `f3` fields occupy the same machine word.\n\nThe GCC compiler has a non-standard `packed` attribute that allows you to create \"Packed\" structures whose size is equal to the sum of the sizes of its individual fields:\n\n```\nstruct A {\n  char  f1; // 1 byte\n  int   f2; // 4 bytes\n  char  f3; // 1 byte\n} __attribute__((packed));\n// 1 + 4 + 1      = 6 bytes\n// size(struct A) = 6 bytes\n```\n\n## Standard C library functions\n\nEach function that can be used externally has a text label associated with it in the symbol table. After compilation, an entry in the symbol table determines the memory location where the first function statement is placed.\n\nFunctions implemented in different object modules but compiled into a single executable file are called in the usual way. The way they are called is no different from calling functions from the same object module.\n\nWhen using *libraries*, they are loaded into a separate memory area, and at the build stage, the location address of the libraries is not known.\n\nMoreover, the location of the program itself, in general, is also assumed to be unknown.\n\nSuch functions that are located in dynamically loaded libraries, including the C standard library, appear in the symbol table with a mark `@plt`. Their implementation in Assembly language looks like:\n```\nfunction@plt:\n\n   // Let's load current PC's IP in a temporary register of IP with some offset.\n   // The table of real functions, which is filled at the stage of\n   // loading the program and dynamic libraries, is located by this offset.\n   add  ip, pc, #0\n   add  ip, ip, #OFFSET_TO_TABLE_BEGIN\n\n   // Load the address value from this table.\n   // This leads to the fact that we jump to the implementation of the real function.\n   ldr  pc, [ip, #OFFSET_TO_FUNCTION_INDEX]\n   \n```\n\nThus, functions from external libraries are located as if in the program itself, but indeed they represent a \"springboard\" for performing real functions.\n"
  },
  {
    "path": "harbour/asm-x86/README.md",
    "content": "# x86 assembler (32-bit, and a few words about 64-bit)\nThe main reference for the set of commands [(converted to HTML)](https://www.felixcloutier.com/x86/).\n\nReference for the MMX, SSE, and AVX command sets [on the Intel website](https://software.intel.com/sites/landingpage/IntrinsicsGuide/).\n\nGood tutorial on x86 Assembly [on WikiBooks](https://en.wikibooks.org/wiki/X86_Assembly)\n\n## 32-bit assembler on 64-bit systems\n\nWe will use a 32-bit instruction set. On 64-bit architectures, the GCC compiler option `-m32` is used for this.\n\nIt is also necessary to install the 32-bit library stack. On Ubuntu it is done with only one command:\n```\nsudo apt-get install gcc-multilib\n```\n\n## Intel and At&T syntax\n\nHistorically, there are two x86 Assembly language syntaxes: AT&T syntax – used in UNIX systems, and Intel syntax – used in DOS/Windows.\n\nThe difference is primarily in the order of command arguments.\n\nThe gcc compiler uses AT&T syntax by default, but can switch to Intel syntax with `-masm=intel` option.\n\nYou can also specify the syntax to use in the first line in the text\nof the program itself:\n```nasm\n.intel_syntax noprefix\n```\n\nThe parameter `noprefix` after `.intel_syntax` indicates here that in addition to the order of the arguments corresponding to the Intel syntax, register names should not begin with the `%` character, and constants should not begin with the `$` character, as it is customary to do in the AT&T syntax.\n\nWe will use this syntax because it is the syntax that most of the available documentation and examples are written with, including documentation from processor manufacturers.\n\n## General purpose processor registers\n\nHistorically, the x86 processor family inherited a set of 8-bit General-purpose registers of the 8080/8085 family called `a`, `b`, `c` and `d`. But since the 8086 processor became 16-bit, the registers were named `ax`, `bx`, `cx`and `dx`.\nIn 32-bit processors they are called `eax`, `ebx`, `ecx` and `edx`, in 64-bit `rax`, `rbx`, `rcx` and `rdx`.\n\nIn addition, x86 has \"dual-purpose\" registers, which can be used, among other things, as General-purpose registers, if you use a limited subset of processor commands:\n\n * `ebp` - the upper boundary of the stack;\n * `esi` - index of the array element that is a source for copy operation;\n * `edi` - index of the array element that is a destination for copy operation.\n\nThe `esp` register contains a pointer to the lower boundary of the stack, so it is not recommended to use it arbitrarily.\n\n### x86-64 registers\n\n64-bit registers for the x86-64 architecture are named starting with the letter `r`. In addition to the registers `rax`...`rsi`,`rdi` general purpose registers`r9`...`r15` can be used. The stack pointer is stored in `rsp`, the upper bound of the stack frame is stored in `rbp`.\n\nThe lower 32-bit parts of the `rax`...`rsi`,`rdi`,`rsp`,`rbp`  registers can be addressed by the names `eax`...`esi`,`edi`,`esp`, `ebp`. When writing values to 32-bit register names, the highest 32 digits are zeroed, which is acceptable for operations on 32-bit unsigned values.\n\nTo work with signed 32-bit values, such as the `int` type, you must first perform the *sign extension* operations with the `movslq` command.\n\n## Some instructions\n\n**For Intel syntax**, the first argument of the command is the one whose value will be modified, and the second – the one which remains unchanged.\n\n```nasm\nadd     DST, SRC        /* DST += SRC */\nsub     DST, SRC        /* DST -= SRC */\ninc     DST             /* ++DST */\ndec     DST             /* --DST */\nneg     DST             /* DST = -DST */\nmov     DST, SRC        /* DST = SRC */\nimul    SRC             /* (eax,edx) = eax * SRC - signed */\nmul     SRC             /* (eax,edx) = eax * SRC - unsigned */\nand     DST, SRC        /* DST &= SRC */\nor      DST, SRC        /* DST |= SRC */\nxor     DST, SRC        /* DST ^= SRC */\nnot     DST             /* DST = ~DST */\ncmp     DST, SRC        /* DST - SRC, the result is not saved, */\ntest    DST, SRC        /* DST & SRC, the result is not saved  */\nadc     DST, SRC        /* DST += SRC + CF */\nsbb     DST, SRC        /* DST -= SRC - CF */\n```\n\n**For AT&T syntax** the order of arguments is the opposite. That is, the command\n`add %eax, %ebx` will calculate the sum of `%eax` and `%ebx` , then save the result\nin register `%ebx`, which is specified as the second argument.\n\n## Processor flags\n\nUnlike ARM processors, where the flag register is updated\nonly if there is a special flag in the command, denoted by a suffix\n`s`, in Intel: processors flags are always updated by most instructions.\n\nThe `ZF` flag is set if the result of operation is zero.\n\nThe `SF` flag is set if the result of the operation is a\nnegative number.\n\nThe `CF` flag is set if the operation results in a transfer from the highest bit of the result. For example, `CF` is set for addition operation if the result of addition of two unsigned numbers cannot be represented by a 32-bit unsigned number.\n\nThe `OF` flag is set if the operation results in an overflow of the signed result. For example, when adding, `OF` is set if the result of adding two signed numbers cannot be represented by a 32-bit signed number.\n\nNote that both addition `add` and subtraction `sub` operations set both the `CF` and the `OF` flags. Addition and subtraction of signed and unsigned numbers are executed exactly in the same way, and so only one instruction is used for both signed and unsigned operations.\n\nThe `test` and `cmp` instructions do not save the result but only change the flags.\n\n## Control the execution order of the program\n\nUnconditional jump is performed using the `jmp` statement\n```nasm\njmp label\n```\n\nConditional jumps check combinations of arithmetic flags:\n```nasm\njz      label   /* jump, if equal to (zero), ZF == 1 */\njnz     label   /* jump, if not equal (not zero), ZF == 0 */\njc      label   /* jump, if CF == 1 */\njnc     label   /* jump, if CF == 0 */\njo      label   /* jump, if OF == 1 */\njno     label   /* jump, if OF == 0 */\njg      label   /* jump, if greater for signed numbers */\njge     label   /* jump, if >= for signed numbers */\njl      label   /* jump, if < for signed numbers */\njle     label   /* jump, if <= for signed numbers */\nja      label   /* jump, if > for unsigned numbers */\njae     label   /* jump, if >= (unsigned) */\njb      label   /* jump, if < (unsigned) */\njbe     label   /* jump, if <= (unsigned) */\n```\n\nFunction call and return are performed by `call` and `ret` commands\n```nasm\ncall    label   /* pushes the return address to the stack, and jumps to label */\nret             /* pulls the return address from the stack and navigates to it */\n```\n\nBesides that there is a complex command for loops organization, which implies that the `ecx` register contains a loop counter:\n```nasm\nloop    label   /* decreases the ecx value by 1; if ecx==0, then\n                   jump to the next instruction, otherwise\n                   jump to label */\n```\n\n## Memory addressing\n\nUnlike RISC processors, x86 use **one of the command arguments** as an address in memory.\n\n**In AT&T syntax** this addressing is written as: `OFFSET(BASE, INDEX, SCALE)`, where `OFFSET` – is a constant, `BASE` and `INDEX` – are registers, and `SCALE` -  is one of the values: `1`,  `2`,  `4` or `8`.\n\nThe memory address is calculated as `OFFSET+BASE+INDEX*SCALE`. `OFFSET`,\n`INDEX` and `SCALE` parameters are optional. In their absence it is implied,\nthat `OFFSET=0`, `INDEX=0`, `SCALE` is equal to the size of the machine word.\n\n**Intel syntax** uses a more obvious notation: `[BASE + INDEX * SCALE + OFFSET]`.\n\n\n## Calling conventions for 32-bit architecture\n\nThe return value of the 32-bit function type is written to the register `eax`. The pair `eax` and `edx` is used to return the 64-bit value.\n\nThe called function must store the values of the general-purpose registers `ebx`, `ebp`, `esi` and `edi` on the stack.\n\nArguments can be passed to a function in different ways, depending on the conventions of the ABI.\n\n\n### Cdecl and stdcall conventions\n\nArgument passing conventions used on 32-bit x86 systems. All function arguments are stacked right-to-left, then a function is called that addresses the arguments through a `ebp` or `esp` pointer with some positive offset.\n\nExample:\n```c\nchar * s = \"Name\";\nint value1 = 123;\ndouble value2 = 3.14159;\nprintf(\"Hello, %s! Val1 = %d, val2 = %g\\n\", s, value1, value2);\n```\n\nHere, before calling `printf`, the values of the variables will be stacked before the function is called:\n```nasm\npush    value2\npush    value1\npush    s\npush    .FormatString\ncall    printf\n```\n\nIf the `stdcall` convention is used, **called** function must remove its arguments from the stack after they were used.\n\nIf the `cdecl` convention is used, the **calling** function must remove from the stack those variables that were passed to the called function.\n\nIn C/C++, the conventions that are used can be specified in functions specifiers, for example:\n```\nvoid __cdecl regular_function(int arg1, int arg2);\n#define WINAPI __stdcall\nvoid WINAPI  winapi_function(int arg1, int arg2);\n```\n\nThe `stdcall` convention is now used primarily in the Windows operating system to refer to WinAPI functions. In all other cases on 32-bit systems  `cdecl` convention is used.\n\n### fastcall convention\n\nIf you want to pass some integer arguments to a function, you can use registers, as in the ARM architecture. This agreement is called `fastcall`.\n\n\nThe `fastcall` convention is used to call kernel functions (system calls) on UNIX-like systems. In particular, Linux uses the `eax` register to pass the system call number, and the `ebx`, `ecx`, and `edx` registers – to pass integer arguments.\n\nA similar approach is used in the x86-64 architecture, where there are more registers available than in the 32-bit x86 architecture.\n\n## Calling conventions for 64-bit architecture AMD64 SystemV ABI\n\nInteger arguments are passed sequentially in registers: `rdi`, `rsi`, `rdx`, `rcx`, `r8`, `r9`. If more than 6 arguments are passed, the remaining arguments are passed through the stack.\n\nReal arguments are passed through registers `xmm0`...`xmm7`.\n\nThe return value of the integer type must be stored in `rax`, for real type – in `xmm0`.\n\nThe called function must store the values of general-purpose registers `rbx`, `rby`, and registers `r12`...`r15` on the stack.\n\nIn addition, when calling a function for a 64-bit architecture, there is an additional requirement – before calling the function, the stack must be aligned to the boundary of 16 bytes, that is, you must reduce the value of `rsp` so it was a multiple of 16. If a stack is used to pass parameters apart from registers, these parameters must be pinned to the bottom aligned edge of the stack.\n\nFunctions are guaranteed a 128-byte \"red zone\" in the stack below the `rsp` register - an area that will not be affected by any external event, such as a signal handler. Thus, it is possible to use memory up to `rsp-128` for addressing local variables.\n"
  },
  {
    "path": "harbour/files/README.md",
    "content": "# File properties\n\n## File information\n\n### `stat` structure\n\nEach file in the file system is associated with a meta-information (status), which is defined by the `struct stat` structure:\n\n```\nstruct stat {\n   dev_t     st_dev;         /* ID of device containing file */\n   ino_t     st_ino;         /* Inode number */\n   mode_t    st_mode;        /* File type and mode */\n   nlink_t   st_nlink;       /* Number of hard links */\n   uid_t     st_uid;         /* User ID of owner */\n   gid_t     st_gid;         /* Group ID of owner */\n   dev_t     st_rdev;        /* Device ID (if special file) */\n   off_t     st_size;        /* Total size, in bytes */\n   blksize_t st_blksize;     /* Block size for filesystem I/O */\n   blkcnt_t  st_blocks;      /* Number of 512B blocks allocated */\n\n   struct timespec st_atim;  /* Time of last access */\n   struct timespec st_mtim;  /* Time of last modification */\n   struct timespec st_ctim;  /* Time of last status change */\n\n   /* Backward compatibility */\n   #define st_atime st_atim.tv_sec      \n   #define st_mtime st_mtim.tv_sec\n   #define st_ctime st_ctim.tv_sec\n};\n\n```\n\nYou can get meta-information about the file using the command `stat FILENAME` or one of the system calls:\n * `int stat(const char *file_name, struct stat *stat_buffer)` - getting information about a file by its name;\n * `int fstat(int fd, struct stat *stat_buffer)` - the same, but for an open file descriptor;\n * `int lstat(const char *path_name, struct stat *stat_buffer)` - similar to `stat`, but if the file name points to a symbolic link, information about the link itself is returned, not the file it refers to.\n\n\n### Access modes and file types in POSIX\n\nIn POSIX there are a few main types of files:\n\n * Regular file (`S_IFREG = 0100000`). Takes place on the drive; contains the normal data.\n * Directory (`S_IFDIR = 0040000`). A special type of file that stores a list of file names.\n * Symbolic link (`S_IFLNK = 0120000`). A file that references another file (including in a different directory or even on a different file system), and in terms of I/O functions, is no different from the file it references.\n * Block (`S_IFBLK = 0060000`) и character (`S_IFCHR = 0020000`) devices. Used as a convenient way to interact with the equipment.\n * Named pipes (`S_IFIFO = 0010000`) and sockets (`S_IFSOCK = 0140000`) for inter-process communication.\n\nThe file type is encoded in the same structure field with access mode (`rwxrwxrwx`) - integer `.st_mode`.\n\nTo select individual file types, bitwise operations are performed using one of the macros: `S_ISREG(m)` ` 'S_ISDIR(m)`, `S_ISCHR(m)`, `S_ISBLK(m)`, `S_ISFIFO(m)`, `S_ISLNK(m)' and 'S_ISSOCK(m)`, which return `0` as false and an arbitrary nonzero value as true.\n\nTo get the access mode, which is encoded in the lower bits`. st_mode`, you can extract them using bitwise operations with the constants `S_IWUSR`, `S_IRGRP`, `S_IXOTH`, etc. A complete list of constants can be found in `man 7 inode`.\n\n### File access\n\nEach file, in addition to the access mode (`rwx` for owner, group and others) has two identifiers – positive integers:\n * `. st_uid` - ID of the file user-owner;\n * `. st_gid` - ID of the file group-owner.\n\n\"Owner\" permissions are applied when the current user's ID (obtained by `getuid()`) matches the `.st_uid` field. Similarly, for a group – when `getgid()` matches `.st_gid`. Otherwise, the \"other\" permissions are applied.\n\nA convenient way to determine the rights of the current user is to use the system call `access`:\n```\nint access(const char *path_name, int mode)\n```\n\nThis system call takes as the `mode` parameter a bitwise combination of the flags `R_OK`, `W_OK`, `X_OK` and `F_OK` — respectively, the ability to read, write, execute a file, and its existence. Returns 0 if the listed attributes are valid for the current user, and -1 otherwise.\n\n\n## File-creation mask\n\nWhen you create new files using the `open` system call (and all high-level functions that use `open`), you must specify the access mode for the newly created files.\n\nIn reality, the access mode may differ from the requested one: for a newly created file (or directory), the *file creation mask* is applied using the bitwise \"AND-NOT\" operation:\n```\n /* Let umask = 0222 */\n open(\"new_file\", O_WRONLY|O_CREAT, 0666); // OK\n\n /* Created a file with attributes 0666 & ~0222 = 0444 */\n```\n\nBy default, the file creation mask is `0000` — it does not impose any restrictions. The `umask` system call allows you to explicitly set a new mask that can be used to prevent accidental creation of files with too weak access rights."
  },
  {
    "path": "harbour/ieee754/README.md",
    "content": "# Real numbers representation\n\nThere are two ways to represent real numbers: with a fixed number of digits for the fractional part (fixed-point), and with a variable number of digits (floating-point).\n\nFixed-point representation is often used where guaranteed accuracy to a certain digit is required, such as in Finance.\n\nFloating-point representation is more common, and all modern processor architectures operate with this format.\n\n\n## Floating point numbers in IEE754 format\n\nThere are two main types of floating-point objects that are defined by the C standard: `float` (uses 4 bytes for storage) and `double` (uses 8 bytes).\n\nThe most significant bit (MSB, also called the high-order bit)\nThe high-order bit in the number representation indicates the sign of a number. Next, in order of bits, the value of *biased exponent* is stored (8 bits for `float` or 11 bits for `double`), followed by the *mantissa* value (23 or 52 bits).\n\nThe biased exponent is necessary in order to be able to store values with a negative exponent in such a representation. The offset for type `float` is `127`, for type `double` -- `1023`.\n\nSo, the result value can be calculated like\n\n```\nValue = (-1)^S * 2^(E-B) * ( 1 + M / (2^M_bits - 1) )\n```\n\nwhere `S` is the sign bit, `E` is the biased exponent, `B` is the bias offset (127 or 1023), and `M` is the mantissa value, `M_bits` is the number of bits in the exponent.\n\n\n## How to get the individual bits of a real number\n\nBitwise operations refer to integer arithmetic, and are not provided for types `float` и `double`. Thus, you need to store a real number in memory, and then read it, interpreting it as an integer. In case of C++, the `reinterpret_cast`operator is used for this. For the C language there are two ways: use pointer casting -- the analog of 'reinterpret_cast', or use the type 'union'.\n\n### Pointers casting\n```\n// We have some real number that is stored in memory\ndouble a = 3.14159;\n\n// Get a pointer to this number\ndouble* a_ptr_as_double = &a;\n\n// Lose type information by casting it to void*\nvoid* a_ptr_as_void = a_ptr_as_void;\n\n// Void* pointer in C can be assigned to any pointer\nuint64_t* a_ptr_as_uint = a_ptr_as_void;\n\n// Well, then just dereferenced pointer\nuint64_t b = *a_as_uint;\n```\n\n### The use of a type `union`\n\nThe `union` type is a data type that is syntactically very similar to the `struct` typeю It means that you can list there several named fields, but conceptually they are completely different data types! If a structure or class has a separate storage space in memoty for each field, this does not happen for `union`, and all fields overlap when placed in memory.\n\nTypically, the `union` type is used as a variant data type (in C++ since the 17th standard `std::variant` is provided for this), but as a side effect -- it is convenient to use type casts in a manner of `reinterpret_cast` ь, without using pointers.\n\n```\n// We have some real number that is stored in memory\ndouble a = 3.14159;\n\n// Use union type\ntypedef union {\n    double     real_value;\n    uint64_t   uint_value;\n} real_or_uint;\n\nreal_or_uint u;\nu.real_value = a;\nuint64_t b = u.uint_value;\n```\n\n## Special values in IEEE 754 format\n\n * Infinity: `E=0xFF...FF`, `M=0`\n * Minus zero (the result of dividing 1 by minus infinity): `S=1`, `E=0`, `M=0`\n * NaN (signaling): `S=0`, `E=0xFF...FF`, `M <> 0`\n * NaN (quiet): `S=0`, `E=0xFF...FF`, `M <> 0`\n\nSome processors, such as the x86 architecture, support an extension of the standard that allows you to more efficiently represent a set of numbers whose values are close to zero. Such numbers are called *denormalized*.\n\nThe feature of a denormalized number is the value of the offset exponent `E=0`. In this case, the numerical value is calculated like:\n\n```\nValue = (-1)^S * ( M / (2^M_bits - 1) )\n```\n"
  },
  {
    "path": "harbour/ints/README.md",
    "content": "# Integer arithmetic\n\n## Integer data types\n\nThe minimum addressable data size is \"typically\" one byte (8 bits). \"Typically\" -- it means that it is not always, and there are different exotic architectures, where \"byte\" is 9 bits (PDP-10), or specialized signal processors with a minimum addressable data size of 16 bits (TMS32F28xx).\n\nThe C standard defines the constant `CHAR_BIT` (in the header file `<limits.h>`), for which it is guaranteed that `CHAR_BIT >= 8`.\n\nA data type representing one byte is historically called a \"character\" -- `char`, which contains exactly `CHAR_BITS` bits.\n\nThe sign of the type `char` is not defined by the standard. For example, it is a signed data type for the x86 architecture, but unsigned -- for ARM. The gcc compiler options `-fsigned-char` and `-funsigned-char` define this behavior.\n\nFor other integer data types: `short`, `int`, `long`, `long long`, the C language standard defines the minimum bit size:\n\n| Data type  | Size                              |\n| -----------| ----------------------------------|\n| `short`    | at least 16 bits                  |\n| `int`      | at least 16 bits, usually 32 bits |\n| `long`     | at least 32 bits                  |\n| `long long`| at least 64 bits, usually 64 bits |\n\nTherefore, you cannot rely on the number of bits in primitive data types, and you should check it with the help of `sizeof` operator, which returns the `number of bytes`, that is, in most cases, how many blocks of size `CHAR_BIT` fit in the data type.\n\nThe `long` data type should be treated with extreme caution: on a 64-bit Unix system it is 64-bit, and, for example, on 64-bit Windows it is 32-bit. Therefore, to avoid confusion, this type of data is not allowed.\n\n## Signed and unsigned data types\n\nInteger data types can be preceded by modifiers `unsigned` or `signed`, which indicate the possibility of negative numbers.\n\nFor signed types, the high-order bit defines the sign of a number: the value `1` is for negative sign.\n\nThe method of internal representation of negative numbers is not regulated by the [C standard](http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570), but all modern computers use the reverse additional code. Moreover, paragraph 6.3.1.3.2 of the C language standard defines a method for converting types from signed to unsigned in such a way that leads to coding with an additional reverse code.\n\nThus, the value `-1` is represented as an integer, all bits of which are equal to one.\n\nFrom the point of view of low-level programming, and the C language in particular, the sign of data types determines only the way of applying various operations.\n\n## Data types with a fixed number of bits\n\nData types that are guaranteed to have a fixed number of digits:\n`int8_t`, `int16_t`, `int32_t`, `int64_t` — for signed, and\n`uint8_t`, `uint16_t`, `uint32_t`, `uint64_t` — for unsigned,\nare defined in header files: `<stdint.h>` (for C99+) and `<cstdint>` (for C++11 and later).\n\n# Overflow\n\nAn integer overflow situation occurs when the result data type does not have enough digits to store the final result. For example, if you add the unsigned 8-bit integers: 255 and 1, you get a result that cannot be represented as an 8-bit value.\n\nFor **unsigned** numbers the overflow situation is normal, and is equivalent to the operation \"addition modulo\".\n\nFor **signed** data types -- it leads to a situation of *Undefined behaviour*. Such situations cannot occur in correct programs.\n\nExample:\n```\nint some_func(int x) {\n    return x+1 > x;\n}\n```\n\nIt makes sense that such a program should always return a value of `1` (or `true`), since we know that `x+1` is always greater than `x`. The compiler can use this fact to optimize the code, and always return a true value. Thus, the behavior of the program depends on the optimization options that were used.\n\n## Undefined behaviour control\n\nThe latest versions of the compilers `clang` and `gcc` (since 6th version) are able to control situations of undefined behavior.\n\nYou can enable the generation of *managed* program code that uses additional run-time checks. Certainly, it comes at the cost of some performance degradation.\n\nSuch tools are called *sanitizers*, designed for different purposes.\n\nThe `-fsanitize=undefined` option is used to enable a sanitizer to monitor the undefined behaviour.\n\n## Overflow control, regardless of sign\n\nInteger overflow means the shift of high-order bit, and many processors, including the x86 family, can diagnose this. C and C++ standards do not provide this capability, but the gcc compiler (since the 5th version) provides **non-standard** built-in functions for performing operations with overflow control.\n\n```\n// Addition operation\nbool __builtin_sadd_overflow (int a, int b, int *res);\nbool __builtin_saddll_overflow (long long int a, long long int b, long long int *res);\nbool __builtin_uadd_overflow (unsigned int a, unsigned int b, unsigned int *res);\nbool __builtin_uaddl_overflow (unsigned long int a, unsigned long int b, unsigned long int *res);\nbool __builtin_uaddll_overflow (unsigned long long int a, unsigned long long int b, unsigned long long int *res);\n\n// Subtraction operation\nbool __builtin_ssub_overflow (int a, int b, int *res)\nbool __builtin_ssubl_overflow (long int a, long int b, long int *res)\nbool __builtin_ssubll_overflow (long long int a, long long int b, long long int *res)\nbool __builtin_usub_overflow (unsigned int a, unsigned int b, unsigned int *res)\nbool __builtin_usubl_overflow (unsigned long int a, unsigned long int b, unsigned long int *res)\nbool __builtin_usubll_overflow (unsigned long long int a, unsigned long long int b, unsigned long long int *res)\n\n// Multiplication operation\nbool __builtin_smul_overflow (int a, int b, int *res)\nbool __builtin_smull_overflow (long int a, long int b, long int *res)\nbool __builtin_smulll_overflow (long long int a, long long int b, long long int *res)\nbool __builtin_umul_overflow (unsigned int a, unsigned int b, unsigned int *res)\nbool __builtin_umull_overflow (unsigned long int a, unsigned long int b, unsigned long int *res)\nbool __builtin_umulll_overflow (unsigned long long int a, unsigned long long int b, unsigned long long int *res)\n\n```\n"
  },
  {
    "path": "harbour/libs/README.md",
    "content": "# Function libraries and their loading\n\n## Functions and pointers to them\n\nProgram code in systems with Von Neumann architecture is placed in memory in the same way as regular data.\n\nThus, it can be loaded or generated while the program is running. Some processors allow you to control which areas of memory can be executed and which are not, and in addition, it is controlled by the kernel. Thus, code can only be executed if it is in memory pages that are marked as executable.\n\n\n## Typification of function pointers\n\nA Declaration like\n```\nint (*p_function)(int a, int b);\n```\nis interpreted as follows: `p_function` is a pointer to a function that takes two integer arguments, and returns a signed integer.\n\nA more general view of a function pointer:\n```\ntypedef ResType (*TypeName)(FuncParameters...);\n```\n\nHere `ResType` is the return type of the target function , `TypeName` is the name of type-pointer, `FuncParameters...` - function parameters.\n\nThe use of the keyword `typedef` is necessary for the C language not to write the entire type every time (similar to `struct`).\n\nThe declaration of pointers to functions is necessary for compiler to know exactly how to use the address of a function, and make possible for compiler to prepare arguments, and understand where to get the return result of the function.\n\n\n## Libraries\n\nAn ELF file can be not only executable file, but also a library that is containing functions. A library differs from an executable in the followinng points:\n * contains a table of available *symbols* - functions and global variables (you can explicitly specify its creation with the `-E` option);\n * can be placed arbitrarily, so the program must be compiled into position-independent code with the option `-fPIC` or `-fPIE`;\n * does not have to have an entry point to the program – functions `_start` and `main`.\n\nThe library is compiled using the `-shared` option:\n```\n > gcc -fPIC -shared -o libmy_great_library.so lib.c\n```\n\nFor Linux and xBSD there is a convention for naming libraries: `libNAME.so`, for Mac - `libNAME.dynlib`, for Windows - `NAME.dll`.\n\nLinking a program to a library implies options:\n * `-lNAME` - specifies the library name without the prefix `lib` and the suffix `.so`;\n * `-LPATH` - specifies the directory name to search for used libraries.\n\n\n## Runtime Search Path\n\nWhen an ELF file is loaded, all the necessary libraries (on which it explicitly depends) are loaded. You can view the list of dependencies using the `ldd` command.\n\nLibraries are located in one of the default directories: `/lib[64]`, `/usr/lib[64]` or `/usr/local/lib[64]`. Additional directories to search for libraries are defined in the environment variable `LD_LIBRARY_PATH`.\n\nIt is possible to explicitly define in the ELF file where to look for the necessary libraries. To do this, use the linker option `ld -rpath PATH`.\n\nTo pass options to `ld`, which is called from `gcc`, you can use the `-Wl, OPTION`.\n\nIn `rpath` you can specify both absolute paths and the variable `$ORIGIN`, which is equal (when the program is loaded) to the directory containing the program itself. This allows you to create a delivery from program and libraries that are not scattered throughout the file system:\n\n```\n > gcc -o program -L. -lmygreat_library program.c \\\n       -Wl,-rpath -Wl,'$ORIGIN/'.       \n```\n\nThis will create an executable file `program` that uses the `libmy_great_library.so`, implying that the library file is in the same directory as the program itself.\n\n\n## Loading libraries at runtime\n\nLibraries do not have to be tied tightly to the program, and can be downloaded as needed. This uses the `dl` feature set, which was included in the 2001 POSIX standard.\n\n * `void *dlopen(const char *filename, int flags)` - loads the library file;\n * `void *dlsym(void *handle, const char *symbol)` - searches the library for the required symbol, and returns its address;\n * `int dlclose(void *handle)` - closes the library, and unloads it from memory if it is no longer used in the program;\n * `char *dlerror()` - returns the error text associated with `dl`.\n\nIf `dlopen` or `dlsym` are unable to open a file or find a character, a null pointer is returned.\n\nExample of usage is in files: [lib.c](lib.c) and [dynload.c](dynload.c).\n\n\n## Position-independent executable file\n\nThe `-fPIE` option of the compiler indicates that it is necessary to generate position-independent code for `main` and `_start`, and the `-pie` option indicates that you need to specify in the ELF file when linking that it is position-independent.\n\nA position-independent executable file in modern systems is placed at a random address.\n\nIf a position-independent executable also contains a table of exported symbols, it is also a library. If there is no `-shared` option, then the compiler assembles the program by removing the character table from it. The `-Wl,-E` option explicitly saves the character table.\n\nExample:\n```\n  # the abc file.c contains int main() { puts(\"abc\"); }\n  > gcc -o program -fPIE -pie -Wl,-E abc.c\n\n  # the program can be run as a regular program\n  > ./program\n  abc\n\n  # and can be used as a library\n  > python3\n  >>> from ctypes import cdll, c_int\n  >>> lib = cdll.LoadLibrary(\"./program\")\n  >>> main = lib[\"main\"]\n  >>> main.restype = c_int\n  >>> ret = main()\n  abc  \n\n ```\n"
  },
  {
    "path": "harbour/mmap/README.md",
    "content": "# Memory pages in virtual address space\n\n## Tools\n\nIn addition to the `gdb` step-by-step debugger, there are additional tools for detecting problems when working with memory.\n\n### Interpreted execution with `valgrind`\n\nThe `valgrind` toolset uses controlled execution of program instructions, modifying its code before executing on the physical processor.\n\nMain instruments:\n * `memcheck` - diagnostics of memory problems: incorrect heap pointers, duplicated memory freeing, reading uninitialized data and forgotten memory freeing.\n * `callgrind` - diagnostics of running program performance.\n\nTo run a program with valgrind, you should build a program with debug information (compile option `-g`), otherwise the output of valgrind will not be informative.\n\nLaunching:\n```\n> valgrind --tool=INSTRUMENT program.jpg ARG1 ARG2 ... ARGn\n```\n\nIf you use the `callgrind` tool, a `callgrind.out` file is generated after the program is executed. This file has XML format, which can be visualized using KCacheGrind (in KDE for all modern Linux distributions), or its cross-platform equivalent [QCacheGrind](https://sourceforge.net/projects/qcachegrindwin/).\n\n### Runtime error checking with sanitizers\n\nIt requires new versions of `clang` or `gcc` and allows you to perform instrumental control during program execution much faster than `valgrind`.\n\nIt is implemented using code generation and some functions replacement, for example replacing `malloc`/`free` with the implementation with additional checks.\n\nThe main sanitizers:\n * AddressSanitizer (`-fsanitize=address`) - diagnoses situations of memory leaks, memory double freeing, stack or heap overflow, and stack pointers being used after the function terminates.\n * MemorySanitizer (`-fsanitize=memory`) - diagnostics of uninitialized data reading situations. Requires the program and all libraries the program is using to be compiled into position-independent code.\n * Undefined Behavior Sanitizer (`-fsanitize=undefined`) - diagnostics of undefined behavior in integer arithmetic: bit shifts, signed overflow, etc.\n\n## mmap system call\n\n```\n#include <sys/mman.h>\n\nvoid *mmap(\n    void *addr,    /* recommended mapping address */\n    size_t length, /* length of the mapping */\n    int prot,      /* desired memory protection flags */\n    int flags,     /* flags for shared mapping */\n    int fd,        /* file descriptor */\n    off_t offset   /* offset relative to the beginning of the file */\n  );\n\nint munmap(void *addr, size_t length) /* unmap existing mapping */\n```\n\nThe `mmap` system call is intended to create an accessible area with a specific address in the virtual address space of a process. This area can be either associated with a specific file (previously opened) or located in RAM. The second case of usage is usually implemented in the `malloc`/`calloc` functions.\n\nMemory can only be allocated page by page. For most architectures, the size of a single page is 4Kb, although x86_64 processors support larger pages: 2Mb and 1Gb.\n\nIn general, you should never rely on a page size of 4096 bytes. It can be found using the `getconf` command or the `sysconf` function:\n```\n# Bash\n> getconf PAGE_SIZE\n4096\n\n/* C */\n#include <unistd.h>\nlong page_size = sysconf(_SC_PAGE_SIZE);\n```\n\nThe `offset` parameter (if a file is used) must be a multiple of the page size; the `length` parameter is not, but the kernel rounds this value to the larger page size. The `addr` parameter (recommended address) can be `NULL`, – in that case the kernel itself assigns an address in the virtual address space.\n\nWhen using file mapping, the `length` parameter is set to the length of the mapped data; if the file size is smaller than the page size, or the last small part of the file is being mapped, the rest of the page is filled with zeros.\n\nA memory page can have access attributes:\n * reading `PROT_READ`;\n * writing `PROT_WRITE`;\n * execution `PROT_EXE`;\n * nothing `PROT_NONE`.\n\nIn the case of mapping to a file, it must be opened for reading or writing according to the required access attributes.\n\n`mmap`flags:\n * `MAP_FIXED` - requires memory to be allocated to the address specified in the first argument; without this flag, the kernel can select the address closest to the address specified.\n * `MAP_ANONYMOUS` - to allocate pages in RAM, not to link to file.\n * `MAP_SHARED` - select pages shared with other processes; in case of file mapping - synchronize changes so that they are available to other processes.\n * `MAP_PRIVATE` - as opposed to`MAP_SHARED`, do not make mapping available to other processes. In the case of mapping to a file, it is readable, and the changes created by the process are not saved to the file.\n"
  },
  {
    "path": "harbour/openssl/README.md",
    "content": "# Encryption using OpenSSL / LibreSSL\n\n## Linux Encryption Basics\n\nCryptography in Linux, as in many other UNIX-like systems, is implemented using the `openssl` package or the fork of` libressl` compatible with it.\n\nThe package provides:\n * command `openssl` to perform operations on the command line\n * library `libcrypto` with the implementation of encryption algorithms\n * library `libssl` with the implementation of interaction via SSL and TLS.\n\n### Calculating Hash Values\n\nCommands:\n * `openssl md5`\n * `openssl sha256`\n * `openssl sha512`\n\ncalculate the hash value for the specified file and output it in a readable form to the standard output stream. The optional option `-binary` indicates the output in binary format. If no filename is specified, a hash value is calculated for the data from the standard input stream.\n\n### Symmetric Encryption\n\nCommand:\n\n```\nopenssl enc -CHIPHER -in FILENAME -out EXIT\n```\n\nPerforms encryption *with a symmetric key*, which means with some “password”, that is the same for both encryption and the reverse decryption operation.\n\nA complete list of supported ciphers is displayed with the `openssl enc -ciphers` command. Most commonly used:\n * `des` is a rather old algorithm using a 56-bit key;\n * `aes256` or` aes-256-cbc` - more reliable and fast enough;\n * `base64` - no encryption (key is not required); A convenient way to convert binary files to text representation and vice versa.\n\nThe `-d` option means inverse conversion, i.e. *decryption*. The `-base64` option implies that the encrypted data is additionally converted to Base64 encoding, for example, to transfer data in the form of text.\n\nAfter running the command, a password and its confirmation will be requested. In the case when you need to automate the launch of the command, the `-pass` option is used, after which it is transmitted how the password is set:\n * `pass: PASSWORD` - the password is set in plain text as an argument to the command line; terribly unsafe;\n * `env: VARIABLE` - the password is set by a specific environment variable; a little better, but can be figured out through `/ proc /.../ environ`;\n * `file: NAME` - the password is taken from the file;\n * `fd: NUMBER` - the password is taken from the file descriptor with the specified number; used at startup via `fork` +` exec`.\n\nSince symmetric encryption algorithms imply the use of a fixed-size key, a text password of arbitrary length is pre-converted using a hash function. By default, SHA-256 is used, but this can be set using the option `-md ALGORITHM`.\n\nIn addition to the password, the key also includes another component - *salt* of 8 bytes in size, which is stored in the encrypted file itself. This value is randomly generated, but for reproducibility, the result can be explicitly set using the `-S HEX` option, where` HEX` is an eight-byte value in hexadecimal notation.\n\n\n### Encryption using a key pair\n\nThe standard algorithm for encryption using a key pair is RSA.\n\nKey generation is performed by the command:\n```\nopenssl genrsa -out FILE BIT\n```\n\nIf the name of the output file is not specified, then the key in text format will be saved to the standard output stream. Typically, RSA keys are stored in files with a suffix of the name `.pem`.\n\nBit depth determines the strength of the key, by default - 2048 bits.\n\nSince the private key must be stored somewhere, and in a safe way, it is considered good practice to store it in encrypted form, encryption with a symmetric key is used for this:\n```\nopenssl genrsa -aes256 -passout PASSWORD OPTIONS\n```\n\nWhen using an encrypted private key, it will be necessary to indicate the password specified during its creation each time.\n\nThe extraction of the public key from the private is carried out by the command:\n```\nopenssl rsa -in PRIVATE_KEY -out PUBLIC_KEY -pubout\n```\n\nIf encryption was used when creating the key pair, you must enter a password or set it using `-passin`.\n\nPublic Key Encryption:\n```\nopenssl rsautl -encrypt -pubin -inkey PUBLIC_KEY -in FILE -out EXIT\n```\n\nThe reverse operation using the private key:\n```\nopenssl rsautl -decrypt -inkey PRIVATE_KEY -in FILE -out EXIT\n```\n\nA limitation of the RSA algorithm is that the size of the encrypted data cannot exceed the size of the key. You can deal with this in the following ways:\n 1. Divide the source data into blocks of 2 or 4 KB in size and encrypt them individually\n 2. Randomly generate a one-time *session key*, which will be used in conjunction with a symmetric encryption algorithm, but will itself be encrypted using RSA.\n\n```\n# Generate a random key 30 bytes long and save\n# its Base64 textual representation in the $ KEY variable\nKEY = `openssl rand -base64 30`\n\n\n# We encrypt the symmetric key using the RSA public key\necho $ KEY | openssl rsautl -encrypt -pubin \\\n                           -inkey public.pem \\\n                           -out symm_key_encrypted\n```\n\n\n## Library\n\nHere is an example of library usage:\n```c\n#include <openssl/sha.h>\n\nint main(int argc, char *argv[])\n{\n    SHA512_CTX context;\n    SHA512_Init(&context);\n    // hash data\n    SHA512_Final(hash, &context);\n}\n```"
  },
  {
    "path": "harbour/pipes/README.md",
    "content": "# Duplicating file descriptors. Pipes.\n\n## Duplicating file descriptors\n\nThe `fcntl` system call allows you to configure various manipulations on open file descriptors. One of the manipulation commands is `F_DUPFD` - creating a *copy* of the descriptor in the current process, but with a different number.\n\nA copy implies that two different file descriptors are associated with the same open file in the process, and share the following attributes:\n * the file object itself;\n * locks associated with the file;\n * the current position of the file;\n * open mode (read /write/add).\n\nAt the same time, the `CLOEXEC` flag is not saved. I means the automatic closing of the file when the `exec` system call is executed.\n\nPOSIX system calls `dup` and `dup2` provide simplified semantics for creating a copy of file descriptors:\n```\n#include <unistd.h>\n\n/* Returns a copy of the new file descriptor, and, similar to `open`, the numeric value of the new file descriptor is the minimum unoccupied number. */\nint dup(int old_fd);\n\n/* Creates a copy of a new file descriptor with an explicit new_fd number. If the file descriptor new_fd was previously opened, it closes it.*/\nint dup2(int old_fd, int new_fd);\n```\n\n## Unnamed pipes\n\n\nA pipe is a pair of related file descriptors, one of which is read-only and the other is write-only.\n\nThe channel is created using the `pipe` system call:\n```\n#include <unistd.h>\n\nint pipe(int pipefd[2]);\n```\n\nAs an argument, the system call `pipe` gets a pointer to an array of two integers, where the file descriptor numbers will be written:\n * `pipefd[0]` - read-only file descriptor;\n * `pipefd[1]` is a file descriptor intended for writing.\n\n### Writing data to pipe\n\nIt is done using the system call `write`. It's first argument is `pipefd[1]`. The channel is buffered. In Linux its size is usually 65K. Possible scenarios of writing behavior:\n\n * the `write` system call terminates immediately if the data size is smaller than the buffer size and there is enough space in the buffer;\n * the `write` system call pauses execution until there will be enough space in the buffer (until the previous data will not be read by someone from the channel);\n * the `write` system call fails with a `Broken pipe` error (delivered via the `SIGPIPE` signal) if the channel on the opposite side has been closed and there is no one to read the data.\n\n### Reading data from a pipe\n\nIt is done using the system call `read`. It's first argument is `pipefd[0]`. Possible reading behavior scenarios:\n\n* if there is data in the pipe's buffer, the system call reads it and exits;\n * if the buffer is empty and there is **at least one** open file descriptor on the opposite side, the execution of `read` is blocked;\n * if the buffer is empty and all file descriptors on the opposite side of the pipe are closed system call `read` immediately shuts down, returning `0`.\n\n\n### dead lock problem\n\nWhen executing `fork`, `dup`, or `dup2` system calls, copies of the file descriptors, associated with the pipe, are created. If you do not close all unnecessary (unused) copies of file descriptors intended for writing, it leads to the fact that when you try to read from the pipe, `read` will be waiting for data instead of shutting down.\n\n```\nint fds_pair[2];\npipe(fds_pair);\n\nif ( 0!=fork() )  // now we have an implicit copy of the file descriptors\n{\n    // write some data to the buffer\n    static const char Hello[] = \"Hello!\";\n    write(fds_pair[1], Hello, sizeof(Hello));\n    close(fds_pair[1]);\n\n    // and now read it back\n    char buffer[1024];\n    read(fds_pair[0], buffer, sizeof(buffer)); // get deadlock!\n}\nelse while (1) shched_yield();\n```\n\nTo avoid this problem, you should check carefully when copies of file descriptors are created and close them when they are not needed.\n"
  },
  {
    "path": "harbour/signals/README.md",
    "content": "# Signals. Part 1\n\n## Introduction\nA signal is a short message transmission mechanism (signal number), typically interrupting the process to which it was sent.\n\nSignals can be sent to the process:\n * by the kernel, usually in case of a critical execution error;\n * by other process;\n * to itself.\n\nSignal numbers start with 1. The value 0 has a special purpose (see below about `kill`). Some signal numbers correspond to POSIX-standard names and destinations, which are described in detail by `man 7 signal`.\n\nWhen a signal is received, the process can:\n 1. Ignore it. It is possible for all signals except `SIGSTOP` and `SIGKILL`.\n 2. Process with a separate function. Except `SIGSTOP` and `SIGKILL`.\n 3. Perform the default action specified by the POSIX standard signal assignment. Typically, this is a process shutdown.\n\nBy default, all signals except `SIGCHILD` (informing about the termination of the child process) and `SIGURG` (informing about the receiving of the TCP segment with priority data), lead to the termination of the process.\n\nIf a process was terminated with a signal rather than using the `exit` system call, it is considered to have an undefined return code. The parent process can monitor this situation using the `WIFSIGNALED` and `WTERMSIG`macros:\n\n```\npid_t child = ...\n...\nint status;\nwaitpid(child, &status, 0);\nif (WIFEXITED(status)) {\n    // the child process was terminated via `exit`\n    int code = WEXITSTATUS(status); // return code\n}\nif (WIFSIGNALED(status)) {\n    // the child process was terminated by a signal\n    int signum = WTERMSIG(status); // signal number\n}\n```\n\nYou can send a signal to any process using the `kill` command. By default, the `SIGTERM` signal is sent, but you can specify which signal to send as an option. In addition, some signals are sent by the terminal, for example Ctrl+C sends a `SIGINT` signal and Ctrl+\\ sends a `SIGQUIT ` signal.\n\n\n## User-defined signals\n\nInitially, POSIX reserved two signal numbers that could be used at the discretion of the user: `SIGUSR1` and `SIGUSR2`.\n\nIn addition, Linux provides a range of signals with numbers from `SIGRTMIN` to `SIGRTMAX`, which can be used at the discretion of the user.\n\nThe default action for all \"user-defined signals\" signals is to shut down the process.\n\n\n## Sending signals programmatically\n\n### System call `kill`\n\nSimilar to the command of the same name, `kill` is intended to send a signal to any process.\n\n```\nint kill(pid_t pid, int signum); // returns 0 or -1 if error\n```\n\nYou can only send signals to processes that belong to the same user as the user on which the 'kill' system call is executed. The exception is the `root` user, who can do everything. If you try to send a signal to another user's process  `kill` will return `-1`.\n\nThe process number may be less than `1` in cases:\n * `0` - send a signal to all processes of the current process group;\n * `-1` - send a signal to all user processes (use with caution!);\n * negative value `-PID` - send signal to all processes of `PID ' group.\n\n \nThe signal number can be set to `0`, in which case no signal will be sent, and `kill` will return `0` if the process (group) with the specified `pid` exists and there are rights to send signals.\n\n### `Raise` and `abort` functions\n\nThe `raise` function is designed to send a process signal to itself. The standard library function `abort` sends itself a `SIGABRT` signal, and is often used to generate exceptions that can be diagnosed at runtime, such as by the `assert` function.\n\n### System call `alarm`\n\nThe system call `alarm` starts a timer, after which the process will send itself a signal `SIGALRM`.\n\n```\nunsigned int alarm(unsigned int seconds);\n```\n\nYou can cancel the previously set timer by calling `alarm` with the parameter `0`. The return value is the number of seconds of the previous timer set.\n\n## Обработка сигналов\n\nСигналы, которые можно перехватить, то есть все, кроме `SIGSTOP` и `SIGKILL`, можно обработать программным способом. Для этого необходимо зарегистрировать функцию-обработчик сигнала.\n\n### Системный вызов `signal`\n```\n#include <signal.h>\n\n// Этот тип определен только в Linux!\ntypedef void (*sighandler_t)(int);\n\nsighandler_t signal(int signum, sighandler_t handler); // для Linux\nvoid (*signal(int signum, void (*func)(int))) (int); // по стандарту POSIX\n```\n\nСистемный вызов `signal` предназначен для того, чтобы зарегистрировать функцию в качестве обработчика определенного сигнала. Первым аргументом является номер сигнала, вторым - указатель на функцию, которая принимает единственный аргумент - номер пришедшего сигнала (т.е. одну функцию можно использовать сразу для нескольких сигналов), и ничего не возвращает.\n\nДва специальных значения функции-обработчика `SIG_DFL` и `SIG_IGN` предназанчены для указания обработчика по умолчанию (т.е. отмены ранее зарегистрированного обработчика) и установки игнорирования сигнала.\n\nСистемный вызов `signal` возвращает указатель на ранее установленный обработчик.\n\n### System-V v.s. BSD\n\nВ стандартах, родоначальниками которых были UNIX System-V и BSD UNIX, используется различное поведение обработчика сигнала, зарегистрированного с помощью `signal`. При определении одного из макросов препроцессора: `_BSD_SOURCE`, `_GNU_SOURCE` или `_DEFAULT_SOURCE` (что подразумевается опцией компиляции `-std=gnu99` или `-std=gnu11`), используется семантика BSD; в противном случае (`-std=c99` или `-std=c11`) - семантика System-V.\n\nОтличия BSD от System-V:\n * В System-V обработчик сигнала выполяется один раз, после чего сбрасывается на обработчик по умолчанию, а в BSD - остается неизменным.\n * В BSD обработчик сигнала не будет вызван, если в это время уже выполняется обработчик того же самого сигнала, а в System-V это возможно.\n * В System-V блокирующие системные вызовы (например, `read`) завершают свою работу при поступлении сигнала, а в BSD большинство блокирующих системных вызовов возобновляют свою работу после того, как обработчик сигнала заверщает свою работу.\n\nПо этой причине, системный вызов `signal` считается устаревшим, и в новом коде использовать его запрещено, за исключением двух ситуаций:\n\n```\nsignal(signum, SIG_DFL); // сброс на обработчик по умолчанию\nsignal(signum, SIG_IGN); // игнорирование сигнала\n```\n\n### Системный вызов `sigaction`\n\nСистемный вызов `sigaction`, в отличии от `signal`, в качестве второго аргумента принимает не указатель на функцию, а указатель на структуру `struct sigaction`, с которой, помимо указателя на функцию, хранится дополнительная информация, описывающая семантику обработки сигнала. Поведение обработчиков, зарегистрированных с помощью `sigaction`, не зависит от операционной системы.\n\n```\nint sigaction(int signum,\n              const struct sigaction *restrict act,\n              struct sigaction *oldact);\n```\n\nТретьим аргументов является указатель на структуру, описывающую обработчик, который был зарегистрирован для этого. Если эта информация не нужна, то можно передать значение `NULL`.\n\nОсновные поля структуры `struct sigaction`:\n * `sa_handler` - указатель на функцию-обработчик с одним аргументом типа `int`, могут быть использованы значения `SIG_DFL` и `SIG_IGN`;\n * `sa_flags` - набор флагов, опиывающих поведение обработчика;\n * `sa_sigaction` - указатель на функцию-обработчик с тремя параметрами, а не одним (используется, если в флагах присутствует `SA_SIGINFO`).\n\nНекоторые флаги, которые можно передавать в `sa_flags`:\n * `SA_RESTART` - продолжать выполнение прерванных системных вызовов (семантика BSD) после завершения обработки сигнала. По умолчанию (если флаг отсутствует) используется семантика System-V.\n * `SA_SIGINFO` - вместо функции из `sa_handler` нужно использовать функцию с тремя параметрами `int signum, siginfo_t *info, void *context`, которой помимо номера сигнала, передается дополнительная информация (например PID отправителя) и пользовательский контекст.\n * `SA_RESETHAND` - после выполнения обработчика сбросить на обработчик по умолчанию (семантика System-V). По умолчанию (если флаг отсутствует) используется семантика BSD.\n * `SA_NODEFER` - при повторном приходе сигнала во время выполени обработчика он будет обработан немедленно (семантика System-V). По умолчанию (если флаг отсутствует) используется семантика BSD.\n\n## Асинхронность обработки сигналов\n\nСигнал может прийти процессу в любой момент времени. При этом, выполнение текущего кода будет прервано, и будет запущен обработчик сигнала.\n\nТаким образом, возникает проблема \"гонки данных\", которая часто встречается в многопоточном программировании.\n\nСуществует безопасный целочисленный (32-разрядный) тип данных, для которого гарантируется атомарность чтения/записи при переключении между выполнением основной программы и выполнением обработчика сигнала: `sig_atomic_t`, объявленный в `<signal.h>`.\n\nКроме того, во время выполнения обработчика сигналов запрещено использовать не потоко-безопасные функции (большинство функций стандартной библиотеки). В то же время, использование системных вызовов - безопасно.\n"
  },
  {
    "path": "harbour/sockets/README.md",
    "content": "# Sockets with connection setup\n\n## Socket\n\nA socket is a file descriptor that is open for both reading and writing. It is used for interaction between:\n * different processes running on the same computer (*host*);\n * different processes running on different *hosts*.\n\nTo create a socket use the `socket` system call:\n\n```\n#include <sys/types.h>\n#include <sys/socket.h>\n\nint socket(\n  int domain,    // domain type\n  int type,      // the type of interaction via the socket\n  int protocol   // protocol number or 0 for auto-selection\n)\n```\n\nThe socket mechanism appeared in the 80's of the XX century, when there was no single standard for network communication, and sockets were an abstraction over any network communication mechanism, supporting a huge number of different protocols.\n\nIn modern systems there are only a few mechanisms defining the socket namespace, that can be considered to be used; others are legacy, that we will not discuss further.\n\n  * `AF_UNIX` (`man 7 unix`) - a namespace of local UNIX sockets that allow different processes to communicate within the same computer, using as the address a unique name (no longer than 107 bytes) of a special file.\n * `AF_INET` (`man 7 ip`) - tuple space consisting of 32-bit IPv4 addresses and 16-bit port numbers. The IP address identifies the host for communication on which the process is running.\n * `AF_INET6` (`man 7 ipv6`) - similar to `AF_INET`, but uses 128-bit IPv6 host addressing; this standard is not yet supported by all hosts and Internet service providers.\n * `AF_PACKET` (`man 7 packet`) - low level interaction.\n\nSockets usually communicate in one of two ways (specified as the second parameter `type`):\n * `SOCK_STREAM` - interacts with system calls `read` and `write` as with a regular file descriptor. In the case of network communication, this implies the use of the `TCP` protocol.\n * `SOCK_DGRAM` - communication without pre-setting interaction to send short messages. In case of communication over the network, this implies the usage of `UDP` protocol.\n\n## Socket pair\n\nSometimes sockets are convenient to use as a mechanism of communication between different threads or related processes: unlike pipes, they are two-way, and in addition, support the processing of the event \"close connection\". A socket pair is created using the `socketpair` system call:\n\n```\nint socketpair(\n  int domain,    // In Linux: AF_UNIX only is supported\n  int type,      // SOCK_STREAM or SOCK_DGRAM\n  int protocol,  // Only value 0 in Linux\n  int sv[2]      // An array of two int's (similar to pipe)\n)\n```\n\nUnlike unnamed pipes, which are created by the `pipe` system call, it does not matter for a pair of sockets which element of the `sv` array is used for reading and which element is used for writing - they are equal.\n\n## Using sockets as a client\n\nSockets can participate in the communication in one of two roles. A process can be a *server*, that means it declares some address (file name, or tuple of IP address and port number) to receive incoming connections, or it can act as a *client*, that means it connects to some server.\n\nOnce the socket is created, it is not ready yet to interact with the `read` and `write` system calls. The interaction with the server is established using the `connect` system call. After successful execution of this system call, interaction becomes possible before the system call `shutdown` won't be executed.\n\n```\nint connect(\n  int sockfd,                  // the socket file descriptor\n\n  const struct sockaddr *addr, // a pointer to an *abstract* structure\n                               // that describes\n                               // the connection address\n\n  socklen_t addrlen            // the size of the real structure\n                               // that is passed as\n                               // the second parameter\n)\n```\n\nSince the C language is not object-oriented, it is necessary to pass as an address:\n 1. A structure whose first field contains an integer with a value that matches the `domain` of the corresponding socket\n 2. The size of this structure.\n\nParticular structures that are \"inherited\" from the abstract `sockaddr` structure can be:\n\n1. For UNIX address space - structure `sockaddr_un`\n\n```\n#include <sys/socket.h>\n#include <sys/un.h>\n\nstruct sockaddr_un {\n  sa_family_t   sun_family;    // you need to write AF_UNIX\n  char          sun_path[108]; // the path to the socket file\n};\n```\n\n2. For addressing in IPv4 - structure `sockaddr_in`:\n\n```\n#include <sys/socket.h>\n#include <netinet/in.h>\n\nstruct sockaddr_in {\n  sa_family_t    sin_family; // you need to write AF_INET\n  in_port_t      sin_port;   // uint16_t port number\n  struct in_addr sin_addr;   // a structure of a single field:\n                             // - in_addr_t s_addr;\n                             //   where in_addr_t - is uint32_t\n};\n```\n\n3. For addressing in IPv6 - structure `sockaddr_in6`:\n\n```\n#include <sys/socket.h>\n#include <netinet/in.h>\n\nstruct sockaddr_in6 {\n  sa_family_t    sin6_family; // you need to write AF_INET6\n  in_port_t      sin6_port;   // uint16_t port number\n  uint32_t       sin6_flowinfo; // additional IPv6 field\n  struct in6_addr sin6_addr;  // a structure of a single field\n                  // declared as union {\n                              //     uint8_t  [16];\n                              //     uint16_t [8];\n                              //     uint32_t [4];\n                              // };\n                              // i.e. the size of in6_addr is 128 bits\n  uint32_t       sin6_scope_id; // additional IPv6 field\n};\n```\n\n\n## IPv4 addresses\n\nThe host address in IPv4 network is a 32-bit unsigned integer in *network byte order*, that is, Big-Endian. The same is for the port numbers.\n\nThe byte order conversion from network to system and vice versa is performed using one of the functions declared in `<arpa/inet.h>`:\n * `uint32_t htonl(uint32_t hostlong)` - 32-bit from system to network byte order;\n * `uint32_t ntohl(uint32_t netlong)` - 32-bit from network to system byte order;\n * `uint16_t htons(uint16_t hostshort)` - 16-bit from system to network byte order;\n * `uint16_t ntohs(uint16_t netshort)` - 16-bit from network to system byte order.\n\nIPv4 addresses are usually written in decimal notation, separating each byte with a dot, for example: `192.168.1.1`. Such kind of notation can be converted from text to a 32-bit address using the `inet_aton` or `inet_addr` function.\n\n\n## Closing the network connection\n\nThe `close` system call is intended to close the *file descriptor*, and it must be called to release an entry in the file descriptor table. This is a necessary but not a sufficient requirement when working with TCP sockets.\n\nBesides closing the file descriptor, it is good to notify the opposite sides that the network connection is closing.\n\nThis notification is done via the `shutdown` system call.\n\n\n## Using sockets as a server\n\nTo use a socket as a server, you should do the following:\n\n 1. Associate a socket with some address. To do this, use the `bind` system call, whose parameters are exactly the same as for the `connect` system call.  If the computer has more than one IP address, the address `0.0.0.0` means `all addresses`. Often when debugging and there is such a problem that the port with a certain number was already busy on the previous run of the program (and, for example, was not correctly closed). It is solved by force reuse of the address:\n\n```\n// In the release build you shouldn't do like this!\n#ifdef DEBUG\nint val = 1;\nsetsockopt(lfd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val));\nsetsockopt(lfd, SOL_SOCKET, SO_REUSEPORT, &val, sizeof(val));\n#endif\n```\n\n 2. Create a queue that will contain incoming but not yet received connections. This is done using the `listen` system call, which takes as a parameter the maximum number of pending connections. For Linux, this value is 128, defined in the constant `SOMAXCONN`.\n\n 3. Accept one connection at a time using the `accept` system call. The second and third parameters of this system call can be `NULL` if we are not interested in the address of the one who connected to us. The `accept` system call blocks execution until an incoming connection appears. After that it returns the file descriptor of the new socket, which is associated with a specific client that is connected to us.\n \n \n## Linux tools for debugging networking\n\n### Network I/O\n\nThe `nc` command (short for` netcat`) works similarly to the `cat` command, but in\nthe argument is not a file name for outputting a data stream, but a pair\n`host port`. The `-u` option means sending a UDP packet.\n\nIf you intend to use only IPv4 addressing, and not IPv6, then\noption `-4` is used.\n\n```\n# Example: transfer data from in.dat to UDP socket on localhost\n# port 3000 and write the output to the out.dat file\n> cat in.dat | nc -4 -u localhost 3000> out.dat\n```\n\n### God mode\n\nThe `wireshark` utility allows you to view absolutely all packets from the` Ethernet` level that pass through the system. This requires `root` privileges, or the` Linux Capabilities` setting for the `/ usr / bin / dumpcap` command, which is part of` wireshark`:\n```\nsudo setcap cap_net_raw,cap_net_admin+eip /usr/bin/dumpcap\n```\n\nSince many network packets pass through the system, you must configure a filter to search only packets of interest.\n\n### Python\n\nThe Python standard library contains socket tools that exactly match their POSIX counterparts.\n\nAn example of sending a UDP message:\n```\nfrom socket import socket, AF_INET, SOCK_DGRAM\n\nIP = \"127.0.0.1\"\nPORT = 3000\n\nsock = socket(AF_INET, SOCK_DGRAM)   # creating UDP-socket\n# Connection is not required\nsock.sendto(\"Hello!\\n\", (IP, PORT))  # sending message\n```\n\nRecieving UDP-messages:\n```\nfrom socket import socket, AF_INET, SOCK_DGRAM\n\nIP = \"127.0.0.1\"\nPORT = 3000\nMAX_SIZE = 1024\n\nsock = socket(AF_INET, SOCK_DGRAM)       # creating UDP-socket\nsock.bind((IP, PORT))                    # declaring port \n\nwhile True:\n    data, addr = sock.recvfrom(MAX_SIZE) # recieving message\n    print(\"Got {} from {}\", data, addr)\n```\n"
  },
  {
    "path": "harbour/time/README.md",
    "content": "## Working with time\n\n### Current time\n\nTime in UNIX systems is defined as the number of seconds that have elapsed since January 1, 1970, and the time is considered as Greenwich mean time (GMT) without daylight saving time (DST).\n\n32-bit systems should cease to exist normally on January 19, 2038, as there will be an overflow of the signed integer type to store the number of seconds.\n\nThe `time` function returns the number of seconds since the beginning of the epoch. The argument of the function (which can be passed `NULL`) is a pointer to the variable where you want to write the result.\n\nIn case when it is required an accuracy higher than 1 second, you can use the `gettimeofday` system call, which allows you to get the current time as a structure:\n```\nstruct timeval {\n  time_t      tv_sec;  // seconds\n  suseconds_t tv_usec; // microseconds\n};\n```\n\nIn this case, despite the fact that the structure defines a field for microseconds, the real accuracy will be about 10-20 milliseconds for Linux.\n\nHigher accuracy can be reached with the `clock_gettime` system call."
  },
  {
    "path": "lectures/fall-2018/Lection07-supplementary-01.c",
    "content": "#include <stdio.h>\n#include <sys/types.h>\n#include <sys/stat.h>\n#include <fcntl.h>\n#include <unistd.h>\n\n\nint\nmain()\n{\n    close(2);\n    close(1);\n    /* 1 = */ open(\"out.txt\", O_WRONLY | O_CREAT, 0640);\n    /* 2 = */ open(\"err.txt\", O_WRONLY | O_CREAT, 0640);\n    printf(\"Hello!\");\n    fprintf(stderr, \"World\");\n    static const char buffer3[] = \"Number three\";\n    static const char buffer4[] = \"Number four\";\n    write(3, buffer3, sizeof(buffer3)-1);\n    lseek(4, 100, SEEK_END);\n    write(4, buffer4, sizeof(buffer4)-1);\n}\n"
  },
  {
    "path": "lectures/fall-2019/Supplementary-06/lib_and_exec_demo/Makefile",
    "content": "first:\n\techo \"Pass: library, program or lib-and-program\"\n\nlib-and-program: file.c\n\tgcc -o lib-and-program \\\n\t\t-fPIC \\\n\t\t-pie \\\n\t\t-Wl,-E \\\n\t\tfile.c\n\nprogram: file.c\n\tgcc -o program file.c\n\nlibrary: file.c\n\tgcc -shared -fPIC -o library.so file.c\n\nclean:\n\trm -f library.so\n\trm -f program\n\trm -f lib-and-program\n"
  },
  {
    "path": "lectures/fall-2019/Supplementary-06/lib_and_exec_demo/file.c",
    "content": "#include <stdio.h>\n\nvoid\ncallable_function()\n{\n    puts(\"The function that might be called\");\n}\n\nint\nmain(int argc, char *argv[])\n{\n    printf(\"Argc = %d\\n\", argc);\n    return 100;\n}\n"
  },
  {
    "path": "lectures/fall-2019/Supplementary-06/lib_and_exec_demo/test.py",
    "content": "#!/usr/bin/python3\n\nimport sys\nfrom ctypes import cdll\n\nlib = cdll.LoadLibrary(\"./\"+sys.argv[1])\nfunc = lib[\"callable_function\"]\nmain = lib[\"main\"]\n\nprint(\"Calling function from lib...\")\nfunc();\n\nprint(\"Calling main(10, NULL)...\")\nret = main(10, 0)\nprint(\"Return value is {}\".format(ret))\n"
  },
  {
    "path": "lectures/fall-2019/Supplementary-06/rpath_demo/Makefile",
    "content": "first:\n\techo \"Pass: library, program-no-rpath or program-with-rpath\"\n\nprogram-no-rpath: library ./src/program.c\n\tmkdir bin || true\n\tgcc -o ./bin/program ./src/program.c \\\n\t\t\t-L./lib -lmygreatlib\n\nprogram-with-rpath: library ./src/program.c\n\tmkdir bin || true\n\tgcc -o ./bin/program ./src/program.c \\\n\t\t\t-L./lib -lmygreatlib \\\n\t\t\t-Wl,-rpath,'$$ORIGIN/../lib/'\n\nlibrary: ./src/mygreatlib.c ./src/mygreatlib.h\n\tmkdir lib || true\n\tgcc -shared -fPIC -o ./lib/libmygreatlib.so ./src/mygreatlib.c\n\nclean:\n\trm -f ./bin/program\n\trm -f ./lib/libmygreatlib.so\n"
  },
  {
    "path": "lectures/fall-2019/Supplementary-06/rpath_demo/src/mygreatlib.c",
    "content": "#include <stdio.h>\n#include \"mygreatlib.h\"\n\nvoid\nsay_hello(const char * to_whom)\n{\n    printf(\"Hello, %s\\n\", to_whom);\n}\n"
  },
  {
    "path": "lectures/fall-2019/Supplementary-06/rpath_demo/src/mygreatlib.h",
    "content": "#pragma once\n#ifndef MYGREATLIB_H\n#define MYGREATLIB_H\n\nvoid say_hello(const char * to_whom);\n    \n#endif /* MYGREATLIB_H */\n"
  },
  {
    "path": "lectures/fall-2019/Supplementary-06/rpath_demo/src/program.c",
    "content": "#include \"mygreatlib.h\"\n\nint main(int argc, char *argv[])\n{\n    say_hello(argv[1]);\n}\n"
  },
  {
    "path": "lectures/fall-2019/Supplementary-06/toyos/Makefile",
    "content": "AS:=as --32\nCC:=gcc -m32\n\nCFLAGS:=-ffreestanding -O2 -Wall -Wextra -nostdlib\nCPPFLAGS:=\nLIBS:=-lgcc\n\nOBJS:=\\\nboot.o \\\nkernel.o \\\n\nall: myos.bin\n\n.PHONEY: all clean iso run-qemu\n\nmyos.bin: $(OBJS) linker.ld\n\t$(CC) -T linker.ld -Wl,--build-id=none -o $@ $(CFLAGS) $(OBJS) $(LIBS)\n\n%.o: %.c\n\t$(CC) -c $< -o $@ -std=gnu99 $(CFLAGS) $(CPPFLAGS)\n\n%.o: %.s\n\t$(AS) $< -o $@\n\nclean:\n\trm -rf isodir\n\trm -f myos.bin myos.iso $(OBJS)\n\niso: myos.iso\n\nisodir isodir/boot isodir/boot/grub:\n\tmkdir -p $@\n\nisodir/boot/myos.bin: myos.bin isodir/boot\n\tcp $< $@\n\nisodir/boot/grub/grub.cfg: grub.cfg isodir/boot/grub\n\tcp $< $@\n\nmyos.iso: isodir/boot/myos.bin isodir/boot/grub/grub.cfg\n\tgrub2-mkrescue -o $@ isodir\n\nrun-qemu: myos.iso\n\tqemu-system-i386 -cdrom myos.iso\n"
  },
  {
    "path": "lectures/fall-2019/Supplementary-06/toyos/README.md",
    "content": "Example from\n[https://wiki.osdev.org/Bare_Bones](https://wiki.osdev.org/Bare_Bones)\n"
  },
  {
    "path": "lectures/fall-2019/Supplementary-06/toyos/boot.s",
    "content": "/* Declare constants for the multiboot header. */\n.set ALIGN,    1<<0             /* align loaded modules on page boundaries */\n.set MEMINFO,  1<<1             /* provide memory map */\n.set FLAGS,    ALIGN | MEMINFO  /* this is the Multiboot 'flag' field */\n.set MAGIC,    0x1BADB002       /* 'magic number' lets bootloader find the header */\n.set CHECKSUM, -(MAGIC + FLAGS) /* checksum of above, to prove we are multiboot */\n \n/* \nDeclare a multiboot header that marks the program as a kernel. These are magic\nvalues that are documented in the multiboot standard. The bootloader will\nsearch for this signature in the first 8 KiB of the kernel file, aligned at a\n32-bit boundary. The signature is in its own section so the header can be\nforced to be within the first 8 KiB of the kernel file.\n*/\n.section .multiboot\n.align 4\n.long MAGIC\n.long FLAGS\n.long CHECKSUM\n \n/*\nThe multiboot standard does not define the value of the stack pointer register\n(esp) and it is up to the kernel to provide a stack. This allocates room for a\nsmall stack by creating a symbol at the bottom of it, then allocating 16384\nbytes for it, and finally creating a symbol at the top. The stack grows\ndownwards on x86. The stack is in its own section so it can be marked nobits,\nwhich means the kernel file is smaller because it does not contain an\nuninitialized stack. The stack on x86 must be 16-byte aligned according to the\nSystem V ABI standard and de-facto extensions. The compiler will assume the\nstack is properly aligned and failure to align the stack will result in\nundefined behavior.\n*/\n.section .bss\n.align 16\nstack_bottom:\n.skip 16384 # 16 KiB\nstack_top:\n \n/*\nThe linker script specifies _start as the entry point to the kernel and the\nbootloader will jump to this position once the kernel has been loaded. It\ndoesn't make sense to return from this function as the bootloader is gone.\n*/\n.section .text\n.global _start\n.type _start, @function\n_start:\n\t/*\n\tThe bootloader has loaded us into 32-bit protected mode on a x86\n\tmachine. Interrupts are disabled. Paging is disabled. The processor\n\tstate is as defined in the multiboot standard. The kernel has full\n\tcontrol of the CPU. The kernel can only make use of hardware features\n\tand any code it provides as part of itself. There's no printf\n\tfunction, unless the kernel provides its own <stdio.h> header and a\n\tprintf implementation. There are no security restrictions, no\n\tsafeguards, no debugging mechanisms, only what the kernel provides\n\titself. It has absolute and complete power over the\n\tmachine.\n\t*/\n \n\t/*\n\tTo set up a stack, we set the esp register to point to the top of the\n\tstack (as it grows downwards on x86 systems). This is necessarily done\n\tin assembly as languages such as C cannot function without a stack.\n\t*/\n\tmov $stack_top, %esp\n \n\t/*\n\tThis is a good place to initialize crucial processor state before the\n\thigh-level kernel is entered. It's best to minimize the early\n\tenvironment where crucial features are offline. Note that the\n\tprocessor is not fully initialized yet: Features such as floating\n\tpoint instructions and instruction set extensions are not initialized\n\tyet. The GDT should be loaded here. Paging should be enabled here.\n\tC++ features such as global constructors and exceptions will require\n\truntime support to work as well.\n\t*/\n \n\t/*\n\tEnter the high-level kernel. The ABI requires the stack is 16-byte\n\taligned at the time of the call instruction (which afterwards pushes\n\tthe return pointer of size 4 bytes). The stack was originally 16-byte\n\taligned above and we've since pushed a multiple of 16 bytes to the\n\tstack since (pushed 0 bytes so far) and the alignment is thus\n\tpreserved and the call is well defined.\n\t*/\n\tcall kernel_main\n \n\t/*\n\tIf the system has nothing more to do, put the computer into an\n\tinfinite loop. To do that:\n\t1) Disable interrupts with cli (clear interrupt enable in eflags).\n\t   They are already disabled by the bootloader, so this is not needed.\n\t   Mind that you might later enable interrupts and return from\n\t   kernel_main (which is sort of nonsensical to do).\n\t2) Wait for the next interrupt to arrive with hlt (halt instruction).\n\t   Since they are disabled, this will lock up the computer.\n\t3) Jump to the hlt instruction if it ever wakes up due to a\n\t   non-maskable interrupt occurring or due to system management mode.\n\t*/\n\tcli\n1:\thlt\n\tjmp 1b\n \n/*\nSet the size of the _start symbol to the current location '.' minus its start.\nThis is useful when debugging or when you implement call tracing.\n*/\n.size _start, . - _start\n"
  },
  {
    "path": "lectures/fall-2019/Supplementary-06/toyos/grub.cfg",
    "content": "menuentry \"myos\" {\n\tmultiboot /boot/myos.bin\n}\n"
  },
  {
    "path": "lectures/fall-2019/Supplementary-06/toyos/kernel.c",
    "content": "#include <stdbool.h>\n#include <stddef.h>\n#include <stdint.h>\n \n \n/* Hardware text mode color constants. */\nenum vga_color {\n\tVGA_COLOR_BLACK = 0,\n\tVGA_COLOR_BLUE = 1,\n\tVGA_COLOR_GREEN = 2,\n\tVGA_COLOR_CYAN = 3,\n\tVGA_COLOR_RED = 4,\n\tVGA_COLOR_MAGENTA = 5,\n\tVGA_COLOR_BROWN = 6,\n\tVGA_COLOR_LIGHT_GREY = 7,\n\tVGA_COLOR_DARK_GREY = 8,\n\tVGA_COLOR_LIGHT_BLUE = 9,\n\tVGA_COLOR_LIGHT_GREEN = 10,\n\tVGA_COLOR_LIGHT_CYAN = 11,\n\tVGA_COLOR_LIGHT_RED = 12,\n\tVGA_COLOR_LIGHT_MAGENTA = 13,\n\tVGA_COLOR_LIGHT_BROWN = 14,\n\tVGA_COLOR_WHITE = 15,\n};\n \nstatic inline uint8_t vga_entry_color(enum vga_color fg, enum vga_color bg) \n{\n\treturn fg | bg << 4;\n}\n \nstatic inline uint16_t vga_entry(unsigned char uc, uint8_t color) \n{\n\treturn (uint16_t) uc | (uint16_t) color << 8;\n}\n \nsize_t strlen(const char* str) \n{\n\tsize_t len = 0;\n\twhile (str[len])\n\t\tlen++;\n\treturn len;\n}\n \nstatic const size_t VGA_WIDTH = 80;\nstatic const size_t VGA_HEIGHT = 25;\n \nsize_t terminal_row;\nsize_t terminal_column;\nuint8_t terminal_color;\nuint16_t* terminal_buffer;\n \nvoid terminal_initialize(void) \n{\n\tterminal_row = 0;\n\tterminal_column = 0;\n\tterminal_color = vga_entry_color(VGA_COLOR_LIGHT_GREY, VGA_COLOR_BLACK);\n\tterminal_buffer = (uint16_t*) 0xB8000;\n\tfor (size_t y = 0; y < VGA_HEIGHT; y++) {\n\t\tfor (size_t x = 0; x < VGA_WIDTH; x++) {\n\t\t\tconst size_t index = y * VGA_WIDTH + x;\n\t\t\tterminal_buffer[index] = vga_entry(' ', terminal_color);\n\t\t}\n\t}\n}\n \nvoid terminal_setcolor(uint8_t color) \n{\n\tterminal_color = color;\n}\n \nvoid terminal_putentryat(char c, uint8_t color, size_t x, size_t y) \n{\n\tconst size_t index = y * VGA_WIDTH + x;\n\tterminal_buffer[index] = vga_entry(c, color);\n}\n \nvoid terminal_putchar(char c) \n{\n\tterminal_putentryat(c, terminal_color, terminal_column, terminal_row);\n\tif (++terminal_column == VGA_WIDTH) {\n\t\tterminal_column = 0;\n\t\tif (++terminal_row == VGA_HEIGHT)\n\t\t\tterminal_row = 0;\n\t}\n}\n \nvoid terminal_write(const char* data, size_t size) \n{\n\tfor (size_t i = 0; i < size; i++)\n\t\tterminal_putchar(data[i]);\n}\n \nvoid terminal_writestring(const char* data) \n{\n\tterminal_write(data, strlen(data));\n}\n\n\nvoid kernel_main(void) \n{\n\t/* Initialize terminal interface */\n\tterminal_initialize();\n \n\t/* Newline support is left as an exercise. */\n    terminal_writestring(\"Hello, kernel World!\\n\");\n}\n"
  },
  {
    "path": "lectures/fall-2019/Supplementary-06/toyos/linker.ld",
    "content": "/* The bootloader will look at this image and start execution at the symbol\n   designated as the entry point. */\nENTRY(_start)\n \n/* Tell where the various sections of the object files will be put in the final\n   kernel image. */\nSECTIONS\n{\n\t/* Begin putting sections at 1 MiB, a conventional place for kernels to be\n\t   loaded at by the bootloader. */\n\t. = 1M;\n \n\t/* First put the multiboot header, as it is required to be put very early\n\t   early in the image or the bootloader won't recognize the file format.\n\t   Next we'll put the .text section. */\n\t.text BLOCK(4K) : ALIGN(4K)\n\t{\n\t\t*(.multiboot)\n\t\t*(.text)\n\t}\n \n\t/* Read-only data. */\n\t.rodata BLOCK(4K) : ALIGN(4K)\n\t{\n\t\t*(.rodata)\n\t}\n \n\t/* Read-write data (initialized) */\n\t.data BLOCK(4K) : ALIGN(4K)\n\t{\n\t\t*(.data)\n\t}\n \n\t/* Read-write data (uninitialized) and stack */\n\t.bss BLOCK(4K) : ALIGN(4K)\n\t{\n\t\t*(COMMON)\n\t\t*(.bss)\n\t}\n \n\t/* The compiler may produce other sections, by default it will put them in\n\t   a segment with the same name. Simply add stuff here as needed. */\n}\n"
  },
  {
    "path": "lectures/fall-2019/Supplementary-08/custom-fd.c",
    "content": "#include <unistd.h>\n\nint main() {\n    static const char Hello1[] =\n        \"Hello, STDOUT!\\n\";\n    static const char Hello2[] =\n        \"Hello, STDERR!\\n\";\n    static const char Hello795[] =\n        \"Hello, group 795!\\n\";\n    write(  1, Hello1  , sizeof(Hello1  )-1);\n    write(  2, Hello2  , sizeof(Hello2  )-1);\n    write(795, Hello795, sizeof(Hello795)-1);\n}\n"
  },
  {
    "path": "lectures/fall-2019/Supplementary-10/memory-map.c",
    "content": "int main()\n{\n    return 0;\n}\n"
  },
  {
    "path": "lectures/fall-2019/Supplementary-10/overcommit.c",
    "content": "#include <stdlib.h>\n#include <stdio.h>\n\nint main()\n{\n    int * ptr = malloc(16ULL*1024*1024*1024); // 16 Gb\n    puts(\"malloc called\");\n    getchar();\n    ptr[0] = 123;\n}\n"
  },
  {
    "path": "lectures/fall-2019/Supplementary-10/test-malloc.c",
    "content": "#include <stdlib.h>\n\nint main()\n{\n    void *ptr = malloc(1);\n    free(ptr);\n    free(ptr);\n}\n"
  },
  {
    "path": "lectures/fall-2019/Supplementary-10/test-malloc2.c",
    "content": "#include <stdlib.h>\n\nint main()\n{\n    void *ptr = malloc(99999999);\n    free(ptr);\n    free(ptr);\n}\n"
  },
  {
    "path": "lectures/fall-2019/Supplementary-11/fork-bomb.c",
    "content": "#include <stdio.h>\n#include <sys/types.h>\n#include <unistd.h>\n#include <stdlib.h>\n#include <string.h>\n#include <sched.h>\n\nint main()\n{\n    char * are_you_sure = getenv(\"ALLOW_FORK_BOMB\");\n    if (!are_you_sure || 0!=strcmp(are_you_sure, \"yes\")) {\n        fprintf(stderr, \"Fork bomb not allowed!\\n\");\n        exit(127);\n    }\n\n    pid_t pid;\n    do {\n        pid = fork();\n    } while (-1 != pid);\n\n    printf(\"Process %d reached out limit on processes\\n\", getpid());\n    while (1) {\n        sched_yield();\n        sleep(1);\n    }\n}\n"
  },
  {
    "path": "lectures/fall-2019/Supplementary-11/process_setup.c",
    "content": "#include <sys/types.h>\n#include <stdio.h>\n#include <errno.h>\n#include <unistd.h>\n#include <fcntl.h>\n#include <stdlib.h>\n#include <sys/wait.h>\n\nint main() {\n    pid_t  pid = fork();\n    if (-1==pid) { perror(\"fork :-(\"); exit(1); }\n    if (0==pid) {\n      chdir(\"/usr/bin\");\n      int fd = open(\"/tmp/out.txt\",\n         \t  O_WRONLY|O_CREAT|O_TRUNC, 0644);\n    dup2(fd, 1); close(fd);\n    execlp(\"ls\", \"ls\", \"-l\", NULL);\n    perror(\"exec :-(\");\n    exit(2);\n  }\n  else {\n    waitpid(pid, NULL, 0);\n  }\n}\n\n"
  },
  {
    "path": "lectures/fall-2019/Supplementary-11/start_child.c",
    "content": "#include <sys/types.h>\n#include <stdio.h>\n#include <unistd.h>\n\nint main() {\n  printf(\"abrakadabra \"); // write\n  fflush(stdout); //\n  pid_t result = fork();\n  if (0==result) {\n      printf(\"I'm son\\n\"); // MINIX, QNX \n  }\n  else {\n      printf(\"I'm parent\\n\");\n  }\n}\n"
  },
  {
    "path": "lectures/fall-2019/Supplementary-12/do_abort.c",
    "content": "#include <stdlib.h>\n\nint main()\n{\n    abort();\n}\n"
  },
  {
    "path": "lectures/fall-2019/Supplementary-12/good-signal-handling.c",
    "content": "#include <signal.h>\n#include <stdio.h>\n#include <string.h>\n#include <unistd.h>\n\nvolatile sig_atomic_t caught_signum = 0;\n\nvoid handler(int signum) {\n    // Do not invoke printf, but just\n    // save signal number\n    caught_signum = signum;\n}\n\nint main() {\n    struct sigaction act;\n    memset(&act, 0, sizeof(act));\n    act.sa_handler = handler;\n    act.sa_flags = SA_RESTART;\n\n    sigaction(SIGTERM, &act, NULL);\n    sigaction(SIGINT, &act, NULL);\n\n    while (1) {\n        pause(); // wait until signal caught and processed\n        printf(\"Got signal %d\\n\", caught_signum);\n    }\n        \n}\n"
  },
  {
    "path": "lectures/fall-2019/Supplementary-12/handle-sigint-sigterm.c",
    "content": "#include <signal.h>\n#include <sched.h>\n#include <stdio.h>\n\nvoid handler(int signum) {\n    // Warning: govnokod!\n    printf(\"Caught signal %d\\n\", signum);\n}\n\nint main() {\n    signal(SIGINT, handler);\n    signal(SIGTERM, handler);\n    while (1) sched_yield();\n}\n"
  },
  {
    "path": "lectures/fall-2019/Supplementary-12/sigaction-handling.c",
    "content": "#include <signal.h>\n#include <sched.h>\n#include <stdio.h>\n#include <string.h>\n\nvoid handle_with_one_arg(int signum)\n{\n    // a bit better, but still govnokod\n    printf(\"Got signal %d\\n\", signum);\n}\n\nvoid\nhandle_with_three_args(int signum, siginfo_t *info, void *ctx)\n{\n    // govnokod is still here\n    printf(\"Got signal %d from process %d\\n\", signum, info->si_pid);\n}\n\nint main() {\n    struct sigaction int_handler;\n    memset(&int_handler, 0, sizeof(int_handler));\n\n    int_handler.sa_handler = handle_with_one_arg;\n    int_handler.sa_flags = SA_RESTART;\n    sigaction(SIGINT, &int_handler, NULL);\n\n    struct sigaction term_handler;\n    memset(&term_handler, 0, sizeof(term_handler));\n\n    term_handler.sa_sigaction = handle_with_three_args;\n    term_handler.sa_flags = SA_RESTART | SA_SIGINFO;\n    sigaction(SIGTERM, &term_handler, NULL);\n\n    while (1) sched_yield();\n}\n"
  },
  {
    "path": "lectures/fall-2019/Supplementary-12/signalfd.c",
    "content": "#include <sys/signalfd.h>\n#include <signal.h>\n#include <stdio.h>\n#include <unistd.h>\n\nint main()\n{\n    // Prepare set of signals\n    sigset_t mask;\n    sigemptyset(&mask);\n    sigaddset(&mask, SIGINT);\n    sigaddset(&mask, SIGQUIT);\n\n    // Disable signals handling (by block)\n    sigprocmask(SIG_BLOCK, &mask, NULL);\n        \n    int fd = signalfd(-1, &mask, 0);\n    \n    struct signalfd_siginfo info;\n    while (read(fd, &info, sizeof(info)) > 0) {\n        printf(\"Got signal %d from PID %d\\n\",\n               info.ssi_signo, info.ssi_pid);\n    }\n    \n}\n"
  },
  {
    "path": "lectures/fall-2019/Supplementary-12/sigprocmask.c",
    "content": "#include <signal.h>\n#include <stdio.h>\n#include <unistd.h>\n\nvolatile sig_atomic_t int_received = 0;\nvolatile sig_atomic_t term_received = 0;\n\nvoid sigint_handler(int num) { int_received ++; }\nvoid sigterm_handler(int num) { term_received = 1;}\n\nint main() {\n    // Register handlers\n    sigaction(SIGINT, &(struct sigaction) {\n                .sa_handler = sigint_handler,\n                .sa_flags = SA_RESTART }, NULL);\n    sigaction(SIGTERM, &(struct sigaction) {\n                .sa_handler = sigterm_handler,\n                .sa_flags = SA_RESTART }, NULL);\n    \n    // Block SIGINT\n    sigset_t sigset;\n    sigemptyset(&sigset);\n    sigaddset(&sigset, SIGINT);\n    sigprocmask(SIG_BLOCK, &sigset, NULL);\n\n    while ( ! term_received ) {\n        pause();\n    }\n    sigprocmask(SIG_UNBLOCK, &sigset, NULL);\n    printf(\"Got %d times SIGINT\\n\", int_received);\n}\n"
  },
  {
    "path": "lectures/fall-2019/Supplementary-12/sigsuspend.c",
    "content": "#include <signal.h>\n#include <stdio.h>\n#include <unistd.h>\n\nvolatile sig_atomic_t int_received = 0;\n\nvoid sigint_handler(int num) { int_received ++; }\n\nint main() {\n    // Register handler\n    sigaction(SIGINT, &(struct sigaction) {\n                .sa_handler = sigint_handler,\n                .sa_flags = SA_RESTART }, NULL);\n    \n    // Block SIGINT\n    sigset_t set_with_int;\n    sigemptyset(&set_with_int);\n    sigaddset(&set_with_int, SIGINT);\n    sigprocmask(SIG_BLOCK, &set_with_int, NULL);\n    \n    // Temporary unblock SIGINT and wait it\n    sigset_t set_withOUT_int;\n    sigfillset(&set_withOUT_int);\n    sigdelset(&set_withOUT_int, SIGINT);\n    sigsuspend(&set_withOUT_int);\n    \n    printf(\"Got %d times SIGINT\\n\", int_received);\n}\n"
  },
  {
    "path": "lectures/fall-2019/Supplementary-12/simpleio.c",
    "content": "#include <stdio.h>\n\nint main() {\n    puts(\"Process is sleeping until character typed\");\n    getchar();\n}\n"
  },
  {
    "path": "lectures/fall-2019/Supplementary-13/ldpreload-example/fakelib.c",
    "content": "#define _GNU_SOURCE\n#include <sys/types.h>\n#include <signal.h>\n#include <unistd.h>\n#include <dlfcn.h>\n#include <stdio.h>\n\ntypedef void (*sighandler_t)(int);\n\ntypedef int (*sigaction_ptr_t)\n(\n int,\n const struct sigaction *restrict,\n struct sigaction *\n);\n\nint sigaction(int signum,\n                      const struct sigaction *restrict act,\n                      struct sigaction *oldact)\n{\n    puts(\"Called fake sigaction\\n\");\n    \n    // Make additional work time to simulate\n    // race condition\n    sleep(3);\n    static sigaction_ptr_t real_sigaction = NULL;\n    if (NULL == real_sigaction) {        \n        real_sigaction = dlsym(RTLD_NEXT, \"sigaction\");\n        if (!real_sigaction) {\n            fputs(dlerror(), stderr);\n            _exit(1);\n        }\n    }\n    return real_sigaction(signum, act, oldact);\n}\n"
  },
  {
    "path": "lectures/fall-2019/Supplementary-13/ldpreload-example/fakelib0.c",
    "content": "#define _GNU_SOURCE\n#include <sys/types.h>\n#include <signal.h>\n#include <unistd.h>\n#include <stdio.h>\n\ntypedef void (*sighandler_t)(int);\n\ntypedef int (*sigaction_ptr_t)\n(\n int,\n const struct sigaction *restrict,\n struct sigaction *\n);\n\nint sigaction(int signum,\n                      const struct sigaction *restrict act,\n                      struct sigaction *oldact)\n{\n    puts(\"Called fake sigaction\\n\");    \n}\n"
  },
  {
    "path": "lectures/fall-2019/Supplementary-13/ldpreload-example/fakelib1.c",
    "content": "#include <stdio.h>\n\n__attribute__((constructor))\nvoid initialize_fakelib() {\n    puts(\"Fake library initialized\");\n}\n\n__attribute__((destructor))\nvoid finalize_fakelib() {\n    puts(\"Fake library unloading\");\n}\n"
  },
  {
    "path": "lectures/fall-2019/Supplementary-13/ldpreload-example/hello.c",
    "content": "#include <stdio.h>\n\nint main() { puts(\"Hello, World!\"); }\n"
  },
  {
    "path": "lectures/fall-2019/Supplementary-13/ldpreload-example/solution.c",
    "content": "#include <signal.h>\n#include <stdio.h>\n#include <string.h>\n#include <unistd.h>\n\nvoid handle_sigint(int signum)\n{\n    static const char Message[] = \"Caught SIGINT\\n\";\n    write(1, Message, sizeof(Message)-1);\n}\n\nint main()\n{\n    // Wrong solution: race condition\n    printf(\"My PID is %d\\n\", getpid());\n    fflush(stdout);\n    \n    struct sigaction action_int;\n    memset(&action_int, 0, sizeof(action_int));\n    action_int.sa_handler = handle_sigint;\n    action_int.sa_flags = SA_RESTART;\n    sigaction(SIGINT, &action_int, NULL);\n    \n    while (1) pause();\n}\n"
  },
  {
    "path": "lectures/fall-2019/Supplementary-13/ptrace/ptrace_catch_string.c",
    "content": "#include <sys/types.h>\n#include <sys/stat.h>\n#include <sys/ptrace.h>\n#include <sys/wait.h>\n#include <sys/user.h>\n#include <unistd.h>\n#include <stdlib.h>\n#include <stdio.h>\n#include <fcntl.h>\n#include <stdbool.h>\n#include <asm/unistd.h>\n#include <signal.h>\n#include <string.h>\n#include <errno.h>\n\nstatic void\npremoderate_write_syscall(pid_t pid, struct user_regs_struct state)\n{\n    size_t orig_buf = state.rsi;   // ecx for i386\n    size_t size = state.rdx;       // rdx for i386\n    char *buffer = calloc(size+sizeof(long), sizeof(*buffer));\n    int val = 0;\n    for (size_t i=0; i<size; ++i) {\n        buffer[i] = ptrace(PTRACE_PEEKDATA, pid, orig_buf+i, NULL);\n    }\n    char *bad_word;\n    if ( (bad_word=strstr(buffer, \"HSE\")) ) {\n         size_t offset = bad_word - buffer;\n         buffer[offset] = 'P';\n         buffer[offset+1] = 'T';\n         buffer[offset+2] = 'U';\n         size_t target_address = orig_buf + offset;\n         long val;\n         memcpy(&val, buffer+offset, sizeof(val));\n         ptrace(PTRACE_POKEDATA, pid, target_address, val);\n    }\n    free(buffer);\n}\n\nint main(int argc, char *argv[])\n{\n    pid_t  pid = fork();\n    if (-1==pid) { perror(\"fork\"); exit(1); }\n    if (0==pid) {\n        ptrace(PTRACE_TRACEME, 0, NULL, NULL);\n        execvp(argv[1], argv+1);\n        perror(\"exec\");\n        exit(2);\n    }\n    else {      \n        int wstatus = 0;\n        struct user_regs_struct state;\n        bool stop = false;\n        while (!stop) {\n            ptrace(PTRACE_SYSCALL, pid, NULL, NULL);\n            waitpid(pid, &wstatus, 0);\n            stop = WIFEXITED(wstatus);\n            if (WIFSTOPPED(wstatus)) {\n                ptrace(PTRACE_GETREGS, pid, 0, &state);\n                if (__NR_write==state.orig_rax) {  // orig_eax for i386\n                    premoderate_write_syscall(pid, state);\n                }              \n            }\n        }            \n    }  \n}\n"
  },
  {
    "path": "lectures/fall-2019/Supplementary-13/wrap-example/fakelib.c",
    "content": "#include <sys/types.h>\n#include <signal.h>\n#include <unistd.h>\n\ntypedef void (*sighandler_t)(int);\n\nint __real_sigaction(int signum,\n                      const struct sigaction *restrict act,\n                      struct sigaction *oldact);\nint __wrap_sigaction(int signum,\n                      const struct sigaction *restrict act,\n                      struct sigaction *oldact)\n{\n    // Make additional work time to simulate\n    // race condition\n    sleep(3);\n    return __real_sigaction(signum, act, oldact);\n}\n"
  },
  {
    "path": "lectures/fall-2019/Supplementary-13/wrap-example/solution.c",
    "content": "#include <signal.h>\n#include <stdio.h>\n#include <string.h>\n#include <unistd.h>\n\nvoid handle_sigint(int signum)\n{\n    static const char Message[] = \"Caught SIGINT\\n\";\n    write(1, Message, sizeof(Message)-1);\n}\n\nint main()\n{\n    // Wrong solution: race condition\n    printf(\"My PID is %d\\n\", getpid());\n    fflush(stdout);\n    \n    struct sigaction action_int;\n    memset(&action_int, 0, sizeof(action_int));\n    action_int.sa_handler = handle_sigint;\n    action_int.sa_flags = SA_RESTART;\n    sigaction(SIGINT, &action_int, NULL);\n    \n    while (1) pause();\n}\n"
  },
  {
    "path": "lectures/spring-2019/Lection14-Supplementary/do_abort.c",
    "content": "#include <stdlib.h>\n\nint main()\n{\n    abort();\n}\n"
  },
  {
    "path": "lectures/spring-2019/Lection14-Supplementary/do_nothing.c",
    "content": "#include <sched.h>\n\nint main()\n{\n    while (1) {\n        sched_yield();\n    }\n}\n"
  },
  {
    "path": "lectures/spring-2019/Lection14-Supplementary/good-signal-handling.c",
    "content": "#include <signal.h>\n#include <stdio.h>\n#include <string.h>\n#include <unistd.h>\n\nvolatile sig_atomic_t caught_signum = 0;\n\nvoid handler(int signum) {\n    // Do not invoke printf, but just\n    // save signal number\n    caught_signum = signum;\n}\n\nint main() {\n    struct sigaction act;\n    memset(&act, 0, sizeof(act));\n    act.sa_handler = handler;\n    act.sa_flags = SA_RESTART;\n\n    sigaction(SIGTERM, &act, NULL);\n    sigaction(SIGINT, &act, NULL);\n\n    while (1) {\n        pause(); // wait until signal caught and processed\n        printf(\"Got signal %d\\n\", caught_signum);\n    }\n        \n}\n"
  },
  {
    "path": "lectures/spring-2019/Lection14-Supplementary/handle-sigint-sigterm.c",
    "content": "#include <signal.h>\n#include <sched.h>\n#include <stdio.h>\n\nvoid handler(int signum) {\n    // Warning: govnokod!\n    printf(\"Caught signal %d\\n\", signum);\n}\n\nint main() {\n    signal(SIGINT, handler);\n    signal(SIGTERM, handler);\n    while (1) sched_yield();\n}\n"
  },
  {
    "path": "lectures/spring-2019/Lection14-Supplementary/sigaction-handling.c",
    "content": "#include <signal.h>\n#include <sched.h>\n#include <stdio.h>\n#include <string.h>\n\nvoid handle_with_one_arg(int signum)\n{\n    // a bit better, but still govnokod\n    printf(\"Got signal %d\\n\", signum);\n}\n\nvoid\nhandle_with_three_args(int signum, siginfo_t *info, void *ctx)\n{\n    // govnokod is still here\n    printf(\"Got signal %d from process %d\\n\", signum, info->si_pid);\n}\n\nint main() {\n    struct sigaction int_handler;\n    memset(&int_handler, 0, sizeof(int_handler));\n\n    int_handler.sa_handler = handle_with_one_arg;\n    int_handler.sa_flags = SA_RESTART;\n    sigaction(SIGINT, &int_handler, NULL);\n\n    struct sigaction term_handler;\n    memset(&term_handler, 0, sizeof(term_handler));\n\n    term_handler.sa_sigaction = handle_with_three_args;\n    term_handler.sa_flags = SA_RESTART | SA_SIGINFO;\n    sigaction(SIGTERM, &term_handler, NULL);\n\n    while (1) sched_yield();\n}\n"
  },
  {
    "path": "lectures/spring-2019/Lection15-Supplementary/signalfd.c",
    "content": "#include <sys/signalfd.h>\n#include <signal.h>\n#include <stdio.h>\n#include <unistd.h>\n\nint main()\n{\n    // Prepare set of signals\n    sigset_t mask;\n    sigemptyset(&mask);\n    sigaddset(&mask, SIGINT);\n    sigaddset(&mask, SIGQUIT);\n\n    // Disable signals handling (by block)\n    sigprocmask(SIG_BLOCK, &mask, NULL);\n        \n    int fd = signalfd(-1, &mask, 0);\n    \n    struct signalfd_siginfo info;\n    while (read(fd, &info, sizeof(info)) > 0) {\n        printf(\"Got signal %d from PID %d\\n\",\n               info.ssi_signo, info.ssi_pid);\n    }\n    \n}\n"
  },
  {
    "path": "lectures/spring-2019/Lection15-Supplementary/sigprocmask.c",
    "content": "#include <signal.h>\n#include <stdio.h>\n#include <unistd.h>\n\nvolatile sig_atomic_t int_received = 0;\nvolatile sig_atomic_t term_received = 0;\n\nvoid sigint_handler(int num) { int_received ++; }\nvoid sigterm_handler(int num) { term_received = 1;}\n\nint main() {\n    // Register handlers\n    sigaction(SIGINT, &(struct sigaction) {\n                .sa_handler = sigint_handler,\n                .sa_flags = SA_RESTART }, NULL);\n    sigaction(SIGTERM, &(struct sigaction) {\n                .sa_handler = sigterm_handler,\n                .sa_flags = SA_RESTART }, NULL);\n    \n    // Block SIGINT\n    sigset_t sigset;\n    sigemptyset(&sigset);\n    sigaddset(&sigset, SIGINT);\n    sigprocmask(SIG_BLOCK, &sigset, NULL);\n\n    while ( ! term_received ) {\n        pause();\n    }\n    sigprocmask(SIG_UNBLOCK, &sigset, NULL);\n    printf(\"Got %d times SIGINT\\n\", int_received);\n}\n"
  },
  {
    "path": "lectures/spring-2019/Lection15-Supplementary/sigsuspend.c",
    "content": "#include <signal.h>\n#include <stdio.h>\n#include <unistd.h>\n\nvolatile sig_atomic_t int_received = 0;\n\nvoid sigint_handler(int num) { int_received ++; }\n\nint main() {\n    // Register handler\n    sigaction(SIGINT, &(struct sigaction) {\n                .sa_handler = sigint_handler,\n                .sa_flags = SA_RESTART }, NULL);\n    \n    // Block SIGINT\n    sigset_t set_with_int;\n    sigemptyset(&set_with_int);\n    sigaddset(&set_with_int, SIGINT);\n    sigprocmask(SIG_BLOCK, &set_with_int, NULL);\n    \n    // Temporary unblock SIGINT and wait it\n    sigset_t set_withOUT_int;\n    sigfillset(&set_withOUT_int);\n    sigdelset(&set_withOUT_int, SIGINT);\n    sigsuspend(&set_withOUT_int);\n    \n    printf(\"Got %d times SIGINT\\n\", int_received);\n}\n"
  },
  {
    "path": "lectures/spring-2019/Lection18-Supplementary/lorem-ipsum-server.cpp",
    "content": "#include <cstring>\n#include <cstdint>\n\nextern \"C\" {\n#include <unistd.h>\n\n#include <arpa/inet.h>\n#include <sys/socket.h>\n#include <sys/stat.h>\n#include <sys/wait.h>\n\n#include <fcntl.h>\n#include <sys/epoll.h>\n#include <sys/time.h>\n#include <errno.h>\n}\n\n#include <string>\n#include <map>\n#include <iostream>\n\n\nstatic const uint16_t PortNumber = 3000;\nstatic const size_t MaxEvents = 10000;\nstatic const size_t QuantumSize = 10 /* bytes */;\nstatic const std::string Message = \"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus elementum sem sit amet ipsum mattis, ut vestibulum diam ornare. Nullam id tortor quis arcu vehicula ornare. Nunc sit amet ultricies felis. Donec auctor sagittis commodo. Aenean cursus, turpis et imperdiet vestibulum, nibh nisi laoreet quam, a posuere ligula arcu non nisi. Sed at sodales neque, eu sollicitudin neque. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nullam ultricies, eros sed finibus efficitur, odio nisl volutpat ex, id tincidunt nunc metus ac elit. Sed gravida dui eu velit pellentesque, sit amet pellentesque nunc tincidunt. Cras dignissim porttitor eros vel accumsan. Sed tellus nisl, viverra quis velit laoreet, scelerisque elementum neque. Fusce purus eros, pretium sed lorem non, maximus accumsan sem. Donec tincidunt lorem eu diam ultrices, sed varius leo lobortis. Nulla nec convallis odio. Maecenas bibendum tellus vel mauris congue maximus. Proin id lectus accumsan, vehicula risus sit amet, gravida velit.\\n\";\n\nstatic sig_atomic_t StopRequest = 0;\n\nstatic int\ncreate_and_bind()\n{\n    int sd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);\n    if (0 > sd) {\n        perror(\"Socket\");\n        exit(1);\n    }\n    sockaddr_in addr;\n    memset(&addr, 0, sizeof(addr));\n    addr.sin_addr.s_addr = INADDR_ANY;\n    addr.sin_family = AF_INET;\n    addr.sin_port = htons(PortNumber);\n    int bs = bind(sd, (sockaddr*) &addr, sizeof(addr));\n    if (0 > bs) {\n        perror(\"Bind\");\n        close(sd);\n        exit(1);\n    }\n    return sd;\n}\n\nstatic void\nmake_non_blocking(int sd)\n{\n    int flags = fcntl(sd, F_GETFL);\n    fcntl(sd, F_SETFL, flags | O_NONBLOCK);\n}\n\n\nstatic void\nstop_handler(int s)\n{\n    StopRequest = 1;\n}\n\nint main(int argc, char* argv[])\n{\n    int status = 0;\n\n    int sock_fd = create_and_bind();\n\n    make_non_blocking(sock_fd);\n\n    status = listen(sock_fd, SOMAXCONN);\n    if (0 > status) {\n        perror(\"Listen\");\n        close(sock_fd);\n        exit(1);\n    }\n\n\n    /* Create epoll queue */\n    int ed = epoll_create1(0);\n\n    epoll_event event;\n    memset(&event, 0, sizeof(event));\n    event.data.fd = sock_fd;\n    event.events = EPOLLIN|EPOLLOUT;\n\n    status = epoll_ctl(ed, EPOLL_CTL_ADD, sock_fd, &event);\n    if (0 > status) {\n        perror(\"Epoll control\");\n        close(sock_fd);\n        exit(1);\n    }\n\n\n    struct sigaction act;\n    memset(&act, 0, sizeof(act));\n    act.sa_handler = stop_handler;\n    act.sa_flags = SA_RESTART;\n    \n    sigaction(SIGINT, &act, nullptr);\n    sigaction(SIGTERM, &act, nullptr);\n    signal(SIGPIPE, SIG_IGN);\n\n    epoll_event *pending_events = new epoll_event[MaxEvents];\n\n    std::map<int,size_t> out_data_positions;\n\n    while ( ! StopRequest ) {\n\n        int n = epoll_wait(ed, pending_events, MaxEvents, -1);\n        if (-1 == n) {\n            break; // Bye!\n        }\n        else if (0 > n) {\n            perror(\"Epoll wait\");\n            close(sock_fd);\n            exit(1);\n        }\n\n        for (int i=0; i<n; ++i) {\n            const uint32_t emask = pending_events[i].events;\n            if ( (emask & EPOLLERR) || (emask & EPOLLHUP) )\n            {\n                if (emask & EPOLLERR)\n                    std::cerr << \"Something wrong!\";\n                int hang_fd = pending_events[i].data.fd;\n                if (out_data_positions.count(hang_fd)) {\n                    out_data_positions.erase(hang_fd);\n                }\n                close(hang_fd);\n                continue;\n            }\n            else if (pending_events[i].data.fd == sock_fd) {\n                // Incoming connection event\n\n                while (true) {\n                    // There is possible several connections at a time,\n                    // so accept them all\n\n                    struct sockaddr_in in_addr;\n                    socklen_t in_addr_size = sizeof(in_addr);\n                    int incoming_fd = accept(sock_fd, (sockaddr*)&in_addr, &in_addr_size);\n                    if (-1 == incoming_fd) {\n                        // This might be not an error!\n                        if (EAGAIN == errno || EWOULDBLOCK == errno) {\n                            break;\n                        }\n                        else {\n                            perror(\"Accept failed\");\n                            close(sock_fd);\n                            exit(1);\n                        }\n                    }\n                    else {\n                        // Register newly created file descriptor for\n                        // event processing\n                        make_non_blocking(incoming_fd);\n                        event.data.fd = incoming_fd;\n                        event.events = EPOLLIN | EPOLLOUT;\n                        status = epoll_ctl(ed, EPOLL_CTL_ADD, incoming_fd, &event);\n                        if (0 > status) {\n                            perror(\"Epoll control for incoming connection\");\n                            close(sock_fd);\n                            exit(1);\n                        }\n                        out_data_positions.insert(std::make_pair(incoming_fd, 0));\n                    }\n                    continue;\n                } /* end while(true) */\n\n            }\n            else if ( emask & EPOLLOUT ) {\n                // Previous data block was successfully sent,\n                // and current connection is ready to eat some\n                // more data\n                int out_fd = pending_events[i].data.fd;\n                size_t last_pos = out_data_positions[out_fd];\n\n                const std::string message_quant = Message.substr(last_pos, QuantumSize);\n                size_t new_pos = message_quant.length() < QuantumSize\n                        ? 0\n                        : last_pos + QuantumSize;\n                write(out_fd, message_quant.c_str(), message_quant.length());\n                out_data_positions[out_fd] = new_pos;\n\n            }\n            else if ( emask & EPOLLIN ) {\n                // Connection wants to write us some data\n                // TODO implement me the same way!\n            }\n            else {\n                std::cerr << \"This branch unreachable!\" << std::endl;\n                close(sock_fd);\n                exit(1);\n            }\n        }\n    }\n\n    delete[] pending_events;\n    shutdown(sock_fd, SHUT_RDWR);\n    close(sock_fd);\n    std::cout << \"Bye!\" << std::endl;\n    return 0;\n}\n"
  },
  {
    "path": "lectures/spring-2019/Lection20-Supplementaty/detached-threads.c",
    "content": "#include <pthread.h>\n#include <stdio.h>\n#include <unistd.h>\n\nstatic void*\nthread_function(void* arg)\n{\n    while (1) {\n        printf(\"Thread %llu is running\\n\", (size_t)arg);\n        sleep(1);\n    }\n}\n\nint main()\n{\n    const size_t N = 10;\n    pthread_t threads[N];\n    for (size_t i=0; i<N; ++i) {\n        pthread_create(&threads[i],\n                       NULL,\n                       thread_function,\n                       (void*) i);\n    }\n    pthread_exit(0);\n}\n"
  },
  {
    "path": "lectures/spring-2020/Lection17-Supplementary/detached-threads.c",
    "content": "#include <inttypes.h>\n#include <stdio.h>\n#include <stdint.h>\n\n#include <pthread.h>\n#include <unistd.h>\n\n\nstatic void*\nthread_function(void* arg)\n{\n    while (1) {\n        printf(\"Thread %\"PRIu64\" is running\\n\", (uint64_t) arg);\n        sleep(1);\n    }\n}\n\nint main()\n{\n    const size_t N = 10;\n    pthread_t threads[N];\n    for (size_t i=0; i<N; ++i) {\n        pthread_create(&threads[i],\n                       NULL,\n                       thread_function,\n                       (void*) i);\n    }\n    //pthread_exit(0);\n}\n"
  },
  {
    "path": "lessons-supplementary/2021-2022/l18-shm/shm.c",
    "content": "#include <fcntl.h>\n#include <stdio.h>\n#include <sys/mman.h>\n#include <unistd.h>\n\nint main(int argc, char *argv[]) {\n    const char *shm_name = argv[1];\n    int fd = shm_open(shm_name, O_RDWR|O_CREAT, 0600);\n    if (-1 == fd) {\n        perror(\"SHM creation failed\");\n        return 1;\n    }\n    close(fd);\n    printf(\"SHM created, press ENTER to release\\n\");\n    getchar();\n    shm_unlink(shm_name);\n}\n"
  },
  {
    "path": "lessons-supplementary/2021-2022/l19-bpf/example1.c",
    "content": "#include <inttypes.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n\n#include <arpa/inet.h>\n#include <net/if.h>\n#include <sys/ioctl.h>\n#include <sys/socket.h>\n#include <linux/if_packet.h>\n#include <net/ethernet.h>\n\nint main(int argc, char *argv[]) {\n\n    /* Part I. Initialization */\n    \n    int sock = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL));\n    if (-1==sock) { perror(\"socket\"); exit(1); } // root or setcap CAP_NET_RAW\n\n    struct ifreq req = { .ifr_name = \"enp0s5\" };\n    ioctl(sock, SIOCGIFINDEX, &req, sizeof(req)); // get 'eth0' if index\n\n    struct sockaddr_ll addr = {    \n        .sll_family = AF_PACKET,           // use packet-level \n        .sll_protocol = htons(ETH_P_ALL),  // accept only Ethernet\n        .sll_ifindex = req.ifr_ifindex     // device index instead of 'address'\n    };\n\n    if (-1==bind(sock, (struct sockaddr*)&addr, sizeof(addr))) {\n        perror(\"bind\"); exit(1);\n    }\n\n    /* Part II. Capture packets */\n\n    for (;;) {\n        char buffer[4096] = {};\n        \n        size_t cnt = recv(sock, buffer, sizeof(buffer), 0);\n        uint32_t from_ip, to_ip;\n\n        /* Extract addresses from IPv4 header */\n        memcpy(&from_ip, buffer+26, sizeof(from_ip));\n        memcpy(&to_ip, buffer+30, sizeof(to_ip));\n\n        /* Make them human-readable */\n        char from_addr[20] = {}, to_addr[20] = {};\n        inet_ntop(AF_INET, &from_ip, from_addr, sizeof(from_addr));\n        inet_ntop(AF_INET, &to_ip, to_addr, sizeof(to_addr));\n\n        printf(\"Got communication from %s to %s\\n\", from_addr, to_addr);\n    }\n}\n"
  },
  {
    "path": "lessons-supplementary/2021-2022/l19-bpf/example2.c",
    "content": "#include <inttypes.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n\n#include <arpa/inet.h>\n#include <net/if.h>\n#include <sys/ioctl.h>\n#include <sys/socket.h>\n#include <linux/if_packet.h>\n#include <linux/filter.h>\n#include <net/ethernet.h>\n\nint main(int argc, char *argv[]) {\n\n    /* Part I. Initialization */\n    \n    int sock = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL));\n    if (-1==sock) { perror(\"socket\"); exit(1); } \n\n    struct ifreq req = { .ifr_name = \"enp0s5\" };\n    ioctl(sock, SIOCGIFINDEX, &req, sizeof(req)); // get 'eth0' if index\n\n    struct sockaddr_ll addr = {    \n        .sll_family = AF_PACKET,           // use packet-level \n        .sll_protocol = htons(ETH_P_ALL),  // accept only Ethernet\n        .sll_ifindex = req.ifr_ifindex     // device index instead of 'address'\n    };\n\n    if (-1==bind(sock, (struct sockaddr*)&addr, sizeof(addr))) {\n        perror(\"bind\"); exit(1);\n    }\n\n    /* Attach cBPF program */\n\n    struct sock_filter code[] = {\n       #include \"filter.inc\"   \n    /* some command values like:\n       { 0x28,  0,  0, 0x0000000c },\n       { 0x15,  0,  5, 0x00000800 },\n              ........\n    */\n    };\n    \n    struct sock_fprog program = {\n        .filter = code,                      // program code\n        .len = sizeof(code)/sizeof(code[0])  // instructions count\n    };\n\n    if (0!=setsockopt(\n                      sock,             // socket FD\n                      SOL_SOCKET,       // the entire socket\n                      SO_ATTACH_FILTER,\t// 'attach filter' command\n                      &program,         // .... command data\n                      sizeof(program)   // .... command size\n                      ))\n    {\n        perror(\"SO_ATTACH_FILTER\"); exit(1);\n    }\n\n    /* Part II. Capture packets */\n\n    for (;;) {\n        char buffer[4096] = {};\n        \n        size_t cnt = recv(sock, buffer, sizeof(buffer), 0);\n        uint32_t from_ip, to_ip;\n\n        /* Extract addresses from IPv4 header */\n        memcpy(&from_ip, buffer+26, sizeof(from_ip));\n        memcpy(&to_ip, buffer+30, sizeof(to_ip));\n\n        /* Make them human-readable */\n        char from_addr[20] = {}, to_addr[20] = {};\n        inet_ntop(AF_INET, &from_ip, from_addr, sizeof(from_addr));\n        inet_ntop(AF_INET, &to_ip, to_addr, sizeof(to_addr));\n\n        printf(\"Got communication from %s to %s\\n\", from_addr, to_addr);\n    }\n}\n"
  },
  {
    "path": "lessons-supplementary/2021-2022/l19-bpf/filter.s",
    "content": "filter_google_dns:\n    ldh     [12]          ; 16 bit Eth proto value after MACs\n    jne     #0x0800, fail ; 0x0800 = IPv4\n    ldb     [23]          ; IP header one byte proto number\n    jne     #17, fail     ; 17 = UDP, 6 = TCP\n    ld      [30]          ; 32 bit IPv4 address value\n    jne     #0x08080808, fail   ; 8.8.8.8\nsuccess:\n    ret     #-1           ; -1 == 0xFFFFFFFF (maximum size)\nfail:\n    ret     #0            ; 0 is empty\n"
  },
  {
    "path": "lessons-supplementary/2021-2022/l20-ebpf/bpf_loader.c",
    "content": "#include <inttypes.h>\n#include <fcntl.h>\n#include <stdio.h>\n#include <linux/bpf.h>\n#include <string.h>\n#include <unistd.h>\n#include <sys/syscall.h>\n\n\nstatic const char License[] = \"GPL\";\n\n\nint main() {\n    int trace_bin_fd = open(\"bpf_program.bin\", O_RDONLY);\n\n    struct bpf_insn instructions[1000] = {};\n\n    size_t trace_bin_size = read(trace_bin_fd, instructions, sizeof(instructions));\n    close(trace_bin_fd);\n    size_t instructions_count = trace_bin_size / 8;\n\n    char error_log[4096] = {};\n    \n    union bpf_attr bpf_argument;\n    bpf_argument.prog_type = BPF_PROG_TYPE_XDP;\n    bpf_argument.insns = (uint64_t) instructions;\n    bpf_argument.insn_cnt = instructions_count;\n    bpf_argument.license = (uint64_t) License;\n    bpf_argument.log_level = 2;\n    bpf_argument.log_buf = (uint64_t) error_log;\n    bpf_argument.log_size = sizeof(error_log);\n\n    int bpf_fd = syscall(SYS_bpf, BPF_PROG_LOAD, &bpf_argument, sizeof(bpf_argument));\n\n    if (-1 == bpf_fd) {\n        perror(\"bpf failed\");\n        fprintf(stderr, \"Program load failed: %s\\n\", error_log); _exit(1);\n    }\n}\n"
  },
  {
    "path": "lessons-supplementary/2021-2022/l20-ebpf/bpf_program.c",
    "content": "int some_bpf_program(void *ctx) {\n    while (1) {}\n    return 0;\n}\n\n"
  },
  {
    "path": "lessons-supplementary/2021-2022/l20-ebpf/call_some_func.c",
    "content": "#include <stdio.h>\n#include <unistd.h>\n\nvoid some_func() {\n    puts(\"I'm some function\");\n}\n\nint main() {\n    printf(\"Started some program with PID = %d\\n\", getpid());\n    while (1) {\n        getchar(); // like pause\n        some_func();\n    }\n}\n"
  },
  {
    "path": "lessons-supplementary/2021-2022/l20-ebpf/trace_call_time.c",
    "content": "BPF_HASH(start_times, u64);\n\nint start_tracing(struct pt_regs *ctx) {\n    u64 pid = bpf_get_current_pid_tgid();\n    u64 start = bpf_ktime_get_ns();\n    start_times.update(&pid, &start);\n    return 0;\n}\n\nint end_tracing(struct pt_regs *ctx) {\n    u64 pid = bpf_get_current_pid_tgid();\n    u64 *start = start_times.lookup(&pid);\n    /*if (0 == start) {\n      return 0;\n    }*/\n    u64 now = bpf_ktime_get_ns();\n    u64 delta = now - *start;\n    bpf_trace_printk(\"Function call time: %d\", delta);\n    return 0;\n}\n"
  },
  {
    "path": "lessons-supplementary/2021-2022/l20-ebpf/trace_some_func.c",
    "content": "int trace_some_func(struct pt_regs *ctx) {\n  u64 pid = bpf_get_current_pid_tgid();\n  bpf_trace_printk(\"Running traccee for PID = %d\", pid);\n  return 0;\n}\n"
  },
  {
    "path": "lessons-supplementary/2021-2022/l20-ebpf/trace_syscall.c",
    "content": "#include <uapi/linux/ptrace.h>\n\nint trace_execv(struct pt_regs *ctx) {\n  char cmd[16];\n  bpf_get_current_comm(&cmd, sizeof(cmd));\n  // filter for command\n  if (! (cmd[0]=='b' && cmd[1]=='a' && cmd[2]=='s' && cmd[3]=='h' && cmd[4]=='\\0')) {\n    return 0;\n  }\n  \n  bpf_trace_printk(\"%s tried to exec\", cmd);\n\n  // bpf_override_return(ctx, 1);\n  \n  return 0;\n}\n"
  },
  {
    "path": "lessons-supplementary/2021-2022/l20-ebpf/trace_syscall_1.c",
    "content": "#include <uapi/linux/ptrace.h>\n\nint trace_execv(struct pt_regs *ctx) {\n  char cmd[16];\n  bpf_get_current_comm(&cmd, sizeof(cmd));\n  // filter for command\n  if (! (cmd[0]=='b' && cmd[1]=='a' && cmd[2]=='s' && cmd[3]=='h' && cmd[4]=='\\0')) {\n    return 0;\n  }\n  \n  bpf_trace_printk(\"%s tried to exec\", cmd);\n\n  bpf_override_return(ctx, 1);\n  \n  return 0;\n}\n"
  },
  {
    "path": "lessons-supplementary/2021-2022/l21-libraries/ctor_dtor/module.c",
    "content": "#include <stdio.h>\n\nvoid function() {\n    puts(\"I'm a function into module\");\n}\n\n__attribute__((constructor))\nstatic void initialize() {\n    puts(\"I'm a module constructor, called once\");\n}\n\n__attribute__((destructor))\nstatic void finalize() {\n    puts(\"I'm a module destructor, it's cool that I'm called\");\n}\n"
  },
  {
    "path": "lessons-supplementary/2021-2022/l21-libraries/ctor_dtor/run_lib.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n\n#include <dlfcn.h>\n\ntypedef void (*func_t)();\n\nint main(int argc, char *argv[]) {\n    const char *lib_name = argv[1];\n    const char *func_name = argv[2];\n\n    printf(\"Loaing library %s\\n\", lib_name);\n    void *lib = dlopen(lib_name, RTLD_LAZY|RTLD_LOCAL);\n    if (NULL == lib) {\n        fprintf(stderr, \"Cant load libary %s: %s\\n\",\n                lib_name, dlerror());\n        exit(1);\n    }\n    printf(\"Loaded library %s\\n\", lib_name);\n\n    func_t func = dlsym(lib, func_name);\n    if (NULL == func) {\n        fprintf(stderr, \"Cant find symbol %s: %s\\n\",\n                func_name, dlerror());\n        exit(1);\n    }\n\n    func();\n\n    printf(\"Unloading library %s\\n\", lib_name);\n    if (0 == dlclose(lib)) {\n        printf(\"Unloaded library %s\\n\", lib_name);\n    } else {\n        printf(\"Library %s still in use and not unloaded\\n\", lib_name);\n    }\n}\n"
  },
  {
    "path": "lessons-supplementary/2021-2022/l21-libraries/export_func_by_name/main.c",
    "content": "#include <stdio.h>\n\n#include <dlfcn.h>\n\nvoid cat() { puts(\"Meaow\"); }\nvoid dog() { puts(\"Huff!\"); }\n\ntypedef void (*func_t)();\n\nint main() {\n    char name[4096] = {};\n    printf(\"Please enter animal kind: \");\n    scanf(\"%s\", name);\n\n    void *myself = dlopen(NULL, RTLD_NOW|RTLD_GLOBAL);\n    func_t func = dlsym(myself, name);\n\n    func();\n}\n"
  },
  {
    "path": "lessons-supplementary/2021-2022/l21-libraries/runnable_lib/main.c",
    "content": "#include <stdio.h>\n#include <unistd.h>\n\n__attribute__((section(\".interp\")))\nconst char service_interp[]\n              = \"/lib/ld-linux-aarch64.so.1\";\n\nvoid function() {\n    puts(\"I'm a function!\");\n}\n\nvoid custom_start() {\n    function();\n    _exit(0);\n}\n"
  },
  {
    "path": "lessons-supplementary/2021-2022/l21-libraries/use_dlopen/run_function.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n\n#include <dlfcn.h>\n\ntypedef double (*func_t)(double);\n\nint main(int argc, char* argv[]) {\n    const char *lib_name = argv[1];\n    const char *func_name = argv[2];\n    double argument = strtod(argv[3], NULL);\n\n    puts(\"Press Enter to load library.\");\n    getchar(); // like pause\n    \n    void *lib = dlopen(lib_name, RTLD_NOW|RTLD_LOCAL);\n    if (NULL == lib) {\n        fprintf(stderr, \"Failed to load library: %s\\n\", dlerror());\n        exit(1);\n    }\n    else {\n        printf(\"Library %s loaded at %zx. Press Enter to continue.\\n\",\n               lib_name, lib);\n        getchar(); // like pause\n    }\n    \n    func_t func = dlsym(lib, func_name);\n    if (NULL == func) {\n        fprintf(stderr, \"Failed to find function: %s\\n\", dlerror());\n        exit(1);\n    }\n    else {\n        printf(\"Found %s in library at %zx. Press Enter to continue.\\n\",\n               func_name, func);\n        getchar(); // like pause\n    }\n    \n    double value = func(argument);\n    printf(\"func(%f) = %f\\n\", argument, value);\n\n    dlclose(lib);\n}\n"
  },
  {
    "path": "lessons-supplementary/2021-2022/l21-libraries/use_mmap/plugin.c",
    "content": "double farenheit_to_celsius(double F) {\n    double C = (F - 32) * 5./9.;\n}\n"
  },
  {
    "path": "lessons-supplementary/2021-2022/l21-libraries/use_mmap/run.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n\n#include <fcntl.h>\n#include <sys/mman.h>\n#include <sys/stat.h>\n#include <unistd.h>\n\ntypedef double (*func_t)(double);\n\nint main(int argc, char *argv[]) {\n    const char *file_name = argv[1];\n    double argument = strtod(argv[2], NULL);\n\n    int fd = open(file_name, O_RDONLY);\n    struct stat st = {};\n    fstat(fd, &st);\n    func_t func = mmap(NULL, st.st_size,\n                       PROT_READ|PROT_EXEC,\n                       MAP_PRIVATE, fd, 0);\n    close(fd);\n    if (MAP_FAILED == func) { perror(\"mmap failed\"); exit(1); }\n    double result = func(argument);\n    printf(\"func(%f) = %f\\n\", argument, result);\n    munmap(func, st.st_size);\n}\n"
  },
  {
    "path": "lessons-supplementary/2021-2022/l21-libraries/use_rpath/library.c",
    "content": "#include <stdio.h>\n\nvoid function() {\n    puts(\"I'm a function from library\");\n}\n"
  },
  {
    "path": "lessons-supplementary/2021-2022/l21-libraries/use_rpath/program.c",
    "content": "extern void function();\n\nint main() {\n    function();\n}\n"
  },
  {
    "path": "lessons-supplementary/2021-2022/l23-grpc/CMakeLists.txt",
    "content": "cmake_minimum_required(VERSION 3.21)\nproject(grpc_tailbook_example)\n\nset(CMAKE_CXX_STANDARD 14)\nset(CMAKE_VERBOSE_MAKEFILE TRUE)\n\nfind_package(PkgConfig REQUIRED)\n\npkg_check_modules(GRPC_CXX REQUIRED grpc++)\npkg_check_modules(PROTOBUF REQUIRED protobuf)\n\ninclude_directories(${PROTOBUF_INCLUDE_DIRS})\ninclude_directories(${GRPC_CXX_INCLUDE_DIRS})\ninclude_directories(${CMAKE_CURRENT_BINARY_DIR})\n\nset(SOURCES\n    cplusplus/profile_server_main.cpp\n    cplusplus/profile_service.cpp\n    cplusplus/profile_service.h\n)\nset(PROTO_SOURCES\n    ${CMAKE_CURRENT_BINARY_DIR}/social_network.pb.cc\n    ${CMAKE_CURRENT_BINARY_DIR}/social_network.pb.h\n)\nset(GRPC_SOURCES\n    ${CMAKE_CURRENT_BINARY_DIR}/social_network.grpc.pb.cc\n    ${CMAKE_CURRENT_BINARY_DIR}/social_network.grpc.pb.h\n)\n\nadd_custom_command(\n    OUTPUT ${PROTO_SOURCES} ${GRPC_SOURCES}\n    COMMAND protoc\n    ARGS\n        --cpp_out=${CMAKE_CURRENT_BINARY_DIR}\n        --go_out=${CMAKE_CURRENT_SOURCE_DIR}/go\n        --python_out=${CMAKE_CURRENT_SOURCE_DIR}/python\n        --grpc_python_out=${CMAKE_CURRENT_SOURCE_DIR}/python\n        --dart_out=grpc:${CMAKE_CURRENT_SOURCE_DIR}/dart/lib/src/generated\n        --grpc_out=${CMAKE_CURRENT_BINARY_DIR}\n        --go_grpc_out=${CMAKE_CURRENT_SOURCE_DIR}/go\n\n        --plugin=protoc-gen-grpc=$ENV{HOME}/bin/grpc_cpp_plugin\n        --plugin=protoc-gen-grpc_python=$ENV{HOME}/bin/grpc_python_plugin\n        --plugin=protoc-gen-go=$ENV{GOPATH}/bin/protoc-gen-go\n        --plugin=protoc-gen-go_grpc=$ENV{GOPATH}/bin/protoc-gen-go-grpc\n        --plugin=protoc-gen-dart=$ENV{HOME}/.pub-cache/bin/protoc-gen-dart\n\n        -I ${CMAKE_CURRENT_SOURCE_DIR}\n        social_network.proto\n    DEPENDS social_network.proto\n)\n\nadd_executable(\n    profile_service\n    ${SOURCES}\n    ${PROTO_SOURCES}\n    ${GRPC_SOURCES}\n)\ntarget_link_libraries(profile_service ${PROTOBUF_LDFLAGS} ${GRPC_CXX_LDFLAGS})\n"
  },
  {
    "path": "lessons-supplementary/2021-2022/l23-grpc/cplusplus/profile_server_main.cpp",
    "content": "#include \"profile_service.h\"\n#include <string>\n#include <grpcpp/grpcpp.h>\n\nint main() {\n    const std::string listen_address = \"localhost:1901\";\n    ProfileManagerService service;\n    grpc::ServerBuilder builder;\n    builder.AddListeningPort(listen_address, grpc::InsecureServerCredentials());\n    builder.RegisterService(&service);\n    std::unique_ptr<grpc::Server> server = builder.BuildAndStart();\n    server->Wait();\n    return 0;\n}\n"
  },
  {
    "path": "lessons-supplementary/2021-2022/l23-grpc/cplusplus/profile_service.cpp",
    "content": "#include \"profile_service.h\"\n\nusing grpc::Status;\nusing grpc::ServerContext;\n\nStatus ProfileManagerService::GetUserProfile(ServerContext *context, const User *request, UserProfile *response) {\n    Status status = Status::OK;\n    if (request->login() == \"vovan\") {\n        response->set_login(request->login());\n        response->set_first_name(\"Вовка\");\n        response->set_last_name(\"Пу\");\n        response->set_age(69);\n        response->set_gender(Gender::Male);\n        response->set_height(170.0);\n\n    } else if (request->login() == \"dimon\") {\n        response->set_login(request->login());\n        response->set_first_name(\"Дима\");\n        response->set_last_name(\"Ме\");\n        response->set_gender(Gender::Male);\n        response->set_age(56);\n        Phone* phone = response->add_phones();\n        phone->set_number(70001234567);\n        phone->set_phone_type(PhoneType::Mobile);\n    } else {\n        status = Status(grpc::StatusCode::NOT_FOUND, \"Profile not found\", request->login());\n    }\n    return status;\n}\n"
  },
  {
    "path": "lessons-supplementary/2021-2022/l23-grpc/cplusplus/profile_service.h",
    "content": "#ifndef GRPC_CHAT_EXAMPLE_PROFILE_SERVICE_H\n#define GRPC_CHAT_EXAMPLE_PROFILE_SERVICE_H\n\n#include \"social_network.grpc.pb.h\"\n\nclass ProfileManagerService: public ProfileManager::Service\n{\npublic:\n    grpc::Status\n    GetUserProfile(::grpc::ServerContext *context, const ::User *request, ::UserProfile *response) override;\n\npublic:\n\n};\n\n#endif //GRPC_CHAT_EXAMPLE_PROFILE_SERVICE_H\n"
  },
  {
    "path": "lessons-supplementary/2021-2022/l23-grpc/dart/.gitignore",
    "content": "# Miscellaneous\n*.class\n*.log\n*.pyc\n*.swp\n.DS_Store\n.atom/\n.buildlog/\n.history\n.svn/\n\n# IntelliJ related\n*.iml\n*.ipr\n*.iws\n.idea/\n\n# The .vscode folder contains launch configuration and tasks you configure in\n# VS Code which you may wish to be included in version control, so this line\n# is commented out by default.\n#.vscode/\n\n# Flutter/Dart/Pub related\n**/doc/api/\n**/ios/Flutter/.last_build_id\n.dart_tool/\n.flutter-plugins\n.flutter-plugins-dependencies\n.packages\n.pub-cache/\n.pub/\n/build/\n\n# Web related\nlib/generated_plugin_registrant.dart\n\n# Symbolication related\napp.*.symbols\n\n# Obfuscation related\napp.*.map.json\n\n# Android Studio will place build artifacts here\n/android/app/debug\n/android/app/profile\n/android/app/release\n"
  },
  {
    "path": "lessons-supplementary/2021-2022/l23-grpc/dart/.metadata",
    "content": "# This file tracks properties of this Flutter project.\n# Used by Flutter tool to assess capabilities and perform upgrades etc.\n#\n# This file should be version controlled and should not be manually edited.\n\nversion:\n  revision: c860cba910319332564e1e9d470a17074c1f2dfd\n  channel: stable\n\nproject_type: app\n"
  },
  {
    "path": "lessons-supplementary/2021-2022/l23-grpc/dart/README.md",
    "content": "# dart\n\nA new Flutter project.\n\n## Getting Started\n\nThis project is a starting point for a Flutter application.\n\nA few resources to get you started if this is your first Flutter project:\n\n- [Lab: Write your first Flutter app](https://flutter.dev/docs/get-started/codelab)\n- [Cookbook: Useful Flutter samples](https://flutter.dev/docs/cookbook)\n\nFor help getting started with Flutter, view our\n[online documentation](https://flutter.dev/docs), which offers tutorials,\nsamples, guidance on mobile development, and a full API reference.\n"
  },
  {
    "path": "lessons-supplementary/2021-2022/l23-grpc/dart/analysis_options.yaml",
    "content": "# This file configures the analyzer, which statically analyzes Dart code to\n# check for errors, warnings, and lints.\n#\n# The issues identified by the analyzer are surfaced in the UI of Dart-enabled\n# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be\n# invoked from the command line by running `flutter analyze`.\n\n# The following line activates a set of recommended lints for Flutter apps,\n# packages, and plugins designed to encourage good coding practices.\ninclude: package:flutter_lints/flutter.yaml\n\nlinter:\n  # The lint rules applied to this project can be customized in the\n  # section below to disable rules from the `package:flutter_lints/flutter.yaml`\n  # included above or to enable additional rules. A list of all available lints\n  # and their documentation is published at\n  # https://dart-lang.github.io/linter/lints/index.html.\n  #\n  # Instead of disabling a lint rule for the entire project in the\n  # section below, it can also be suppressed for a single line of code\n  # or a specific dart file by using the `// ignore: name_of_lint` and\n  # `// ignore_for_file: name_of_lint` syntax on the line or in the file\n  # producing the lint.\n  rules:\n    # avoid_print: false  # Uncomment to disable the `avoid_print` rule\n    # prefer_single_quotes: true  # Uncomment to enable the `prefer_single_quotes` rule\n\n# Additional information about this file can be found at\n# https://dart.dev/guides/language/analysis-options\n"
  },
  {
    "path": "lessons-supplementary/2021-2022/l23-grpc/dart/lib/main.dart",
    "content": "import 'package:flutter/material.dart';\nimport 'package:grpc_demo/src/main_screen.dart';\n\nvoid main() {\n  final app = MainScreen(Uri.parse('http://localhost:8081'));\n  runApp(app);\n}"
  },
  {
    "path": "lessons-supplementary/2021-2022/l23-grpc/dart/lib/src/generated/social_network.pb.dart",
    "content": "///\n//  Generated code. Do not modify.\n//  source: social_network.proto\n//\n// @dart = 2.12\n// ignore_for_file: annotate_overrides,camel_case_types,unnecessary_const,non_constant_identifier_names,library_prefixes,unused_import,unused_shown_name,return_of_invalid_type,unnecessary_this,prefer_final_fields\n\nimport 'dart:core' as $core;\n\nimport 'package:fixnum/fixnum.dart' as $fixnum;\nimport 'package:protobuf/protobuf.dart' as $pb;\n\nimport 'social_network.pbenum.dart';\n\nexport 'social_network.pbenum.dart';\n\nclass User extends $pb.GeneratedMessage {\n  static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'User', createEmptyInstance: create)\n    ..aOS(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'login')\n    ..aOS(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'password')\n    ..hasRequiredFields = false\n  ;\n\n  User._() : super();\n  factory User({\n    $core.String? login,\n    $core.String? password,\n  }) {\n    final _result = create();\n    if (login != null) {\n      _result.login = login;\n    }\n    if (password != null) {\n      _result.password = password;\n    }\n    return _result;\n  }\n  factory User.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r);\n  factory User.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r);\n  @$core.Deprecated(\n  'Using this can add significant overhead to your binary. '\n  'Use [GeneratedMessageGenericExtensions.deepCopy] instead. '\n  'Will be removed in next major version')\n  User clone() => User()..mergeFromMessage(this);\n  @$core.Deprecated(\n  'Using this can add significant overhead to your binary. '\n  'Use [GeneratedMessageGenericExtensions.rebuild] instead. '\n  'Will be removed in next major version')\n  User copyWith(void Function(User) updates) => super.copyWith((message) => updates(message as User)) as User; // ignore: deprecated_member_use\n  $pb.BuilderInfo get info_ => _i;\n  @$core.pragma('dart2js:noInline')\n  static User create() => User._();\n  User createEmptyInstance() => create();\n  static $pb.PbList<User> createRepeated() => $pb.PbList<User>();\n  @$core.pragma('dart2js:noInline')\n  static User getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<User>(create);\n  static User? _defaultInstance;\n\n  @$pb.TagNumber(1)\n  $core.String get login => $_getSZ(0);\n  @$pb.TagNumber(1)\n  set login($core.String v) { $_setString(0, v); }\n  @$pb.TagNumber(1)\n  $core.bool hasLogin() => $_has(0);\n  @$pb.TagNumber(1)\n  void clearLogin() => clearField(1);\n\n  @$pb.TagNumber(2)\n  $core.String get password => $_getSZ(1);\n  @$pb.TagNumber(2)\n  set password($core.String v) { $_setString(1, v); }\n  @$pb.TagNumber(2)\n  $core.bool hasPassword() => $_has(1);\n  @$pb.TagNumber(2)\n  void clearPassword() => clearField(2);\n}\n\nclass Phone extends $pb.GeneratedMessage {\n  static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'Phone', createEmptyInstance: create)\n    ..e<PhoneType>(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'phoneType', $pb.PbFieldType.OE, defaultOrMaker: PhoneType.Home, valueOf: PhoneType.valueOf, enumValues: PhoneType.values)\n    ..a<$fixnum.Int64>(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'number', $pb.PbFieldType.OU6, defaultOrMaker: $fixnum.Int64.ZERO)\n    ..hasRequiredFields = false\n  ;\n\n  Phone._() : super();\n  factory Phone({\n    PhoneType? phoneType,\n    $fixnum.Int64? number,\n  }) {\n    final _result = create();\n    if (phoneType != null) {\n      _result.phoneType = phoneType;\n    }\n    if (number != null) {\n      _result.number = number;\n    }\n    return _result;\n  }\n  factory Phone.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r);\n  factory Phone.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r);\n  @$core.Deprecated(\n  'Using this can add significant overhead to your binary. '\n  'Use [GeneratedMessageGenericExtensions.deepCopy] instead. '\n  'Will be removed in next major version')\n  Phone clone() => Phone()..mergeFromMessage(this);\n  @$core.Deprecated(\n  'Using this can add significant overhead to your binary. '\n  'Use [GeneratedMessageGenericExtensions.rebuild] instead. '\n  'Will be removed in next major version')\n  Phone copyWith(void Function(Phone) updates) => super.copyWith((message) => updates(message as Phone)) as Phone; // ignore: deprecated_member_use\n  $pb.BuilderInfo get info_ => _i;\n  @$core.pragma('dart2js:noInline')\n  static Phone create() => Phone._();\n  Phone createEmptyInstance() => create();\n  static $pb.PbList<Phone> createRepeated() => $pb.PbList<Phone>();\n  @$core.pragma('dart2js:noInline')\n  static Phone getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<Phone>(create);\n  static Phone? _defaultInstance;\n\n  @$pb.TagNumber(1)\n  PhoneType get phoneType => $_getN(0);\n  @$pb.TagNumber(1)\n  set phoneType(PhoneType v) { setField(1, v); }\n  @$pb.TagNumber(1)\n  $core.bool hasPhoneType() => $_has(0);\n  @$pb.TagNumber(1)\n  void clearPhoneType() => clearField(1);\n\n  @$pb.TagNumber(2)\n  $fixnum.Int64 get number => $_getI64(1);\n  @$pb.TagNumber(2)\n  set number($fixnum.Int64 v) { $_setInt64(1, v); }\n  @$pb.TagNumber(2)\n  $core.bool hasNumber() => $_has(1);\n  @$pb.TagNumber(2)\n  void clearNumber() => clearField(2);\n}\n\nclass UserProfile extends $pb.GeneratedMessage {\n  static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'UserProfile', createEmptyInstance: create)\n    ..aOS(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'login')\n    ..aOS(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'firstName')\n    ..aOS(3, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'lastName')\n    ..a<$core.int>(4, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'age', $pb.PbFieldType.OU3)\n    ..a<$core.double>(5, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'height', $pb.PbFieldType.OD)\n    ..e<Gender>(6, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'gender', $pb.PbFieldType.OE, defaultOrMaker: Gender.Male, valueOf: Gender.valueOf, enumValues: Gender.values)\n    ..pc<Phone>(10, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'phones', $pb.PbFieldType.PM, subBuilder: Phone.create)\n    ..aOS(100, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'avatarUrl')\n    ..hasRequiredFields = false\n  ;\n\n  UserProfile._() : super();\n  factory UserProfile({\n    $core.String? login,\n    $core.String? firstName,\n    $core.String? lastName,\n    $core.int? age,\n    $core.double? height,\n    Gender? gender,\n    $core.Iterable<Phone>? phones,\n    $core.String? avatarUrl,\n  }) {\n    final _result = create();\n    if (login != null) {\n      _result.login = login;\n    }\n    if (firstName != null) {\n      _result.firstName = firstName;\n    }\n    if (lastName != null) {\n      _result.lastName = lastName;\n    }\n    if (age != null) {\n      _result.age = age;\n    }\n    if (height != null) {\n      _result.height = height;\n    }\n    if (gender != null) {\n      _result.gender = gender;\n    }\n    if (phones != null) {\n      _result.phones.addAll(phones);\n    }\n    if (avatarUrl != null) {\n      _result.avatarUrl = avatarUrl;\n    }\n    return _result;\n  }\n  factory UserProfile.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r);\n  factory UserProfile.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r);\n  @$core.Deprecated(\n  'Using this can add significant overhead to your binary. '\n  'Use [GeneratedMessageGenericExtensions.deepCopy] instead. '\n  'Will be removed in next major version')\n  UserProfile clone() => UserProfile()..mergeFromMessage(this);\n  @$core.Deprecated(\n  'Using this can add significant overhead to your binary. '\n  'Use [GeneratedMessageGenericExtensions.rebuild] instead. '\n  'Will be removed in next major version')\n  UserProfile copyWith(void Function(UserProfile) updates) => super.copyWith((message) => updates(message as UserProfile)) as UserProfile; // ignore: deprecated_member_use\n  $pb.BuilderInfo get info_ => _i;\n  @$core.pragma('dart2js:noInline')\n  static UserProfile create() => UserProfile._();\n  UserProfile createEmptyInstance() => create();\n  static $pb.PbList<UserProfile> createRepeated() => $pb.PbList<UserProfile>();\n  @$core.pragma('dart2js:noInline')\n  static UserProfile getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<UserProfile>(create);\n  static UserProfile? _defaultInstance;\n\n  @$pb.TagNumber(1)\n  $core.String get login => $_getSZ(0);\n  @$pb.TagNumber(1)\n  set login($core.String v) { $_setString(0, v); }\n  @$pb.TagNumber(1)\n  $core.bool hasLogin() => $_has(0);\n  @$pb.TagNumber(1)\n  void clearLogin() => clearField(1);\n\n  @$pb.TagNumber(2)\n  $core.String get firstName => $_getSZ(1);\n  @$pb.TagNumber(2)\n  set firstName($core.String v) { $_setString(1, v); }\n  @$pb.TagNumber(2)\n  $core.bool hasFirstName() => $_has(1);\n  @$pb.TagNumber(2)\n  void clearFirstName() => clearField(2);\n\n  @$pb.TagNumber(3)\n  $core.String get lastName => $_getSZ(2);\n  @$pb.TagNumber(3)\n  set lastName($core.String v) { $_setString(2, v); }\n  @$pb.TagNumber(3)\n  $core.bool hasLastName() => $_has(2);\n  @$pb.TagNumber(3)\n  void clearLastName() => clearField(3);\n\n  @$pb.TagNumber(4)\n  $core.int get age => $_getIZ(3);\n  @$pb.TagNumber(4)\n  set age($core.int v) { $_setUnsignedInt32(3, v); }\n  @$pb.TagNumber(4)\n  $core.bool hasAge() => $_has(3);\n  @$pb.TagNumber(4)\n  void clearAge() => clearField(4);\n\n  @$pb.TagNumber(5)\n  $core.double get height => $_getN(4);\n  @$pb.TagNumber(5)\n  set height($core.double v) { $_setDouble(4, v); }\n  @$pb.TagNumber(5)\n  $core.bool hasHeight() => $_has(4);\n  @$pb.TagNumber(5)\n  void clearHeight() => clearField(5);\n\n  @$pb.TagNumber(6)\n  Gender get gender => $_getN(5);\n  @$pb.TagNumber(6)\n  set gender(Gender v) { setField(6, v); }\n  @$pb.TagNumber(6)\n  $core.bool hasGender() => $_has(5);\n  @$pb.TagNumber(6)\n  void clearGender() => clearField(6);\n\n  @$pb.TagNumber(10)\n  $core.List<Phone> get phones => $_getList(6);\n\n  @$pb.TagNumber(100)\n  $core.String get avatarUrl => $_getSZ(7);\n  @$pb.TagNumber(100)\n  set avatarUrl($core.String v) { $_setString(7, v); }\n  @$pb.TagNumber(100)\n  $core.bool hasAvatarUrl() => $_has(7);\n  @$pb.TagNumber(100)\n  void clearAvatarUrl() => clearField(100);\n}\n\nclass Message extends $pb.GeneratedMessage {\n  static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'Message', createEmptyInstance: create)\n    ..aOM<User>(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'sender', subBuilder: User.create)\n    ..aOM<User>(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'receiver', subBuilder: User.create)\n    ..aOS(3, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'content')\n    ..hasRequiredFields = false\n  ;\n\n  Message._() : super();\n  factory Message({\n    User? sender,\n    User? receiver,\n    $core.String? content,\n  }) {\n    final _result = create();\n    if (sender != null) {\n      _result.sender = sender;\n    }\n    if (receiver != null) {\n      _result.receiver = receiver;\n    }\n    if (content != null) {\n      _result.content = content;\n    }\n    return _result;\n  }\n  factory Message.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r);\n  factory Message.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r);\n  @$core.Deprecated(\n  'Using this can add significant overhead to your binary. '\n  'Use [GeneratedMessageGenericExtensions.deepCopy] instead. '\n  'Will be removed in next major version')\n  Message clone() => Message()..mergeFromMessage(this);\n  @$core.Deprecated(\n  'Using this can add significant overhead to your binary. '\n  'Use [GeneratedMessageGenericExtensions.rebuild] instead. '\n  'Will be removed in next major version')\n  Message copyWith(void Function(Message) updates) => super.copyWith((message) => updates(message as Message)) as Message; // ignore: deprecated_member_use\n  $pb.BuilderInfo get info_ => _i;\n  @$core.pragma('dart2js:noInline')\n  static Message create() => Message._();\n  Message createEmptyInstance() => create();\n  static $pb.PbList<Message> createRepeated() => $pb.PbList<Message>();\n  @$core.pragma('dart2js:noInline')\n  static Message getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<Message>(create);\n  static Message? _defaultInstance;\n\n  @$pb.TagNumber(1)\n  User get sender => $_getN(0);\n  @$pb.TagNumber(1)\n  set sender(User v) { setField(1, v); }\n  @$pb.TagNumber(1)\n  $core.bool hasSender() => $_has(0);\n  @$pb.TagNumber(1)\n  void clearSender() => clearField(1);\n  @$pb.TagNumber(1)\n  User ensureSender() => $_ensure(0);\n\n  @$pb.TagNumber(2)\n  User get receiver => $_getN(1);\n  @$pb.TagNumber(2)\n  set receiver(User v) { setField(2, v); }\n  @$pb.TagNumber(2)\n  $core.bool hasReceiver() => $_has(1);\n  @$pb.TagNumber(2)\n  void clearReceiver() => clearField(2);\n  @$pb.TagNumber(2)\n  User ensureReceiver() => $_ensure(1);\n\n  @$pb.TagNumber(3)\n  $core.String get content => $_getSZ(2);\n  @$pb.TagNumber(3)\n  set content($core.String v) { $_setString(2, v); }\n  @$pb.TagNumber(3)\n  $core.bool hasContent() => $_has(2);\n  @$pb.TagNumber(3)\n  void clearContent() => clearField(3);\n}\n\nclass Empty extends $pb.GeneratedMessage {\n  static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'Empty', createEmptyInstance: create)\n    ..hasRequiredFields = false\n  ;\n\n  Empty._() : super();\n  factory Empty() => create();\n  factory Empty.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r);\n  factory Empty.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r);\n  @$core.Deprecated(\n  'Using this can add significant overhead to your binary. '\n  'Use [GeneratedMessageGenericExtensions.deepCopy] instead. '\n  'Will be removed in next major version')\n  Empty clone() => Empty()..mergeFromMessage(this);\n  @$core.Deprecated(\n  'Using this can add significant overhead to your binary. '\n  'Use [GeneratedMessageGenericExtensions.rebuild] instead. '\n  'Will be removed in next major version')\n  Empty copyWith(void Function(Empty) updates) => super.copyWith((message) => updates(message as Empty)) as Empty; // ignore: deprecated_member_use\n  $pb.BuilderInfo get info_ => _i;\n  @$core.pragma('dart2js:noInline')\n  static Empty create() => Empty._();\n  Empty createEmptyInstance() => create();\n  static $pb.PbList<Empty> createRepeated() => $pb.PbList<Empty>();\n  @$core.pragma('dart2js:noInline')\n  static Empty getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<Empty>(create);\n  static Empty? _defaultInstance;\n}\n\n"
  },
  {
    "path": "lessons-supplementary/2021-2022/l23-grpc/dart/lib/src/generated/social_network.pbenum.dart",
    "content": "///\n//  Generated code. Do not modify.\n//  source: social_network.proto\n//\n// @dart = 2.12\n// ignore_for_file: annotate_overrides,camel_case_types,unnecessary_const,non_constant_identifier_names,library_prefixes,unused_import,unused_shown_name,return_of_invalid_type,unnecessary_this,prefer_final_fields\n\n// ignore_for_file: UNDEFINED_SHOWN_NAME\nimport 'dart:core' as $core;\nimport 'package:protobuf/protobuf.dart' as $pb;\n\nclass Gender extends $pb.ProtobufEnum {\n  static const Gender Male = Gender._(0, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'Male');\n  static const Gender Female = Gender._(1, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'Female');\n\n  static const $core.List<Gender> values = <Gender> [\n    Male,\n    Female,\n  ];\n\n  static final $core.Map<$core.int, Gender> _byValue = $pb.ProtobufEnum.initByValue(values);\n  static Gender? valueOf($core.int value) => _byValue[value];\n\n  const Gender._($core.int v, $core.String n) : super(v, n);\n}\n\nclass PhoneType extends $pb.ProtobufEnum {\n  static const PhoneType Home = PhoneType._(0, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'Home');\n  static const PhoneType Work = PhoneType._(1, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'Work');\n  static const PhoneType Mobile = PhoneType._(2, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'Mobile');\n\n  static const $core.List<PhoneType> values = <PhoneType> [\n    Home,\n    Work,\n    Mobile,\n  ];\n\n  static final $core.Map<$core.int, PhoneType> _byValue = $pb.ProtobufEnum.initByValue(values);\n  static PhoneType? valueOf($core.int value) => _byValue[value];\n\n  const PhoneType._($core.int v, $core.String n) : super(v, n);\n}\n\n"
  },
  {
    "path": "lessons-supplementary/2021-2022/l23-grpc/dart/lib/src/generated/social_network.pbgrpc.dart",
    "content": "///\n//  Generated code. Do not modify.\n//  source: social_network.proto\n//\n// @dart = 2.12\n// ignore_for_file: annotate_overrides,camel_case_types,unnecessary_const,non_constant_identifier_names,library_prefixes,unused_import,unused_shown_name,return_of_invalid_type,unnecessary_this,prefer_final_fields\n\nimport 'dart:async' as $async;\n\nimport 'dart:core' as $core;\n\nimport 'package:grpc/service_api.dart' as $grpc;\nimport 'social_network.pb.dart' as $0;\nexport 'social_network.pb.dart';\n\nclass ProfileManagerClient extends $grpc.Client {\n  static final _$getUserProfile = $grpc.ClientMethod<$0.User, $0.UserProfile>(\n      '/ProfileManager/GetUserProfile',\n      ($0.User value) => value.writeToBuffer(),\n      ($core.List<$core.int> value) => $0.UserProfile.fromBuffer(value));\n\n  ProfileManagerClient($grpc.ClientChannel channel,\n      {$grpc.CallOptions? options,\n      $core.Iterable<$grpc.ClientInterceptor>? interceptors})\n      : super(channel, options: options, interceptors: interceptors);\n\n  $grpc.ResponseFuture<$0.UserProfile> getUserProfile($0.User request,\n      {$grpc.CallOptions? options}) {\n    return $createUnaryCall(_$getUserProfile, request, options: options);\n  }\n}\n\nabstract class ProfileManagerServiceBase extends $grpc.Service {\n  $core.String get $name => 'ProfileManager';\n\n  ProfileManagerServiceBase() {\n    $addMethod($grpc.ServiceMethod<$0.User, $0.UserProfile>(\n        'GetUserProfile',\n        getUserProfile_Pre,\n        false,\n        false,\n        ($core.List<$core.int> value) => $0.User.fromBuffer(value),\n        ($0.UserProfile value) => value.writeToBuffer()));\n  }\n\n  $async.Future<$0.UserProfile> getUserProfile_Pre(\n      $grpc.ServiceCall call, $async.Future<$0.User> request) async {\n    return getUserProfile(call, await request);\n  }\n\n  $async.Future<$0.UserProfile> getUserProfile(\n      $grpc.ServiceCall call, $0.User request);\n}\n\nclass ChatServiceClient extends $grpc.Client {\n  static final _$sendMessage = $grpc.ClientMethod<$0.Message, $0.Empty>(\n      '/ChatService/SendMessage',\n      ($0.Message value) => value.writeToBuffer(),\n      ($core.List<$core.int> value) => $0.Empty.fromBuffer(value));\n  static final _$receiveMessages = $grpc.ClientMethod<$0.User, $0.Message>(\n      '/ChatService/ReceiveMessages',\n      ($0.User value) => value.writeToBuffer(),\n      ($core.List<$core.int> value) => $0.Message.fromBuffer(value));\n\n  ChatServiceClient($grpc.ClientChannel channel,\n      {$grpc.CallOptions? options,\n      $core.Iterable<$grpc.ClientInterceptor>? interceptors})\n      : super(channel, options: options, interceptors: interceptors);\n\n  $grpc.ResponseFuture<$0.Empty> sendMessage($0.Message request,\n      {$grpc.CallOptions? options}) {\n    return $createUnaryCall(_$sendMessage, request, options: options);\n  }\n\n  $grpc.ResponseStream<$0.Message> receiveMessages($0.User request,\n      {$grpc.CallOptions? options}) {\n    return $createStreamingCall(\n        _$receiveMessages, $async.Stream.fromIterable([request]),\n        options: options);\n  }\n}\n\nabstract class ChatServiceBase extends $grpc.Service {\n  $core.String get $name => 'ChatService';\n\n  ChatServiceBase() {\n    $addMethod($grpc.ServiceMethod<$0.Message, $0.Empty>(\n        'SendMessage',\n        sendMessage_Pre,\n        false,\n        false,\n        ($core.List<$core.int> value) => $0.Message.fromBuffer(value),\n        ($0.Empty value) => value.writeToBuffer()));\n    $addMethod($grpc.ServiceMethod<$0.User, $0.Message>(\n        'ReceiveMessages',\n        receiveMessages_Pre,\n        false,\n        true,\n        ($core.List<$core.int> value) => $0.User.fromBuffer(value),\n        ($0.Message value) => value.writeToBuffer()));\n  }\n\n  $async.Future<$0.Empty> sendMessage_Pre(\n      $grpc.ServiceCall call, $async.Future<$0.Message> request) async {\n    return sendMessage(call, await request);\n  }\n\n  $async.Stream<$0.Message> receiveMessages_Pre(\n      $grpc.ServiceCall call, $async.Future<$0.User> request) async* {\n    yield* receiveMessages(call, await request);\n  }\n\n  $async.Future<$0.Empty> sendMessage(\n      $grpc.ServiceCall call, $0.Message request);\n  $async.Stream<$0.Message> receiveMessages(\n      $grpc.ServiceCall call, $0.User request);\n}\n"
  },
  {
    "path": "lessons-supplementary/2021-2022/l23-grpc/dart/lib/src/generated/social_network.pbjson.dart",
    "content": "///\n//  Generated code. Do not modify.\n//  source: social_network.proto\n//\n// @dart = 2.12\n// ignore_for_file: annotate_overrides,camel_case_types,unnecessary_const,non_constant_identifier_names,library_prefixes,unused_import,unused_shown_name,return_of_invalid_type,unnecessary_this,prefer_final_fields,deprecated_member_use_from_same_package\n\nimport 'dart:core' as $core;\nimport 'dart:convert' as $convert;\nimport 'dart:typed_data' as $typed_data;\n@$core.Deprecated('Use genderDescriptor instead')\nconst Gender$json = const {\n  '1': 'Gender',\n  '2': const [\n    const {'1': 'Male', '2': 0},\n    const {'1': 'Female', '2': 1},\n  ],\n};\n\n/// Descriptor for `Gender`. Decode as a `google.protobuf.EnumDescriptorProto`.\nfinal $typed_data.Uint8List genderDescriptor = $convert.base64Decode('CgZHZW5kZXISCAoETWFsZRAAEgoKBkZlbWFsZRAB');\n@$core.Deprecated('Use phoneTypeDescriptor instead')\nconst PhoneType$json = const {\n  '1': 'PhoneType',\n  '2': const [\n    const {'1': 'Home', '2': 0},\n    const {'1': 'Work', '2': 1},\n    const {'1': 'Mobile', '2': 2},\n  ],\n};\n\n/// Descriptor for `PhoneType`. Decode as a `google.protobuf.EnumDescriptorProto`.\nfinal $typed_data.Uint8List phoneTypeDescriptor = $convert.base64Decode('CglQaG9uZVR5cGUSCAoESG9tZRAAEggKBFdvcmsQARIKCgZNb2JpbGUQAg==');\n@$core.Deprecated('Use userDescriptor instead')\nconst User$json = const {\n  '1': 'User',\n  '2': const [\n    const {'1': 'login', '3': 1, '4': 1, '5': 9, '10': 'login'},\n    const {'1': 'password', '3': 2, '4': 1, '5': 9, '9': 0, '10': 'password', '17': true},\n  ],\n  '8': const [\n    const {'1': '_password'},\n  ],\n};\n\n/// Descriptor for `User`. Decode as a `google.protobuf.DescriptorProto`.\nfinal $typed_data.Uint8List userDescriptor = $convert.base64Decode('CgRVc2VyEhQKBWxvZ2luGAEgASgJUgVsb2dpbhIfCghwYXNzd29yZBgCIAEoCUgAUghwYXNzd29yZIgBAUILCglfcGFzc3dvcmQ=');\n@$core.Deprecated('Use phoneDescriptor instead')\nconst Phone$json = const {\n  '1': 'Phone',\n  '2': const [\n    const {'1': 'phone_type', '3': 1, '4': 1, '5': 14, '6': '.PhoneType', '10': 'phoneType'},\n    const {'1': 'number', '3': 2, '4': 1, '5': 4, '10': 'number'},\n  ],\n};\n\n/// Descriptor for `Phone`. Decode as a `google.protobuf.DescriptorProto`.\nfinal $typed_data.Uint8List phoneDescriptor = $convert.base64Decode('CgVQaG9uZRIpCgpwaG9uZV90eXBlGAEgASgOMgouUGhvbmVUeXBlUglwaG9uZVR5cGUSFgoGbnVtYmVyGAIgASgEUgZudW1iZXI=');\n@$core.Deprecated('Use userProfileDescriptor instead')\nconst UserProfile$json = const {\n  '1': 'UserProfile',\n  '2': const [\n    const {'1': 'login', '3': 1, '4': 1, '5': 9, '10': 'login'},\n    const {'1': 'first_name', '3': 2, '4': 1, '5': 9, '10': 'firstName'},\n    const {'1': 'last_name', '3': 3, '4': 1, '5': 9, '10': 'lastName'},\n    const {'1': 'age', '3': 4, '4': 1, '5': 13, '10': 'age'},\n    const {'1': 'height', '3': 5, '4': 1, '5': 1, '10': 'height'},\n    const {'1': 'gender', '3': 6, '4': 1, '5': 14, '6': '.Gender', '10': 'gender'},\n    const {'1': 'phones', '3': 10, '4': 3, '5': 11, '6': '.Phone', '10': 'phones'},\n    const {'1': 'avatar_url', '3': 100, '4': 1, '5': 9, '9': 0, '10': 'avatarUrl', '17': true},\n  ],\n  '8': const [\n    const {'1': '_avatar_url'},\n  ],\n};\n\n/// Descriptor for `UserProfile`. Decode as a `google.protobuf.DescriptorProto`.\nfinal $typed_data.Uint8List userProfileDescriptor = $convert.base64Decode('CgtVc2VyUHJvZmlsZRIUCgVsb2dpbhgBIAEoCVIFbG9naW4SHQoKZmlyc3RfbmFtZRgCIAEoCVIJZmlyc3ROYW1lEhsKCWxhc3RfbmFtZRgDIAEoCVIIbGFzdE5hbWUSEAoDYWdlGAQgASgNUgNhZ2USFgoGaGVpZ2h0GAUgASgBUgZoZWlnaHQSHwoGZ2VuZGVyGAYgASgOMgcuR2VuZGVyUgZnZW5kZXISHgoGcGhvbmVzGAogAygLMgYuUGhvbmVSBnBob25lcxIiCgphdmF0YXJfdXJsGGQgASgJSABSCWF2YXRhclVybIgBAUINCgtfYXZhdGFyX3VybA==');\n@$core.Deprecated('Use messageDescriptor instead')\nconst Message$json = const {\n  '1': 'Message',\n  '2': const [\n    const {'1': 'sender', '3': 1, '4': 1, '5': 11, '6': '.User', '10': 'sender'},\n    const {'1': 'receiver', '3': 2, '4': 1, '5': 11, '6': '.User', '10': 'receiver'},\n    const {'1': 'content', '3': 3, '4': 1, '5': 9, '10': 'content'},\n  ],\n};\n\n/// Descriptor for `Message`. Decode as a `google.protobuf.DescriptorProto`.\nfinal $typed_data.Uint8List messageDescriptor = $convert.base64Decode('CgdNZXNzYWdlEh0KBnNlbmRlchgBIAEoCzIFLlVzZXJSBnNlbmRlchIhCghyZWNlaXZlchgCIAEoCzIFLlVzZXJSCHJlY2VpdmVyEhgKB2NvbnRlbnQYAyABKAlSB2NvbnRlbnQ=');\n@$core.Deprecated('Use emptyDescriptor instead')\nconst Empty$json = const {\n  '1': 'Empty',\n};\n\n/// Descriptor for `Empty`. Decode as a `google.protobuf.DescriptorProto`.\nfinal $typed_data.Uint8List emptyDescriptor = $convert.base64Decode('CgVFbXB0eQ==');\n"
  },
  {
    "path": "lessons-supplementary/2021-2022/l23-grpc/dart/lib/src/main_screen.dart",
    "content": "import 'package:flutter/material.dart';\nimport 'package:flutter/services.dart';\nimport 'package:grpc/grpc.dart';\nimport 'package:grpc/grpc_connection_interface.dart';\nimport 'package:grpc/grpc_or_grpcweb.dart';\nimport 'package:grpc/grpc_web.dart';\nimport 'package:grpc_demo/src/generated/social_network.pb.dart';\nimport 'package:grpc_demo/src/generated/social_network.pbgrpc.dart';\n\nclass MainScreen extends StatefulWidget {\n  final Uri grpcLocation;\n  const MainScreen(this.grpcLocation, {Key? key}) : super(key: key);\n\n  @override\n  State createState() => MainScreenState();\n}\n\nclass MainScreenState extends State<MainScreen> {\n\n  final TextEditingController _loginName = TextEditingController();\n  UserProfile? _loadedProfile;\n  String? _errorText;\n  late final ProfileManagerClient _profileManagerApi;\n\n\n  @override\n  void initState() {\n    super.initState();\n    final grpcChannel = GrpcWebClientChannel.xhr(widget.grpcLocation);\n    _profileManagerApi = ProfileManagerClient(grpcChannel);\n  }\n\n  void loadUserProfile() {\n    String login = _loginName.text;\n    final futureProfile = _profileManagerApi.getUserProfile(User(login: login));\n    futureProfile.then((UserProfile value) {\n      setState((){\n        _errorText = null;\n        _loadedProfile = value;\n      });\n    })\n    .onError((error, stackTrace) {\n      setState(() {\n        _loadedProfile = null;\n        _errorText = error.toString() + '\\n' + stackTrace.toString();\n      });\n    });\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    List<Widget> items = [];\n    final searchBox = Card(\n      child: Row(\n        mainAxisAlignment: MainAxisAlignment.center,\n        children: [\n          SizedBox(\n            width: 450,\n            height: 64,\n            child: TextField(\n              controller: _loginName,\n              onEditingComplete: loadUserProfile,\n            )\n          ),\n          ElevatedButton(\n            onPressed: loadUserProfile,\n            child: const Icon(Icons.search),\n          ),\n        ]\n      ),\n    );\n    items.add(searchBox);\n    if (_loadedProfile != null) {\n      items.add(buildProfile(context));\n    }\n    if (_errorText != null) {\n      items.add(Text(_errorText!,\n        style: TextStyle(\n          color: Theme.of(context).errorColor,\n        ),\n      ));\n    }\n\n    return MaterialApp(\n      title: 'gRPC Demo',\n      home: Scaffold(\n        appBar: AppBar(\n          title: const Text('gRPC Demo'),\n          centerTitle: true,\n        ),\n        body: Column(children: items),\n      ),\n    );\n  }\n\n  Widget buildProfile(BuildContext context) {\n    final theme = Theme.of(context);\n    List<Row> items = [];\n    void addRowItem(String label, String value) {\n      items.add(Row(\n        children: [\n          Text(label + ': ',\n            style: TextStyle(color: theme.primaryColor),\n            textAlign: TextAlign.right,\n          ),\n          Text(value),\n        ],\n      ));\n    }\n    addRowItem('First name', _loadedProfile!.firstName);\n    addRowItem('Last name', _loadedProfile!.lastName);\n    addRowItem('Age', '${_loadedProfile!.age} years');\n    return Card(\n      child: Column(children: items),\n    );\n  }\n\n}"
  },
  {
    "path": "lessons-supplementary/2021-2022/l23-grpc/dart/pubspec.yaml",
    "content": "name: grpc_demo\ndescription: A new Flutter project.\n\n# The following line prevents the package from being accidentally published to\n# pub.dev using `flutter pub publish`. This is preferred for private packages.\npublish_to: 'none' # Remove this line if you wish to publish to pub.dev\n\n# The following defines the version and build number for your application.\n# A version number is three numbers separated by dots, like 1.2.43\n# followed by an optional build number separated by a +.\n# Both the version and the builder number may be overridden in flutter\n# build by specifying --build-name and --build-number, respectively.\n# In Android, build-name is used as versionName while build-number used as versionCode.\n# Read more about Android versioning at https://developer.android.com/studio/publish/versioning\n# In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion.\n# Read more about iOS versioning at\n# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html\nversion: 1.0.0+1\n\nenvironment:\n  sdk: \">=2.16.2 <3.0.0\"\n\n# Dependencies specify other packages that your package needs in order to work.\n# To automatically upgrade your package dependencies to the latest versions\n# consider running `flutter pub upgrade --major-versions`. Alternatively,\n# dependencies can be manually updated by changing the version numbers below to\n# the latest version available on pub.dev. To see which dependencies have newer\n# versions available, run `flutter pub outdated`.\ndependencies:\n  grpc: ^3.0.2\n  flutter:\n    sdk: flutter\n\n\n  # The following adds the Cupertino Icons font to your application.\n  # Use with the CupertinoIcons class for iOS style icons.\n  cupertino_icons: ^1.0.2\n\ndev_dependencies:\n  flutter_test:\n    sdk: flutter\n\n  # The \"flutter_lints\" package below contains a set of recommended lints to\n  # encourage good coding practices. The lint set provided by the package is\n  # activated in the `analysis_options.yaml` file located at the root of your\n  # package. See that file for information about deactivating specific lint\n  # rules and activating additional ones.\n  flutter_lints: ^1.0.0\n\n# For information on the generic Dart part of this file, see the\n# following page: https://dart.dev/tools/pub/pubspec\n\n# The following section is specific to Flutter.\nflutter:\n\n  # The following line ensures that the Material Icons font is\n  # included with your application, so that you can use the icons in\n  # the material Icons class.\n  uses-material-design: true\n\n  # To add assets to your application, add an assets section, like this:\n  # assets:\n  #   - images/a_dot_burr.jpeg\n  #   - images/a_dot_ham.jpeg\n\n  # An image asset can refer to one or more resolution-specific \"variants\", see\n  # https://flutter.dev/assets-and-images/#resolution-aware.\n\n  # For details regarding adding assets from package dependencies, see\n  # https://flutter.dev/assets-and-images/#from-packages\n\n  # To add custom fonts to your application, add a fonts section here,\n  # in this \"flutter\" section. Each entry in this list should have a\n  # \"family\" key with the font family name, and a \"fonts\" key with a\n  # list giving the asset and other descriptors for the font. For\n  # example:\n  # fonts:\n  #   - family: Schyler\n  #     fonts:\n  #       - asset: fonts/Schyler-Regular.ttf\n  #       - asset: fonts/Schyler-Italic.ttf\n  #         style: italic\n  #   - family: Trajan Pro\n  #     fonts:\n  #       - asset: fonts/TrajanPro.ttf\n  #       - asset: fonts/TrajanPro_Bold.ttf\n  #         weight: 700\n  #\n  # For details regarding fonts from package dependencies,\n  # see https://flutter.dev/custom-fonts/#from-packages\n"
  },
  {
    "path": "lessons-supplementary/2021-2022/l23-grpc/dart/web/index.html",
    "content": "<!DOCTYPE html>\n<html>\n<head>\n  <!--\n    If you are serving your web app in a path other than the root, change the\n    href value below to reflect the base path you are serving from.\n\n    The path provided below has to start and end with a slash \"/\" in order for\n    it to work correctly.\n\n    For more details:\n    * https://developer.mozilla.org/en-US/docs/Web/HTML/Element/base\n\n    This is a placeholder for base href that will be replaced by the value of\n    the `--base-href` argument provided to `flutter build`.\n  -->\n  <base href=\"$FLUTTER_BASE_HREF\">\n\n  <meta charset=\"UTF-8\">\n  <meta content=\"IE=Edge\" http-equiv=\"X-UA-Compatible\">\n  <meta name=\"description\" content=\"A new Flutter project.\">\n\n  <!-- iOS meta tags & icons -->\n  <meta name=\"apple-mobile-web-app-capable\" content=\"yes\">\n  <meta name=\"apple-mobile-web-app-status-bar-style\" content=\"black\">\n  <meta name=\"apple-mobile-web-app-title\" content=\"dart\">\n  <link rel=\"apple-touch-icon\" href=\"icons/Icon-192.png\">\n\n  <!-- Favicon -->\n  <link rel=\"icon\" type=\"image/png\" href=\"favicon.png\"/>\n\n  <title>dart</title>\n  <link rel=\"manifest\" href=\"manifest.json\">\n</head>\n<body>\n  <!-- This script installs service_worker.js to provide PWA functionality to\n       application. For more information, see:\n       https://developers.google.com/web/fundamentals/primers/service-workers -->\n  <script>\n    var serviceWorkerVersion = null;\n    var scriptLoaded = false;\n    function loadMainDartJs() {\n      if (scriptLoaded) {\n        return;\n      }\n      scriptLoaded = true;\n      var scriptTag = document.createElement('script');\n      scriptTag.src = 'main.dart.js';\n      scriptTag.type = 'application/javascript';\n      document.body.append(scriptTag);\n    }\n\n    if ('serviceWorker' in navigator) {\n      // Service workers are supported. Use them.\n      window.addEventListener('load', function () {\n        // Wait for registration to finish before dropping the <script> tag.\n        // Otherwise, the browser will load the script multiple times,\n        // potentially different versions.\n        var serviceWorkerUrl = 'flutter_service_worker.js?v=' + serviceWorkerVersion;\n        navigator.serviceWorker.register(serviceWorkerUrl)\n          .then((reg) => {\n            function waitForActivation(serviceWorker) {\n              serviceWorker.addEventListener('statechange', () => {\n                if (serviceWorker.state == 'activated') {\n                  console.log('Installed new service worker.');\n                  loadMainDartJs();\n                }\n              });\n            }\n            if (!reg.active && (reg.installing || reg.waiting)) {\n              // No active web worker and we have installed or are installing\n              // one for the first time. Simply wait for it to activate.\n              waitForActivation(reg.installing || reg.waiting);\n            } else if (!reg.active.scriptURL.endsWith(serviceWorkerVersion)) {\n              // When the app updates the serviceWorkerVersion changes, so we\n              // need to ask the service worker to update.\n              console.log('New service worker available.');\n              reg.update();\n              waitForActivation(reg.installing);\n            } else {\n              // Existing service worker is still good.\n              console.log('Loading app from service worker.');\n              loadMainDartJs();\n            }\n          });\n\n        // If service worker doesn't succeed in a reasonable amount of time,\n        // fallback to plaint <script> tag.\n        setTimeout(() => {\n          if (!scriptLoaded) {\n            console.warn(\n              'Failed to load app from service worker. Falling back to plain <script> tag.',\n            );\n            loadMainDartJs();\n          }\n        }, 4000);\n      });\n    } else {\n      // Service workers not supported. Just drop the <script> tag.\n      loadMainDartJs();\n    }\n  </script>\n</body>\n</html>\n"
  },
  {
    "path": "lessons-supplementary/2021-2022/l23-grpc/dart/web/manifest.json",
    "content": "{\n    \"name\": \"dart\",\n    \"short_name\": \"dart\",\n    \"start_url\": \".\",\n    \"display\": \"standalone\",\n    \"background_color\": \"#0175C2\",\n    \"theme_color\": \"#0175C2\",\n    \"description\": \"A new Flutter project.\",\n    \"orientation\": \"portrait-primary\",\n    \"prefer_related_applications\": false,\n    \"icons\": [\n        {\n            \"src\": \"icons/Icon-192.png\",\n            \"sizes\": \"192x192\",\n            \"type\": \"image/png\"\n        },\n        {\n            \"src\": \"icons/Icon-512.png\",\n            \"sizes\": \"512x512\",\n            \"type\": \"image/png\"\n        },\n        {\n            \"src\": \"icons/Icon-maskable-192.png\",\n            \"sizes\": \"192x192\",\n            \"type\": \"image/png\",\n            \"purpose\": \"maskable\"\n        },\n        {\n            \"src\": \"icons/Icon-maskable-512.png\",\n            \"sizes\": \"512x512\",\n            \"type\": \"image/png\",\n            \"purpose\": \"maskable\"\n        }\n    ]\n}\n"
  },
  {
    "path": "lessons-supplementary/2021-2022/l23-grpc/go/chat_server_main.go",
    "content": "package main\n\nimport (\n\tpb \"chat_server/generated/chat_server\"\n\t\"google.golang.org/grpc\"\n\t\"log\"\n\t\"net\"\n)\n\nfunc main() {\n\tservice := ChatService{}\n\tservice.Initialize()\n\tvar opts []grpc.ServerOption\n\tgrpcServer := grpc.NewServer(opts...)\n\tpb.RegisterChatServiceServer(grpcServer, &service)\n\tlistener, err := net.Listen(\"tcp\", \"localhost:1902\")\n\tif err != nil {\n\t\tlog.Fatalf(\"Cant listen: %v\", err)\n\t}\n\tgrpcServer.Serve(listener)\n}\n"
  },
  {
    "path": "lessons-supplementary/2021-2022/l23-grpc/go/chat_service.go",
    "content": "package main\n\nimport (\n\tpb \"chat_server/generated/chat_server\"\n\t\"context\"\n\t\"google.golang.org/grpc/codes\"\n\t\"google.golang.org/grpc/status\"\n)\n\ntype ChatService struct {\n\tpb.UnimplementedChatServiceServer\n\tInboxes map[string]chan *pb.Message\n}\n\nfunc (service *ChatService) Initialize() {\n\tservice.Inboxes = make(map[string]chan *pb.Message)\n}\n\nfunc (service *ChatService) SendMessage(ctx context.Context, message *pb.Message) (*pb.Empty, error) {\n\treceiver := message.Receiver.Login\n\tinbox, exists := service.Inboxes[receiver]\n\tif !exists {\n\t\treturn nil, status.Error(codes.NotFound, \"User is not present online\")\n\t}\n\tinbox <- message\n\treturn &pb.Empty{}, nil\n}\n\nfunc (service *ChatService) ReceiveMessages(user *pb.User, sink pb.ChatService_ReceiveMessagesServer) error {\n\tinbox, exists := service.Inboxes[user.Login]\n\tif !exists {\n\t\tservice.Inboxes[user.Login] = make(chan *pb.Message)\n\t\tinbox = service.Inboxes[user.Login]\n\t} else {\n\t\tinbox = service.Inboxes[user.Login]\n\t}\n\tfor message := range inbox {\n\t\tif err := sink.Send(message); err != nil {\n\t\t\tbreak\n\t\t}\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "lessons-supplementary/2021-2022/l23-grpc/go/go.mod",
    "content": "module chat_server\n\ngo 1.18\n\nrequire (\n\tgoogle.golang.org/grpc v1.45.0\n\tgoogle.golang.org/protobuf v1.26.0\n)\n\nrequire (\n\tgithub.com/golang/protobuf v1.5.2 // indirect\n\tgolang.org/x/net v0.0.0-20200822124328-c89045814202 // indirect\n\tgolang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd // indirect\n\tgolang.org/x/text v0.3.0 // indirect\n\tgoogle.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013 // indirect\n)\n\nreplace chat_server v0.0.0 => ./generated/chat_server\n"
  },
  {
    "path": "lessons-supplementary/2021-2022/l23-grpc/python/main.py",
    "content": "import asyncio\n\nimport grpc\nimport social_network_pb2_grpc as gen\nimport social_network_pb2 as pb\n\nHOST = 'localhost'\nPROFILES_PORT = 1901\nCHAT_PORT = 1902\n\nprofiles_server = grpc.insecure_channel(f'{HOST}:{PROFILES_PORT}')\nprofile_manager = gen.ProfileManagerStub(profiles_server)\n\nchat_server = grpc.insecure_channel(f'{HOST}:{CHAT_PORT}')\nchat_service = gen.ChatServiceStub(chat_server)\n\n\ndef get_profile(name: str) -> pb.UserProfile:\n    user = pb.User(login=name)\n    return profile_manager.GetUserProfile(user)\n\n\ndef send_message(sender: str, receiver: str, text: str):\n    from_user = pb.User(login=sender)\n    to_user = pb.User(login=receiver)\n    message = pb.Message(sender=from_user, receiver=to_user, content=text)\n    chat_service.SendMessage(message)\n\n\ndef start_receiving(login: str):\n    user = pb.User(login=login)\n    messages_stream = chat_service.ReceiveMessages(user)\n    for message in messages_stream:\n        print(f'-- Got message from {message.sender.login}: {message.content}')\n\n"
  },
  {
    "path": "lessons-supplementary/2021-2022/l23-grpc/python/social_network_pb2.py",
    "content": "# -*- coding: utf-8 -*-\n# Generated by the protocol buffer compiler.  DO NOT EDIT!\n# source: social_network.proto\n\"\"\"Generated protocol buffer code.\"\"\"\nfrom google.protobuf.internal import enum_type_wrapper\nfrom google.protobuf import descriptor as _descriptor\nfrom google.protobuf import descriptor_pool as _descriptor_pool\nfrom google.protobuf import message as _message\nfrom google.protobuf import reflection as _reflection\nfrom google.protobuf import symbol_database as _symbol_database\n# @@protoc_insertion_point(imports)\n\n_sym_db = _symbol_database.Default()\n\n\n\n\nDESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\\n\\x14social_network.proto\\\"9\\n\\x04User\\x12\\r\\n\\x05login\\x18\\x01 \\x01(\\t\\x12\\x15\\n\\x08password\\x18\\x02 \\x01(\\tH\\x00\\x88\\x01\\x01\\x42\\x0b\\n\\t_password\\\"7\\n\\x05Phone\\x12\\x1e\\n\\nphone_type\\x18\\x01 \\x01(\\x0e\\x32\\n.PhoneType\\x12\\x0e\\n\\x06number\\x18\\x02 \\x01(\\x04\\\"\\xb9\\x01\\n\\x0bUserProfile\\x12\\r\\n\\x05login\\x18\\x01 \\x01(\\t\\x12\\x12\\n\\nfirst_name\\x18\\x02 \\x01(\\t\\x12\\x11\\n\\tlast_name\\x18\\x03 \\x01(\\t\\x12\\x0b\\n\\x03\\x61ge\\x18\\x04 \\x01(\\r\\x12\\x0e\\n\\x06height\\x18\\x05 \\x01(\\x01\\x12\\x17\\n\\x06gender\\x18\\x06 \\x01(\\x0e\\x32\\x07.Gender\\x12\\x16\\n\\x06phones\\x18\\n \\x03(\\x0b\\x32\\x06.Phone\\x12\\x17\\n\\navatar_url\\x18\\x64 \\x01(\\tH\\x00\\x88\\x01\\x01\\x42\\r\\n\\x0b_avatar_url\\\"J\\n\\x07Message\\x12\\x15\\n\\x06sender\\x18\\x01 \\x01(\\x0b\\x32\\x05.User\\x12\\x17\\n\\x08receiver\\x18\\x02 \\x01(\\x0b\\x32\\x05.User\\x12\\x0f\\n\\x07\\x63ontent\\x18\\x03 \\x01(\\t\\\"\\x07\\n\\x05\\x45mpty*\\x1e\\n\\x06Gender\\x12\\x08\\n\\x04Male\\x10\\x00\\x12\\n\\n\\x06\\x46\\x65male\\x10\\x01*+\\n\\tPhoneType\\x12\\x08\\n\\x04Home\\x10\\x00\\x12\\x08\\n\\x04Work\\x10\\x01\\x12\\n\\n\\x06Mobile\\x10\\x02\\x32\\x37\\n\\x0eProfileManager\\x12%\\n\\x0eGetUserProfile\\x12\\x05.User\\x1a\\x0c.UserProfile2T\\n\\x0b\\x43hatService\\x12\\x1f\\n\\x0bSendMessage\\x12\\x08.Message\\x1a\\x06.Empty\\x12$\\n\\x0fReceiveMessages\\x12\\x05.User\\x1a\\x08.Message0\\x01\\x42\\x17Z\\x15generated/chat_serverb\\x06proto3')\n\n_GENDER = DESCRIPTOR.enum_types_by_name['Gender']\nGender = enum_type_wrapper.EnumTypeWrapper(_GENDER)\n_PHONETYPE = DESCRIPTOR.enum_types_by_name['PhoneType']\nPhoneType = enum_type_wrapper.EnumTypeWrapper(_PHONETYPE)\nMale = 0\nFemale = 1\nHome = 0\nWork = 1\nMobile = 2\n\n\n_USER = DESCRIPTOR.message_types_by_name['User']\n_PHONE = DESCRIPTOR.message_types_by_name['Phone']\n_USERPROFILE = DESCRIPTOR.message_types_by_name['UserProfile']\n_MESSAGE = DESCRIPTOR.message_types_by_name['Message']\n_EMPTY = DESCRIPTOR.message_types_by_name['Empty']\nUser = _reflection.GeneratedProtocolMessageType('User', (_message.Message,), {\n  'DESCRIPTOR' : _USER,\n  '__module__' : 'social_network_pb2'\n  # @@protoc_insertion_point(class_scope:User)\n  })\n_sym_db.RegisterMessage(User)\n\nPhone = _reflection.GeneratedProtocolMessageType('Phone', (_message.Message,), {\n  'DESCRIPTOR' : _PHONE,\n  '__module__' : 'social_network_pb2'\n  # @@protoc_insertion_point(class_scope:Phone)\n  })\n_sym_db.RegisterMessage(Phone)\n\nUserProfile = _reflection.GeneratedProtocolMessageType('UserProfile', (_message.Message,), {\n  'DESCRIPTOR' : _USERPROFILE,\n  '__module__' : 'social_network_pb2'\n  # @@protoc_insertion_point(class_scope:UserProfile)\n  })\n_sym_db.RegisterMessage(UserProfile)\n\nMessage = _reflection.GeneratedProtocolMessageType('Message', (_message.Message,), {\n  'DESCRIPTOR' : _MESSAGE,\n  '__module__' : 'social_network_pb2'\n  # @@protoc_insertion_point(class_scope:Message)\n  })\n_sym_db.RegisterMessage(Message)\n\nEmpty = _reflection.GeneratedProtocolMessageType('Empty', (_message.Message,), {\n  'DESCRIPTOR' : _EMPTY,\n  '__module__' : 'social_network_pb2'\n  # @@protoc_insertion_point(class_scope:Empty)\n  })\n_sym_db.RegisterMessage(Empty)\n\n_PROFILEMANAGER = DESCRIPTOR.services_by_name['ProfileManager']\n_CHATSERVICE = DESCRIPTOR.services_by_name['ChatService']\nif _descriptor._USE_C_DESCRIPTORS == False:\n\n  DESCRIPTOR._options = None\n  DESCRIPTOR._serialized_options = b'Z\\025generated/chat_server'\n  _GENDER._serialized_start=413\n  _GENDER._serialized_end=443\n  _PHONETYPE._serialized_start=445\n  _PHONETYPE._serialized_end=488\n  _USER._serialized_start=24\n  _USER._serialized_end=81\n  _PHONE._serialized_start=83\n  _PHONE._serialized_end=138\n  _USERPROFILE._serialized_start=141\n  _USERPROFILE._serialized_end=326\n  _MESSAGE._serialized_start=328\n  _MESSAGE._serialized_end=402\n  _EMPTY._serialized_start=404\n  _EMPTY._serialized_end=411\n  _PROFILEMANAGER._serialized_start=490\n  _PROFILEMANAGER._serialized_end=545\n  _CHATSERVICE._serialized_start=547\n  _CHATSERVICE._serialized_end=631\n# @@protoc_insertion_point(module_scope)\n"
  },
  {
    "path": "lessons-supplementary/2021-2022/l23-grpc/python/social_network_pb2_grpc.py",
    "content": "# Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT!\n\"\"\"Client and server classes corresponding to protobuf-defined services.\"\"\"\nimport grpc\n\nimport social_network_pb2 as social__network__pb2\n\n\nclass ProfileManagerStub(object):\n    \"\"\"Missing associated documentation comment in .proto file.\"\"\"\n\n    def __init__(self, channel):\n        \"\"\"Constructor.\n\n        Args:\n            channel: A grpc.Channel.\n        \"\"\"\n        self.GetUserProfile = channel.unary_unary(\n                '/ProfileManager/GetUserProfile',\n                request_serializer=social__network__pb2.User.SerializeToString,\n                response_deserializer=social__network__pb2.UserProfile.FromString,\n                )\n\n\nclass ProfileManagerServicer(object):\n    \"\"\"Missing associated documentation comment in .proto file.\"\"\"\n\n    def GetUserProfile(self, request, context):\n        \"\"\"Missing associated documentation comment in .proto file.\"\"\"\n        context.set_code(grpc.StatusCode.UNIMPLEMENTED)\n        context.set_details('Method not implemented!')\n        raise NotImplementedError('Method not implemented!')\n\n\ndef add_ProfileManagerServicer_to_server(servicer, server):\n    rpc_method_handlers = {\n            'GetUserProfile': grpc.unary_unary_rpc_method_handler(\n                    servicer.GetUserProfile,\n                    request_deserializer=social__network__pb2.User.FromString,\n                    response_serializer=social__network__pb2.UserProfile.SerializeToString,\n            ),\n    }\n    generic_handler = grpc.method_handlers_generic_handler(\n            'ProfileManager', rpc_method_handlers)\n    server.add_generic_rpc_handlers((generic_handler,))\n\n\n # This class is part of an EXPERIMENTAL API.\nclass ProfileManager(object):\n    \"\"\"Missing associated documentation comment in .proto file.\"\"\"\n\n    @staticmethod\n    def GetUserProfile(request,\n            target,\n            options=(),\n            channel_credentials=None,\n            call_credentials=None,\n            insecure=False,\n            compression=None,\n            wait_for_ready=None,\n            timeout=None,\n            metadata=None):\n        return grpc.experimental.unary_unary(request, target, '/ProfileManager/GetUserProfile',\n            social__network__pb2.User.SerializeToString,\n            social__network__pb2.UserProfile.FromString,\n            options, channel_credentials,\n            insecure, call_credentials, compression, wait_for_ready, timeout, metadata)\n\n\nclass ChatServiceStub(object):\n    \"\"\"Missing associated documentation comment in .proto file.\"\"\"\n\n    def __init__(self, channel):\n        \"\"\"Constructor.\n\n        Args:\n            channel: A grpc.Channel.\n        \"\"\"\n        self.SendMessage = channel.unary_unary(\n                '/ChatService/SendMessage',\n                request_serializer=social__network__pb2.Message.SerializeToString,\n                response_deserializer=social__network__pb2.Empty.FromString,\n                )\n        self.ReceiveMessages = channel.unary_stream(\n                '/ChatService/ReceiveMessages',\n                request_serializer=social__network__pb2.User.SerializeToString,\n                response_deserializer=social__network__pb2.Message.FromString,\n                )\n\n\nclass ChatServiceServicer(object):\n    \"\"\"Missing associated documentation comment in .proto file.\"\"\"\n\n    def SendMessage(self, request, context):\n        \"\"\"Missing associated documentation comment in .proto file.\"\"\"\n        context.set_code(grpc.StatusCode.UNIMPLEMENTED)\n        context.set_details('Method not implemented!')\n        raise NotImplementedError('Method not implemented!')\n\n    def ReceiveMessages(self, request, context):\n        \"\"\"Missing associated documentation comment in .proto file.\"\"\"\n        context.set_code(grpc.StatusCode.UNIMPLEMENTED)\n        context.set_details('Method not implemented!')\n        raise NotImplementedError('Method not implemented!')\n\n\ndef add_ChatServiceServicer_to_server(servicer, server):\n    rpc_method_handlers = {\n            'SendMessage': grpc.unary_unary_rpc_method_handler(\n                    servicer.SendMessage,\n                    request_deserializer=social__network__pb2.Message.FromString,\n                    response_serializer=social__network__pb2.Empty.SerializeToString,\n            ),\n            'ReceiveMessages': grpc.unary_stream_rpc_method_handler(\n                    servicer.ReceiveMessages,\n                    request_deserializer=social__network__pb2.User.FromString,\n                    response_serializer=social__network__pb2.Message.SerializeToString,\n            ),\n    }\n    generic_handler = grpc.method_handlers_generic_handler(\n            'ChatService', rpc_method_handlers)\n    server.add_generic_rpc_handlers((generic_handler,))\n\n\n # This class is part of an EXPERIMENTAL API.\nclass ChatService(object):\n    \"\"\"Missing associated documentation comment in .proto file.\"\"\"\n\n    @staticmethod\n    def SendMessage(request,\n            target,\n            options=(),\n            channel_credentials=None,\n            call_credentials=None,\n            insecure=False,\n            compression=None,\n            wait_for_ready=None,\n            timeout=None,\n            metadata=None):\n        return grpc.experimental.unary_unary(request, target, '/ChatService/SendMessage',\n            social__network__pb2.Message.SerializeToString,\n            social__network__pb2.Empty.FromString,\n            options, channel_credentials,\n            insecure, call_credentials, compression, wait_for_ready, timeout, metadata)\n\n    @staticmethod\n    def ReceiveMessages(request,\n            target,\n            options=(),\n            channel_credentials=None,\n            call_credentials=None,\n            insecure=False,\n            compression=None,\n            wait_for_ready=None,\n            timeout=None,\n            metadata=None):\n        return grpc.experimental.unary_stream(request, target, '/ChatService/ReceiveMessages',\n            social__network__pb2.User.SerializeToString,\n            social__network__pb2.Message.FromString,\n            options, channel_credentials,\n            insecure, call_credentials, compression, wait_for_ready, timeout, metadata)\n"
  },
  {
    "path": "lessons-supplementary/2021-2022/l23-grpc/social_network.proto",
    "content": "syntax = \"proto3\";\n\n// Part I - implemented by C++ server\n\nmessage User {\n  string  login = 1;\n  optional string  password = 2;\n}\n\nenum Gender {\n  Male    = 0;\n  Female  = 1;\n}\n\nenum PhoneType {\n  Home    = 0;\n  Work    = 1;\n  Mobile  = 2;\n}\n\nmessage Phone {\n  PhoneType   phone_type = 1;\n  uint64      number = 2;\n}\n\nmessage UserProfile {\n  string  login = 1;\n  string  first_name = 2;\n  string  last_name = 3;\n  uint32  age = 4;\n  double  height = 5;\n  Gender  gender = 6;\n  repeated Phone  phones = 10;\n  optional string avatar_url = 100;\n}\n\nservice ProfileManager {\n  rpc GetUserProfile(User) returns (UserProfile);\n}\n\n// Part II - implemented by GoLang server\noption go_package = \"generated/chat_server\";\n\nmessage Message {\n  User    sender = 1;\n  User    receiver = 2;\n  string  content = 3;\n}\n\nmessage Empty {}\n\n\nservice ChatService {\n  rpc SendMessage(Message) returns (Empty);\n  rpc ReceiveMessages(User) returns (stream Message);\n}"
  },
  {
    "path": "lessons-supplementary/2021-2022/l24-kernel-fs/fuse/fusepy-memory-example.py",
    "content": "#!/usr/bin/env python\nfrom __future__ import print_function, absolute_import, division\n\nimport logging\n\nfrom collections import defaultdict\nfrom errno import ENOENT\nfrom stat import S_IFDIR, S_IFLNK, S_IFREG\nfrom sys import argv, exit\nfrom time import time\n\nfrom fuse import FUSE, FuseOSError, Operations, LoggingMixIn\n\nif not hasattr(__builtins__, 'bytes'):\n    bytes = str\n\nclass Memory(LoggingMixIn, Operations):\n    'Example memory filesystem. Supports only one level of files.'\n\n    def __init__(self):\n        self.files = {}\n        self.data = defaultdict(bytes)\n        self.fd = 0\n        now = time()\n        self.files['/'] = dict(st_mode=(S_IFDIR | 0o755), st_ctime=now,\n                               st_mtime=now, st_atime=now, st_nlink=2)\n\n    def chmod(self, path, mode):\n        self.files[path]['st_mode'] &= 0o770000\n        self.files[path]['st_mode'] |= mode\n        return 0\n\n    def chown(self, path, uid, gid):\n        self.files[path]['st_uid'] = uid\n        self.files[path]['st_gid'] = gid\n\n    def create(self, path, mode):\n        self.files[path] = dict(st_mode=(S_IFREG | mode), st_nlink=1,\n                                st_size=0, st_ctime=time(), st_mtime=time(),\n                                st_atime=time())\n\n        self.fd += 1\n        return self.fd\n\n    def getattr(self, path, fh=None):\n        if path not in self.files:\n            raise FuseOSError(ENOENT)\n\n        return self.files[path]\n\n    def getxattr(self, path, name, position=0):\n        attrs = self.files[path].get('attrs', {})\n\n        try:\n            return attrs[name]\n        except KeyError:\n            return ''       # Should return ENOATTR\n\n    def listxattr(self, path):\n        attrs = self.files[path].get('attrs', {})\n        return attrs.keys()\n\n    def mkdir(self, path, mode):\n        self.files[path] = dict(st_mode=(S_IFDIR | mode), st_nlink=2,\n                                st_size=0, st_ctime=time(), st_mtime=time(),\n                                st_atime=time())\n\n        self.files['/']['st_nlink'] += 1\n\n    def open(self, path, flags):\n        self.fd += 1\n        return self.fd\n\n    def read(self, path, size, offset, fh):\n        return self.data[path][offset:offset + size]\n\n    def readdir(self, path, fh):\n        return ['.', '..'] + [x[1:] for x in self.files if x != '/']\n\n    def readlink(self, path):\n        return self.data[path]\n\n    def removexattr(self, path, name):\n        attrs = self.files[path].get('attrs', {})\n\n        try:\n            del attrs[name]\n        except KeyError:\n            pass        # Should return ENOATTR\n\n    def rename(self, old, new):\n        self.files[new] = self.files.pop(old)\n\n    def rmdir(self, path):\n        self.files.pop(path)\n        self.files['/']['st_nlink'] -= 1\n\n    def setxattr(self, path, name, value, options, position=0):\n        # Ignore options\n        attrs = self.files[path].setdefault('attrs', {})\n        attrs[name] = value\n\n    def statfs(self, path):\n        return dict(f_bsize=512, f_blocks=4096, f_bavail=2048)\n\n    def symlink(self, target, source):\n        self.files[target] = dict(st_mode=(S_IFLNK | 0o777), st_nlink=1,\n                                  st_size=len(source))\n\n        self.data[target] = source\n\n    def truncate(self, path, length, fh=None):\n        self.data[path] = self.data[path][:length]\n        self.files[path]['st_size'] = length\n\n    def unlink(self, path):\n        self.files.pop(path)\n\n    def utimens(self, path, times=None):\n        now = time()\n        atime, mtime = times if times else (now, now)\n        self.files[path]['st_atime'] = atime\n        self.files[path]['st_mtime'] = mtime\n\n    def write(self, path, data, offset, fh):\n        self.data[path] = self.data[path][:offset] + data\n        self.files[path]['st_size'] = len(self.data[path])\n        return len(data)\n\n\nif __name__ == '__main__':\n    if len(argv) != 2:\n        print('usage: %s <mountpoint>' % argv[0])\n        exit(1)\n\n    logging.basicConfig(level=logging.DEBUG)\n    fuse = FUSE(Memory(), argv[1], foreground=True)\n    \n"
  },
  {
    "path": "lessons-supplementary/2021-2022/l24-kernel-fs/modules/Makefile",
    "content": "obj-m += hello.o hello-with-param.o\n\nall:\n\tmake -C /lib/modules/`uname -r`/build M=$(PWD) V=1 modules\n\nclean:\n\tmake -C /lib/modules/`uname -r`/build M=$(PWD) V=1 clean\n"
  },
  {
    "path": "lessons-supplementary/2021-2022/l24-kernel-fs/modules/hello-with-param.c",
    "content": "#include <linux/module.h>  // modules macros\n#include <linux/moduleparam.h>\n#include <linux/kernel.h>  // basic kernel routines\n\nint some_flag_value = 0;\n\nmodule_param(some_flag_value, int, 0600);\n\nint\ninit_module()\n{\n    if (some_flag_value) {\n        printk(\"Module created with some non-zero flag\\n\");\n    }\n    else {\n        printk(\"Module created with zero flag\\n\");\n    }\n    return 0; // on success\n}\n\nvoid\ncleanup_module()\n{\n    printk(\"Unloading kernel module 'hello'\\n\");\n}\n\nMODULE_LICENSE(\"GPL\");\n"
  },
  {
    "path": "lessons-supplementary/2021-2022/l24-kernel-fs/modules/hello.c",
    "content": "#include <linux/module.h>  // modules macros\n#include <linux/kernel.h>  // basic kernel routines\n\nint\ninit_module()\n{\n    printk(\"Hello from newly created module!\\n\");\n    return 0; // on success\n}\n\nvoid\ncleanup_module()\n{\n    printk(\"Unloading kernel module 'hello'\\n\");\n}\n\nMODULE_LICENSE(\"My Great License to use only in Russia\");\n\n\n"
  },
  {
    "path": "lessons-supplementary/2021-2022/l24-kernel-fs/modules/hello2.c",
    "content": "#include <linux/module.h>  // modules macros\n#include <linux/kernel.h>  // basic kernel routines\n\nint\ninit_module()\n{\n    printk(\"Hello from newly created module!\\n\");\n    return 0; // on success\n}\n\nvoid\ncleanup_module()\n{\n    printk(\"Unloading kernel module 'hello'\\n\");\n}\n\nMODULE_LICENSE(\"My Great License to use only in Russia\");\n\n\n"
  },
  {
    "path": "practice/.clang-format",
    "content": "---\nLanguage:        Cpp\nAccessModifierOffset: -2\nAlignAfterOpenBracket: AlwaysBreak\nAlignConsecutiveAssignments: false\nAlignConsecutiveDeclarations: false\nAlignEscapedNewlines: Right\nAlignOperands:   true\nAlignTrailingComments: true\nAllowAllParametersOfDeclarationOnNextLine: false\nAllowShortBlocksOnASingleLine: true\nAllowShortCaseLabelsOnASingleLine: false\nAllowShortFunctionsOnASingleLine: None\nAllowShortIfStatementsOnASingleLine: false\nAllowShortLoopsOnASingleLine: false\nAlwaysBreakAfterDefinitionReturnType: None\nAlwaysBreakAfterReturnType: None\nAlwaysBreakBeforeMultilineStrings: false\nAlwaysBreakTemplateDeclarations: MultiLine\nBinPackArguments: false\nBinPackParameters: false\nBraceWrapping:\n  AfterClass:      false\n  AfterControlStatement: false\n  AfterEnum:       false\n  AfterFunction:   true\n  AfterNamespace:  false\n  AfterObjCDeclaration: false\n  AfterStruct:     false\n  AfterUnion:      false\n  AfterExternBlock: false\n  BeforeCatch:     false\n  BeforeElse:      false\n  IndentBraces:    false\n  SplitEmptyFunction: true\n  SplitEmptyRecord: true\n  SplitEmptyNamespace: true\nBreakBeforeBinaryOperators: None\nBreakBeforeBraces: Linux\nBreakBeforeInheritanceComma: false\nBreakInheritanceList: BeforeColon\nBreakBeforeTernaryOperators: true\nBreakConstructorInitializersBeforeComma: false\nBreakConstructorInitializers: BeforeColon\nBreakAfterJavaFieldAnnotations: false\nBreakStringLiterals: true\nColumnLimit:     80\nCommentPragmas:  '^ IWYU pragma:'\nCompactNamespaces: false\nConstructorInitializerAllOnOneLineOrOnePerLine: false\nConstructorInitializerIndentWidth: 4\nContinuationIndentWidth: 4\nCpp11BracedListStyle: true\nDerivePointerAlignment: false\nDisableFormat:   false\nExperimentalAutoDetectBinPacking: false\nFixNamespaceComments: true\nIncludeBlocks:   Preserve\nIncludeCategories:\n  - Regex:           '^\"(llvm|llvm-c|clang|clang-c)/'\n    Priority:        2\n  - Regex:           '^(<|\"(gtest|gmock|isl|json)/)'\n    Priority:        3\n  - Regex:           '.*'\n    Priority:        1\nIncludeIsMainRegex: '(Test)?$'\nIndentCaseLabels: false\nIndentPPDirectives: None\nIndentWidth:     4\nIndentWrappedFunctionNames: false\nJavaScriptQuotes: Leave\nJavaScriptWrapImports: true\nKeepEmptyLinesAtTheStartOfBlocks: true\nMacroBlockBegin: ''\nMacroBlockEnd:   ''\nMaxEmptyLinesToKeep: 1\nNamespaceIndentation: None\nObjCBinPackProtocolList: Auto\nObjCBlockIndentWidth: 2\nObjCSpaceAfterProperty: false\nObjCSpaceBeforeProtocolList: true\nPenaltyBreakAssignment: 2\nPenaltyBreakBeforeFirstCallParameter: 19\nPenaltyBreakComment: 300\nPenaltyBreakFirstLessLess: 120\nPenaltyBreakString: 1000\nPenaltyBreakTemplateDeclaration: 10\nPenaltyExcessCharacter: 1000000\nPenaltyReturnTypeOnItsOwnLine: 60\nPointerAlignment: Left\nReflowComments:  false\nSortIncludes:    true\nSortUsingDeclarations: true\nSpaceAfterCStyleCast: false\nSpaceAfterTemplateKeyword: true\nSpaceBeforeAssignmentOperators: true\nSpaceBeforeCpp11BracedList: false\nSpaceBeforeCtorInitializerColon: true\nSpaceBeforeInheritanceColon: true\nSpaceBeforeParens: ControlStatements\nSpaceBeforeRangeBasedForLoopColon: true\nSpaceInEmptyParentheses: false\nSpacesBeforeTrailingComments: 1\nSpacesInAngles:  false\nSpacesInContainerLiterals: true\nSpacesInCStyleCastParentheses: false\nSpacesInParentheses: false\nSpacesInSquareBrackets: false\nStandard:        Cpp11\nStatementMacros:\n  - Q_UNUSED\n  - QT_REQUIRE_VERSION\nTabWidth:        4\nUseTab:          Never\n...\n"
  },
  {
    "path": "practice/aarch64/README.md",
    "content": "# Архитектура AArch64 (armv8)\n\n## Кросс-компиляция и запуск программ на x86\n\nПроцесс сборки программ, предназначенных для другой процессорной архитектуры или операционной системы называется кросс-компиляцией.\n\nДля этого необходимо специальная версия компилятора `gcc`, предназначенного для другой платформы. Во многих дистрибутивах существуют отдельные пакеты компилятора для других платформ, включая различные варианты ARM. Готовую сборку компилятора для armv8 можно взять из проекта Linaro: [http://releases.linaro.org/components/toolchain/binaries/7.5-2019.12/aarch64-linux-gnu/](http://releases.linaro.org/components/toolchain/binaries/7.5-2019.12/aarch64-linux-gnu/).\n\nПолные названия команд `gcc` имеют вид *триплетов*:\n\n```\nARCH-OS[-VENDOR]-gcc\nARCH-OS[-VENDOR]-g++\nARCH-OS[-VENDOR]-gdb\n\nи т. д.\n```\n\nгде `ARCH` - это имя архитектуры: `i686`,  `x86_64`,  `arm`,  `aarch64`,  `ppc` и т.д.; `OS` - целевая операционная система, например `linux`, `win32` или `darwin`; а необязательный фрагмент триплета `VENDOR` - соглашения по бинарному интерфейсу, если их для платформы существует несколько, например для ARM это может быть `gnu` (стандартное соглашение Linux) или `none` (без операционной системы, просто голое железо).\n\nВыполнение программ, предназначенных для других архитектур, возможно только интерпретацией инородного набора команд. Для этого предназначены специальные программы - эмуляторы.\n\nАрхитектуры arm/aarch64, как и многие другие архитектуры, поддерживает эмулятор QEMU.\n\nЭмулировать можно как компьютерную систему целиком, по аналогии с VirtualBox, так и только набор команд процессора, используя при этом окружение хост-системы Linux.\n\nКоманды QEMU имеют вид:\n\n```\nqemu-ARCH\nqemu-system-ARCH\n```\n\nгде ARCH - это имя эмулируемой архитектуры. Команды, в названии которых присутствует system, запускают эмуляцию компьютерной системы, и для их использования необходимо установить операционную систему.\n\nКоманды без system требуют в качестве обязательного аргумента имя выполняемого файла для ОС Linux, и эмулируют только набор команд процессора в пользовательском режиме, выполняя \"инородный\" исполняемых файл так, как будто это обычная программа.\n\nПоскольку большинство программ, скомпилированных для Linux и другой процессорной архитектуры,, подразумевают использование стандартной библиотеки Си, необходимо использовать именно версию glibc для для нужной архитектуры. Минимальное окружение с необходимыми библиотеками можно взять из проекта Linaro (см. ссылку выше), и скормить его qemu с помощью опции -L ПУТЬ_К_SYSROOT.\n\nПример компиляции и запуска:\n\n```\n# в предположении, что компилятор распакован в /opt/aarch64-gcc,\n# а sysroot - в /opt/aarch64-sysroot\n\n# Компилируем\n> /opt/aarch64-gcc/bin/aarch64-linux-gnu-gcc -o program hello.c\n\n# На выходе получаем исполняемый файл, который не запустится\n> ./program\nbash: ./program: cannot execute binary file: Exec format error\n\n# Но мы можем запустить его с помощью qemu-aarch64\n> qemu-aarch64 -L /opt/aarch64-sysroot ./program\nHello, World!\n```\n\nУ команд `qemu-*` предусмотрена возможность запуска в режиме отладки, - в этом случае возможно взаимодействие с `qemu` точно так же, как и с `gdbserver`:\n\n```\n# Компилируем с отладочной информацией\n> /opt/aarch64-gcc/bin/aarch64-linux-gnu-gcc -g -o program hello.c\n \n# Запускаем под qemu в режиме отладки\n# Обратите внимание на опцию -g ПОРТ\n> qemu-aarch64 -L /opt/aarch64-sysroot -g 1234 ./program\n \n# В другом терминале можно подключиться к программе из gdb\n> /opt/aarch64-gcc/bin/aarch64-linux-gnu-gdb ./program\n(gdb) target remote localhost 1234\n```\n\n## Программирование на языке ассемблера armv8\n\nПрограммы на языка ассемблера для компилятора GNU сохраняются в файле, имя которого оканчивается на `.s` или `.S`. Во втором случае (с заглавной буквой) подразумевается, что текст программы может быть обработан препроцессором, то есть можно использовать конструкции `#define` или `#include`.\n\nДля компиляции используется одна из команд: `aarch64-linux-gnu-as` или `aarch64-linux-gnu-gcc`. В первом случае текст только компилируется в объектный файл, во втором - в выполняемую программу, скомпонованную со стандартной библиотекой Си, из которой можно использовать функции ввода-вывода.\n\nПолная документация по архитектуре команд armv8 приведена в [официальном источнике](https://developer.arm.com/documentation/ddi0487/latest). Описание основных инструкций приведено в разделе *C3: A64 Instruction Set Overview*.\n\n### Общий синтаксис программ на ассемблере\n\nРазберем синтаксис ассемблера armv8 на примере реализации функции `f`, которая доступна извне, и вычисляет выражение\n`A*x*x + B*x + C`, где `A`, `B`, `C` и `x` являются агументами функции `f(A,B,C,x)`.\n\n```\n// Это комментарий, как в Си/С++\n   .text      // начало секции .text с кодом программы\n   .global f  // указание о том, что метка f будет доступна извне\n              // (аналог extern в языке Си)\n              \nf:            // метка - имя функции или строки для перехода\n    \n    // последовательность команд для вычисления\n    mul       x0, x0, x3\n    mul       x0, x0, x3\n    mul       x1, x1, x3\n    add       x0, x0, x1\n    add       x0, x0, x2\n    // возвращаемся из функции f\n    ret\n```\n\n### Целочисленные регистры\n\nПроцессор может выполнять операции только над регистрами - ячейками памяти в ядре процессора. У armv8 есть 31 регистр общего назначения, доступных программно: `x0`, `x1`,  ... , `x30`. Размер каждого регистра - 64 бит. Обращение по именам `w0`, `w1`, ..., `w30` к этим регистрам означает использование младших 32-битных частей соответствующих регистров с префиксом `x`. \n\nРегистры `x30` и `sp` следует изменять с осторожностью, поскольку у них есть специальное назначения для всех платформ armv8, независимо от используемой операционной системы: `x30` хранит адрес возврата из функции, `sp` - указатель на вершину стека. Кроме того, в системе Linux предусмотрены следующие соглашения об использовании регистров:\n\n| Регистры        | Назначение                                                   |\n| --------------- | ------------------------------------------------------------ |\n| `x0` ... `x7`   | аргументы функции и возвращаемое значение (`x0`)             |\n| `x8` ... `x18`  | временные регистры, для которые не гарантируется сохранение результата, если вызывать какую-либо функцию |\n| `x19` ... `x28` | регистры, для которых гарантируется, что вызываемая функция их не будет портить |\n| `x29`           | указатель на границу фрейма функции, обычно используется отладчиком |\n| `x30`      | адрес возврата из функции                                    |\n| `sp`      | указатель на вершину стека                                   |\n\nПомимо регистров общего назначения предусмотрены еще два специальных регистра: `xzr` - всегда хранит значение `0`, и `pc` - Program Counter, который хранит адрес следующей инструкции, которая должна быть выполнена.\n\n### Флаги\n\nВыполнение команд может приводить к появлению некоторой дополнительной информации, которая хранится в *регистре флагов*. Флаги относятся к последней выполненной команде. Основные флаги, это:\n\n- `C`: Carry - возникло беззнаковое переполнение\n- `V`: oVerflow - возникло знаковое переполнение\n- `N`: Negative - отрицательный результат\n- `Z`: Zero - обнуление результата.\n\n### Команды процессора\n\nКоманды процессора выполняются определенные действия над регистрами. Некоторые команды, которые называются командами переходов, позволяют менять значение регистра `pc`. \n\nДля арифметических команд необходимо указывать в качестве первого аргумента регистр, в который нужно записать результат, а остальные аргументы - это аргументы операции. Если используются регистры с префиксом `w`, то подразумевается 32-битная арифметика, если регистры с префиксом `x` - 64-битная арифметика. Для того, чтобы арифметические команды изменяли флаги, необходимо указать им суффикс `s`, - в противном случае значения флагов не изменятся.\n\n#### Арифметические и поразрядные операции\n\nБазовые арифметические команды:\n\n* `add Xd, Xa, Xb` // Xd ← Xa + Xb\n* `sub Xd, Xa, Xb` // Xd ← Xa - Xb\n* `mul Xd, Xa, Xb` // Xd ← Xa * Xb\n* `madd Xd, Xa, Xb, Xc` // Xd ← Xa * Xb + Xc\n* `umaddl Xd, Wa, Wb, Xc` // Xd ← Wa * Wb + Xc, где Wa и Wb - беззнаковые значения\n* `smaddl Xd, Wa, Wb, Xc` // Xd ← Wa * Wb + Xc, где Wa и Wb - знаковые значения\n* `udiv Xd, Xa, Xb` // Xd ← Xa / Xb, где Xa и Xb - беззнаковые значения\n* `sdiv Xd, Xa, Xb` // Xd ← Xa / Xb, где Xa и Xb - знаковые значения\n\nПобитовые операции:\n\n* `and Xd, Xa, Xb` // Xd ← Xa & Xb\n* `orr Xd, Xa, Xb` // Xd ← Xa | Xb\n* `eor Xd, Xa, Xb` // Xd ← Xa ^ Xb\n* `asr Xd, Xa, Xb` // Xd ← Xa >> Xb (арифметический сдвиг, деление на степени 2)\n* `lsr Xd, Xa, Xb` // Xd ← Xa >> Xb (логический сдвиг)\n* `lsl Xd, Xa, Xb` // Xd ← Xa << Xb (логический сдвиг)\n\nКопирование и приведения типов:\n\n* `mov Xd, Xa` // Xd ← Xa\n* `uxtb Xd, Xa` // Xd ← Xa, где Xa - это uint8_t\n* `uxth Xd, Xa` // Xd ← Xa, где Xa - это uint16_t\n* `uxtw Xd, Xa` // Xd ← Xa, где Xa - это uint32_t\n* `sxtb Xd, Xa` // Xd ← Xa, где Xa - это int8_t\n* `sxth Xd, Xa` // Xd ← Xa, где Xa - это int16_t\n* `sxtw Xd, Xa` // Xd ← Xa, где Xa - это int32_t\n\n### Команды управления ходом программы\n\nВнутри программы отдельные инструкции могут иметь метки, - именованные относительные адреса в программе, которые ассемблер при сборке программы заменяет на численные смещения относительно текущей инструкции. Управление ходом программы осуществляется инструкциями перехода на метки, которые могут быть как безусловными, так и выполняться при выполнении определенного условия.\n\n#### Безусловный переход на другую инструкцию\n\nКоманды безусловного перехода:\n\n* `b LABEL` - безусловный переход на метку `LABEL`, которая закодирована в команду как смещение относительно текущей выполняемой инструкции, эта инструкция обычно используется для переходов внутри функции;\n* `bl LABEL` - безусловный переход на метку `LABEL`, как в случае инструкции `b`, но при этом в регистр `x30` сохраняется адрес следующей за `bl` инструкции, чтобы к нему можно было вернуться инструкцией `ret`; обычно эта инструкция используется для вызова функций, метки которых  синтаксически ничем не отличаются от меток внутри функций.\n\nМетки должны быть отдалены от текущей инструкции не более чем на `±32Mb`. Этого обычно более чем достаточно для вызова функции из того же самого исполняемого файла, где используется вызов функции, но возможны и вызовы функций, которые в памяти располагаются значительно дальше, например функции библиотек. Для таких случаев предусмотрена возможность перехода на инструкцию по 64-битному адресу:\n\n* `br X` - безусловный переход на адрес, который хранится в регистре `X`;\n* `brl X` - безусловный переход на адрес, который хранится в регистре `X`, с сохранением адреса возврата в регистре `x30` , который может быть использован инструкцией `ret`.\n\n#### Условные переходы\n\nАрифметические операции, у которых в названии указан суффикс `s`, а также команда сравнения регистров  `cmp Xa, Xb` изменяют набор флагов процессора. Эти флаги могут использоваться в качестиве условий для операций условного перехода, которые кодируются в виде суффиксов инструкции `b`:\n\n```\nEQ        equal  (Z)\nNE        not equal  (!Z)\nCS or HS  carry set / unsigned higher or same  (C)\nCC or LO  carry clear / unsigned lower  (!C)\nMI        minus / negative  (N)\nPL        plus / positive or zero  (!N)\nVS        overflow set  (V)\nVC        overflow clear  (!V)\nHI        unsigned higher  (C && !Z)\nLS        unsigned lower or same  (!C || Z)\nGE        signed greater than or equal  (N == V)\nLT        signed less than  (N != V)\nGT        signed greater than  (!Z && (N == V))\nLE        signed less than or equal  (Z || (N != V))\n```\n\nПример. Реализация цикла, который в Си нотации эквивалентен `for (x0=0; x0<x1; x0++) {}`:\n\n```\n  mov    x0, 0      // x0 = 0\n  mov    x1, 10     // x1 = какое-то значение, например 10\nLoopBegin:          // метка начала цикла\n  cmp    x0, x1     // сравниваем  \n  bge    LoopEnd    // переходим в конец цикла, если x0 >= x1\n  // какие-то инструкции внутри цикла\n  add    x0, x0, 1  // инкремент переменной цикла\n  b      LoopBegin  // переходим к началу цикла, где будет проверка\nLoopEnd:\n  // цикл закончился\n```\n\n### Взаимодействие с памятью\n\nВ традиционной для RISC-архитектур модели адресации, процессоры умеют выполнять действия только над регистрами. Любое взаимодействие с памятью осуществляется отдельными командами загрузки и сохранения. \n\nДля базового обращения к памяти используются инструкции:\n\n* `ldr Rd, [Xa]` - прочитать из памяти содержимое по адресу, указанному в регистре `Xa` и сохранить результат в `Rd`\n* `str Ra, [Xd]` - сохранить содержимое регистра  `Ra` в памяти по адресу, указанному в регистре  `Xd`\n\nЭти инструкции оперируют 32 или 64-битными данными (размерность определяется названием регистра). Для чтения/записи операндов меньшего размера эти инструкции имеют дополнительные суффиксы:\n\n * `ldrb`/`strb` - для `uint8_t`\n * `ldrsb`/`strsb` - для `int8_t`\n * `ldrh`/`strh` - для `uint16_t`\n * `ldrsh`/`strsh` - для `int16_t`\n * `ldrsw`/`strsw` - для `int32_t`\n\nОбратите внимание на то, что для меньшей, чем размер регистра, разрядности используются разные инструкции для знаковых и беззнаковых типов данных. Это связано с тем, как должен обрабатываться старший (знаковый) бит целого числа, - либо оставаться на месте, либо становиться старшим битом регистра.\n\nДля  64-битной архитектуры ARMv8 (но не для 32-битных архитектур ARM) возможно указание смещения относительно базового адреса. Этот синтаксис может быть использован для обращения к полям структур, если известен адрес начала структуры в памяти, либо для обращения к элементам массивов по индексу.\n\n```\n// загрузка значения по адресу из x1 со смещением 8 байт\n  ldr  x0, [x1, 8]       // x0 = *(x1 + 8)\n\n// загрузка значения по адресу из x1 с индексом элемента x2,\n// в предположении, что размер одного элемента равен 8 байтам\n  ldr  x0, [x1, x2, lsl 3]  // x0 = *(x1 + x2 * (1 << 3))\n```\n\n"
  },
  {
    "path": "practice/aarch64-functions/README.md",
    "content": "# Стек и вызов функций\n\n## Функции и метки\n\nВсе метки, за исключением меток, начинающихся с префикса `.L`, попадают в таблицу символов, которую можно посмотреть командой `objdump -t`. Метки могут быть как локальными, то есть предназначенными только для использования в пределах объектного файла, так и глобальными, - доступными извне. Чтобы сделать метку глобальной, необходимо использовать директиву `.global` в исходном тексте на языке ассемблера. С точки зрения внутреннего представления, функции и метки - это одни и тоже.\n\nПереход на метку осуществляется инструкцией `b`, возможно, с каким-то суффиксом-условием. Для вызова функций используется инструкция `bl`, которая выполняет следующие действия:\n\n1. Сохраняет в регистр `x30` значение `pc + 4`\n2. Выполняет переход на метку, указанную в инструкции.\n\nРегистр `x30` при этом имеет специальное назначение - Link Register, в некоторых источниках его еще называют `lr`, хотя компилятор `gcc` явно это имя не поддерживает для архитектуры `aarh64`. Смысл этого регистра в том, что он хранит адрес возврата из функции.\n\n## Соглашение о вызовах функций в Linux/AArch64\n\nНабор регистров процессора существует только в единственном экземпляре, поэтому при вызове функций все регистры используются повторно, и это необходимо учитывать. Для определенных групп регистров, по соглашению, принятому в Linux (и большинстве других систем), принято следующее функциональное назначение:\n\n* Регистры с `x0` до `x7` включительно используются для передачи аргументов в функцию. Кроме того, регистр `x0` используется для вовзрата значения. \n* Регистр `x8` используется в качестве указателя `this` для объектно-ориентированных языков программирования.\n* Регистры  c `x9` по `x15` используются как временные регистры.\n* Регистры `x16` и `x17` используются как временные регистры для вычисления адреса функции во время прыжка из секции `.plt`. \n* Регистр `x18` используется как указатель на хранилище Thread-local переменных.\n* Регистры с `x19` по `x28` должны быть сохранены, если они используются функцией.\n* Регистр `x29` используется как Frame Pointer, используемый для быстрого разворачиваения стека вызовов.\n* Регистр `x30` - это Link Register, используемый инструкцией `ret`.\n* Регистр `x31` (для `gcc` его имя `sp`) - это указатель на вершину стека.\n\nВ случае вызова функции **не гарантируется**, что не будут изменены значения регистров с `x0` по `x18` включительно, поскольку реализация функции вправе использовать эти регистру по своему усмотрению.\n\nДля регистров с `x19` по `x31` гарантируется, что их значения не изменятся в случае вызова функции. В случае, если какой-либо функции требуется использование этих регистров, то функция обязана восстановить их исходные значения перед вызовом инструкции `ret`.\n\n## Стек вызовов\n\nДля хранения локальный значений и сохранения регистров с `x19` по `x30` может использоваться оперативная память в специальной области, которая называется *стек*. Память для стека имеет фиксированный размер (для Linux по умолчанию это обычно 8Мб), и она выделена перед началом выполнения программы.\n\nДанные в стек помещаются сверху вниз, а указатель на нижнюю границу стека для каждой функции хранится в регистре `sp`. Кроме того, для архитектуры `aarch64` (независимо от используемой операционной системы) требуется, чтобы стек был выровнен по границе в 16 байт, в противном случае обращение к стеку приведет к ошибке Bus Error.\n\nПример. Выделение памяти на стеке и сохранение регистра `lr`:\n\n```assembly\nfunction:\n    // выделяем память на стеке, просто перемещая указатель sp\n    // обратите внимание на то, что изменять стек можно на число,\n    // кратное 16, даже если требуется меньше памяти\n    sub sp, sp, 16\n    // сохраняем на стек регистр x30\n    // можно сохранять как в самый низ стека [sp],\n    // так и со смещением в 8 байт [sp, 8]\n    str x30, [sp, 8]\n    \n    // .... какой-то код с использованием инструкции bl\n    \n    // перед выходом восстанавливаем из стека x30\n    ldr x30, [sp, 8]\n    // вовзращаем значение sp в исходное\n    add sp, sp, 16\n    // теперь можно выходить\n    ret\n```\n\n"
  },
  {
    "path": "practice/arm/README.md",
    "content": "# Разработка под архитектуру ARM\n\n## Кросс-компиляция\n\nПроцесс сборки программ, предназначенных для другой процессорной архитектуры или операционной системы называется кросс-компиляцией.\n\nДля этого необходимо специальная версия компилятора `gcc`,\nпредназначенного для другой платформы. Во многих дистрибутивах существуют отдельные пакеты компилятора для других платформ, включая ARM.\n\nКроме того, для архитектуры ARM можно скачать готовую\nпоставку \"все-в-одном\" из проекта Linaro: [http://releases.linaro.org/components/toolchain/binaries/7.3-2018.05/arm-linux-gnueabi/](http://releases.linaro.org/components/toolchain/binaries/7.3-2018.05/arm-linux-gnueabi/).\n\nПолные названия команд `gcc` имеют вид *триплетов*:\n```\nARCH-OS[-VENDOR]-gcc\nARCH-OS[-VENDOR]-g++\nARCH-OS[-VENDOR]-gdb\n\nи т. д.\n```\nгде `ARCH` - это имя архитектуры: `i686`, `x86_64`, `arm`, `ppc` и т.д.; `OS` - целевая операционная система, например `linux`, `win32` или `darwin`; а необязательный фрагмент триплета `VENDOR` - соглашения по бинарному интерфейсу, если их для платформы существует несколько, например для ARM это может быть `gnueabi` (стандартное соглашение Linux) или `none-eabi` (без операционной системы, просто голое железо).\n\nДля ARM ещё часто различают название архитектуры на `arm`\n(soft float) и `armhf` (hard float). В первом случае подразумевается отсутствие блока с плавающей точкой, поэтому\nвсе операции эмулируются программно, во втором случае - выполняются аппаратно.\n\n\n## Выполнение программ для не родных архитектур\n\nВыполнение программ, предназначенных для других архитектур, возможно только интерпретацией инородного набора команд. Для этого предназначены специальные программы - *эмуляторы*.\n\nАрхитектуру ARM, как и многие другие архитектуры, поддерживает эмулятор [QEMU](https://www.qemu.org/).\n\nЭмулировать можно как компьютерную систему целиком, по аналогии с VirtualBox, так и только набор команд процессора, используя при этом окружение хост-системы Linux.\n\n### Запуск бинарников ARM в родном окружении\n\nЭтот эмулятор входит в состав всех распространенных дистрибутивов. Команды qemu имеют вид:\n```\nqemu-ARCH\nqemu-system-ARCH\n```\n\nгде `ARCH` - это имя эмулируемой архитектуры. Команды, в названии которых присутствует `system`, запускают эмуляцию компьютерной системы, и для их использования необходимо установить операционную систему.\n\nКоманды без `system` требуют в качестве обязательного аргумента имя выполняемого файла для ОС Linux, и эмулируют  только набор команд процессора в *пользовательском режиме*, выполняя \"инородный\" исполняемых файл так, как будто это обычная программа.\n\nПоскольку большинство программ, скомпилированных для ARM Linux, подразумевают использование стандартной библиотеки Си, необходимо использовать именно версию glibc для ARM. Минимальное окружение с необходимыми библиотеками можно взять из проекта Linaro (см. ссылку выше), и скормить его qemu с помощью опции `-L ПУТЬ_К_SYSROOT`.\n\nПример компиляции и запуска:\n```\n# в предположении, что компилятор распакован в /opt/arm-gcc,\n# а sysroot - в /opt/arm-sysroot\n\n# Компилируем\n> /opt/arm-gcc/bin/arm-linux-gnueabi-gcc -marm -o program hello.c\n\n# На выходе получаем исполняемый файл, который не запустится\n> ./program\nbash: ./program: cannot execute binary file: Exec format error\n\n# Но мы можем запустить его с помощью qemu-arm\n> qemu-arm -L /opt/arm-sysroot ./program\nHello, World!\n\n```\n\n### Запуск ARM-программ в эмуляции окружения Raspberry Pi\n\nИдеальный вариант для тестирования и отладки - это использовать настоящее железо, например Raspberry Pi.\n\nЕсли под рукой нет компьютера с ARM-процессором, то можно\nвыполнять эмуляцию ПК с установленной системой Raspbian.\n\nСкачать образ можно отсюда: [гуглодиск](https://drive.google.com/open?id=11lc_f-_crhP-CJi_FEYb4DE0u9TMViT4)\n"
  },
  {
    "path": "practice/arm_globals_plt/README.md",
    "content": "# Адресация данных в памяти и использование библиотечных функций\n\n* [Reference по ARM](../asm/arm_basics/arm_reference.pdf)\n\n## Основные команды\n\nКак свойственно классической RISC-архитектуре, процессор ARM\nможет выполнять операции только над регистрами. Для доступа к памяти используются отдельные команды *загрузки* (`ldr`) и\n*сохранения* (`str`).\n\nОбщий вид команд:\n```\nLDR{условие}{тип} Регистр, Адрес\nSTR{условие]{тип} Регистр, Адрес\n```\nгде `{условие}` - это условие выполнения команды, может быть\nпустым (см. предыдущий семинар); `{тип}` - тип данных:\n * `B` - беззнаковый байт\n * `SB` - знаковый байт\n * `H` - полуслово (16 бит)\n * `SB`- знаковое полуслово\n * `D` - двойное слово.\n\nЕсли тип в названии команды не указан, то подразумевается\nобычное слово. Обратите внимание, что для выполнения операций загрузки/сохранения данных, меньших, чем машинное слово, отдельно выделяются знаковые команды, которые\nделают аккуратное расширение бит нулями, сохраняя при этом\nстарший знаковый бит.\n\nВ случае операций загрузки/сохранения пары регистров (двойное слово), регистр должен быть с четным номером. Второе машинное слово подразумевается в соседнем регистре с номером `Rn+1`.\n\n## Адресация\n\nАдрес имеет вид:\n`[R_base {, offset}]`\nгде `R_base` - имя регистра, который содержит базовый адрес в памяти, а необязательный параметр `offset` - смещение относительно адреса. Итоговый адрес определяется как\n`*R_base + offset`.\n\nСмещение может быть как именем регистра, так и численной константой, закодированной в команду. Регистры обычно используются для индексации элементов массива, константы - для доступа к полям структуры или локальным переменным и аргументам относительно `[sp]`.\n\n## Адресация полей Си-структур\n\nПо стандарту языка Си, поля в памяти структур размещаются по следующим правилам:\n * порядок полей в памяти соответствует порядку полей в описании структуры\n * размер структуры должен быть кратен размеру машинного слова\n * данные внутри машинных слов размещаются таким образом, чтобы быть прижатыми к их границам.\n\nТаким образом, размер структуры не всегда совпадает с суммой размеров отдельных полей. Например:\n\n```\nstruct A {\n  char  f1; // 1 байт\n  int   f2; // 4 байта\n  char  f3; // 1 байт\n};\n// 1 + 4 + 1      = 6 байт\n// size(struct A) = 12 байт\n```\n\nВ данном примере поле `f1` занимает часть машинного слова, поле `f2` - имеет размер 4 байта, поэтому занимает уже следующее машинное слово, и для поля `f3` приходится использовать ещё одно. Простая перестановка полей местами позволяет сэкономить 4 байта:\n\n```\nstruct A {\n  char  f1; // 1 байт\n  char  f3; // 1 байт\n  int   f2; // 4 байта  \n};\n// 1 + 1 + 4      = 6 байт\n// size(struct A) = 8 байт\n```\nВ этом случае поля `f1` и `f3` занимают одно и то же машинное слово.\n\nКомпилятор GCC имеет нестандартный аттрибут `packed`, позволяющий создавать \"упакованные\" структуры, размер которых равен сумме размеров отдельных его полей:\n\n```\nstruct A {\n  char  f1; // 1 байт\n  int   f2; // 4 байта\n  char  f3; // 1 байт\n} __attribute__((packed));\n// 1 + 4 + 1      = 6 байт\n// size(struct A) = 6 байт\n```\n\n## Функции стандартной Си-библиотеки\n\nС каждой функцией, которую можно использовать извне, связана некоторая текстовая метка в таблице символов. После компиляции, запись в таблице символов определяет место в памяти, где размещается первая инструкция функции.\n\nФункции, реализованные в разных объектных модулях, но компонуемые в один исполняемый файл, вызываются обычным образом. Способ их вызова ничем не отличается от вызова функций из одного и того же объектного модуля.\n\nПри использовании *библиотек*, они загружаются в отдельную область памяти, и на этапе компоновки адрес размещения библиотек не известен.\n\nБолее того, размещение самой программы, в общем случае, также предполагается неизвестным.\n\nТакие функции, которые находятся в динамически загружаемых бибилиотеках, включая стандартную библиотеку Си, отображаются в таблице символов с пометкой `@plt`. Их реализация выглядит на языке ассемблера примерно следующим образом:\n\n```\nfunction@plt:\n\n   // Во временный регистр IP загружаем текущий PC\n   // с некоторым смещением. По этому смещению находится\n   // таблица адресов реальных функций, которая заполняется\n   // на этапе загрузки программы и динамических библиотек\n   add  ip, pc, #0\n   add  ip, ip, #OFFSET_TO_TABLE_BEGIN\n\n   // Загружаем значение адреса из этой таблицы в PC.\n   // Это приводит к тому, что переходим к выполнению\n   // реальной функции.\n   ldr  pc, [ip, #OFFSET_TO_FUNCTION_INDEX]\n   \n```\n\nТаким образом, функции из внешних библиотек разполагаются как бы в самой программе, но представляют собой \"трамплин\" для выполнения реальной функций.\n"
  },
  {
    "path": "practice/asm/arm_basics/README.md",
    "content": "# Основы ассемблера ARM\n\n## Написание и компиляция программ\n\nПрограммы на языка ассемблера для компилятора GNU сохраняются в файле, имя которого оканчивается на `.s` или `.S`. Во втором случае (с заглавной буквой) подразумевается, что текст программы может быть обработан препроцессором.\n\nДля компиляции используется одна из команд:\n`arm-linux-gnueabi-as` или `arm-linux-gnueabi-gcc`. В первом случае текст только компилируется в объектный файл, во втором - в выполняемую программу, скомпонованную со стандартной библиотекой Си, из которой можно использовать функции ввода-вывода.\n\nПроцессоры ARM поддерживают два набора команд: основной 32-битный `arm`, и уплотнённый 16-битный `thumb`, между которыми процессор умеет переключаться. В рамках данного семинара мы будем использовать 32-битный набор инструкций, поэтму тексты нужно компилировать с опцией `-marm`.\n\n## Общий синтакис\n\n```\n// Это комментарий, как в C++\n\n    .text      // начало секции .text с кодом программы\n    .global f  // указание о том, что метка f\n               // является доступной извне (аналог extern)\n\nf:             // метка (заканчивается двоеточием)\n\n     // последовательность команд\n     mul   r0, r0, r3\n     mul   r0, r0, r3\n     mul   r1, r1, r3\n     add   r0, r0, r1\n     add   r0, r0, r2  \n     mov   r1, r0\n     bx    lr\n```\n\n## Регистры\n\nПроцессор может выполнять операции только над *регистрами* - 32-биьными ячейками памяти в ядре процессора. У ARM есть\n16 регистров, доступных программно: `r0`, `r1`, ... ,`r15`.\n\nУ регистров `r13`...`r15` имеются специальные назначения и дополнительные имена:\n\n * `r15` = `pc`: Program Counter - указатель на текущую выполняемую инструкцию\n * `r14` = `lr`: Link Register - хранит адрес возврата из функции\n * `r13` = `sp`: Stack Pointer - указатель на вершину стека.\n\n## Флаги\n\nВыполнение команд может приводить к появлению некоторой дополнительной информации, которая хранится в *регистре флагов*. Флаги относятся к последней выполненной команде. Основные флаги, это:\n\n * `C`: Carry - возникло беззнаковое переполнение\n * `V`: oVerflow - возникло знаковое переполнение\n * `N`: Negative - отрицательный результат\n * `Z`: Zero - обнуление результата.\n\n## Команды\n\nПолный перечень 32-битных команд см. в [этом reference](arm_reference.pdf), начиная со 151 страницы.\n\nАрхитектура ARM-32 подразумевает, что почти все команды могут иметь *условное выполнение*. Условие кодируется 4-мя битами в самой команде, а с точки зрения синтаксиса ассемблера у команд могут быть суффиксы.\n\nТаким образом, каждая команда состоит из двух частей (без разделения пробелами): сама команда и её суффикс.\n\n## Базовые арифметические операции\n\n* `AND regd, rega, argb`  // regd ← rega & argb\n* `EOR regd, rega, argb`  // regd ← rega ^ argb\n* `SUB regd, rega, argb`  // regd ← rega − argb\n* `RSB regd, rega, argb`  // regd ← argb - rega\n* `ADD regd, rega, argb`  // regd ← rega + argb\n* `ADC regd, rega, argb`  // regd ← rega + argb + carry\n* `SBC regd, rega, argb`  // regd ← rega − argb − !carry\n* `RSC regd, rega, argb`  // regd ← argb − rega − !carry\n* `TST rega, argb`        // set flags for rega & argb\n* `TEQ rega, argb`        // set flags for rega ^ argb\n* `CMP rega, argb`        // set flags for rega − argb\n* `CMN rega, argb`        // set flags for rega + argb\n* `ORR regd, rega, argb`  // regd ← rega | argb\n* `MOV regd, arg`         // regd ← arg\n* `BIC regd, rega, argb`  // regd ← rega & ~argb\n* `MVN regd, arg`         // regd ← ~argb\n\n## Суффиксы-условия\n\n```\nEQ        equal  (Z)\nNE        not equal  (!Z)\nCS or HS  carry set / unsigned higher or same  (C)\nCC or LO  carry clear / unsigned lower  (!C)\nMI        minus / negative  (N)\nPL        plus / positive or zero  (!N)\nVS        overflow set  (V)\nVC        overflow clear  (!V)\nHI        unsigned higher  (C && !Z)\nLS        unsigned lower or same  (!C || Z)\nGE        signed greater than or equal  (N == V)\nLT        signed less than  (N != V)\nGT        signed greater than  (!Z && (N == V))\nLE        signed less than or equal  (Z || (N != V))\n```\n\n## Переходы\n\nСчетчик `pc` автоматически увеличивается на 4 при выполнении\nочередной инструкции. Для ветвления программ изпользуются команды:\n\n * `B label` - переход на метку; используется внутри функций для ветвлений, связанных с циклами или условиями\n * `BL label` - сохранение текущего `pc` в `lr` и переход на `label`; обычно используется для вызова функций\n * `BX register` - переход к адресу, указанному в регистре; обычно используется для выхода из функций.\n\n## Работа с памятью\n\nПроцессор может выполнять операции только над регистрами. Для взаимодействия с памятью используются отдельные инструкции загрузки/сохранения регистров.\n\n* `LDR regd, [regaddr]` - загружает машинное слово из памяти по адресу, хранящимся в regaddr, и сохраняет его в регистре regd\n* `STR reds, [regaddr]` - сохраняет в памяти машинное слово из регистра regs по адресу, указанному в регистре regaddr.\n"
  },
  {
    "path": "practice/asm/arm_load_store/README.md",
    "content": "# Адресация данных в памяти\n\n## Дригие материалы семинара\n\n* [Reference по ARM](../arm_basics/arm_reference.pdf)\n* [Лекция по IEEE754](../../../lectures/fall-2018/Lection03-InstrEncoding_IEEE754.pdf)\n\n\n\n## Основные команды\n\nКак свойственно классической RISC-архитектуре, процессор ARM\nможет выполнять операции только над регистрами. Для доступа к памяти используются отдельные команды *загрузки* (`ldr`) и\n*сохранения* (`str`).\n\nОбщий вид команд:\n```\nLDR{условие}{тип} Регистр, Адрес\nSTR{условие]{тип} Регистр, Адрес\n```\nгде `{условие}` - это условие выполнения команды, может быть\nпустым (см. предыдущий семинар); `{тип}` - тип данных:\n * `B` - беззнаковый байт\n * `SB` - знаковый байт\n * `H` - полуслово (16 бит)\n * `SB`- знаковое полуслово\n * `D` - двойное слово.\n\nЕсли тип в названии команды не указан, то подразумевается\nобычное слово. Обратите внимание, что для выполнения операций загрузки/сохранения данных, меньших, чем машинное слово, отдельно выделяются знаковые команды, которые\nделают аккуратное расширение бит нулями, сохраняя при этом\nстарший знаковый бит.\n\nВ случае операций загрузки/сохранения пары регистров (двойное слово), регистр должен быть с четным номером. Второе машинное слово подразумевается в соседнем регистре с номером `Rn+1`.\n\n## Адресация\n\nАдрес имеет вид:\n`[R_base {, offset}]`\nгде `R_base` - имя регистра, который содержит базовый адрес в памяти, а необязательный параметр `offset` - смещение относительно адреса. Итоговый адрес определяется как\n`*R_base + offset`.\n\nСмещение может быть как именем регистра, так и численной константой, закодированной в команду. Регистры обычно используются для индексации элементов массива, константы - для доступа к полям структуры или локальным переменным и аргументам относительно `[sp]`.\n\n## Адресация полей Си-структур\n\nПо стандарту языка Си, поля в памяти структур размещаются по следующим правилам:\n * порядок полей в памяти соответствует порядку полей в описании структуры\n * размер структуры должен быть кратен размеру машинного слова\n * данные внутри машинных слов размещаются таким образом, чтобы быть прижатыми к их границам.\n\nТаким образом, размер структуры не всегда совпадает с суммой размеров отдельных полей. Например:\n\n```\nstruct A {\n  char  f1; // 1 байт\n  int   f2; // 4 байта\n  char  f3; // 1 байт\n};\n// 1 + 4 + 1      = 6 байт\n// size(struct A) = 12 байт\n```\n\nВ данном примере поле `f1` занимает часть машинного слова, поле `f2` - имеет размер 4 байта, поэтому занимает уже следующее машинное слово, и для поля `f3` приходится использовать ещё одно. Простая перестановка полей местами позволяет сэкономить 4 байта:\n\n```\nstruct A {\n  char  f1; // 1 байт\n  char  f3; // 1 байт\n  int   f2; // 4 байта  \n};\n// 1 + 1 + 4      = 6 байт\n// size(struct A) = 8 байт\n```\nВ этом случае поля `f1` и `f3` занимают одно и то же машинное слово.\n\nКомпилятор GCC имеет нестандартный аттрибут `packed`, позволяющий создавать \"упакованные\" структуры, размер которых равен сумме размеров отдельных его полей:\n\n```\nstruct A {\n  char  f1; // 1 байт\n  int   f2; // 4 байта\n  char  f3; // 1 байт\n} __attribute__((packed));\n// 1 + 4 + 1      = 6 байт\n// size(struct A) = 6 байт\n```\n"
  },
  {
    "path": "practice/asm/nostdlib_baremetal/README.md",
    "content": "# Жизнь без стандартной библиотеки\n\nОсновной reference по набору команд [преобразованный в\nHTML](https://www.felixcloutier.com/x86/).\n\n\n## Инструменты для сборки без стандартной библиотеки\n\n### Инструменты GNU\n\nПри компоновке с опцией `-nostdlib` линковщик не\nвключает функцию `main`, и не связывает\nпрограмму со стандартной библиотекой языка Си.\n\nПолучаемый на выходе файл - обычный выполняемый файл в\nформате ELF, который можно выполнить в операционной системе.\n\nРазмещение различных секций файла при компоновке можно\nуказать в специальном ld-файле (подробнее см. [LD:\nScripts](https://sourceware.org/binutils/docs/ld/Scripts.html)),\nкоторый указывается опцией `-T имя_файла`.\n\nДля того, чтобы при компоновке не включалась лишняя\nинформация о том, каким компилятором собрана программа,\nисползуется опция линковщика `--build-id=none`.\n\nДля выделения кода самой программы из ELF-файла можно\nиспользовать утилиту `objcopy`.\n\n### Ассемблер NASM\n\nАссемблер `nasm` использует хоть и похожий на Intel, но всё\nже немного отличающийся по синтаксису язык. Этот ассемблер,\nв отличии от GNU, поддерживает много выходных форматов, в\nтом числе flat-файлы, предназначенные для непосредственной\nзаливки программатором или загрузки в память.\n\n## Взаимодействие с внешним миром в Linux\n\n#### Общие сведения о системных вызовах\n\nСистемные вызовы - это функции, реализованные в ядре операционной системы, и поэтому обычные процессы могут вызывать их только используя специальные команды, которые переключают процессор в режим ядра. Для доступа к системным вызовам используются нестандартные способы вызова: либо механизм прерываний (команда `int`), либо специализированная команда архитектуры x86-64 `syscall`.\n\nДля большинства (но не для всех) системных вызовов реализованы Си-сигнатуры, которые описаны во 2-м разделе man-страниц. Поскольку соглашения о вызовах обычных Си-функций отличаются от соглашений о системных вызовах, стандартная библиотека языка Си содержит короткие функции-оболочки, единственная задача которых - это переложить аргументы в соотвествии с требуемым соглашением, после чего выполнить системный вызов, и вернуть результат.\n\nПримеры некоторых системных вызовов в Linux:\n\n * `exit` (`_exit` в Си-нотации) = `1` - выход из программы;\n * `read` = `3` - чтение из файлового дескриптора;\n * `write` = `4` - запись в файловый дескриптор;\n * `brk` (`sbrk` в Си-нотации) = `45` - перемещение границы\n   сегмента данных программы.\n\nДля обращения к произвольному системному вызову по его номеру, например, если для него не реализована функция-оболочка в стандартной Си-библиотеке, используется функция `syscall`:\n\n```c\n#include <unistd.h>\n#include <sys/syscall.h>\n\nint main()\n{\n    const char Hello[] = \"Hello!\\n\";\n    \n    // эквивалентно вызову\n    // write(1, Hello, sizeof(Hello)-1);\n    syscall(SYS_write, 1, Hello, sizeof(Hello)-1);\n}\n```\n\n#### 32-разрядные системы x86\n\nОперационная система Linux реализует системные вызовы через программное прерывание с номером `0x80`, которое можно инициировать командой `int`. В регистре `eax` хранится номер системного вызова, в регистрах `ebx`, `ecx`, `edx`, `esi`, `edi` передаются аргументы, а возвращаемое значение\nпередается через `eax`.\n\nНомера системных вызовов на x86 перечислены в файле\n`/usr/include/asm/unistd_32.h`.\n\nПример для x86 (вывод строки `Hello` с использованием системного вызова `write`):\n```asm\n    .text\n    ......\n    mov   eax, 4  // 4 - номер write\n    mov   ebx, 1  // 1 - файловый дескриптор stdout\n    mov   ecx, hello_ptr // указатель на hello\n    mov   edx, 5  // количество байт в выводе\n    int   0x80    // системный вызов Linux\n    ......\n    .data\nhello:\n    .string \"Hello\"\nhello_ptr:\n    .long   hello\n```\n\n#### 64-разрядные системы x86-64\n\nВ 64-битных системах возможно использовать соглашения о системных вызовах для 32-битных платформ x86, но этот механизм используется исключительно для обеспечения работоспособности старых 32-битных программ. При использовании инструкции `int 0x80` аргументы, передаваемые через регистры, усекаюстся до 32-битных значений, что может приводить к неопределенному поведению, например, если передаются указатели.\n\n``` c\n// переменная хранится на стеке, поэтому ее адрес\n// имеет достаточно большое значение в виртуальном\n// 64-разрядном адресном пространстве процесса \nchar buffer[1024];\n\n// если использовать int 0x80, значение указателя buffer\n// будет записано в 32-битный регистр ecx, что приведет к\n// ошибке Segmentation Fault\nssize_t bytes_read = read(0, buffer, sizeof(buffer));\n```\n\nРодным для архитектуры x86-64 соглашением в Linux является использование команды процессора `syscall`, где номер системного вызова передается через `rax`, а аргументы передаются через регистры: `rdi`, `rsi`, `rdx`, `r10`, `r8` и `r9`. Обратите внимание, что не все используемые регистры совпадают со стандартным соглашением о вызовах в x86-64, например, вместо регистра `rcx` используется регистр `r10`. Кроме того, использование команды `syscall` может испортить содержимое регистров `rcx` и `r11`.\n\nНомера системных вызовов для использования их командой `syscall`, хранятся в заголовочном файле `/usr/include/sys/syscall.h`, и большинство из них совпадают (хотя это ничем не гарантируется) с номерами системных вызовов для 32-битных системных вызовов архитектуры x86.\n\nПример для x86-64 (вывод строки `Hello` с использованием системного вызова `write`):\n\n```asm\n    .text\n    ......\n    mov   rax, 4  // 4 - номер write\n    mov   rdi, 1  // 1 - файловый дескриптор stdout\n    mov   rsi, hello_ptr // указатель на hello\n    mov   rdx, 5  // количество байт в выводе\n    syscall       // системный вызов Linux\n    ......\n    .data\nhello:\n    .string \"Hello\"\nhello_ptr:\n    .quad   hello\n```\n\n## Взаимодействие с внешним миром через BIOS или в DOS (историческая справка)\n\nДо момента загрузки операционной системы, обработка\nввода-вывода осуществляется с помощью подпрограмм,\nпредоставляемых BIOS (Basic Input Output System).\n\nРазные подсистемам (\"сервисам\") соответствуют различные\nномера прерываний. Например, прерывание `0x10`\nпредназначено для вывода на экран, а прерывание `0x09` - за\nчтение с клавиатуры.\n\nНекоторые операционные системы, например DOS, не запрещают\nиспользование прерываний BIOS, а дополняют их своими\nмеханизмами.\n\nПодробное описание функций BIOS и DOS -\n[здесь](http://www.codenet.ru/progr/dos/).\n\nОтдельно стоит рассмотреть взаимодействие с выводом на\nэкран. Поскольку вывод через прерывание является хоть и\nуниверсальным, но все же медленным способом, то лучше\nиспользовать прямую запись в видеопамять VGA.\n\nВидеопамять VGA в архитектуре x86 располагается в диапазоне\n`0xA000...0xDFFFF` (256Кб начиная с 640Кб), и делится на\n\"окна\", - области, назначение которых зависит от\nиспользуемого [режима\nработы](https://wiki.osdev.org/VGA_Hardware#Memory_Layout_in_text_modes).\n\nВ стандартном текстовом видеорежиме, вывод символа в\nпозицию `(X, Y)` осуществляется записью двух байт по адресу\n`0xB8000+Y*80*2+X*2`, где младший байт означает код\nсимвола, а старший - цвет символа и фона.\n\n\n## Стадии загрузки системы\n\n### Включение компьютера\n\nСразу после запуска компьютера, управление передаётся\nпрограмме из ROM-памяти (часто именуемую BIOS, хотя это не\nсовсем корректно), задача которой - выполнить\nдиагностику системы, определить конфигурацию оборудования,\nи загрузить программу-загрузчик с определенного диска, чтобы\nпередать ей управление.\n\nПрограмма-загрузчик может располагаться:\n * в классической PC-системе - в первых 512 байтах диска;\n * в современных системах с EFI/UEFI - выделяется\n   определенная область в Flash-памяти на системной плате,\n   куда установщик операционной системы записывает свой\n   загрузчик.\n\n### Загрузка системы через MBR\n\nMaster Boot Record имеет размер 512 байт, и состоит из двух\nчастей: программы-загрузчика и первичной таблицы разделов\nдиска. Признаком того, что MBR имеет загрузчик, является\nзначение `0x55AA` в последних двух байтах. Размер первичной\nтаблицы разделов для PC - 64 байта, таким образом, для\nзагрузчика остается всего 446 байт (512-2-64).\n\nЕсли загрузчик является достаточно сложным (например, GRUB\nв графическом режиме со всякими красивостями и умной\nкомандной строкой), то его делят на две части: в MBR и\nчастично - на разделе диска.\n\nПервые 446 байт загружаются с диска в память по адресу\n`0x7C00`, а область памяти от `0x0000` до `0x7C00`\nсчитается зарезервированной под стек. При этом, процессор\nx86 работает в 16-битном реальном режиме, со старинной\nсегментной адресацией памяти. Пример программирования MBR -\n[здесь](http://joebergeron.io/posts/post_two.html).\n\nЗадача загрузчика - это найти на диске файл с *ядром*\nсистемы, загрузить его в память, и передать ему\nуправление. Примеры файлов ядра:\n * `C:\\msdos.sys` - для DOS;\n * `C:\\Windows\\System32\\ntoskrnl.exe` - для Windows;\n * `/boot/vmlinuz` - символическая ссылка на zlib-сжатый\n   образ ядра в Linux.\n\nФормат файла ядра - как правило, соответствует обычному\nисполняемому файлу (PE для Windows или ELF для Linux), но\nна него накладываются некоторые ограничения о размещении\nданных внутри файла, и кроме того, этот файл не может иметь\nзависимости от каких-либо библиотек.\n\n### Загрузка ядра Linux, формат `multiboot`.\n\nЗагрузчик GRUB загружает ELF-файл с ядром, распаковывает\nего при необходимости, и размещает по адресу, начиная с\n1Мб.\n\nДалее загрузчик ищет Magic-метку заголовка `multiboot` в первых\n32К загруженного файла ядра, сразу после которой идет набор\nфлагов и контрольная сумма заголовка. После этого заголовка, в самом\nфайле следует 16К памяти под стек, а сразу после него -\nначало программы, которую нужно выполнять.\n\nТаким образом, при компиляции ядер, необходимо строго\nуказывать очерёдность различных секций, чтобы GRUB смог\nзапустить ядро.\n\nПодробнее - [здесь](https://wiki.osdev.org/Bare_Bones).\n\n### Запуск ядра\n\nЯдро регистрирует вектор прерываний, выполняет дальнейшую\nинициализацию оборудования, загружая при необходимости\nразличные драйверы устройств. Когда ядро полностью\nзагружено, то выполняется загрузка первой программы, которая\nвыполняется в режиме пользователя:\n * `C:\\command.com` - для DOS;\n * `C:\\Windows\\System32\\smms.exe` - для Windows;\n * `/boot/initrd` - для Linux.\n\n### Дальнейшие стадии запуска\n\nПроцесс `initrd`, в зависимости от дистрибутива:\n * классический Unix-way: запускает набор shell-скриптов в\n одном из подкаталогов `/etc/init.d/rcX.d`, где `X` -\n уровень запуска по умолчанию, прописанный в файле\n `/etc/inittab`;\n * SystemD-way: запускает программу `systemd`, которая\n   имеет свой набор конфигурационных файлов, по которым\n   строит дерево зависимостей различных служб, и запускает их.\n"
  },
  {
    "path": "practice/asm/nostdlib_baremetal/toyos/Makefile",
    "content": "AS:=as --32\nCC:=gcc -m32\n\nCFLAGS:=-ffreestanding -O2 -Wall -Wextra -nostdlib\nCPPFLAGS:=\nLIBS:=-lgcc\n\nOBJS:=\\\nboot.o \\\nkernel.o \\\n\nall: myos.bin\n\n.PHONEY: all clean iso run-qemu\n\nmyos.bin: $(OBJS) linker.ld\n\t$(CC) -T linker.ld -Wl,--build-id=none -o $@ $(CFLAGS) $(OBJS) $(LIBS)\n\n%.o: %.c\n\t$(CC) -c $< -o $@ -std=gnu99 $(CFLAGS) $(CPPFLAGS)\n\n%.o: %.s\n\t$(AS) $< -o $@\n\nclean:\n\trm -rf isodir\n\trm -f myos.bin myos.iso $(OBJS)\n\niso: myos.iso\n\nisodir isodir/boot isodir/boot/grub:\n\tmkdir -p $@\n\nisodir/boot/myos.bin: myos.bin isodir/boot\n\tcp $< $@\n\nisodir/boot/grub/grub.cfg: grub.cfg isodir/boot/grub\n\tcp $< $@\n\nmyos.iso: isodir/boot/myos.bin isodir/boot/grub/grub.cfg\n\tgrub-mkrescue -o $@ isodir\n\nrun-qemu: myos.iso\n\tqemu-system-i386 -cdrom myos.iso\n"
  },
  {
    "path": "practice/asm/nostdlib_baremetal/toyos/README.md",
    "content": "Example from\n[https://wiki.osdev.org/Bare_Bones](https://wiki.osdev.org/Bare_Bones)\n"
  },
  {
    "path": "practice/asm/nostdlib_baremetal/toyos/boot.s",
    "content": "/* Declare constants for the multiboot header. */\n.set ALIGN,    1<<0             /* align loaded modules on page boundaries */\n.set MEMINFO,  1<<1             /* provide memory map */\n.set FLAGS,    ALIGN | MEMINFO  /* this is the Multiboot 'flag' field */\n.set MAGIC,    0x1BADB002       /* 'magic number' lets bootloader find the header */\n.set CHECKSUM, -(MAGIC + FLAGS) /* checksum of above, to prove we are multiboot */\n \n/* \nDeclare a multiboot header that marks the program as a kernel. These are magic\nvalues that are documented in the multiboot standard. The bootloader will\nsearch for this signature in the first 8 KiB of the kernel file, aligned at a\n32-bit boundary. The signature is in its own section so the header can be\nforced to be within the first 8 KiB of the kernel file.\n*/\n.section .multiboot\n.align 4\n.long MAGIC\n.long FLAGS\n.long CHECKSUM\n \n/*\nThe multiboot standard does not define the value of the stack pointer register\n(esp) and it is up to the kernel to provide a stack. This allocates room for a\nsmall stack by creating a symbol at the bottom of it, then allocating 16384\nbytes for it, and finally creating a symbol at the top. The stack grows\ndownwards on x86. The stack is in its own section so it can be marked nobits,\nwhich means the kernel file is smaller because it does not contain an\nuninitialized stack. The stack on x86 must be 16-byte aligned according to the\nSystem V ABI standard and de-facto extensions. The compiler will assume the\nstack is properly aligned and failure to align the stack will result in\nundefined behavior.\n*/\n.section .bss\n.align 16\nstack_bottom:\n.skip 16384 # 16 KiB\nstack_top:\n \n/*\nThe linker script specifies _start as the entry point to the kernel and the\nbootloader will jump to this position once the kernel has been loaded. It\ndoesn't make sense to return from this function as the bootloader is gone.\n*/\n.section .text\n.global _start\n.type _start, @function\n_start:\n\t/*\n\tThe bootloader has loaded us into 32-bit protected mode on a x86\n\tmachine. Interrupts are disabled. Paging is disabled. The processor\n\tstate is as defined in the multiboot standard. The kernel has full\n\tcontrol of the CPU. The kernel can only make use of hardware features\n\tand any code it provides as part of itself. There's no printf\n\tfunction, unless the kernel provides its own <stdio.h> header and a\n\tprintf implementation. There are no security restrictions, no\n\tsafeguards, no debugging mechanisms, only what the kernel provides\n\titself. It has absolute and complete power over the\n\tmachine.\n\t*/\n \n\t/*\n\tTo set up a stack, we set the esp register to point to the top of the\n\tstack (as it grows downwards on x86 systems). This is necessarily done\n\tin assembly as languages such as C cannot function without a stack.\n\t*/\n\tmov $stack_top, %esp\n \n\t/*\n\tThis is a good place to initialize crucial processor state before the\n\thigh-level kernel is entered. It's best to minimize the early\n\tenvironment where crucial features are offline. Note that the\n\tprocessor is not fully initialized yet: Features such as floating\n\tpoint instructions and instruction set extensions are not initialized\n\tyet. The GDT should be loaded here. Paging should be enabled here.\n\tC++ features such as global constructors and exceptions will require\n\truntime support to work as well.\n\t*/\n \n\t/*\n\tEnter the high-level kernel. The ABI requires the stack is 16-byte\n\taligned at the time of the call instruction (which afterwards pushes\n\tthe return pointer of size 4 bytes). The stack was originally 16-byte\n\taligned above and we've since pushed a multiple of 16 bytes to the\n\tstack since (pushed 0 bytes so far) and the alignment is thus\n\tpreserved and the call is well defined.\n\t*/\n\tcall kernel_main\n \n\t/*\n\tIf the system has nothing more to do, put the computer into an\n\tinfinite loop. To do that:\n\t1) Disable interrupts with cli (clear interrupt enable in eflags).\n\t   They are already disabled by the bootloader, so this is not needed.\n\t   Mind that you might later enable interrupts and return from\n\t   kernel_main (which is sort of nonsensical to do).\n\t2) Wait for the next interrupt to arrive with hlt (halt instruction).\n\t   Since they are disabled, this will lock up the computer.\n\t3) Jump to the hlt instruction if it ever wakes up due to a\n\t   non-maskable interrupt occurring or due to system management mode.\n\t*/\n\tcli\n1:\thlt\n\tjmp 1b\n \n/*\nSet the size of the _start symbol to the current location '.' minus its start.\nThis is useful when debugging or when you implement call tracing.\n*/\n.size _start, . - _start\n"
  },
  {
    "path": "practice/asm/nostdlib_baremetal/toyos/grub.cfg",
    "content": "menuentry \"myos\" {\n\tmultiboot /boot/myos.bin\n}\n"
  },
  {
    "path": "practice/asm/nostdlib_baremetal/toyos/kernel.c",
    "content": "#include <stdbool.h>\n#include <stddef.h>\n#include <stdint.h>\n \n \n/* Hardware text mode color constants. */\nenum vga_color {\n\tVGA_COLOR_BLACK = 0,\n\tVGA_COLOR_BLUE = 1,\n\tVGA_COLOR_GREEN = 2,\n\tVGA_COLOR_CYAN = 3,\n\tVGA_COLOR_RED = 4,\n\tVGA_COLOR_MAGENTA = 5,\n\tVGA_COLOR_BROWN = 6,\n\tVGA_COLOR_LIGHT_GREY = 7,\n\tVGA_COLOR_DARK_GREY = 8,\n\tVGA_COLOR_LIGHT_BLUE = 9,\n\tVGA_COLOR_LIGHT_GREEN = 10,\n\tVGA_COLOR_LIGHT_CYAN = 11,\n\tVGA_COLOR_LIGHT_RED = 12,\n\tVGA_COLOR_LIGHT_MAGENTA = 13,\n\tVGA_COLOR_LIGHT_BROWN = 14,\n\tVGA_COLOR_WHITE = 15,\n};\n \nstatic inline uint8_t vga_entry_color(enum vga_color fg, enum vga_color bg) \n{\n\treturn fg | bg << 4;\n}\n \nstatic inline uint16_t vga_entry(unsigned char uc, uint8_t color) \n{\n\treturn (uint16_t) uc | (uint16_t) color << 8;\n}\n \nsize_t strlen(const char* str) \n{\n\tsize_t len = 0;\n\twhile (str[len])\n\t\tlen++;\n\treturn len;\n}\n \nstatic const size_t VGA_WIDTH = 80;\nstatic const size_t VGA_HEIGHT = 25;\n \nsize_t terminal_row;\nsize_t terminal_column;\nuint8_t terminal_color;\nuint16_t* terminal_buffer;\n \nvoid terminal_initialize(void) \n{\n\tterminal_row = 0;\n\tterminal_column = 0;\n\tterminal_color = vga_entry_color(VGA_COLOR_LIGHT_GREY, VGA_COLOR_BLACK);\n\tterminal_buffer = (uint16_t*) 0xB8000;\n\tfor (size_t y = 0; y < VGA_HEIGHT; y++) {\n\t\tfor (size_t x = 0; x < VGA_WIDTH; x++) {\n\t\t\tconst size_t index = y * VGA_WIDTH + x;\n\t\t\tterminal_buffer[index] = vga_entry(' ', terminal_color);\n\t\t}\n\t}\n}\n \nvoid terminal_setcolor(uint8_t color) \n{\n\tterminal_color = color;\n}\n \nvoid terminal_putentryat(char c, uint8_t color, size_t x, size_t y) \n{\n\tconst size_t index = y * VGA_WIDTH + x;\n\tterminal_buffer[index] = vga_entry(c, color);\n}\n \nvoid terminal_putchar(char c) \n{\n\tterminal_putentryat(c, terminal_color, terminal_column, terminal_row);\n\tif (++terminal_column == VGA_WIDTH) {\n\t\tterminal_column = 0;\n\t\tif (++terminal_row == VGA_HEIGHT)\n\t\t\tterminal_row = 0;\n\t}\n}\n \nvoid terminal_write(const char* data, size_t size) \n{\n\tfor (size_t i = 0; i < size; i++)\n\t\tterminal_putchar(data[i]);\n}\n \nvoid terminal_writestring(const char* data) \n{\n\tterminal_write(data, strlen(data));\n}\n\n\nvoid kernel_main(void) \n{\n\t/* Initialize terminal interface */\n\tterminal_initialize();\n \n\t/* Newline support is left as an exercise. */\n    terminal_writestring(\"Hello, kernel World!\\n\");\n}\n"
  },
  {
    "path": "practice/asm/nostdlib_baremetal/toyos/linker.ld",
    "content": "/* The bootloader will look at this image and start execution at the symbol\n   designated as the entry point. */\nENTRY(_start)\n \n/* Tell where the various sections of the object files will be put in the final\n   kernel image. */\nSECTIONS\n{\n\t/* Begin putting sections at 1 MiB, a conventional place for kernels to be\n\t   loaded at by the bootloader. */\n\t. = 1M;\n \n\t/* First put the multiboot header, as it is required to be put very early\n\t   early in the image or the bootloader won't recognize the file format.\n\t   Next we'll put the .text section. */\n\t.text BLOCK(4K) : ALIGN(4K)\n\t{\n\t\t*(.multiboot)\n\t\t*(.text)\n\t}\n \n\t/* Read-only data. */\n\t.rodata BLOCK(4K) : ALIGN(4K)\n\t{\n\t\t*(.rodata)\n\t}\n \n\t/* Read-write data (initialized) */\n\t.data BLOCK(4K) : ALIGN(4K)\n\t{\n\t\t*(.data)\n\t}\n \n\t/* Read-write data (uninitialized) and stack */\n\t.bss BLOCK(4K) : ALIGN(4K)\n\t{\n\t\t*(COMMON)\n\t\t*(.bss)\n\t}\n \n\t/* The compiler may produce other sections, by default it will put them in\n\t   a segment with the same name. Simply add stuff here as needed. */\n}\n"
  },
  {
    "path": "practice/asm/x86_basics/README.md",
    "content": "# Ассемблер архитектуры x86 (32-bit, и немного про 64-bit)\n\nОсновной reference по набору команд [преобразованный в HTML](https://www.felixcloutier.com/x86/).\n\nReference по наборам команд MMX, SSE и AVX [на сайте Intel](https://software.intel.com/sites/landingpage/IntrinsicsGuide/).\n\nНеплохой учебник по ассемблеру x86 [на WikiBooks](https://en.wikibooks.org/wiki/X86_Assembly)\n\n## 32-разрядный ассемблер в 64-битных системах\n\nМы будем использовать 32-разрядный набор инструкций. На 64-битных архитектурах\nдля этого используется опция компилятора gcc `-m32`.\n\nКроме того, необходимо установить стек 32-разрядных библиотек. В Ubuntu\nэто делается всего одной командой:\n```\nsudo apt-get install gcc-multilib\n```\n\n## Синтаксис AT&T и Intel\n\nИсторически сложилось два синтаксиса языка ассемблера x86: синтаксис AT&T,\nиспользуемый в UNIX-системах, и синтаксис Intel, используемый в DOS/Windows.\n\nРазличие, в первую очередь, относится к порядку аргументов команд.\n\nКомпилятор gcc по умолчанию использует синтаксис AT&T, но с указанием опции\n`-masm=intel` может переключаться в синтаксис Intel.\n\nКроме того, можно указать используемый синтаксис первой строкой в тексте\nсамой программы:\n```nasm\n.intel_syntax noprefix\n```\n\nЗдесь параметр `noprefix` после `.intel_syntax` указывает на то, что помимо порядка аргументов, соответствующих синтаксису Intel, ещё и имена регистров не должны начинаться с символа `%`, а константы - с символа `$`, как это принято в синтаксисе AT&T.\n\nМы будем использовать именно этот синтаксис, поскольку с его использованием\nнаписано большинство доступной документации и примеров, включая документацию\nот производителей процессоров.\n\n## Регистры процессора общего назначения\n\nИсторически семество процессоров x86 унаследовало набор 8-битных регистров\nобщего назначения\nсемества 8080/8085, которые назывались `a`, `b`, `c` и `d`. Но поскольку\nпроцессор 8086 стал 16-битным, то регистры стали назваться `ax`, `bx`, `cx`\nи `dx`.\nВ 32-битных процессорах они называются `eax`, `ebx`, `ecx` и `edx`, в\n64-битных `rax`, `rbx`, `rcx` и `rdx`.\n\nКроме того, в x86 есть регистры \"двойного назначения\", которые можно использовать, в том числе, в качестве регистров общего назначения, если пользоваться ограниченным подмножеством команд процессора:\n\n * `ebp` - верхняя граница стека;\n * `esi` - индекс элемента массива, из которого выполняется копирование;\n * `edi` - индекс элемента массива, в который выполняется копирование.\n\nРегистр `esp` содержит указатель на нижнюю границу стека, поэтому произвольным образом его использовать не рекомендуется.\n\n### Регистры x86-64\n\n64-разрядные регистры для архитектуры x86-64 именуются начиная с буквы `r`. Помимо регистров `rax`...`rsi`, `rdi` можно использовать регистры общего назначение `r9`...`r15`. Указатель стека хранится в `rsp`, верхняя граница стекового фрейма - в `rbp`.\n\nМладшие 32-разрядные части регистров `rax`...`rsi`,`rdi`,`rsp`,`rbp` можно адресовать по именам `eax`...`esi`,`edi`,`esp`,`ebp`. При записи значений по 32-битным именам регистров, старшие 32 разряда обнуляются, что приемлемо для операций над 32-разрядными беззнаковыми значениями.\n\nДля работы со знаковыми 32-разрядными значениями, например типом `int`, необходимо предварительно выполнять операции *знакового расширения* с помощью команды `movslq`\n\n## Некоторые инструкции\n\n**Для синтаксиса Intel** первым аргументов команды является тот, значение которого\nбудет модифицировано, а вторым - которое остается неизменным.\n\n```nasm\nadd     DST, SRC        /* DST += SRC */\nsub     DST, SRC        /* DST -= SRC */\ninc     DST             /* ++DST */\ndec     DST             /* --DST */\nneg     DST             /* DST = -DST */\nmov     DST, SRC        /* DST = SRC */\nimul    SRC             /* (eax,edx) = eax * SRC - знаковое */\nmul     SRC             /* (eax,edx) = eax * SRC - беззнаковое */\nand     DST, SRC        /* DST &= SRC */\nor      DST, SRC        /* DST |= SRC */\nxor     DST, SRC        /* DST ^= SRC */\nnot     DST             /* DST = ~DST */\ncmp     DST, SRC        /* DST - SRC, результат не сохраняется, */\ntest    DST, SRC        /* DST & SRC, результат не сохраняется  */\nadc     DST, SRC        /* DST += SRC + CF */\nsbb     DST, SRC        /* DST -= SRC - CF */\n```\n\n**Для синтаксиса AT&T** порядок аргументов - противоположный, то есть команда\n`add %eax, %ebx` вычислит сумму `%eax` и `%ebx`, после чего сохранит результат\nв регистр `%ebx`, который указан вторым аргументом.\n\n## Флаги процессора\n\nВ отличии от процессоров ARM, где обновление регистра флагов производится\nтолько при наличии специального флага в команде, обозначаемого суффиксом\n`s`, в процессорах Intel флаги обновляются всегда большинстом инструкций.\n\nФлаг `ZF` устанавливается, если в результате операции был получен нуль.\n\nФлаг `SF` устанавливается, если в результате операции было получено\nотрицательное число.\n\nФлаг `CF` устанавливается, если в результате выполнения операции произошел\nперенос из старшего бита результата. Например, для сложения `CF` устанавливается\nесли результат сложения двух беззнаковых чисел не может быть представлен\n32-битным беззнаковым числом.\n\nФлаг `OF` устанавливается, если в результате выполняния операции произошло\nпереполнение знакового результата. Например, при сложении `OF` устанавливается,\nесли результат сложения двух знаковых чисел не может быть представлен\n32-битным знаковым числом.\n\nОбратите внимание, что и сложение `add`, и вычитание `sub` устанавливают\nодновременно и флаг `CF`, и флаг `OF`. Сложение и вычитание знаковых и\nбеззнаковых чисел выполняется совершенно одинаково, и поэтому используется одна\nинструкция и для знаковой, и для беззнаковой операции.\n\nИнструкции `test` и `cmp` не сохраняют результат, а только меняют флаги.\n\n## Управление ходом программы\n\nБезусловный переход выполняется с помощью инструкции `jmp`\n```nasm\njmp label\n```\n\nУсловные переходы проверяют комбинации арифметических флагов:\n```nasm\njz      label   /* переход, если равно (нуль), ZF == 1 */\njnz     label   /* переход, если не равно (не нуль), ZF == 0 */\njc      label   /* переход, если CF == 1 */\njnc     label   /* переход, если CF == 0 */\njo      label   /* переход, если OF == 1 */\njno     label   /* переход, если OF == 0 */\njg      label   /* переход, если больше для знаковых чисел */\njge     label   /* переход, если >= для знаковых чисел */\njl      label   /* переход, если < для знаковых чисел */\njle     label   /* переход, если <= для знаковых чисел */\nja      label   /* переход, если > для беззнаковых чисел */\njae     label   /* переход, если >= (беззнаковый) */\njb      label   /* переход, если < (беззнаковый) */\njbe     label   /* переход, если <= (беззнаковый) */\n```\n\nВызов функции и возврат из неё осуществляются командами `call` и `ret`\n```nasm\ncall    label   /* складывает в стек адрес возврата, и переход на label */\nret             /* вытаскивает из стека адрес возврата и переходит к нему */\n```\n\nКроме того, есть составная команда для организации циклов, которая\nподразумевает, что в регистре `ecx` находится счётчик цикла:\n```nasm\nloop    label   /* уменьшает значение ecx на 1; если ecx==0, то\n                   переход на следующую инструкцию, в противном случае\n                   переход на label */\n```\n\n\n## Адресация памяти\n\nВ отличии от RISC-процессоров, x86 позволяет использовать в качестве\n**один из аргументов** команды как адрес в памяти.\n\n**В синтаксисе AT&T** такая адресация записывается в виде:\n`OFFSET(BASE, INDEX, SCALE)`, где `OFFSET` - это константа, `BASE` и `INDEX` -\nрегистры, а `SCALE` - одно из значений: `1`, `2`, `4` или `8`.\n\nАдрес в памяти вычисляется как `OFFSET+BASE+INDEX*SCALE`. Параметры `OFFSET`,\n`INDEX` и `SCALE` являются опциональными. При их отсутсвтвии подразумевается,\nчто `OFFSET=0`, `INDEX=0`, `SCALE` равен размеру машинного слова.\n\n**В синтаксисе Intel** используется более очевидная нотация:\n`[BASE + INDEX * SCALE + OFFSET]`.\n\n\n## Соглашения о вызовах для 32-разрядной архитектуры\n\nВозвращаемое значение 32-разрядного типа функции записывается в регистр\n`eax`, для возврата 64-разрядного значения используется пара `eax` и\n`edx`.\n\nВызываемая функция обязана сохранять на стеке значения регистров общего назначения `ebx`, `ebp`, `esi` и `edi`.\n\nАргументы могут передаваться в функцию различными способами, в зависимости\nот соглашений, принятых в ABI.\n\n\n### Соглашения cdecl и stdcall\n\nСоглашения о передаче аргументов, используемые на 32-разрядных\nсистемах архитектуры x86. Все аргументы функций складываются справа-налево\nв стек, затем вызывается функция, которая адресует аргументы через указатель\n`ebp` или `esp` с некоторым положительным смещением.\n\nПример:\n```c\nchar * s = \"Name\";\nint value1 = 123;\ndouble value2 = 3.14159;\nprintf(\"Hello, %s! Val1 = %d, val2 = %g\\n\", s, value1, value2);\n```\n\nЗдесь перед вызовом `printf` в стек будут сложены значения, переменных,\nпрежде чем вызвана функция:\n```nasm\npush    value2\npush    value1\npush    s\npush    .FormatString\ncall    printf\n```\n\nВ случае использования соглашения `stdcall`, **вызываемая** функция\nобязана удалить из стека переданные её аргументы после их использования.\n\nВ случае использования соглашения `cdecl`, **вызывающая** функция обязана\nудалить из стека те переменные, которые были переданы в вызываемую функцию.\n\nНа языках Си/С++ используемые соглашения можно указывать в специцикаторах\nфункций, например:\n```\nvoid __cdecl regular_function(int arg1, int arg2);\n#define WINAPI __stdcall\nvoid WINAPI  winapi_function(int arg1, int arg2);\n```\n\nСоглашение `stdcall` сейчас используется в основном в операционной системе\nWindows для обращения к функциям WinAPI. Во всех остальных случаях на\n32-разрядных системах используется `cdecl`.\n\n### Соглашение fastcall\n\nЕсли требуется передать в функцию немного целочисленных аргументов, то можно\nиспользовать регистры, как в архитектуре ARM. Такое соглашение называется\n`fastcall`.\n\nСоглашение `fastcall` используется для вызова функций ядра (системных вызовов)\nв UNIX-подобных системах. В частности, в Linux регистр `eax` используется\nдля передачи номера системного вызова, а регистры `ebx`, `ecx` и `edx` -\nдля передачи целочисленных аргументов.\n\nАналогичный подход используется и в архитектуре x86-64, где доступных\nрегистров больше, чем в 32-разрядной архитектуре x86.\n\n## Соглашения о вызовах для 64-разрядной архитектуры SystemV AMD64 ABI\n\nЦелочисленные аргументы передаются последовательно в регистрах: `rdi`, `rsi`, `rdx`, `rcx`, `r8`, `r9`. Если передается более 6 аргументов, то оставшиеся - через стек.\n\nВещественные аргументы передаются через регистры `xmm0`...`xmm7`.\n\nВозвращаемое значение целочисленного типа должно быть сохранено в `rax`,  вещественного - в `xmm0`.\n\nВызываемая функция обязана сохранять на стеке значения регистров общего назначения `rbx`, `rbp`, и регистры `r12`...`r15`.\n\nКроме того, при вызове функции для 64-разрядной архитектуры есть дополнительное требование - перед вызовом функции стек должен быть выровнен по границе 16 байт, то есть необходимо уменьшить значение `rsp` таким образом, оно было кратно 16. Если кроме регистров задействуется стек для передачи параметров, то они должны быть прижаты к нижней выровненной границе стека.\n\nДля функций гарантируется 128-байтная \"красная зона\" в стеке ниже регистра `rsp` - область, которая не будет затронута внешним событием, например, обработчиком сигнала. Таким образом, можно задействовать для адресации локальных переменных память до `rsp-128`.\n"
  },
  {
    "path": "practice/asm/x86_fpmath/README.md",
    "content": "# Вещественная арифметика на x86\n\nОсновной reference по набору команд [преобразованный в HTML](https://www.felixcloutier.com/x86/).\n\nReference по наборам команд MMX, SSE и AVX [на сайте Intel](https://software.intel.com/sites/landingpage/IntrinsicsGuide/).\n\n## Сопроцессор x87\n\nОперации над вещественными числами выполняются отдельными\nблоками процессора. Исторически сложилось, что для\nвещественной арифметики использовался отдельный\n*сопроцессор*, а начиная с процессоров 486 (1991 год) этот\nсопроцессор был интегрирован в кристалл основного\nпроцессора.\n\nТаким образом, в целях совместимости со старым кодом,\nвыполнение вещественных операций над числами с плавающей\nточкой выполняется из предположения наличия сопроцессора.\n\nКомпилятор `gcc` использует по умолчанию именно этот способ\nработы с вещественными числами для 32-разрядной архитектуры\n(эквивалентно опции `-mfpmath=387`). Для 64-разрядной\nархитектуры по умолчанию\nиспользуется набор команд SSE (`-mfpmath=sse`).\n\nВзаимодействие с сопроцессором x87 организовано в\nформе записи операндов в стек и выполнения операций\nнад элементами этого стека. Команды сопроцессора обычно\nначинаются с буквы `f`, и оперируют с регистрами,\nкоторые обозначаются от `st(0)` (вершина стека) до\n`st(7)` (последний регистр FPU).\n\nВыполнять арифметические FPU-операции можно над любыми\nрегистрами этого стека, но операции, позволяющие\nвзаимодействие c памятью, возможны только через\nвершину стека `st(0)`.\n\nОсновные инструкции x86:\n```\nfld SIZE ptr ADDR // загрузить в стек значение из памяти\nfld REG           // поместить на вершину стека значение из другого регистра st(X)\nfld1              // поместить на вершину стека значение 1\nfldz              // 0\nfldpi             // π\nfst SIZE ptr ADDR // сохранить из стека в память\n\nfild SIZE ptr ADDR // загрузить целое число в стек\nfist SIZE ptr ADDR // сохранить целое число из стека\n\nfcom              // сравнение st(0) с памятью\nfcomi             // сравнение st(0) с st(i)\n\nfadd\nfsub\nfmul\nfdiv              // операции над вещественными операндами\n\nfiadd\nfisub\nfimul\nfidiv             // операции над целочисленными операндами\n```\n\n## Регистры MMX/SSE/AVX/AVX-512\n\nВ современных Intel/AMD процессорах есть 16 регистров\n(в 32-разрядном режиме доступны только 8),\nкоторые предназначены как для вещественных операций, так\nи для целочисленных.\n\n128-битные регистры MMX/SSE именуются `xmm0`...`xmm7`,\n`xmm8`...`xmm15`.\n\n256-битные регистры AVX `ymm0`...`ymm15` подразумевают, что\nих младшие 128 бит совпадают с регистрами MMX/SSE.\n\n512-битные регистры AVX-512 (новые Xeon и Core i9)\n`zmm0`...`zmm15` подразумевают, что младшие 256 бит\nсовпадают с регистрами AVX.\n\n\n## Скалярные инструкции SSE\n\nНесмотря на свой большой размер, регистры SSE можно\nиспользовать как обычные скалярные, что намного эффективнее,\nчем x87 FPU.\n\nВ отличии от регистров в стеке `st(0)`...`st(7)`, все\nрегистры SSE являются равнозначными.\n\n```\n// Копирование регистр-регистр и регистр-память\nmovsd   DST, SRC  // пересылка double\nmovss   DST, SRC  // пересылка float\n\n// Арифметические\naddsd   DST, SRC   // DST += SRC, double\naddss   DST, SRC   // DST += SRC, float\nsubsd   DST, SRC   // DST -= SRC, double\nsubss   DST, SRC   // DST -= SRC, float\nmulsd   DST, SRC   // DST *= SRC, double\nmulss   DST, SRC   // DST *= SRC, float\ndivsd   DST, SRC   // DST /= SRC, double\ndivss   DST, SRC   // DST /= SRC, float\nsqrtsd  DST, SRC   // DST = sqrt(SRC), double\nsqrtss  DST, SRC   // DST = sqrt(SRC), float\nmaxsd   DST, SRC   // DST = max(DST, SRC), double\nmaxss   DST, SRC   // DST = max(DST, SRC), float\nminsd   DST, SRC   // DST = min(DST, SRC), double\nminss   DST, SRC   // DST = min(DST, SRC), float\n\n// Преобразования\ncvtsd2si DST, SRC  // double -> int\ncvtsi2sd DST, SRC  // int -> double\n\n// Сравнения (операция DST-SRC, которая меняет флаги)\ncomisd  DST, SRC  // для double\ncomiss  DST, SRC  // для float\n\n```\n\n## Соглашения о вызовах для x86 (32 бит)\n\nВсе вещественные аргументы передаются в функцию через стек.\nВозвращаемое вещественное значение должно быть сохранено\nв регистре `st(0)`, причем это требование необходимо\nсоблюдать **даже при использовании регистров SSE**. Это\nнеобходимо для того, чтобы вызывающая функция могла\nиспользовать вещественный результат, независимо от способа\nреализации вызываемой функции.\n\nПереместить результат из регистра SSE в регистр x87\nможно только с ипользованием памяти:\n```\nsub     esp, 8          // выделяем 8 байт на стеке\nmovsd   [esp], xmm0     // копируем из xmm0 в память\nfld     qword ptr [esp] // загружаем из памяти в стек x87\nadd     esp, 8          // освобождаем память на стеке\n```\n\n\n## Векторные инструкции SSE и intrisics-функции на Си\n\nМежду регистрами можно выполнять *векторные* операции, то\nесть операции сразу над несколькими 8, 16, 32 или 64-битными\nзначениями, которые хранятся в паре 128-битных регистров.\n\nОбщий вид таких команд следующий:\n```\nOPERATION p [s|d]\n```\nгде `OPERATION` - это одна из операций `add`, `mul` и т.д.,\nбуква `p` в названии команды является сокращением от\n`p`acked, а `s` или `d` - это `s`ingle или `d`ouble точность\nвещественных чисел.\n\nЗагрузка/сохранение выполняется вариантами команды `mov`:\n```\nmov[ap|up][s|d]   DST, SRC\n```\nгде `ap` - загрузка/сохнанение из памяти, выровненной по\nгранице размера регистра (16 байт), `up` - для\nневыровненной памяти.\n\nИспользование операндов в памяти для операций, отличных от\n`mov`, возможно только для выровненной памяти.\n\nДля задействования векторных инструкций не обязательно\nиспользовать язык ассемблера. Компиляторы Intel и `gcc`\nимеют поддержку псевдо-функций, объявленных в заголовочных\nфайлах вида `*intrin.h`, которые транслируются в эти\nинструкции при компиляции. Подробный Reference\n[доступен здесь](https://software.intel.com/sites/landingpage/IntrinsicsGuide/).\n"
  },
  {
    "path": "practice/bash-grep-sed/README.md",
    "content": "# Программирование командной строки\n\n## Командные интерпретаторы\n\nКомандная строка в UNIX-подобных системах выполняет текстовые команды, и этот текст может быть записан в виде построчной программы, пригодной к выполнению из файла.\n\nСкрипт командной строки обычно начинается со строчки вида:\n\n```\n#!/usr/bin/env bash\n```\n\nДанная строка синтаксически является комментарием во многих языках программирования, но имеет специальное назначение в UNIX-подобных системах. Исполняемые файлы, которые начинаются с символов `#!` подразумевают запуск программы-интерпретатора, указанной после `#!`, которой передается в качестве аргумента передается имя файла скрипта.\n\nВ программы-интерпретатора должен быть указан ее полный путь. Расположение некоторых интерпретаторов, например `/bin/sh`, является стандартизированным для всех UNIX-подобных систем, для других, например bash, этот путь может быть как `/bin/bash`, так и `/usr/bin/bash`  или `/usr/local/bin/bash`, - гарантировать путь однозначно нельзя. Для поиска интерпретатора в переменной окружения `PATH` используется утилита `/usr/bin/env`, которая устанавливает переменные окружения, включая `PATH`, и запускает программу, переданную ей в качестве аргумента.\n\n## Язык программирования SH\n\nВ разных Linux-системах и других UNIX-подобных системах используются различные интерпретаторы командной строки, общим предком которых является классический интерпретатор `/bin/sh`. При этом, сам интерпретатор `/bin/sh` является символической ссылкой на используемый в дистрибутиве интерпретатор по умолчанию (за исключением MacOS, где `/bin/sh` - это `bash` старой версии, в то время как в системе используется `zsh`).\n\nМожно считать, что программа, ориентированная на `/bin/sh` может быть запущена на любой UNIX-подобной системе, но при написании таких скриптов необходимо ориентироваться на общее подмножество функциональности различных интерпретаторов, которое регламентировано стандартом POSIX. В дальнейшем мы будем использовать интерпретатор `bash`, который присутствует во всех популярных дистрибутивах Linux, и обладает широкой фунциональностью. \n\nКаждая команда shell-скрипта может располагаться на отдельной строке, либо заканчиваться символом точки с запятой, если необходимо записать в одну строку несколько команд.\n\nКоманды скриптов, в большинстве случаев, - это внешние программы, которые располагаются в одном из каталогов, перечисленных в переменной окружения `PATH`, но некоторые команды не могут быть реализованы как отдельные программы, поскольку изменяют текущее окружение, что не может быть сделано внешней программмой. Примерами таких команд являются:\n\n* `cd` - изменение текущего каталога;\n* `export` - делают переменную доступной дочерним процессам;\n* `read` - читает текст из файла или стандартного потока ввода, и записывает результат в переменную;\n* `ulimit` - устанавливает ограничения ресурсов на текущий сеанс;\n* `exit` - завершает работу командного интерпретатора.\n\nКроме того, поскольку запуск внешних программ является ресурсозатратной операцией, в некоторых оболочках отдельные часто используемые команды реализованы как встроенные, хотя их функциональность дублируется внешними одноименными программами, например команда `echo` для интерпретаторов `bash` и `zsh`, или команда `[` для оболочки `bash` . Полный список встроенных команд для текущей оболочки можно получить командой `man builtins`.\n\nКоманды могут иметь аргументы, которые разделяютя пробелами. В случае, если аргумент должен содержать пробел, или какой-либо другой символ, имеющий специальное назначение, такой аргумент нужно заключать в кавычки. Также зарезервированные символы можно экранировать с помощью символа `\\`. Список зарезервированных символов, помимо пробельных, которые нужно экранировать, или заключать в кавычки:\n\n```\n| & ; < > ( ) $ ` \\ \" ' * ? [ # ~ = %\n```\n\nЭкранирование символа переноса строки выполняется специальным образом, что обусловлено необходимостью читабельности кода скрипта: `$'\\n'`.\n\n### Особенности синтаксиса и специальные символы\n\nВ скриптах используются три вида кавычек, которые имеют различное семантическое назначение:\n\n* 'одинарные кавычки' - сохраняет текст без изменений;\n* \\`обратные одинарные кавычки\\` - выполняют команду и результатом является вывод этой команды;\n* \"двойные кавычки\" - внутри них возможно экранирование символом \\, подстановка значений переменных (начинаются с символа $), и возможно выполнение команд с помощью вложенных `обратных кавычек`.\n\nПеременные объявляются символом `=`, причем пробелы вокруг этого символа не допускаются. Использовать переменные можно в виде `$переменная` или `${переменная}`. \n\n```\n# значение 123\nvar1=123\n# пустое значение\nvar2=\n# строка\nvar3=\"hello world\"\n# так нельзя - будет ошибка\nvar4 = 123\n\n# обращение к переменным\n# вывод будет: $var1 hello world\necho '$var1' \"$var2\" \"$var3\" \"$var_not_exist\"\n```\n\nИспользование несуществующей переменной не приводит к ошибке, - будет просто пустое значение.\n\n### Выполнение команд и получение их результата\n\nЕсли необходимо сохранить вывод команды в переменную, то используется один из двух способов:\n\n* \\`обратные одинарные кавычки\\`, между которыми заключена команда и ее аргументы;\n* заключение к конструкцию `$()`.\n\nЕсли вывод команды содержит в конце символы перевода строк, то они удаляются. \n\nПример:\n\n```\nos_name=`uname -s`\narch_name=$(uname -m)\necho \"OS is $os_name running on $arch_name\"\n# OS is Linux running on x86_64\n```\n\nЕсли необходимо получить код возврата команды, а не результат ее вывода, то можно использовать переменную `$?` сразу после ее выполнения. \n\n### Специальные переменные и аргументы\n\n* `$?` - целочисленный код возврата последней команды;\n* `$0`...`$9` - аргументы команды от 0 до 9, при этом `$0` - имя самого скрипта;\n* `$#` - целочисленное значение количества аргументов;\n* `$@` - список всех аргументов, начиная с первого;\n* `$*` - строка, которая содержит список всех аргументов, начиная с первого.\n\n### Объявление и вызов функций\n\nФункции - это команды, которые доступны только из текущего скрипта, которым можно передавать аргументы, и они могут возвращать текст с помощью записи на \"стандартный поток вывода\".\n\nИнтерпретаторы `bash` и `zsh` поддерживают три вида синтаксиса объявлений:\n\n```\n# 1. Только имя и скобки\nvery_important_function() {\n   # реализация функции\n}\n\n# 2. Полный синтаксис \nfunction very_important_function() {\n   # реализация функции\n}\n\n# 3. Без скобок\nfunction very_important_function {\n   # реализация функции\n}\n\n# вызов функции с двумя аргументами, и сохранением результата\nvalue=$(very_important_function hello world)\n\n# вывзов функции без сохранения возвращаемого результата\nvery_important_function hello world\n```\n\nЕсли необходимо обеспечить совместимость с произвольным интерпретатором POSIX  `sh`, то можно использовать только первый вариант объявления.\n\nАргументы в функцию передаются точно так же, как и в команду, и доступны через специальные переменные.\n\nВсе переменные, объявляенные внутри функции, становятся доступными глобально после ее завершения. Если переменные нужно только локально, то в `bash` и `zsh` перед объявлением переменной можно использовать ключевое слово `local`.\n\nФункции можно импортировать из другого файла, используя синтаксис `. имя_файла`.\n\n### Перенаправление вывода\n\nДля передачи данных от одной функции/команды к другой, не обязательно сохранять результаты в переменную, можно передавать из через механизм перенаправления, используя оператор `|`.\n\n```\nfunction f() {\n   # вывод kek и списка аргументов\n   echo \"kek $*\"\n}\n\nfunction g() {\n   # замена e на E\n   sed 's/e/E/g'\n}\n\nfunction h() {\n   # замена d на первый аргумент функции\n   echo $0\n   sed \"s/d/$1/g\"\n}\n\nf first second third | g | h Meaow\n# kEk first sEconMeaow thirMeaow\n\n# команда wc -c подсчитвает количество байт\nf | wc -c \n# 5\n\n```\n\n### Условное выполнение последовательности команд\n\nРезультатом работы команды, помимо вывода, является целочисленный код возврата, причем целое число должно быть в диапазоне от 0 до 127. Значение 0 означает успешное завершение команды, остальные значения, - признак \"ошибки\" или ложного значения. Код завершения предыдущей выполненной команды хранится в переменной `$?`. При объявлении функций код возврата определяется кодом возврата последней выполненной внутри функции команды, либо задается с помощью оператора `return`.\n\nКоманды можно объединять в последовательности, которые выполняются в зависимости от результата выполнения предыдущей команды:\n\n* `cmd1 && cmd2` - `cmd2` будет выполнена, если `cmd1` выполнена успешно, а итоговый результат - это код возврата `cmd2`;\n* `cmd1 || cmd2` - `cmd2` будет выполнена, если не удалось успешно выполнить `cmd1`, результат - либо значение 0, либо код возврата `cmd2`.\n\nВыполнение цепочки команд можно заключать в круглые скобки для указания приоритетов логических операций.\n\n```\nfunction f() {\n    echo \"I'm function f\"\n    return 0\n}\n\nfunction g() {\n    echo \"I'm function g\"\n    return 5\n}\n\nfunction h() {\n    echo \"I'm function h\"\n    return 0\n}\n\nf && g && h    # вывод только от f и g, но не h\necho \"---\"\nf && (g || h)  # вывод от f, g и h\n```\n\nПредусмотрены две программы, которые не делают абсолютно ничего, а только возвращают код 0 или 1: это команда `true`, и команда `false`. Они предназначены для использования внутри таких \"логических выражений\", например, если нужно подавить код ошибки для необязательной операции:\n\n```\nrm -f файл_который_не_существует || true  # всегда будет успешный код возврата\n```\n\n### Конструкция условия и команда [\n\nЛогические условия могут быть использованы условных конструкций, как в обычных языках программирования.\n\n```\nif true\nthen\n   # эта часть всегда будет выполняться\nfi\n\nif false\nthen\n   # это не будет выполняться никогда\nfi\n```\n\nАргументом команды `if` может быть любая команда. Истинным условием считается нулевой код возврата, а ложным - ненулевой. Для выполнения различных логических операций служит конструкция `[ .... ]`,  которая реализована, в общем случае, с помощью отдельной команды `[`.\n\n* `$x -eq $y` - истина, если значения `$x` и `$y` равны; \n* `$x -nq $y` - истина, если значения `$x` и `$y` не равны; \n* `$x -gt $y` - истина, если значение `$x` > `$y`;\n* `$x -lt $y` - истина, если значение `$x` < `$y`; \n* `$x -ge $y` - истина, если значение `$x` >= `$y`;\n* `$x -le $y` - истина, если значение `$x` <= `$y`;\n* `-n $str` - истина, если строка `$str` не пустая;\n* `-z $str` - истина, если строка `$str` пустая;\n* `$str1 = $str2` - истина, если строки `$str1` и `$str2` равны;\n* `-e $pathname` - истина, если существует путь `$pathname`;\n* `-f $filename` - истина, если существует обычный файл `$filename`;\n* `-d $dirname` - истина, если существует каталог `$dirname`;\n* `-x $filename` - истина, если существует обычный файл `$filename`, и он является выполняемым.\n\nВнутри конструкции `[ ... ]` можно использовать круглые скобки для указания приоритетов, и оператор отрицания `!`.\n\nВажно особенностью этой конструкции является то, что между символами `[`, `]` и разными операторами внутри конструкции обязавтельно должны быть пробельные символы, поскольку это вызов команды с аргументами.\n\n### Циклы\n\nПростейшим циклом является цикл `while`, аргумент которого точно такой же, как у конструкции `if`.\n\n```\nwhile true\ndo\n   # не только простейшая, но и самая \n   # опасная конструкция, поскольку цикл\n   # может никогда не завершиться\ndone\n```\n\nКонструкция `for` предназначена для итерации по элементам списка.\n\n```\n# 1. Итерация по элементам простого списка\nfor item in i love akos\ndo\n   echo \"$item\"\ndone\n\n# 2. Итерация по элемента генерируемого по маске списка файлов\nfor filename in *.txt\ndo\n   echo \"$filename might be plain text\"\ndone\n```\n\nИнтерпретаторы `bash` и `zsh` имеют еще одну, нестандартную для POSIX `sh`, конструкцию циклов, которая синтаксически близка к Си-подобным языкам.\n\n```\n# только bash/zsh\n\nfor (( i=0; i<10; i++ ))\ndo\n   echo \"$i\"\ndone\n```\n\n \n\n### Internal Field Separator\n\nЭлементы списка разделяются символом пробел в самом скрипте, если они перечислены после ключевого слова `in`, но могут также быть прочитаны из файла, либо получены из произвольной строки.\n\n```\nfor item in $(echo \"i love    akos\")\ndo\n  echo \"$item\"\ndone\n\n# i\n# love\n# akos\n```\n\nВ этом случае разделителями считаются подряд идущие последовательности пробельных символов: пробел, табуляция и символ перевода строки. Часто бывает необходимо переопределить символ разделителя, например для обработки текстовых файлов определенного формата. Для этого предназначена специальная переменная интерпретатора `IFS` (аббривеатура от Internal Field Separator).\n\n```\nIFS=💞\nfor item in $(echo \"i💞love💞💞💞akos\")\ndo\n    echo \"$item\"\ndone\n\n# i\n# love\n# \n# \n# akos\n```\n\nПосле переопределения символа в переменной `IFS` , разделители не группируются. В качестве разделителя можно использовать только односимвольную строку, причем интерпретаторы `bash` и `zsh` корректно обрабатывают многобайтные символы Юникода, но это не гарантируется для других интерпретаторов.\n\nЧтение из файла можно организовать либо через команду `cat`, либо используя встроенную функцию `read` (обычно используется внутри цикла `while`), которая читает очередную лексему, ограниченную разделителем из `IFS`, и возвращает код 0, в случае успешного чтения.\n\n### Арифметика\n\nКомандный интерпретатор `sh` позволяет вычислять произвольные арифметические выражения, но с принципиальным ограничением: допускается только знаковая целочисленная арифметика. Синтаксически операции эквивалентны таковым в других языках программирования, а сами выражения заключаются в конструкцию `$(( ... ))`. В отличии от команды `[`, круглые скобки не являются внешней командой, поэтому пробельные символы не обязательны.\n\n```\na=5\nb=3\nc=$(($a+$b))\nd=$(($a/$b))\n\necho \"a = $a, b = $b, c = $c, d = $d\"\n# a = 5, b = 3, c = 8, d = 1\n```\n\nДля вычислений с вещественнозначными значениями можно использовать простой консольный калькулятор `bc` (Basic Calculator). Эта программа выполняет вычисление арифметических выражений, позволяет использовать функции логарифма, экспоненты и тригонометрические функции.\n\n```\necho '(1+3)*2' | bc\n# 8\n\n# по умолчанию используется целочисленная арифметика,\n# флаг -l подключает дополнительную функциональность\necho '(1+3)/2.5' | bc -l\n# 1.60000000000000000000\n```\n\n### Массивы\n\nМассивы являются нестандартным расширением  `sh`, реализованным (по-разному) в командных интерпретаторах `bash` и `zsh`.\n\nПеременные массива объявляются как список, разделенный пробельными символами, заключенный в круглые скобки. Пустой массив обявляется как `()`.\n\nЭлементы массива можно индексировать целыми числами с 0 (для `bash`) или с 1 (для `zsh`). Индексы указываются в квадратных скобках, поэтому для исключения неоднозначности операторов, при использовании значения из массива, обязательно заключать переменную с индексом в фигурные скобки.\n\nАдресация массива целиком (например, для вывода), а не его отдельного элемента осуществляется с указанием индекса `[@]`. Размер массива определяется как `${#массив[@]}`.\n\n```\n# пример для bash - индексация с 0\n\narray=(1 2 3 4 5 6)\narray_size=${#array[@]}\n\nfor (( i=0; i<$array_size; i++ ))\ndo\n    # удвоенное значение\n    array[$i]=$(( ${array[$i]} * 2 ))\ndone\n\necho \"${array[@]}\"\n```\n\n## Обработка текстов в командной строке\n\nЗадачи обработки текстов возникают очень часто, и во многих случаях для их решения совершенно не обязательно писать программы на высокоуровневых языках программирования, - можно воспользоваться стандартными утилитами среды POSIX.\n\n### Регулярные выражения\n\nРегулярное выражение - это текстовый шаблон, включающий в себя специальные символы-подставновки, который предназначен для поиска и замен в тексте.\n\nСинтаксис описания регулярных выражений бывает различный, наиболее распространенный из них - это в формате языка программирования Perl, который также используется во многих других языках программирования.\n\nСтандарт POSIX для регулярных выражений при этом является менее функциональным, и определяет два уровня языка описания: базовый (BRE) и расширенный (ERE). Расширенный синтаксис POSIX отличается от базового тем, что не требует обязательного экранирования символов скобок, а также вводит операции `?`, `+` и `|`. В дальнейшем будем использовать именно расширенный синтаксис (утилиты `sed` и `grep` требуют явного указания ключа `-E` для работы в расширенном синтаксисе).\n\nДля тестирования регулярных выражений можно использовать веб-приложение [regex101.com](https://regex101.com), которое не поддерживает синтаксис POSIX, поэтому при написании выражений нужно не забывать о том, что не поддерживаются PCRE-специфичные конструкции, например определения классов символов через символ `\\`.\n\nСимволы-подстановки, используемые в регулярных выражениях:\n\n* `^` - начало строки;\n* `.` - любой символ;\n* `[   ]` - любой символ или диапазон символов, из перечисленных в квадратных скобках;\n* `[^  ]` - то же самое, но с отрицанием;\n* `$` - признак конца строки;\n* `(   )` - группа символов или подстановок;\n* `*` - повторение предыдущего символа 0 или более раз;\n* `?` - повторение предыдущего символа 0 или 1 раз (только ERE);\n* `+` - повторение предыдущего символа 1 или более раз  (только ERE);\n* `{n}` - повторение предыдущего символа ровно `n` раз (только ERE);\n* `{m, n}` - повторение предыдущего символа от `m` до `n` раз (только ERE);\n* `|` - выбор одного из вариантов, между которыми встретился этот символ  (только ERE).\n\n### Утилита grep\n\nУтилита `grep` построчно просматривает текст из файла или стандартного потока ввода, и выполняет фильтрацию содержимого, оставляя только те строки текста, которые соответствуют шаблону.\n\nПример. Содержимое исходного файла `test.txt`:\n\n```\nмама мыла раму\nпапа кушал сидр\nакос любят все\nмы все умрем\nфизтех чемпион физтех лучше всех\n```\n\n```\n# только одна строка, которая содержит слово \"мама\"\n> grep мама test.txt\nмама мыла раму\n\n# строки, которые содержат слова \"мама\" и \"папа\"\n> grep .а.а test.txt\nмама мыла раму\nпапа кушал сидр\n\n# все строки, которые начинаются со слова из четырех букв\n> grep -E '^.{4} ' test.txt\nмама мыла раму\nпапа кушал сидр\nакос любят все\n```\n\nВ последнем примере обратите внимание на следующие особенности:\n\n* необходима опция `-E`, поскольку используется конструкция `{n}`, определенная в расширенном стандарте;\n* регулярное выражение содержит символ пробела, поэтому заключено в кавычки.\n\nЧасти регулярного выражения, которые заключены в круглые скобки, запоминают вхождение текста, и могут быть использованы в самом шаблоне. Эти вхождения нумеруются от `\\1` до `\\9`.\n\n```\n# найти все строки, в которых слово из алфавита [а-яА-Я]\n# повторяется через одно слово\n> grep -E '([а-яА-Я]+) .+ \\1'\nфизтех чемпион физтех лучше всех \n```\n\nВместе с утилитой `grep` часто используется утилита `cut`, которая в найденной строке выбирает определенный \"столбец\", считая разделителем либо символ табуляции, либо какой-то произвольно заданный символ.\n\n```\n# найдем все вторые слова в строках, которые начинаются\n# со слова из четырех букв\n> grep -E '^.{4} ' test.txt | cut -d ' ' -f 2\nмыла\nкушал\nлюбят\n```\n\n### Потоковый редактор sed\n\nПомимо поиска, второй важный класс задач со строками, - это редактирование текста. Для автоматизации используются командные текстовые редакторы, такие как `sed` или `awk`. В отличии от обычных текстовых редакторов с пользовательским интерфейсом, потоковые редакторы оперируют набором команд редактирования. \n\nКоманды `sed` разделяются символом `;` и выполняют одно из действий: вставка в начало, вставка в конец, удаление и замена текста. Общий вид команд: `[ПОЗИЦИЯ]ДЕЙСТВИЕ`, где `ПОЗИЦИЯ` - это необязательная часть команды, определяющая позицию курсора редактирвоания, `ДЕЙСТВИЕ` - однобуквенная команда с возможными аргументами.\n\nОсновные команды редактирования:\n\n* `d` удаление;\n* `a` добавление текста после курсора;\n* `i` добавление текста перед курсором;\n* `s` замена текста по шаблону.\n\n`ПОЗИЦИЯ` описывается одним в одном из форматов:\n\n* `ЧИСЛО` - номер строки, которые нумеруются с 1;\n* `ЧИСЛО~ШАГ` - номер строки с повторением действия через определенное количество шагов;\n* `$` - последняя строка;\n* `/РЕГУЛЯРКА/` - все строки, сопоставленные с шаблоном.\n\nНабор команд является обязательным позиционным аргументом для команды `sed`. Как и для утилиты `grep`, если предполагается использование расширенного синтаксиса регулярных выражений, необходим флаг `-E`. \n\nЕсли утилите `sed` не указывать имя входного файла, то подразумевается взаимодействие со стандартными потоками ввода и вывода. Если указываются файлы (их может быть несколько), то прозводится чтение из указанных файлов, причем по умолчанию используется сквозная нумерация строк по всем файлам. Для того, чтобы каждый файл обрабатывался по-отдельности, необходима опция `-s`.\n\nОпция `-i`, также как и для `clang-format`, подразумевает сохранение изменений в исходный файл, а не вывод результата на стандартный поток вывода. Используйте эту опцию с осторожностью.\n\n#### Примеры\n\n```\n# удалить первую и последнюю строки из файла\n> sed '1d; $d' test.txt\nпапа кушал сидр\nакос любят все\nмы все умрем\n```\n\n```\n# удалить все нечетные строки из файла\n> sed '1~2d' test.txt\nпапа кушал сидр\nмы все умрем\n```\n\n```\n# вставить строку #!/bin/cat в начало файла\n> sed '1i#!/bin/cat' test.txt\n#!/bin/cat\nмама мыла раму\nпапа кушал сидр\nакос любят все\nмы все умрем\nфизтех чемпион физтех лучше всех\n```\n\n```\n# вставить пустую строку после первой строки\n> sed '1a\\ ' test.txt\nмама мыла раму\n\nпапа кушал сидр\nакос любят все\nмы все умрем\nфизтех чемпион физтех лучше всех\n```\n\n```\n# заменить слова \"мама\" и \"папа\" на \"родитель\"\n> sed -E 's/(мама|папа) /родитель /' test.txt\nродитель мыла раму\nродитель кушал сидр\nакос любят все\nмы все умрем\nфизтех чемпион физтех лучше всех\n```\n\n```\n# удалить все комментарии из Python-файлов текущего каталога\n# (без контроля синтаксиса, в том числе из строковых констант)\n> sed -i '/ *#/d' *.py\n```\n\n```\n# поменять местами два первых слова в каждой строке\n> sed -E 's/([а-я]+) ([а-я]+) (.*)/\\2 \\1 \\3/' test.txt\nмыла мама раму\nкушал папа сидр\nлюбят акос все\nвсе мы умрем\nчемпион физтех физтех лучше всех\n\n# то же самое, то только для строк, начинающихся с буквы \"м\"\n> sed -E '/^[м].*/s/([а-я]+) ([а-я]+) (.*)/\\2 \\1 \\3/' test.txt\nмыла мама раму\nпапа кушал сидр\nакос любят все\nвсе мы умрем\nфизтех чемпион физтех лучше всех\n```\n\n\n\n\n\n\n\n\n\n"
  },
  {
    "path": "practice/bpf/README.md",
    "content": "# Berkley Packet Filter\n\nОсновной класс задач, в которых применяется чтение из RAW-сокетов, - это мониторинг системы. При этом возникает проблема большого потока данных, который необходимо обрабатывать, и постоянное переключение контекста между выполнением кода в пространстве ядра и в пространстве пользователя существенно снижает производительность.\n\nПоскольку принимать нужно не все пакеты, проходящие через сетевой интерфейс, а только те, которые соответствуют некоторым критериям, то логично перенести логику фильтрации в адресное пространство ядра, а затем получать от ядра только те пакеты, которые не отвергнуты фильтром.\n\nВ качестве примера использования можно рассмотреть утилиту `tcpdump`, которая принимает в качестве аргумента текстовую строку, описывающую функцию фильтрации, и отображает только те события, которые соответствуют фильтру. В своей реализации утилита `tcpdump` использует BPF.\n\nДополнительные материалы (English only):\n\n* Документация из поставки ядра Linux: [Linux Socket Filtering aka Berkley Packet Filter (BPF)](https://github.com/torvalds/linux/blob/v5.6/Documentation/networking/filter.txt)\n* [man 2 bpf ](http://man7.org/linux/man-pages/man2/bpf.2.html)\n* [BPF and XDP Reference Guide](https://cilium.readthedocs.io/en/latest/bpf/#bpf-and-xdp-reference-guide)\n\n## Классический BPF: Linux Socket Filter\n\n### Мониторинг сети и задача фильтрации\n\nРассмотрим задачу фильрации пакетов на уровне Data Link Layer, и будем отлавливать те из них, которые соответствуют некоторому критерию. Для простоты можно рассмотреть IPv4/UDP-сообщения к определенному DNS-серверу, - такие запросы будет легко отлаживать.\n\nСоздадим Data-Link сокет, и свяжем его с определенным сетевым интерфейсом, например `eth0`:\n\n```c\nint sock = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL));\n\n/* Не забываем проверять ошибки! Packet-сокет невозможно создать, \nне имея права root или настроенный CAP_NET_RAW */\nif (-1==sock) { perror(\"socket\"); exit(1); } \n\n/* Далее нужно связать сокет с определенным сетевым интерфейсом */\nstruct ifreq req;\nmemset(&req, 0, sizeof(req));\nstrncpy(req.ifr_name, \"eth0\", IFNAMSIZ);\nioctl(sock, SIOCGIFINDEX, &req, sizeof(req)); // определяем индекс eth0\n\n/* Для Data-Link Layer нужно заполнить только часть полей адреса,\nостальные должны быть инициализированы нулями */\nstruct sockaddr_ll addr;\nmemset(&addr, 0, sizeof(addr));\naddr.sll_family = AF_PACKET;  // указываем, что структура для PACKET\naddr.sll_protocol = htons(ETH_P_ALL);  // нас интересует только Ethernet\naddr.sll_ifindex = req.ifr_ifindex; // индекс устройства (см. выше)\n\n/* Привязываем сокет к определенному адресу. Если не вызывать bind,\nто нужно использовать recvfrom и sendto с явным указанием адреса */\nif (-1==bind(sock, (struct sockaddr*)&addr, sizeof(addr))) {\n    perror(\"bind\"); exit(1);\n}\n```\n\nТеперь можно наблюдать за тем, что проходит через этот сетевой интерфейс.\n\n```c\nfor (;;) {\n    char buffer[4096];\n    memset(buffer, 0, sizeof(buffer));\n    \n    /* Читаем блок данных из сетевого устройства */\n    size_t cnt = recv(sock, buffer, sizeof(buffer), 0);\n\n    uint32_t from_ip, to_ip;\n\n    /* Извлекаем адреса источника и получателя из заголовка IPv4 */\n    memcpy(&from_ip, buffer+26, sizeof(from_ip));\n    memcpy(&to_ip, buffer+30, sizeof(to_ip));\n\n    char from_addr[20], to_addr[20];\n    memset(from_addr, 0, sizeof(from_addr));\n    memset(to_addr, 0, sizeof(to_addr));\n    inet_ntop(AF_INET, &from_ip, from_addr, sizeof(from_addr));\n    inet_ntop(AF_INET, &to_ip, to_addr, sizeof(to_addr));\n\n    printf(\"Got communication from %s to %s\\n\", from_addr, to_addr);\n}\n```\n\nНа реально используемой системе можно будет наблюдать огромное количество пакетов сетевого взаимодействия. \n\n### Виртуальная машина Classic-BPF\n\nПрограмма фильтрации, загружаемая в ядро, состоит из набора 64-битных RISC-команд, которые выполняются виртуальной машиной, либо могут быть транслированы в нативный код. \n\nКаждая инструкция кодируется следующим образом:\n\n```c\nstruct sock_filter {\t\n\t__u16\tcode;   // 16 бит - код команды\n\t__u8\tjt;     // 8 бит - смещение для true/jump-инструкций\n\t__u8\tjf;     // 8 бит - смещение для false/jump-инструкций\n\t__u32\tk;      // 32 бит - поле для произвольных данных\n};\n```\n\nУ виртуальной машины есть только два 32-битных регистра: аккумулятор `A`, над которым можно выолнять произвольные действия, и счетчик инструкций `X`. Возможен доступ к \"памяти\", при этом адресуется содержимое исследуемого сетевого пакета.\n\nПоскольку виртуальная машина была спроектирована по аналогии с реально существующим процессором Motorola 6502, то для этого набора команд существует язык ассемблера. Программная реализация ассемблера BPF находится в поставке исходных текстах ядра Linux: `tools/bpf/bpf_asm`. \n\n#### Команды ассемблера BPF\n\n* Перемещение данных: \n  - загрузить в регистр `A`: `ld` - слово, `ldh` - полуслово, `ldb` - байт;\n  - сохранить значение в памяти: `st` для регистра `A`, `stx` для регистра `X`;\n  - перемещение между регистрами: `tax` - из `A` в `X`, `txa` - из `X` в `A`\n* Арифметические операции над регистром `A`: `add`, `sub`, `mul`, `div`, `mod`, `neg`, `and`, `or`, `xor`, `lsh`, `rsh` \n* Переходы на метку:\n  * `jmp` - безусловный переход;\n  * `jeq`, `jne`, `jlt`, `jle`, `jgt`, `jge` - условный переход, при этом можно опционально указать вторую метку, на которую будет выполнен переход в случае не выполнения условия\n* Завершение работы: команда `ret` завершает работу и возвращает результат обработки фильтра.\n\n#### Пример программы на cBPF\n\nРассмотрим задачу фильтрации Ethernet-фреймов: будем принимать только фреймы, внутри которых содержатся UDP-сообщения, адресованные DNS Google по адресу `8.8.8.8`. \n\n```asm\nfilter_google_dns:\n    ldh     [12]                ; 16-бит значение после двух MAC-адресов\n    jne     #0x0800, fail       ; проверяем, что внутри кадра у нас IPv4-пакет\n    ldb     [23]                ; 8-бит значение типа протокола в заголовке IP\n    jne     #17, fail           ; 17 - это UDP, 6 - это TCP\n    ld      [30]                ; 4-байтное значение IP-адреса\n    jne     #0x08080808, fail   ; сравниваем с адресом 8.8.8.8\nsuccess:\n    ret     #-1                 ; значение -1 == 0xFFFFFFFF\nfail:\n    ret     #0                  ; значение  0\n```\n\nДанная программа проверяет, что внутри Ethernet-фрейма содержится действительно IPv4-пакет, который, в свою очередь, содержит сообщение типа UDP, и адресован получателю `8.8.8.8`. Возвращаемое значение - это максимальное количество байт, которое фильтр должен пропустить. Таким образом, значение `0` означает отклонение пакета, а максимально возможное беззнаковое целочисленное значение - пропуск пакета целиком.\n\n### Загрузка программы-фильтра в ядро \n\nК сокету можно прикрепить BPF-фильтр, используя системный вызов `setsockopt`:\n\n```c\nsetsockopt(\n    sock,         \t\t// файловый дескриптор сокета\n    SOL_SOCKET,   \t\t// опция предназначена для сокета в целом\n    SO_ATTACH_FILTER,\t// команда \"присоединить фильтр\"\n    \n    // указатель на структуру, которая содержит cBPF-программу\n    struct *sock_fprog program,\n    // размер аргумента; требуется как generic-параметр для setsockopt\n    sizeof(struct sock_fprog)\n);\n```\n\nСама структура программы состоит из двух полей: указателя на последовательность инструкций, и количество инструкций (не байт!) в программе. Каждая инструкция - это 8 байт, которые можно описать структурой `struct sock_filter`. \n\nАссемблер BPF, который имеется в составе исходных текстов ядра Linux, имеет опцию для вывода байткода в формате Си-структур, и этот вывод можно включить в код препроцессором.\n\n```bash\n> linux-5.15/tools/bpf/bpf_asm -c filter.s >filter.inc\n```\n\n```c\nstruct sock_filter[] code = {\n    #incldue \"filter.inc\"   \n    /*\n    Здесь препроцессор вставит как есть текст вывода ассемблера:\n    { 0x28,  0,  0, 0x0000000c },\n    { 0x15,  0,  5, 0x00000800 },\n    { 0x30,  0,  0, 0x00000017 },\n    { 0x15,  0,  3, 0x00000011 },\n    { 0x20,  0,  0, 0x0000001e },\n    { 0x15,  0,  1, 0x08080808 },\n    { 0x06,  0,  0, 0xffffffff },\n    { 0x06,  0,  0, 0000000000 },\n    */\n};\n\nstruct sock_fprog program = {\n    .filter = code,   // указатель на последовательность инструкций\n\n    /* Количество инструкций можно рассчитать как размер всех\n    инструкций в байтах, деленный на размер одной инструкции */\n    .len = sizeof(code)/sizeof(code[0])\n};\n```\n\nЗагрузка программы подразумевает её обязательную проверку верификатором, который проверяет, что: 1) все инструкции в программе корректны; 2) размер программы не превышает 4096 инструкций; 3) программа не содержит циклов.\n\nВ случае отклонения программы верификатором, системный вызов `setsockopt` вернёт значение `-1`.\n\n## Linux Extended BPF\n\nНачиная с ядра 3.19 в ядре Linux появилась новая реализация BPF, которая реализует виртуальную машину со следующими свойствами:\n\n* 11 регистров вместо 2, 10 из них - общего назначения\n* все регистры - 64-битные\n* появился стек, таким образом можно делать вложенные функции\n* вызовы некоторых встроенных функций в адресном пространстве ядра.\n\nНачиная с версии ядра 4.1, кроме возможности фильтрации сетевых пакетов, виртуальная машина EBPF позволяет выполнять код при наступлении определенных событий ядра. \n\nКроме того, EBPF-программы могут использовать постоянные хранилища данных (`maps`), которые доступны также из адресного пространства процесса: массивы и хеш-таблицы.\n\nТаким образом, область применения расширилась до трассировки и измерения производительности.\n\n### Системный вызов bpf\n\n```c\nint bpf(\n    // Команда взаимодействия с подсистемой EBPF\n    int cmd,\n    // Аргумент команды\n    union bpf_attr *attr,\n    // Размер аргумента команды\n    unsigned int size\n);\n```\n\nИспользование системного вызова `bpf` на момент 02 апреля 2020 подразумевает использование функции `syscall`, поскольку Си-оболочка для него не реализована в `glibc`. Кроме того, многие возможности пока ещё не задокументированы.\n\nОсновные команды для `bpf`:\n\n* `BPF_PROG_LOAD` - загрузить программу в ядро, и проверить её верификатором; возвращает дескриптор программы\n* `BPF_MAP_CREATE`, и другие команды `BPF_MAP_*` - создание постоянного хранилища, и операции над ним.\n\nДля каждой команды существует отдельная структура аргумента, описанная в `<linux/bpf.h>`.\n\nПрограммы, в свою очередь, могут быть разными по назначению:\n\n* `BPF_PROG_TYPE_SOCKET_FILTER` - простой фильтр, как в Classic BPF\n* `BPF_PROG_TYPE_KPROBE` - программа для обработки событий ядра `kprobe`\n* `BPF_PROG_TYPE_XDP` - продвинутая фильтрация пакетов как в файрволе\n* [и ещё много типов - см `<uapi/linux/bpf.h>`]\n\nПрограмма должна содержать функцию - точку входа, единственным аргументом которой, в регистре `R1`, будет указатель на контекст выполнения, тип которого зависит от типа программы.\n\n### Загрузка программы\n\nПрограмма (возможно) загружается в ядро системы после вызова `bpf`:\n\n```c\n/* Приходится использовать syscall, т.к. нет оболочки в glibc */\nint program_id = syscall(  \n    SYS_bpf,               // номер системного вызова bpf\n    BPF_PROG_LOAD,         // команда для загрузки программы\n    &bpf_argument,         // указатель на аргумент команды\n    sizeof(bpf_argument)   // ... и размер аргумента\n);\n\n/* В случае успеха, будет возвращен файловый дескриптор программы.\n   Для освобождения ресурсов, когда программа станет не нужна,\n   нужно использовать обычный close() */\n\nclose(program_id);\n```\n\nАргумент команды для загрузки - это структура (точнее, объявленная как `union`), в которой должны быть заполнены поля:\n\n```c\nchar bpf_code[4096*8] = ....;\nchar loader_log[65535];\n\nunion bpf_attr bpf_argument = {\n    .prog_type = BPF_PROG_TYPE_SOCKET_FILTER,  // тип программы\n    .insns = bpf_code,                         // указатель на бинарный код\n    .insn_cnt = sizeof(bpf_code) / 8,          // количество инструкций\n    .log_level = 1,                            // вести ли лог загрузки?\n    .log_buf = loader_log,                     // куда писать лог загрузки\n    .log_size = sizeof(loader_log),            // размер буфера для лога\n    .license = \"GPL\"                           // должна быть GPL-совместимая\n};\n```\n\nЛог загрузки содержит текст, который очень полезен при отладке, поскольку не любая программа будет считаться корректной с точки зрения верификатора.\n\nЗагруженную программу, если это фильтр для сокета, можно прикрутить к сокету:\n\n```c\nsetsockopt(sock, SOL_SOCKET, SO_ATTACH_BPF, &program_id, sizeof(program_id));\n```\n\n### Байт-код EBPF-программ\n\nФормат EBPF программ отличается от Classic BPF, поэтому использовать старый ассемблер не получится.\n\nФормат инструкции:\n\n```c\nstruct bpf_insn {\n    __u8  code,        // 8-бит код команды\n    __u8  dst_reg:4,   // 4-бит регистр-назначение \n    __u8  src_ref:4,   // 4-бит регистр-источник\n    __s16 off,         // 16-бит значение для относительного адреса\n    __s32 imm          // 32-бит значение для кодирования констант\n};\n```\n\n### Компиляция EBPF-программ \n\nКомпилировать EBPF-программы можно с помощью свежих версий тулкита CLang/LLVM. Необходимо проверить, что LLVM поддерживает цель `bpf`:\n\n```\n> llc --version\nLLVM (http://llvm.org/):\n  LLVM version 9.0.1\n  Optimized build.\n  Default target: x86_64-unknown-linux-gnu\n  Host CPU: skylake\n\n  Registered Targets:\n     ......\n    bpf        - BPF (host endian)\n     ......\n```\n\nРассмотрим компиляцию тривиальной программы, которая запрещает все пакеты:\n\n```c\n/* trivial.c */\nint trivial_socket_filter(void *ctx) {\n    return 0;  // change to -1 to allow all\n}\n```\n\nСкомпилируем эту программу в объектный файл для цели `bpf`:\n\n```\n> clang -c -target bpf trivial.c\n```\n\nВ результате получим объектный файл, который содержит примерно такой код:\n\n```\n> llvm-objdump -d trivial.o\ntrivial.o:      file format ELF64-BPF\n\nDisassembly of section .text:\n\n0000000000000000 trivial_socket_filter:\n       0:       7b 1a f8 ff 00 00 00 00 *(u64 *)(r10 - 8) = r1\n       1:       b7 00 00 00 00 00 00 00 r0 = 0\n       2:       95 00 00 00 00 00 00 00 exit\n```\n\nНулевая инструкция загружает в стек переменную `ctx`, первая присваивает значение `r0 = 0` (по соглашению о вызовах, это возвращаемое значение), а вторая выполняет выход из функции.\n\nДля того, извлечь из объектного файла кусок кода, можно использовать утилиту `objcopy`. В дальнейшем этот код можно будет загрузить в программу как простые бинарные данные.\n\n```bash\n> llvm-objcopy     \\    # требуется использовать objcopy из поставки LLVM\n         -O binary \\    # выходной формат - бинарный\n         -j .text  \\    # копируем только секцию .text\n         trivial.o \\    # имя входного файла\n         trivial.bin    # имя файла с результатом\n```\n\nС помощью `objcopy` можно получить бинарный файл размером 24 байта, который содержит только код (ровно три инструкции), но не заголовки и таблицы.\n\n```\n> hexdump -C trivial.bin\n00000000  7b 1a f8 ff 00 00 00 00  b7 00 00 00 00 00 00 00  |{...............|\n00000010  95 00 00 00 00 00 00 00                           |........|\n00000018\n```\n\n### Ограничения на EBPF-программы\n\nКак и в случае с Classic BPF, загружаемые в ядро программы проходят строгую валидацию. Размер программы может составлять 4096 инструкций (до Linux 5.1), либо до миллиона инструкций (начиная с версии Linux 5.1). \n\nТак же валидатор проверяет, что программа гарантированно завершится за конечное время, поэтому использовать циклы в Си-программах можно только если на этапе компиляции известно число итераций. Для того, чтобы компилятор \"развернул\" циклы в длинную линейную программу, нужно перед циклом указать директиву `#pragma unroll` и указать опцию компиляции `-funroll-loops`.\n\nФункции стандартной Си-библиотеки становятся не доступны, поскольку программа выполняется в ограниченном окружении. Тем не менее, есть набор функций, доступных для EBPF-программ, они перечислены в [man 7 bpf-helpers](http://man7.org/linux/man-pages/man7/bpf-helpers.7.html).\n\nЕщё одним ограничением является порядок доступа к данным в памяти. Например, он должен быть выровненным, и вполне безобидная конструкция не пройдёт проверку валидатором:\n\n```c\nctx = 0;   // начало пакета\nunsigned int ip_dest = *(unsigned int*)(ctx+30); // извлекаем IP-адрес\n```\n\nВывод лога валидатора:\n\n```\n6: (61) r1 = *(u32 *)(r1 +30)\ninvalid bpf_context access off=30 size=4\n```\n\nПоэтому приходится использовать низкоуровневые LLVM-функции для доступа к данным напрямую:\n\n```c\n/* llvm builtin functions that eBPF C program may use to\n * emit BPF_LD_ABS and BPF_LD_IND instructions\n */\nunsigned long long load_byte(void *skb,\n                             unsigned long long off) asm(\"llvm.bpf.load.byte\");\nunsigned long long load_half(void *skb,\n                             unsigned long long off) asm(\"llvm.bpf.load.half\");\nunsigned long long load_word(void *skb,\n                             unsigned long long off) asm(\"llvm.bpf.load.word\");\n```\n\n## BPF Compiler Collection\n\nSee tutorial: [bcc Python Developer Tutorial](https://github.com/iovisor/bcc/blob/master/docs/tutorial_bcc_python_developer.md)\n\n\n\n"
  },
  {
    "path": "practice/codestyle.md",
    "content": "# Coding Standards для языка Си\n\n## Форматирование программ\n\n### Отступы и скобочки\n\nСтрогих требований к расстановке отступов нет. Какой стиль хотите использовать - тот и используйте. Но нужно придерживаться следующих правил:\n\n  * Код не должен выглядеть страшно.\n  * Ширина кода вашей программы не должна превышать 76 символов. Изначально это требование было продиктовано особенностями текстовых терминалов (80х25 минус рамочки редактора), но и в современной жизни, даже на больших экранах, часто бывает нужно расположить два текста сравнения, либо перегружать экран вспомогательнымии окнами (отладчики etc.).\n  * Отступы должны быть кратны 2-м пробелам.\n  * Нельзя смешивать отступы пробелами и символы табуляции. Используйте что-то одно.\n\n### Объявление функций\n\nНа языке Си, в отличии от С++ или Java, принято выносить отделение возвращаемых типов функций и их модификаторов от названия на отдельную строку. Например, так:\n\n```\nstatic void\nmy_function(int arg1, char arg2, void *arg3)\n{\n    /* function body */\n}\n```\n\nВо-первых, типы возвращаемых значений бывают достаточно длинными (вроде `struct имя_структуры`), да ещё и модификаторы на некоторых платформах встречаются вида `extern __declspec(dllexport)`. В общем, тут трудно уложиться в положенные 76 символов.\n\nВо-вторых, это удобно при поиске функций по регулярному выражению `^имя_функции`, где символ `^` означает начало строки.\n\n\n## Используемые типы данных\n\nДля знаковых целочисленных типов данных используются короткие имена: `char` (8 бит), `int` (как правило, 32 бит). Всё. Никаких `long long` или `short` использовать не нужно. Есть замечательный заголовочный файл `<stdint.h>`, в котором перечислены типы данных с фиксированной точностью: `int8_t`, `int16_t`, `int32_t`, `int64_t`. Имена `char` и `int` являются общераспространенными синонимами для `int8_t` и `int32_t`, поэтому их использование не возбраняется за исключением особо экзотических случаев.\n\nА вот использование ключевого слова `long` - строго запрещено, поскольку на разных платформах и компиляторах его размер разный.\n\nДля беззнаковых типов данных нужно использовать только имена из `<stdint.h>`: `uint8_t`, `uint16_t`, `uint32_t` и `uint64_t`.\n\nОбратите внимание, что `char` - это синоним понятия \"байт\", а вовсе не символ.\n\n## Объявления констант\n\n\nИспользование `#define` для объявления констант - строго запрещено!\n\nЦелочисленные константы нужно объявлять как перечисления:\n```\nenum {\n    CONST_VALUE_1 = 1,\n    CONST_VALUE_2 = 567,\n    CONST_VALUE_3 = 890\n};\n```\nКонстанты всех остальных типов данных - в стиле C++ по-Саттеру:\n```\nstatic const double PI = 3.14159;\nstatic const char *PATH = \"/usr/local\";\n```\n\n## Имена\n\nСамое главное - учите simplified English. За транслит нужно расстреливать! Но и не злоупотребляйте редко используемыми словами, подобранными через multitran.ru. Всё-таки не исключено, что ваш код будут читать индусы или китайцы, для которых английский также не является родным языком.\n\nНа Си приняты `безумно_длинные_имена_функций_или_пар_модуль_подчеркивание_метод`, которые можно читать только в виде Python-style имен с подчеркиванием, но не `CamelStyle`. Для имен типов данных `CamelStyle` допустим, но не рекомендуется.\n\nДля типов данных обычно используется запись с суффиксом `_t`. Например:\n\n```\nstruct MyStruct {\n    int  field_1;\n    char field_2;\n};\n\ntypedef struct MyStruct my_struct_t;\n```\n\nДля переменных давайте понятные имена на английском языке; не нужно делать однобуквенные сокращения. Исключение - общепринятые однобуквенные целочисленные переменные: `N` (именно заглавная буква, ибо традиция), `i`, `j`, `k`.\n\n## Используемые конструкции\n\n### Оператор `goto`\n\nВсем известно, что оператор `goto` (дословный перевод на русский язык: `иди_на`) строго запрещён в промышленном программировании, а для языка Java - это вообще специально зарезервированное ключевое слово, которое приводит к ошибке компиляции.\n\nНо в системном программировании допускается единственный случай, когда использование `goto` целесообразно: завершение работы функции при обработке ошибок, когда требуется гарантированно освободить некоторые ресурсы.\n\nПример:\n```\nint\nmy_function()\n{\n    char* memory  = calloc(BUFFER_SIZE, sizeof(char));\n    int   fd_read = open(PATH, O_RDONLY);\n    int   result  = NO_ERROR;\n\n    /* ... something unimportant ... */\n\n    if (/* error occured */ ) {\n        result = SOME_ERROR_CODE;\n        goto Function_End;\n    }\n\nFunction_End:\n    free(memory);\n    close(fd_read);\n    return result;\n}\n```\n\nВо всех остальных случаях использовать оператор `goto` строго запрещено, как и в высокоуровневом программировании.\n\n### Массивы переменного размера\n\nНа языке Си, в отличии от C++, допускаются массивы переменного размера. Но использовать их можно только в том случае, если их размер заведомо предсказуем.\n\nВот так допустимо:\n```\nenum {N = 100};\nchar array[N];\n```\n\nТак тоже:\n```\nuint8_t N = /* some value, but not more 255 */;\nchar array[N];\n```\n\nА это - очень плохо:\n```\nint N;\nscanf(\"%d\", N); /* what if 9999999999999999999 at input? */\nchar array[N];\n```\n"
  },
  {
    "path": "practice/epoll/README.md",
    "content": "# Мультиплексирование ввода-вывода\r\n\r\n## Неблокирующий ввод-вывод\r\n\r\nСистемные вызовы `read` и `write` блокируют выполнение текущего потока выполнения в случае отсутствия данных или места в буферах ввода или вывода.\r\n\r\nВ случае, когда процесс работает одновременно с несколькими файловыми дескрипторами, такое поведение может стать узким местом в производительности.\r\n\r\nДля того, чтобы избежать простоя на операциях ввода-вывода, предусмотрен аттрибут файлового дескриптора `O_NONBLOCK`, который можно установить как при открытии файла, так и для уже открытого файлового дескриптора с помощью системного вызова `fcntl`:\r\n\r\n```\r\nint fd = ....;                   // какой-то файловый дескриптор\r\nint flags = fcntl(fd, F_GETFL);  // получить предыдущие флаги открытия/создания\r\nflags |= O_NONBLOCK;             // добавить флаг неблокируемости\r\nfcntl(fd, F_SETFL, flags);       // установить новые флаги\r\n```\r\n\r\nПопытка чтения из файлового дескриптора, если в буфере нет данных, или записи в файловый дескриптор, если в буфере нет места, приведет к ошибке. То есть, системный вызов `read` или `write` завершит работу со значением `-1`, и при этом в переменной `errno` будет зафиксировано значение `EAGAIN`.\r\n\r\nВ этом случае, можно попробовать выполнить операцию ввода или вывода с файловым дескриптором позже, а тем временем обработать другие файловые дескрипторы.\r\n\r\n\r\n## Обработка событий в Linux\r\n\r\nЕсли основное назначение программы - это обработка данных из файловых дескрипторов, то неблокирующий ввод-вывод может приводить к 100% загрузке процессора даже в том случае, когда с процессом не происходит никакого взаимодействия. Для того, чтобы это избежать, необходимо ставить процесс в состояние ожидания до тех пор, пока не возникнет какое-либо событие, связанное с одним из файловых дескрипторов, требующее реакции.\r\n\r\nТакой механизм является системно-зависимым (вне стандарта POSIX); в системе Linux он реализуется через механизм `epoll(7)`, в системах FreeBSD и MacOS - через системный вызов `kqueue`. Эти механизмы реализованы идентично, и различаются только в API.\r\n\r\n\r\n### Очередь ядра\r\n\r\nОчередь ядра в системе Linux создается с помощью системного вызова `epoll_create` или `epoll_create1`. Результатом является файловый дескриптор (который должен быть закрыт, когда очередь станет не нужна), который связан с некоторым специальным объектом - очередью ядра, в которую складываются события по некоторому фильтру.\r\n\r\nСобытие описывается структурой `epoll_event`:\r\n```\r\n#include <sys/epoll.h>\r\n\r\ntypedef union {\r\n    void*       ptr;\r\n    int         fd;\r\n    uint32_t    u32;\r\n    uint64_t    u64;\r\n} epoll_data_t;\r\n\r\nstruct epoll_event {\r\n    uint32_t        events; // маска событий\r\n    epoll_data_t    data;   // произвольное поле, заполненное при регистрации\r\n};\r\n```\r\n\r\nМаска событий - это набор из различных флагов:\r\n * `EPOLLIN` - готовность к чтению;\r\n * `EPOLLOUT` - готовность к записи;\r\n * `EPOLLHUP` - соединение разорвано;\r\n * `EPOLLERR` - ошибка ввода-вывода.\r\n\r\n### Ожидание событий\r\n\r\nПеревести процесс в ожидание поступления событий можно с помощью системного вызова `epoll_wait` или `epoll_pwait`.\r\n\r\n```\r\nstruct epoll_event events[MaxEventsToRead];\r\nint N = epoll_wait(\r\n                   int epoll_fd,  // дескриптор, созданный epoll_create\r\n                   struct epoll_event *events, // куда прочитать события\r\n                   int MaxEventsToRead, // размер массива для чтения\r\n                   int timeout // таймаут в миллисекундах, или -1\r\n                  );\r\n```\r\nСистемный вызов `epoll_wait` ожидает появление *хотя бы одного* события из зарегистрированных на наблюдение, блокируя выполнение текущего потока. Поскольку за время простоя процесса или потока может появиться несколько событий, то значение переменной `N` после выполнения `epoll_wait` может быть больше `1`, - это количество событий, которые были прочитаны в массив `events`.\r\n\r\nЕсли был указан параметр `timeout` в значение, отличное от `-1`, либо во время ожидания поступил сигнал, то значение `N` может стать равным `-1`, в `errno` будет записано значение `EINTR`.\r\n\r\nСистемный вызов `epoll_pwait` дополнительно принимает ещё один аргумент - маску сигналов, которые нужно блокировать на время ожидания.\r\n\r\nПосле выполнения `epoll_wait`, в массив `events` будет записано `N` структур `epoll_event`, которые содержат описание того, что произошло с наблюдаемыми файловыми дескрипторами. При этом, если с одним и тем же файловым дескриптором произошло несколько событий, то они группируются в одно событие.\r\n\r\n\r\n### Регистрация файловых событий для отслеживания изменений\r\n\r\nДля того, чтобы события, связанные с определенными файловыми дескрипторами, отслеживались и попадали в очередь ядра, их необходимо явно зарегистрировать с помощью `epoll_ctl`:\r\n\r\n```\r\nepoll_ctl(\r\n          int epoll_fd, // дескриптор, созданный epoll_create\r\n          int op, // одна из операций:\r\n                  //  - EPOLL_CTL_ADD - добавить дескриптор в наблюдение\r\n                  //  - EPOLL_CTL_MOD - модицифировать параметры наблюдения\r\n                  //  - EPOLL_CTL_DEL - убрать дескриптор из наблюдения\r\n          int fd, // дескриптор, события над которым нас интересуют\r\n          struct epoll_event *event // структура, в которой описаны:\r\n                                    //  - маска интересуемых событий events\r\n                                    //  - произвольные данные, которые\r\n                                    //    as-is попадают в структуру, читаемую\r\n                                    //    epoll_wait\r\n         );\r\n```\r\n\r\nДобавить в один дескприптор `epoll` несколько раз один и тот же файловый дескриптор не получится, - это приведет к ошибке `EEXIST`. Если необходимо модицифировать маску наблюдаемых событий для уже зарегистрированного события, то нужно использовать операцию `EPOLL_CTL_MOD` вместо `EPOLL_CTL_ADD`. В том случае, если всё же необходимо определить разные обработчики событий для одного и того же файлового дескриптора, то можно создать его дубликат с помощью `dup2`.\r\n\r\nЕсли файловый дескриптор был закрыт, то он автоматически удаляется из наблюдаемых файловых дескприпторов.\r\n\r\n### Отслеживание по изменению фронта (Edge-Triggerd) v.s. отслеживание по состоянию (Level-Triggered)\r\n\r\nОписание физической аналогии приведено в статье [Edge Triggered v.s. Level Triggered Interrupts](https://venkateshabbarapu.blogspot.com/2013/03/edge-triggered-vs-level-triggered.html) (на англ.).\r\n\r\nПо умолчанию события регистрируются в режиме отслеживания по значению, то есть, по факту наличия флага готовности операций ввода и вывода, если в буфере есть готовые данные или место для записи.\r\n\r\nРегистрация обработки событий в режим Eddge-Triggered возможна с указанием флага `EPOLLET`. В этом случае повышается скорость реакции на событие за счет того, что событие регистрируется в тот момент, когда оно только начинает быть готовым, например в буфер ввода начали поступать данные. Недостатком этого подхода является то, что регистрируется только факт изменения состояния, и если, например, не прочитать данные из буфера полностью, то в следующий раз событие готовности к чтению не будет обнаружено, т.к. оно уже произошло.\r\n\r\n\r\n## Событийно-ориентированное программирование\r\n\r\nПомимо ввода-вывода, в системе Linux могут быть использованы другие файловые дескрипторы специального назначения, которые также как и обычные, можно регистрировать в наблюдение через `epoll`.\r\n\r\n### Пара виртуальных сокетов\r\n\r\nПара виртуальных сокетов создается с помощью системного вызова `socketpair`, который, по аналогии с `pipe`, заполняет массив из двух целочисленных значений. Эти файловые дескрипторы могут быть использованы для взаимодействия родственных процессов, от отличаются от неименованных каналов тем, что:\r\n 1. Являются двунаправленными\r\n 2. Их можно настраивать через `setsockopts`, как обычные сокеты\r\n 3. Обрабатывается отдельная операция \"завершения соединения\", которая, в случае в `epoll` регистрируется как событие `EPOLLIN` с 0 количеством байт.\r\n\r\n```\r\n// Пример:\r\nint pair[2];\r\nsocketpair(AF_UNIX,     // в Linux поддерживается только UNIX,\r\n           SOCK_STREAM, // еще можно SOCK_DGRAM\r\n           0,           // автоматический выбор протокола\r\n           pair         // массив из 2-х int, куда будут записаны дескрипторы\r\n          );\r\n```\r\n\r\n### SignalFD, TimerFD, EventFD\r\n\r\nСистемные вызовы `signalfd`, `timerfd_create`, и `eventfd` реализованы только в Linux, и создают специальные файловые дескрипторы, из которых можно читать события:\r\n * поступления определенных сигналов (`signalfd`);\r\n * срабатывание таймера (`timerfd_create`);\r\n * уведомления, пересылаемые разными потоками (`read` и `write` через `eventfd`).\r\n"
  },
  {
    "path": "practice/exec-rlimit-ptrace/README.md",
    "content": "# fork-МАГИЯ-exec\n\n## Системный вызов exec\n\nФормальное описание системного вызова exec: [man 2 execve](http://ru.manpages.org/execve/2)\n\nСистемный вызов `exec` предназначен для замены программы текущего процесса. Как правило, используется совместно с `fork`, но не обязательно.\n\nСи-оболочки для системного вызова `exec` имеют несколько разных сигнатур.\n\n```\nint execve(const char *filename,\n           char *const argv[],\n           char *const envp[]);           \nint execvpe(.....) // параметры аналогично execve\n\nint execv(const char *filename, char *const argv[])\nint execvp(......) // параметры аналогично execv\n\nint execle(const char *filename,\n           const char arg0, ..., /* NULL */,\n           const char env0, ..., /* NULL */);\n\nint execl(const char *filename,\n          const char arg0, ..., /* NULL */);\nint execlp(......) // параметры аналогично execl\n\n```\n\nРазличные буквы в суффиксах названий `exec` означают?\n * `v` или `l` - параметры передаются в виде массивов (`v`), заканчивающихся элементом `NULL`, либо в виде переменного количества аргументов (`l`), где признаком конца перечисления аргументов является значение `NULL`.\n * `e` - кроме аргументов программы передаются переменные окружения в виде строк `КЛЮЧ=ЗНАЧЕНИЕ`.\n * `p` - именем программы может быть не только имя файла, но и имя, которое нужно найти в одном из каталогов, перечисленных в переменной окружения `PATH`.\n\nВозвращаемым значением `exec` может быть только значение `-1` (признак ошибки). В случае успеха, возвращаемое значение уже в принципе не имеет никакого смысла, поскольку будет выполняться другая программа.\n\nАргументы программы - это то, что передаётся в функцию `main` (на самом деле, они доступны из `_start`, поскольку располагаются на стеке). Первым аргументом (с индексом `0`), как правило, является имя программы, но это не является обязательным требованием.\n\nКлассическим способом запуска новой программы является пара системных вызовов:\n\n```\nif (0 == fork) {\n  execlp(program, program, NULL);\n  perror(\"exec\"); exit(1);\n}\n```\n\nЗамена выполняемой программы с помощью `exec` оставляет неизменным многие аттрибуты процесса, например, открытые файловые дескрипторы, лимиты, и переменные окружения, установленные через `setenv`.\n\nТаким образом, между вызовами `fork` и `exec` можно провести дополнительную настройку программы перед выполнением.\n\n```\nif (0 == fork) {\n  // Заменить стандартные потоки ввода/вывода на файлы\n  // (имитация операции >ВЫХОД <ВХОД в bash)\n  close(0);\n  close(1);\n  /* 0 = */ open(in_file, O_RDONLY);\n  /* 1 = */ open(out_file, O_WRONLY|O_CREAT|O_TRUNC, 0640);\n\n  execlp(program, program, NULL);\n  perror(\"exec\"); exit(1);\n}\n```\n\nДля того, чтобы случайно (в достаточно больших программах) не передать открытый файловый дескриптор новой программе, в системном вызове `open` предусмотрен флаг открытия `O_CLOEXEC`, который означает, что файл должен быть закрыт при вызове `exec`.\n\n\n## Лимиты\n\nС процессом связаны некоторые лимиты (ограничения) на используемое процессорное время, максимальный объём памяти, количество файловых дескрипторов, процессов и т. д.\n\nЛимиты подразделяются на *жёсткие*, которые обычные пользователи могут только уменьшать (хотя `root` может и увеличивать), и *мягкие*, которые де-факто являются значениями по умолчанию, и их можно увеличить до жёсткого лимита.\n\nПримерами жёстких лимитов являются ограничения на количество процессов или объём доступной памяти. Пример мягкого лимита - это размер стека, который по умолчанию в Linux равен 8Мб, но может быть изменен произвольным образом, в том числе в большую сторону.\n\nПоскольку изменение размера стека - очень опасная операция, которая может нарушить структуру размещения данных в памяти, изменять этот лимит можно только до запуска функции `main`, либо непосредственно перед выполнением `exec`. В противном случае, поведение программы не определено.\n\nПолучение и установка лимитов осуществляются с помощью системных вызовов `getrlimit` и `setrlimit`.\n\nПримеры лимитов см. в [get_limits.c](get_limits.c).\n\nПример изменения размера стека см. в [shell_with_custom_stack_size.c](shell_with_custom_stack_size.c).\n\n\n## Трассировка выполнения программы\n\nВ Linux (не POSIX!) имеется системный вывов `ptrace`, назначение которого - обеспечить возможность отладки.\n\nПервым аргументом `ptrace` является команда, а дальше - некоторое количество аргументов команды.\n\nЕсли между вызовами `fork` и `exec` выполнить `ptrace(PTRACE_TRACEME,0,0,0)`, то процесс будет приостановлен до тех пор, пока к нему не подключится отладчик, либо программа, которая ведёт себя подобно отладчику, и не разрешит продолжить выполнение дальше.\n\nНекоторые команды, которые программа-\"отладчик\" может посылать исследуемому процессу:\n\n * `PTRACE_CONT, pid, 0, signo` - продолжить выполнение процесса. Если `signo` не 0, то отправляется сигнал.\n * `PTRACE_SINGLESTEP, pid, 0, 0` - выполнить одну инструкцию.\n * `PTRACE_SYSCALL, pid, 0, 0` - продолжить выполнение до системного вызова.\n * `PTRACE_GETREGS, pid, 0, &state)` - получить значение регистров процессора и сохранить в структуру `user_regs_state`, объявленную в файле `<sys/user.h>`.\n * `PTRACE_SETREGS, pid, 0, &state)` - модифицировать регистры процессора.\n * `PTRACE_PEEKDATA, pid, addr, 0` - прочитать одно машинное слово из памяти процесса по указанному адресу `addr`.\n * `PTRACE_POKEDATA, pid, addr, data` - записать одно машинное слово `data` в память процесса по указанному адресу `addr`.\n\nПример перехвата системного вызова `write`, который цензурирует нехорошее английское слово в тексте вывода см. в [ptrace_catch_string.c](ptrace_catch_string.c).\n"
  },
  {
    "path": "practice/exec-rlimit-ptrace/get_limits.c",
    "content": "#include <sys/time.h>\n#include <sys/resource.h>\n#include <stdio.h>\n#include <string.h>\n\nstatic void\nprint_limit(int val, const char *name)\n{\n    struct rlimit rlim;\n    memset(&rlim, 0, sizeof(rlim));\n    getrlimit(val, &rlim);\n    printf(\"%-15s│ \", name);\n    if (RLIM_INFINITY==rlim.rlim_cur)\n        printf(\"∞         │ \");\n    else if (4==sizeof(void*))\n        printf(\"%-10u│ \", rlim.rlim_cur);\n    else\n        printf(\"%-10llu│ \", rlim.rlim_cur);\n    if (RLIM_INFINITY==rlim.rlim_max)\n        printf(\"∞\\n\");\n    else if (4==sizeof(void*))\n        printf(\"%-11u\\n\", rlim.rlim_max);\n    else\n        printf(\"%-11llu\\n\", rlim.rlim_max);\n    \n}\n\nint main() {\n    printf(\"───────────────┬───────────┬────────────\\n\");\n    printf(\"     Name      │   Soft    │    Hard    \\n\");\n    printf(\"───────────────┼───────────┼────────────\\n\");\n    print_limit(RLIMIT_AS, \"RLIMIT_AS\");\n    print_limit(RLIMIT_CPU, \"RLIMIT_CPU\");\n    print_limit(RLIMIT_DATA, \"RLIMIT_DATA\");\n    print_limit(RLIMIT_FSIZE, \"RLIMIT_LOCKS\");\n    print_limit(RLIMIT_NICE, \"RLIMIT_NICE\");\n    print_limit(RLIMIT_NOFILE, \"RLIMIT_NOFILE\");\n    print_limit(RLIMIT_RSS, \"RLIMIT_RSS\");\n    print_limit(RLIMIT_STACK, \"RLIMIT_STACK\");\n    printf(\"───────────────┴───────────┴────────────\\n\");\n}\n"
  },
  {
    "path": "practice/exec-rlimit-ptrace/ptrace_catch_string.c",
    "content": "#include <sys/types.h>\n#include <sys/stat.h>\n#include <sys/ptrace.h>\n#include <sys/wait.h>\n#include <sys/user.h>\n#include <unistd.h>\n#include <stdlib.h>\n#include <stdio.h>\n#include <fcntl.h>\n#include <stdbool.h>\n#include <asm/unistd.h>\n#include <signal.h>\n#include <string.h>\n#include <errno.h>\n\nstatic void\npremoderate_write_syscall(pid_t pid, struct user_regs_struct state)\n{\n    size_t orig_buf = state.rsi;   // ecx for i386\n    size_t size = state.rdx;       // rdx for i386\n    char *buffer = calloc(size+sizeof(long), sizeof(*buffer));\n    int val = 0;\n    for (size_t i=0; i<size; ++i) {\n        buffer[i] = ptrace(PTRACE_PEEKDATA, pid, orig_buf+i, NULL);\n    }\n    char *bad_word;\n    if ( (bad_word=strstr(buffer, \"fuck\")) ) {\n         size_t offset = bad_word - buffer + 1; // 'u' letter\n         buffer[offset] = '*';                      \n         size_t target_address = orig_buf + offset;\n         long val;\n         memcpy(&val, buffer+offset, sizeof(val));\n         ptrace(PTRACE_POKEDATA, pid, target_address, val);\n    }\n    free(buffer);\n}\n\nint main(int argc, char *argv[])\n{\n    pid_t  pid = fork();\n    if (-1==pid) { perror(\"fork\"); exit(1); }\n    if (0==pid) {\n        ptrace(PTRACE_TRACEME, 0, NULL, NULL);\n        execvp(argv[1], argv+1);\n        perror(\"exec\");\n        exit(2);\n    }\n    else {      \n        int wstatus = 0;\n        struct user_regs_struct state;\n        bool stop = false;\n        while (!stop) {\n            ptrace(PTRACE_SYSCALL, pid, NULL, NULL);\n            waitpid(pid, &wstatus, 0);\n            stop = WIFEXITED(wstatus);\n            if (WIFSTOPPED(wstatus)) {\n                ptrace(PTRACE_GETREGS, pid, 0, &state);\n                if (__NR_write==state.orig_rax) {  // orig_eax for i386\n                    premoderate_write_syscall(pid, state);\n                }              \n            }\n        }            \n    }  \n}\n"
  },
  {
    "path": "practice/exec-rlimit-ptrace/shell_with_custom_stack_size.c",
    "content": "#include <sys/types.h>\n#include <sys/wait.h>\n#include <sys/time.h>\n#include <sys/resource.h>\n#include <stdint.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <unistd.h>\n\nint main(int argc, char *argv[])\n{\n    uint64_t stack_size = strtoull(argv[1], NULL, 10);\n    stack_size *= 1024; // convert from KB to bytes\n    pid_t pid = fork();\n    if (-1==pid) { perror(\"fork\"); exit(1); }\n    if (0==pid) {\n        // set new limits before exec\n        struct rlimit rlim;\n        getrlimit(RLIMIT_STACK, &rlim);\n        if (RLIM_INFINITY==rlim.rlim_max || rlim.rlim_max>stack_size) {\n            rlim.rlim_cur = stack_size;\n        }\n        else {\n            rlim.rlim_cur = rlim.rlim_max;\n        }\n        setrlimit(RLIMIT_STACK, &rlim);\n        execlp(\"bash\", \"bash\", NULL);\n        /* perror(\"exec\"); --- can't use stdlib after stack size change */ \n        _exit(1);\n    }\n    else {\n        int wstatus;\n        waitpid(pid, &wstatus, 0);\n        return WEXITSTATUS(wstatus);\n    }\n}\n"
  },
  {
    "path": "practice/fdup-pipe/README.md",
    "content": "# Дублирование файловых дескрипторов. Каналы\r\n\r\n## Дублирование файловых дескрипторов\r\n\r\nСистемный вызов `fcntl` позволяет настраивать различные манипуляции над открытыми файловыми дескрипторами. Одной из команд манипуляции является `F_DUPFD` - создание *копии* дескриптора в текущем процессе, но с другим номером.\r\n\r\nКопия подразумевает, что два разных файловых дескриптора связаны с одним открытым файлом в процессе, и разделяют следующие его аттрибуты:\r\n * сам файловый объект;\r\n * блокировки, связанные с файлом;\r\n * текущая позиция файла;\r\n * режим открытия (чтение/запись/добавление).\r\n\r\nПри этом, не сохраняется флаг `CLOEXEC`, который предпиывает автоматическое закрытие файла при выполнении системного вызова `exec`.\r\n\r\nУпрощённой семантикой для создания копии файловых дескрипторов являются системные вызовы POSIX: `dup` и `dup2`:\r\n```\r\n#include <unistd.h>\r\n\r\n/* Возвращает копию нового файлового дескриптора, при этом, по аналогии\r\n   с open, численное значение нового файлового дескриптора - минимальный\r\n   не занятый номер. */\r\nint dup(int old_fd);\r\n\r\n/* Создаёт копию нового файлового дескриптора с явно указанным номером new_fd.\r\n   Если ранее файловый дескриптор new_fd был открыт, то закрывает его. */\r\nint dup2(int old_fd, int new_fd);\r\n```\r\n\r\n## Неименованные каналы\r\n\r\nКанал - это пара связанных между собой файловых дескрипторов, один из которых предназначен для только для чтения, а другой - только для записи.\r\n\r\nКанал создается с помощью системного вызова `pipe`:\r\n```\r\n#include <unistd.h>\r\n\r\nint pipe(int pipefd[2]);\r\n```\r\n\r\nВ качестве аргумента системному вызову `pipe` передается указатель на массив и двух целых чисел, куда будут записаны номера файловых дескрипторов:\r\n * `pipefd[0]` - файловый дескриптор, предназначенный для чтения;\r\n * `pipefd[1]` - файловый дескриптор, предназначенный для записи.\r\n\r\n### Запись данных в канал\r\n\r\nОсуществляется с помощью системного вызова `write`, первым аргументом которого является `pipefd[1]`. Канал является буферизованным, под Linux обычно его размер 65К. Возможные сценарии поведения при записи:\r\n\r\n * системный вызов `write` завершается немедленно, если размер данных меньше размера буфера, и в буфере есть место;\r\n * системный вызов `write` приостанавливает выполнение до тех пор, пока не появится место в буфере, то есть предыдущие данные не будут кем-то прочитаны из канала;\r\n * системный вызов `write` завершается с ошибкой `Broken pipe` (доставляется через сигнал `SIGPIPE`), если с противоположной стороны канал был закрыт, и данные читать некому.\r\n\r\n### Чтение данных из канала\r\n\r\nОсуществляется с помощью системного вызова `read`, первым аргументом которого является `pipefd[0]`. Возможные сценарии поведения при чтении:\r\n\r\n * если в буфере канала есть данные, то `read` читает их, и завершает свою работу;\r\n * если буфер пустой и есть **хотя бы один** открытый файловый дескриптор с противоположной стороны, то выполнение `read` блокируется;\r\n * если буфер пустой и все файловые дескрипторы с противоположной стороны каналы закрыты, то `read` немедленно завершает работу, возвращая `0`.\r\n\r\n\r\n### Проблема dead lock\r\n\r\nПри выполнении системных вызовов `fork`, `dup` или `dup2` создаются копии файловых дескрипторов, связанных с каналом. Если не закрывать все лишние (неиспользуемые) копии файловых дескрипторов, предназначенных для записи, то это приводит к тому, что при очередной попытке чтения из канала, `read` вместо того, чтобы завершить работу, будет находиться в ожидании данных.\r\n\r\n```\r\nint fds_pair[2];\r\npipe(fds_pair);\r\n\r\nif ( 0!=fork() )  // теперь у нас существует неявная копия файловых дескрипторов\r\n{\r\n    // немного записываем в буфер\r\n    static const char Hello[] = \"Hello!\";\r\n    write(fds_pair[1], Hello, sizeof(Hello));\r\n    close(fds_pair[1]);\r\n\r\n    // а теперь читаем обратно\r\n    char buffer[1024];\r\n    read(fds_pair[0], buffer, sizeof(buffer)); // получаем dead lock!\r\n}\r\nelse while (1) shched_yield();\r\n```\r\n\r\nДля того, чтобы избежать этой проблемы, необходимо тщательно следить за тем, в какие моменты создаются копии файловых дескрипторов, и закрывать их тогда, когда они не нужны.\r\n"
  },
  {
    "path": "practice/file_io/README.md",
    "content": "# Файловый ввод-вывод\n\n## Файловые дескрипторы\n\nФайловые дескрипторы - это целые числа, однозначно идентифицирующие открытые файлы в рамках одной программы.\n\nКак правило, при запуске процесса дескрипторы `0`, `1` и `2` уже заняты стандартным потоком ввода (`stdin`), стандартным потоком вывода (`stdout`) и стандартным потоком ошибок (`stderr`).\n\nФайловые дескрипторы могут быть созданы с помощью операции создания или открытия файла.\n\n\n## Системные вызовы open/close\n\nСистемный вызов `open` предназначен для создания файлового дескриптора из существующего файла, и имеет формальную сигнатуру:\n\n```\nint open(const char *path, int oflag, ... /* mode_t mode */);\n```\n\nПервый параметр - имя файла (полное, или относительно текущего каталога). Второй параметр - параметры открытия файла, третий (опциональный) - права доступа на файл при его создании.\n\nОсновные параметры открытия файлов:\n * `O_RDONLY` - только для чтения;\n * `O_WRONLY` - только на запись;\n * `O_RDWR` - чтение и запись;\n * `O_APPEND` - запись в конец файла;\n * `O_TRUNC`- обнуление файла при открытии;\n * `O_CREAT` - создание файла, если не существует;\n * `O_EXCL` - создание файла только если он не существует.\n\nВ случае успеха возвращается неотрицательное число - дескриптор, в случае ошибки - значение `-1`.\n\n\n## Обработка ошибок в POSIX\n\nКод ошибки последней операции хранится в глобальной целочисленной \"переменной\" `errno` (на самом деле, в современных реализациях - это макрос). Значения кодов можно определить из `man`-страниц, либо вывести текст ошибки с помощью функции `perror`.\n\n\n## Аттрибуты файлов в POSIX\n\nВ случае создания файла, обязательным параметром является набор POSIX-аттрибутов доступа к файлу. Как правило, они кодируются в восьмиричной системе исчисления в виде `0ugo`, где `u` - права доступа для владельца файла, `g` - права доступа для всех пользователей группы файла, `o` - для остальных.\n\nВ восьмеричной записи значения от 0 до 7 соответствуют комбинации трёх бит:\n```\n00: ---\n01: --x\n02: -w-\n03: -wx\n04: r--\n05: r-x\n06: rw-\n07: rwx\n```\n\n\n## Чтение и запись в POSIX\n\nЧтение и запись осуществляются с помощью системных вызовов:\n```\nssize_t read(int fd, void *buf, size_t count);\nssize_t write(int fd, const void *buf, size_t count);\n```\nЗдесь `buf` - указатель на буфер данных, а `count` - максимальное количество байт для чтения/записи.\n\nКак правило, в `count` указывается размер буфера данных при чтении, или количество данных при записи.\n\nВозвращаемый тип `ssize_t` - целочисленный, определенный в диапазоне `[-1...SSIZE_MAX]`, где `SSIZE_MAX` обычно совпадает с `SIZE_MAX/2`. Значение `-1` используется в качестве признака ошибки, неотрицательные значения - количество записанных/прочитанных байт, которое может быть меньше, чем `count`.\n\nЕсли системный вызов `read` вернул значение `0`, то достигнут конец файла, либо был закрыт канал ввода.\n\n## Навигация по файлу в POSIX\n\nЕсли файл является обычным, то можно выполнять перемещение текущей позиции в файле.\n```\noff_t lseek(int fd, off_t offset, int whence);\n```\nЭтот системный вызов предназначен для перемещения текущего указателя на файл.\n\nТретий параметр `whence` один из трех стандартных способов перемещения:\n * `SEEK_SET` - указать явным образом позицию в файле;\n * `SEEK_CUR` - сместить указатель на определенное смещение относительно текущей позиции;\n * `SEEK_END` - сместить указатель на определенное смещение относительно конца файла.\n\nСистемный вызов `lseek` возвращает текущую позицию в файле, либо значение `-1` в случае возникновения ошибки.\n\nТип `off_t` является знаковым, и по умолчанию 32-разрядным. Для того, чтобы уметь работать с файлами размером больше 2-х гигабайт, определяется значение переменной препроцессора **до подключения заголовочных файлов**:\n```\n#define _FILE_OFFSET_BITS 64\n```\n\nВ этом случае, тип данных `off_t` становится 64-разрядным. Определить значение переменных препроцессора можно не меняя исходные тексты программы, передав компилятору опцию `-DКЛЮЧ=ЗНАЧЕНИЕ`:\n```\n# Скомпилировать программу с поддержкой больших файлов\ngcc -D_FILE_OFFSET_BITS=64 legacy_source.c\n```\n\n## Компиляция и запуск Windows-программ из Linux\n\nДля кросс-компиляции используется компилятор gcc с целевой системой `w64-mingw`. Устанавливается из пакета:\n * `mingw32-gcc` - для Fedora\n * `gcc-mingw-w64` - для Ubuntu\n * `mingw32-cross-gcc` - для openSUSE.\n\nСкомпилировать программу для Windows можно командой:\n```\n$ i686-w64-mingw-gcc -m32 program.c\n# На выходе получаем файл a.exe, а не a.out\n```\n\nОбратите внимание, что система Linux, в отличии от Windows различает регистр букв в файловой системе, поэтому нужно использовать стандартные заголовочные файлы WinAPI в нижнем регистре:\n```\n#include <windows.h> // правильно\n#include <Windows.h> // скомпилируется в Windows, но не в Linux\n```\n\nЗапустить полученный файл можно с помощью WINE:\n```\n$ WINEDEBUG=-all wine a.exe\n```\n\nУстановка переменной окружения `WINEDEBUG` в значение `-all` приводит к тому, что в консоль не будет выводится отладочная информация, связанная с подсистемой `wine`, которая перемешивается с выводом самой программы.\n\n## Файловые дескрипторы и другие типы данных в WinAPI\n\nВ системе Windows для файловых дескрипторов используется тип `HANDLE`.\n\nДля однобайтных строк используется тип `LPCSTR`, для многобайтных - `LPCWSTR`.\n\nФункции WinAPI, которые имеют разные варианты поддержки строк, и работают с однобайтными функциями, заканчиваются на букву `A`, а функции, которые работают с многобайтными файлами - на букву `W`.\n\nДля беззнакового 32-разрядного числа, используемого для флагов - тип `DWORD`.\n\nПолный список типов данных [в документации от Microsoft](https://docs.microsoft.com/en-us/windows/desktop/winprog/windows-data-types).\n\n## Функции WinAPI для работы с файлами\n\nФайл можно открыть с помощью функции [CreateFile](https://docs.microsoft.com/ru-ru/windows/desktop/api/fileapi/nf-fileapi-createfilea).\n\nЧтение и запись - с помощью функций [ReadFile](https://docs.microsoft.com/en-us/windows/desktop/api/fileapi/nf-fileapi-readfile) и [WriteFile](https://docs.microsoft.com/en-us/windows/desktop/api/fileapi/nf-fileapi-writefile).\n\nНавигация по файлу - с помощью функции [SetFilePointerEx](https://docs.microsoft.com/ru-ru/windows/desktop/api/fileapi/nf-fileapi-setfilepointerex).\n"
  },
  {
    "path": "practice/fork/README.md",
    "content": "# Процессы\n\nВ UNIX-системах поддерживается многозадачность, которая обеспечивается: параллельным выполнением нескольких задач, изоляцией адресного пространства каждой задачи.\n\nПолный список процессов можно получить с помощью команды `ps -A`.\n\nУбить какой-то процесс можно с помощью команды `kill`.\n\n## Свойства процессов\n\nУ каждого процесса существует свои обособленные:\n * адресное пространство начиная с `0x00000000`;\n * набор файловых дескрипторов для открытых файлов.\n\nКроме того, каждый процесс может находиться в одном из состояний:\n * работает (Running);\n * приостановлен до возникновения определенного события (Suspended);\n * приостановлен до явного сигнала о том, что нужно продолжить работу (sTopped);\n * более не функционирует, не занимает память, но при этом не удален из таблицы процессов (Zombie).\n\nКаждый процесс имеет свой уникальный идентификатор - Process ID (PID), который присваивается системой инкрементально. Множество доступных PID является ограниченным, и его исчерпание проводит к невозможности создания нового процесса (что является механизмом действия форк-бомбы).\n\n## Иерархия процессов\n\nМежду процессами существуют родственные связи \"предок-потомок\", таким образом, иерархия процессов представляет собой древовидную структуру. Корнем дерева процессов является процесс с PID=1, который называется `init` (в классических UNIX-системах, в том числе xBSD или консервативных Linux-дистрибутивах), либо `systemd`.\n\nРодительские процессы могут завершаться раньше, чем завершаются их потомки. В этом случае, осиротевшие процессы становятся прямыми потомками процесса с PID=1, то есть `init` или `systemd`.\n\nПроцессы можно объединять в *группы процессов* (process group), - множества процессов, которым доставляются *сигналы* о некоторых событиях. Например, в одну группу могут объединяться все процессы, запущенные из одной вкладки приложения-терминала.\n\nОбъединение нескольких групп процессов называется *сеансом* (session). Как правило, в сеансы объединяются группы процессов в рамках одного входа пользователя в систему (их может быть несколько, например, несколько входов по `ssh`).\n\n## Системный вызов `fork`\n\nСоздание нового процесса осуществляется с помощью системного вызова `fork`, который создаёт почти точную копию текущего процесса, причём оба процесса продолжают своё выполнение со следующей, после вызова `fork`, инструкции. Различить родительский процесс от его копии - дочернего процесса, можно по возвращаемому значению `fork`: для родительского процесса возвращается PID вновь созданного процесса, а для дочернего - число 0.\n\n```\npid_t process_id; // в большинстве систем pid_t совпадает с int\nif ( -1 == ( process_id=fork() ) ) {\n  perror(\"fork\"); // ошибка создания нового процесса\n}\nelse if ( 0 == process_id ) {\n  printf(\"I'm a child process!\");\n}\nelse {\n  printf(\"I'm a parent process, created child %d\", process_id);\n}\n```\n\nСитуация, когда `fork` возвращает значение -1, как правило означает, что система исчерпала допустимый лимит ресурсов на создание новых процессов.\n\nПример реализации форк-бомбы, назначение которой - исчерпать лимит на количество запущенных процессов:\n\n```\n#include <stdio.h>\n#include <sys/types.h>\n#include <unistd.h>\n#include <stdlib.h>\n#include <string.h>\n#include <sched.h>\n\nint main()\n{\n    char * are_you_sure = getenv(\"ALLOW_FORK_BOMB\");\n    if (!are_you_sure || 0!=strcmp(are_you_sure, \"yes\")) {\n        fprintf(stderr, \"Fork bomb not allowed!\\n\");\n        exit(127);\n    }\n\n    pid_t pid;\n    do {\n        pid = fork();\n    } while (-1 != pid);\n\n    printf(\"Process %d reached out limit on processes\\n\", getpid());\n    while (1) {\n        sched_yield();\n    }\n}\n```\n\n## Завершение работы процесса\n\nЛюбой процесс, при добровольном завершении работы, должен явным образом сообщить об этом системе с помощью системного вызова `exit`.\n\nПри написании программ на языках Си или С++ этот системный вызов выполняется после завершения работы функции `main`, вызванной из функции `_start`, которая неявным образом генерируется компилятором.\n\nОбратите внимание, что в стандартной библиотеке языка Си уже существует одноименная функция `exit`, поэтому название Си-оболочки для системного вызова начинается с символа подчеркивания: `_exit`.\n\nФункция `exit` отличается от системного вызова `_exit` тем, что предварительно сбрасывает содержимое буферов вывода, а также последовательно вызывает все функции, зарегистрированные с помощью `atexit`.\n\nВ качестве аргумента принимается целое число, - код возврата из программы. Несмотря на то, что код возврата является 32-разрядным, к нему применяется операция поразрядного \"и\" с маской `0xFF`. Таким образом, диапазон кодов возврата находится от 0 до 255.\n\nКод возврата предназначен для того, чтобы сообщить родительскому процессу причину завершения своей работы.\n\n## Чтение кода возврата дочернего процесса\n\nСемейство системных вызовов `wait*` предназначено для ожидания завершения работы процесса, и получения информации о том, как процесс жил и умер.\n\n * `wait(int *wstatus)` - ожидание завершения любого дочернего процесса, возвращает информацию о завершении работы;\n * `waitpid(pid_t pid, int *wstatus, int options)` - ожидание (возможно неблокирующее) завершения работы конкретного процесса, возвращает информации о завершении работы;\n * `wait3(int *wstatus, int options, struct rusage *rusage)` - ожидание (возможно неблокирующее) завершения любого дочернего процесса, возвращает информацию о завершении работы и статистике использования ресурсов;\n * `wait4(pid_t pid, int *wstatus, int options, struct rusage *rusage)` - ожидание (возможно неблокирующее) завершения конкретного процесса, возвращает информацию о завершении работы и статистике использования ресурсов.\n\nЕсли в программе предусмотрено создание более одного дочернего процесса, то использовать системные вызовы `wait` и `wait3` настоятельно не рекомендуется, поскольку дочерние процессы могут завершать свою работу произвольным образом, и это может привести к неоднозначному поведению. Вместо них нужно использовать `waitpid` или `wait4`.\n\nСостояние возврата, которое можно прочитать из таблицы процессов после того, как процесс перестал функционировать, - это причина завершения работы, код возврата, если процесс завершился через `_exit`, или номер убившего его сигнала, если процесс был принудительно завершён. Это состояние закодировано в 32-битном значении, формат которого строго не определен стандартом POSIX. Для извлечения информации используется нобор макросов:\n * `WIFEXITED(wstatus)` - возвращает значение, отличное от 0, если процесс был завершен с помощью системного вызова `_exit`;\n * `WIFSIGNALED(wstatus)` - возвращает значение, отличное от 0, если процесс был завершен принудительно;\n * `WEXITSTATUS(wstatus)` - выделяет код возврата в диапазоне от 0 до 255;\n * `WTERMSIG(wstatus)` - выделяет номер сигнала, если процесс был завершён принудительно.\n\nЧтение кода возврата, - это не право, а обязанность родительского процесса. В противном случае, дочерний процесс, который завершил свою работу, становится процессом-зомби, информация о завершении которого продолжает занимать место в таблице процессов. Завершение работы родительского процесса при работающих дочерних приводит к тому, что код возврата будет прочитан процессом с PID=1, который автоматически становится родительским для \"осиротевших\" процессов.\n"
  },
  {
    "path": "practice/function-pointers/README.md",
    "content": "# Библиотеки функций и их загрузка\n\n## Функции и указатели на них\n\nКод программ в системах с Фон-Неймановской архитектурой размещается в памяти точно так же, как и обычные данные.\n\nТаким образом, он может быть загружен или сгенерирован во время работы программы. Некоторые процессоры позволяют контролировать, какие участки памяти могут быть выполняемые, а какие - нет, и кроме того, это контролируется ядром. Таким образом, выполнить код можно только при условии, что он находится в страницах памяти, помеченных как выполняемые.\n\n\n## Типизация указателей на функции\n\nОбъявление вида\n```\nint (*p_function)(int a, int b);\n```\nинтерптетируется следующим образом: `p_function` - это указатель на функцию, которая принимает два целочисленных аргумента, и возвращает целое знаковое число.\n\nБолее общий вид указателя на функцию:\n```\ntypedef ResType (*TypeName)(FuncParameters...);\n```\n\nЗдесь `ResType` - возвращаемый тип целевой функции, `TypeName` - имя типа-указателя, `FuncParameters...` - параметры функции.\n\nИспользование ключевого слова `typedef` является необходимым для языка Си, чтобы каждый раз не писать полностью тип (по аналогии со `struct`).\n\nОбъявление указателей на функции необходимо для того, чтобы компилятор знал, как именно использовать адрес какой-то функции, и мог подготовить аргументы, и разбраться с тем, откуда брать возвращаемый результат функции.\n\n\n## Библиотеки\n\nELF-файл может быть не только исполняемым, но и библиотекой, содержащей функции. Библиотека отличается от исполняемого файла тем, что:\n * содержит таблицу доступных *символов* - функций и глобальных переменных (можно явно указать её создание опцией `-E`);\n * может быть размещена произвольным образом, поэтому программа обязана быть скомпилирована в позиционно-независимый код с опцией `-fPIC` или `-fPIE`;\n * не обязана иметь точку входа в программу - функции `_start` и `main`.\n\nКомпиляция библиотеки производится с помощью опции `-shared`:\n```\n > gcc -fPIC -shared -o libmy_great_library.so lib.c\n```\n\nВ Linux и xBSD для именования библиотек используется соглашение `libИМЯ.so`, для Mac - `libИМЯ.dynlib`, для Windows - `ИМЯ.dll`.\n\nСвязывание программы с библиотекой подразумевает опции:\n * `-lИМЯ` - указыватся имя библиотеки без префикса `lib` и суффикса `.so`;\n * `-LПУТЬ` - указывается имя каталога для поиска используемых библиотек.\n\n\n## Runtime Search Path\n\nПри загрузке ELF-файла загружаются все необходимые библиотеки, от которых он явно зависит. Посмотреть список зависимостей можно с помощью команды `ldd`.\n\nБиблиотеки располагаются в одном из стандартных каталогов: `/lib[64]`, `/usr/lib[64]` или `/usr/local/lib[64]`. Дополнительные каталоги для поиска библиотек определяются в переменной окружения `LD_LIBRARY_PATH`.\n\nСуществует возможность явно определить в ELF-файле, где искать необходимые библиотеки. Для этого используется опция линковщика `ld -rpath ПУТЬ`.\n\nДля передачи опций `ld`, который вызывается из `gcc`, используется опция `-Wl,ОПЦИЯ`.\n\nВ `rpath` можно указывать как абсолютные пути, так и переменную `$ORIGIN`, которая при загрузке программы раскрывается в каталог, содержащий саму программу. Это позволяет создавать поставку из программы и библиотек, которые не раскиданы по всей файловой системе:\n\n```\n > gcc -o program -L. -lmygreat_library program.c \\\n       -Wl,-rpath -Wl,'$ORIGIN/'.       \n```\n\nЭто создаст выполняемый файл `program`, который использует библиотеку `libmy_great_library.so`, подразумевая, что файл с библиотекой находится в том же каталоге, что и сама программа.\n\n\n## Загрузка библиотек во время выполнения\n\nБиблиотеки можно не привязывать намертво к программе, а загружать по мере необходимости. Для этого используется набор функций `dl`, которые вошли в стандарт POSIX 2001 года.\n\n * `void *dlopen(const char *filename, int flags)` - загружает файл с библиотекой;\n * `void *dlsym(void *handle, const char *symbol)` - ищет в библиотеке необходимый символ, и возвращает его адрес;\n * `int dlclose(void *handle)` - закрывает библиотеку, и выгружает её из памяти, если она больше в программе не используется;\n * `char *dlerror()` - возвращает текст ошибки, связянной с `dl`.\n\nЕсли `dlopen` или `dlsym` не могут открыть файл или найти символ, то возвращается нулевой указатель.\n\nПример использования - в файлах [lib.c](lib.c) и [dynload.c](dynload.c).\n\n\n## Позиционно-независимый исполняемый файл\n\nОпция `-fPIE` компилятора указывает на то, что нужно сгенерировать позиционно-независимый код для `main` и `_start`, а опция `-pie` - о том, что нужно при линковке указать в ELF-файле, что он позиционно-независимый.\n\nПозиционно-независимый выполняемый файл в современных системах размещается по случайному адресу.\n\nЕсли позиционно-независимый исполняемый файл ещё и содержит таблицу экспортируемых символов, то он одновременно является и библиотекой. Если отсутствует опция `-shared`, то компилятор собирает программу, удаляя из неё таблицу символов. Явным образом сохранение таблицы символов задается опцией `-Wl,-E`.\n\nПример:\n```\n  # файл abc.c содержит int main() { puts(\"abc\"); }\n  > gcc -o program -fPIE -pie -Wl,-E abc.c\n\n  # программа может выполняться как обычная программа\n  > ./program\n  abc\n\n  # и может быть использована как библиотека\n  > python3\n  >>> from ctypes import cdll, c_int\n  >>> lib = cdll.LoadLibrary(\"./program\")\n  >>> main = lib[\"main\"]\n  >>> main.restype = c_int\n  >>> ret = main()\n  abc  \n\n ```\n"
  },
  {
    "path": "practice/function-pointers/dynload.c",
    "content": "#include <dlfcn.h>\n#include <stdio.h>\n#include <stdlib.h>\n\ntypedef void (*func_t)(int);\n\nint main()\n{\n    void * lib = dlopen(\"libmylib.so\", RTLD_NOW);\n    if (! lib) {\n        fprintf(stderr, \"dlopen error: %s\\n\", dlerror());\n        exit(1);\n    }\n    void * entry = dlsym(lib, \"some_func\");\n    if (! entry) {\n        fprintf(stderr, \"dlsym error: %s\\n\", dlerror());\n        exit(1);\n    }\n    func_t func = entry;\n    func(123);\n    dlclose(lib);\n}\n"
  },
  {
    "path": "practice/function-pointers/func-pointer.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <math.h>\n\ntypedef\ndouble (*unary_real_function_t)(double);\n\nunary_real_function_t funcs[] = { &sqrt, exp, log, NULL };\n\nint main() {\n    double x = 100;\n    unary_real_function_t func = NULL;\n    double y;\n    for (int i=0; funcs[i]; ++i) {\n        func = funcs[i];\n        y = func(x);\n        printf(\"func(%g) = %g\\n\", x, y);\n    }\n}\n"
  },
  {
    "path": "practice/function-pointers/lib.c",
    "content": "#include <stdio.h>\n\nvoid some_func(int a) {\n    printf(\"%d\\n\", a);\n}\n"
  },
  {
    "path": "practice/function-pointers/main.c",
    "content": "extern void some_func(int x);\n#include <stdio.h>\n\nint main()\n{\n    some_func(123);\n    fgetc(stdin);\n}\n\n"
  },
  {
    "path": "practice/fuse/README.md",
    "content": "# Реализация файловых систем без написания модулей ядра\n\n## Общие сведения\n\nФайловые системы обычно реализуются в виде модулей ядра, которые работают в адресном пространстве ядра. \n\nМонтирование осуществляется командой [`mount(8)`](http://man7.org/linux/man-pages/man8/mount.8.html), которой необходимо указать:\n\n* *точку монтирования* - каталог в виртуальной файловой системе, в котором будет доступно содержимое смонтированной файловой системы;\n* *тип файловой системы* - один из поддерживаемых типов: `ext2`, `vfat` и др. Если не указать тип файловой системы, то ядро попытается автоматически определить её тип, но сделать это ему не всегда удаётся;\n* *устройство для монтирования* - как правило, блочное, устройство для монтирования реальных устройств, либо URI для сетевых ресурсов, либо имя файла для монтирования образа.\n\nВызов команды `mount` без параметров отображает список примонтированных файловых систем. \n\nПостоянные файловые системы, которые монтируются при загрузке системы, перечислены в файле `/etc/fstab`, формат которого описан в [`fstab(5)`](http://man7.org/linux/man-pages/man5/fstab.5.html). Если точка монтирования указана в этом файле, то для монтирования файловой системы достаточно указать команде `mount` только точку монтирования.\n\nНекоторые типы файловых систем реализованы в виде сервисов, которые работают в пространстве пользователя, как обычные процессы. Ядро взаимодействует с ними, используя файл символьного устройства `/dev/fuse`. Когда ядру необходимо обслужить запрос к виртуальной файловой системе, то в случае, если точка монтирования содержит файловую систему FUSE, ядро формирует запрос в специальном формате, и отправляет его тому процессу, который открыл файл `/dev/fuse` и зарегистрировал открытый файловый дескриптор в качестве параметра системного вызова [`mount(2)`](http://man7.org/linux/man-pages/man2/mount.2.html). После этого процесс обязан сформировать ответ, который будет обработан модулем ядра `fuse.ko` и ядро выполнит запрошенную файловую операцию.\n\nПодсистема FUSE реализована в Linux и FreeBSD. \n\n## Пример реализации - файловая система SSH\n\nПротокол SSH предназначен для терминального доступа к любой UNIX-системе, на которой запущен сервис `sshd`. Авторизация осуществляется через пароль, в этом случае нужно будет его ввести после запуска команды `ssh`, либо с использованием асимметричных RSA-ключей.\n\nИспользование ключей обычно является более безопасным (при условии, что полностью запрещена авторизация по паролю), поскольку случайно подобранный ключ намного сложнее подобрать по словарю, чем пароль. Для создания пары ключей используется команда `ssh-keygen`, которая создает пару файлов `~/.ssh/id_rsa` и `~/.ssh/id_rsa.pub` , первый из которых является приватным ключом, а второй - публичным. Содержимое публичного ключа можно добавить отдельной строкой в текстовый файл `~/.ssh/authorized_keys` на целевом хосте, и после этого можно будет подключаться без ввода пароля. Для копирования ключа на удаленный сервер в большинстве дистрибутивов предусмотрен скрипт `ssh-copy-id`.\n\nС помощью ssh можно не только интерактивно взаимодействовать с удаленным хостом, но и выполнять отдельные команды, если указывать из последними аргументами.\n\n**Пример:** создание каталога на удаленном хосте и копирование в него файла с локального компьютера.\n\n```bash\n# ssh user  @ host    \"command to execute\"\n> ssh victor@10.0.2.4 \"mkdir ~/some_dir\"\n\n# get contents   pipe                    write contents to file\n> cat /bin/bash   |  ssh victor@10.0.2.4 \"cat >~/some_dir/bash\" \n\n# set file attributes\n> ssh victor@10.0.2.4 \"chmod a+x ~/some_dir/bash\"\n\n# ensure file has been copied\n> ssh ssh victor@10.0.2.4 \"ls -l ~/some_dir/\"\ntotal 1024\n-rwxr-xr-x  1 victor  victor  1012552 Apr 21 10:17 bash\n```\n\nТаким образом, используя ssh в сочетании со стандартными командами POSIX, можно реализовать произвольные файловые операции над удаленной файловой системой. Этот подход реализован в реализации файловой системы [sshfs](https://github.com/libfuse/sshfs).\n\n```bash\n# local directory for remote contents\n> mkdir remote_host\n\n# mount   user@host    :path    local mount point\n> sshfs victor@10.0.2.4:/       remote_host\n```\n\nПри этом, реализация `sshfs` использует только `fork+exec` для запуска команды `ssh`, и работает в адресном пространстве пользователя, а не ядра, и использует обычные сторонние библиотеки:\n\n```bash\n> ldd /usr/bin/sshfs\n        linux-vdso.so.1 (0x00007ffff1985000)\n        libfuse.so.2 => /lib64/libfuse.so.2 (0x00007f33e781e000)\n        libgthread-2.0.so.0 => /usr/lib64/libgthread-2.0.so.0 (0x00007f33e761c000)\n        libglib-2.0.so.0 => /usr/lib64/libglib-2.0.so.0 (0x00007f33e7305000)\n        libpthread.so.0 => /lib64/libpthread.so.0 (0x00007f33e70e7000)\n        libc.so.6 => /lib64/libc.so.6 (0x00007f33e6d2d000)\n        libdl.so.2 => /lib64/libdl.so.2 (0x00007f33e6b29000)\n        libpcre.so.1 => /usr/lib64/libpcre.so.1 (0x00007f33e689c000)\n        /lib64/ld-linux-x86-64.so.2 (0x00007f33e7c71000)\n```\n\n## Библиотека libfuse\n\n### Реализация демона FUSE\n\nБиблиотека [libfuse](https://github.com/libfuse/libfuse) реализует функциональность взаимодействия с модулем ядра `fuse.ko` через файловый дескриптор файла `/dev/fuse`. Поскольку проект существует достаточно давно, программный интерфейс (API) библиотеки претерпел множество изменений, и перед включением заголовочного файла необходимо указать версию программного интерфейса, который будет использован:\n\n```c\n#define FUSE_USE_VERSION 30   // API version 3.0\n#include <fuse.h>\n```\n\nВ дальнейшем значение макроса `FUSE_USE_VERSION` будет использовано препроцессором при обработке файла `fuse.h` для условной подстановки соответствующих определенной версии сигнатур функций. До версии 3.0 изменений накопилось очень много, поэтому используется отдельная библиотека `libfuse3.so` вместо `libfuse.so`, в дальнейшем мы будем использовать именно её. \n\nДля FUSE не реализован пакет CMake, но существует описание в формате `pkg-config`, которое можно использовать из CMake:\n\n```cmake\nfind_package(PkgConfig REQUIRED)\npkg_check_modules(FUSE REQUIRED fuse3)\n\nlink_libraries(${FUSE_LIBRARIES})         # -lfuse3 -lpthread\ninclude_directories(${FUSE_INCLUDE_DIRS}) # -I/usr/include/fuse3\ncompile_options(${FUSE_CFLAGS_OTHER})     # empty since fuse 3.0\n```\n\nРеализация файловой системы - это программа-демон, которая ожидает запросы от ядра и обслуживает их. Реализация тривиальной программы:\n\n```c\nstatic struct fuse_operations operations = {\n    // pointers to callback functions\n};\n\nint main(int argc, char *argv[]) {\n    // arguments to be preprocessed before passing to /sbin/mount.fuse3\n    struct fuse_args args = FUSE_ARGS_INIT(argc, argv);\n    \n    // run daemon\n    int ret = fuse_main(\n        args.argc, args.argv, // arguments to be passed to /sbin/mount.fuse3\n        &operations,          // pointer to callback functions\n        NULL                  // optional pointer to user-defined data\n    );\n    return ret;\n}\n```\n\nДемон использует стандартный для монтирования набор аргументов командной строки, аналогичный команде [`mount.fuse(8)`](http://man7.org/linux/man-pages/man8/mount.fuse.8.html). Как минимум, требуется один позиционный аргумент - это точка монтирования. После выполнения операции монтирования, демон продолжает работать в фоновом режиме, используя `fork`. Для работы в текущем процессе (foreground) используется опция `-f`, что бывает полезно для отладки. Опция `-u` означает операцию отключения ранее зарегистрированной точки монтирования.\n\nНабор операций, который используется при обработке запросов от ядра может быть не полным, то есть не покрывать всю функциональность файловой системы, или даже пустым. В этом случае, при попытке обращения к файловой системе, возникнет ошибка. Так, для примера выше, команда `ls` завершит работу с ошибкой:\n\n```bash\n# start empty FUSE implementation\n> ./my_filesystem work_dir\n\n# try to get filesystem contents\n> ls work_dir\nls: cannot access 'work_dir': Function not implemented\n\n# umount filesystem and stop the daemon\n> fusermount3 -u work_dir\n```\n\n### Реализация функциональности\n\nРеализация функциональности файловой системы определяется указателями на соответствующий функции в структуре `struct fuse_operations`, причем для большинства полей имена совпадают с именами соответствующих системных вызовов (за исключением системного вызова  `stat`, поле которого называется `getattr`). Для большинства из функций возвращаемым значением является целое число: `0` в случае успеха, и отрицательное значение в случае ошибки. Значение модуля кода ошибки соответствует ожидаемому коду ошибки, который будет записан в `errno` после выполнения соответствующего системного вызова. Так, ошибке \"файл не найден\" соответствует возвращаемое значение `-ENOENT`, где значение константы `ENOENT` определено в заголовочном файле `<errno.h>` .\n\n**Полное описание** callback-функций доступно по ссылке: [https://libfuse.github.io/doxygen/structfuse__operations.html](https://libfuse.github.io/doxygen/structfuse__operations.html).\n\n#### Получение списка файлов\n\nРассмотрим тривиальную файловую систему, которая содержит ровно два файла: `a.txt` и `b.txt`  с одинаковым содержимым. Как минимум, необходимо иметь возможность узнать содержимое файловой системы, реализовав для этого [`readdir(2)`](http://man7.org/linux/man-pages/man2/readdir.2.html) для чтения содержимого каталога, и [`stat(2)`](http://man7.org/linux/man-pages/man2/stat.2.html) для получения атрибутов файлов, включая атрибуты самого корневого каталога, иначе будет невозможно узнать о том, что он действительно является каталогом, и тем самым - получить его содержимое.\n\n```c\n// contents to be accessed by reading files\nstatic const char DummyData[] = \"Hello, World!\\n\";\n\n// callback function to be called after 'stat' system call\nint my_stat(const char *path, struct stat *st, struct fuse_file_info *fi)\n{\n    // check if accessing root directory\n    if (0==strcmp(\"/\", path)) {\n        st->st_mode = 0555 | S_IFDIR;  // file type - dir, access read only\n        st->st_nlink = 2;              // at least 2 links: '.' and parent\n        return 0;                      // success!\n    }\n    if (0!=strcmp(\"/a.txt\", path) && 0!=strcmp(\"/b.txt\", path)) {\n        return -ENOENT; // error: we have no files other than a.txt and b.txt\n    }\n\n    st->st_mode = S_IFREG | 0444;    // file type - regular, access read only\n    st->st_nlink = 1;                // one link to file\n    st->st_size = sizeof(DummyData); // bytes available\n    return 0;                        // success!\n}\n\n// callback function to be called after 'readdir' system call\nint my_readdir(const char *path, void *out, fuse_fill_dir_t filler, off_t off,\n               struct fuse_file_info *fi, enum fuse_readdir_flags flags)\n{\n    if (0 != strcmp(path, \"/\")) {\n        return -ENOENT;  // we do not have subdirectories\n    }\n\n    // two mandatory entries: the directory itself and its parent\n    filler(out, \".\", NULL, 0, 0);\n    filler(out, \"..\", NULL, 0, 0);\n\n    // directory contents\n    filler(out, \"a.txt\", NULL, 0, 0);\n    filler(out, \"b.txt\", NULL, 0, 0);\n\n    return 0;  // success\n}\n\nstruct fuse_operations operations = {\n    .readdir = my_readdir,   // callback function pointer for 'readdir'\n    .getattr = my_stat,      // callback function pointer for 'stat'\n};\n```\n\nТеперь реализация файловой системы позволяет получить содержимое каталога:\n\n```bash\n> ./my_filesystem work_dir\n\n> ls -l work_dir\ntotal 0\n-r--r--r-- 1 root root 15 Jan  1  1970 a.txt\n-r--r--r-- 1 root root 15 Jan  1  1970 b.txt\n\n# try to get file contents - still not implemented 'open' and 'read'\n> cat work_dir/a.txt\ncat: work_dir/a.txt: Function not implemented\n\n# umount filesystem and stop the daemon\n> fusermount3 -u work_dir\n```\n\nОбратите внимание, что даты создания файлов - 1 января 1970 года, - это соответствует значению 0 для формата времени в UNIX, а владелец файла и группа - пользователь и группа `root`, численные значения `uid` и `gid` которых равны 0. Эти поля могут быть также заполнены при реализации `stat`.\n\nКроме того, утилита `ls` отображает `total 0`, поскольку это значение в выводе является количеством занятых блоков на диске, и эта информация отсутствует в атрибутах файлов.\n\n#### Чтение данных\n\nДля того, чтобы прочитать данные из файла, его нужно, как минимум, успешно открыть, и кроме того, реализовать функцию чтения, которая соответствует поведению системного вызова `read`.\n\n```c\n// callback function to be called after 'open' system call\nint my_open(const char *path, struct fuse_file_info *fi)\n{    \n    if (0!=strcmp(\"/a.txt\", path) && 0!=strcmp(\"/b.txt\", path)) {\n        return -ENOENT;   // we have only two files in out filesystem\n    }\n    if (O_RDONLY != (fi->flags & O_ACCMODE)) {\n        return -EACCES;   // file system is read-only, so can't write\n    }\n    return 0;  // success!\n}\n\n// contents of file\nstatic const char DummyData[] = \"Hello, World!\\n\";\n\n// callback function to be called after 'read' system call\nint my_read(const char *path, char *out, size_t size, off_t off,\n         struct fuse_file_info *fi)\n{\n    // 'read' might be called with arbitary arguments, so check them\n    if (off > sizeof(DummyData))\n        return 0;\n    // reading might be called within some non-zero offset\n    if (off+size > sizeof(DummyData))\n        size = sizeof(DummyData) - off;\n    const void *data = DummyData + off;\n    // copy contents into the buffer to be filled by 'read' system call\n    memcpy(out, data, size);\n    // return value is bytes count (0 or positive) or an error (negative)\n    return size;\n}\n\n// register functions as callbacks\nstruct fuse_operations operations = {\n    .readdir = my_readdir,\n    .getattr = my_stat,\n    .open    = my_open,\n    .read    = my_read,\n};\n```\n\nТеперь можно прочитать содержимое файла:\n\n```bash\n> ./my_filesystem work_dir\n\n# get file contents - OK\n> cat work_dir/a.txt\nHello, World!\n\n# try to create new file - still not implemented\n> touch work_dir/new_file.txt\ntouch: cannot touch 'work_dir/new_file.txt': Function not implemented\n\n# umount filesystem and stop the daemon\n> fusermount3 -u work_dir\n```\n\n### Опции монтирования\n\nУ монтирования файловых систем могут быть опции, например точка монтирования и файл с образом, либо устройство, либо любой другой источник данных для файловой системы. Некоторые опции являются обязательными для всех FUSE-систем, например указание точки монтирования, а часть из них - быть специфичными для реализации определенных файловых систем.\n\nФункция, реализующая работу FUSE-демона `fuse_main` принимает два аргумента: количество опций `argc` и массив строк `argv`, по аналогии с функцией `main`. Если какие-то опции не распознаны `fuse_main`, либо их не достаточно для монтирования файловой системы, то эта функция завершается с ошибкой.\n\nДля выделения, специфичных для конкретной файловой системы, опций используется модифицируемый список опций `fuse_args`, который инициализируется макросом `FUSE_ARGS_INIT(argc, argv)`.\n\nДля извлечения специфичных опций по некоторым шаблонам используется функция `fuse_opt_parse`, которая принимает описания опций, которые необходимо распознать, выполняет разбор аргументов командной строки, и извлекает обработанные опции из массива `argv` структуры `fuse_args`, чтобы они потом не попали в `fuse_main`.\n\nОписанием одной опции является структура  `fuse_opt`, которая содержит:\n\n* текстовую строку, содержащую шаблон аргумента, и возможно, форматную строку для значения, которое необходимо извлечь;\n* смещение в байтах относительно начала структуры, которую заполняет функция `fuse_opt_parse`, если параметр встретился среди аргументов командной строки;\n* целочисленное значение, которое будет записано в структуру; игнорируется в случае, если шаблон содержит формат значения, который нужно извлечь.\n\nДля разбора опций необходимо определить массив из структур `fuse_opt`, где последний элемент, по аналогии со строками, заполнен нулями, и вызвать `fuse_opt_parse`, передав указатель на структуру с опциями, которую необходимо заполнить по результатам их разбора.\n\n```c\nint main(int argc, char *argv[])\n{\n    // initialize modificable array {argc, argv}\n    struct fuse_args args = FUSE_ARGS_INIT(argc, argv);\n\n    // struct to be filled by options parsing\n    typedef struct {\n        char *src;\n        int  help;\n    } my_options_t;\n\n    my_options_t my_options;\n    memset(&my_options, 0, sizeof(my_options));\n\n    // options specifications\n    struct fuse_opt opt_specs[] = {\n        // pattern: match --src then string\n        // the string value to be written to my_options_t.src\n        { \"--src %s\", offsetof(my_options_t, src)   , 0     },\n        // pattern: match --help\n        // if found, 'true' value to be written to my_options_t.help\n        { \"--help\"  , offsetof(my_options_t, help)  , true  },\n        // end-of-array: all zeroes value\n        { NULL      , 0                             , 0     }\n    };\n\t\n    // parse command line arguments, store matched by 'opt_specs'\n    // options to 'my_options' value and remove them from {argc, argv}\n    fuse_opt_parse(&args, &my_options, opt_specs, NULL);\n\n    if (my_options.help) {\n        show_help_and_exit();\n    }\n\n    if (my_options.src) {\n        open_filesystem(my_options.src);\n    }\n\n    // pass rest options but excluding --src and --help to mount.fuse3 \n    int ret = fuse_main(args.argc, args.argv, &operations, NULL);\n\n    return ret;\n}\n```\n\n"
  },
  {
    "path": "practice/http-curl/README.md",
    "content": "# Протокол HTTP и библиотека cURL\r\n\r\n## Протокол HTTP\r\n\r\n### Общие сведения\r\n\r\nПротокол HTTP используется преимущественно браузерами для загрузки и отправки контента. Кроме того, благодаря своей простоте и универсальности, он часто используется как высокоуровневый протокол клиент-серверного взаимодействия.\r\n\r\nБольшинтсво серверов работают с версией протокола `HTTP/1.1`, который подразумевает взаимодействие в текстовом виде через TCP-сокет. Клиент отправляет на сервер текстовый запрос, который содержит:\r\n * Команду запроса\r\n * Заголовки запроса\r\n * Пустую строку - признак окончания заголовков запроса\r\n * Передаваемые данные, если они подразумеваются\r\n\r\nВ ответ сервер должен отправить:\r\n * Статус обработки запроса\r\n * Заголовки ответа\r\n * Пустую строку - признак окончания заголовков ответа\r\n * Передаваемые данные, если они подразумеваются\r\n\r\nСтандартным портом для `HTTP` является порт 80, для `HTTPS` - порт с номером 443, но это жёстко не регламентировано, и при необходимости номер порта может быть любым.\r\n\r\n### Основные команды и заголовки HTTP\r\n\r\n * `GET` - получить содержимое по указанному URL;\r\n * `HEAD` - получить только метаинформацию (заголовки) по указанному URL, но не содержимое;\r\n * `POST` - отправить данные на сервер и получить ответ.\r\n\r\nКроме основных команд, в протоколе HTTP можно определять произвольные дополнительные команды в текстовом виде (естественно, для этого потребуется поддержка как со стороны сервера, так и клиента). Например, расширение WebDAV протокола HTTP, предназначенное для передачи файлов, дополнительно определяет команды `PUT`, `DELETE`, `MKCOL`, `COPY`, `MOVE`.\r\n\r\nЗаголовки - это строки вида `ключ: значение`, определяющие дополнительную метаинформацию запроса или ответа.\r\n\r\nПо стандарту `HTTP/1.1`, в любом запросе должен быть как минимум один заголовок - `Host`, определяющий имя сервера. Это связано с тем, что с одним IP-адресом, на котором работает HTTP-сервер, может быть связано много доменных имен.\r\n\r\nПолный список заголовков можно посмотреть [в Википедии](https://en.wikipedia.org/wiki/List_of_HTTP_header_fields).\r\n\r\nПример взаимодействия:\r\n```\r\n$ telnet ejudge.atp-fivt.org\r\n$ telnet ejudge.atp-fivt.org 80\r\nTrying 87.251.82.74...\r\nConnected to ejudge.atp-fivt.org.\r\nEscape character is '^]'.\r\nGET / HTTP/1.1\r\nHost: ejudge.atp-fivt.org\r\n\r\nHTTP/1.1 200 OK\r\nServer: nginx/1.14.0 (Ubuntu)\r\nDate: Tue, 23 Apr 2019 21:18:43 GMT\r\nContent-Type: text/html; charset=UTF-8\r\nContent-Length: 4383\r\nConnection: keep-alive\r\nLast-Modified: Mon, 04 Feb 2019 17:01:28 GMT\r\nETag: \"111f-58114719b3ca3\"\r\nAccept-Ranges: bytes\r\n\r\n<html>\r\n  <head>\r\n    <meta charset=\"utf-8\"/>\r\n    <title>АКОС ФИВТ МФТИ</title>\r\n  </head>\r\n...\r\n```\r\n\r\n### Протокол HTTPS\r\n\r\nПротокол HTTP**S** - это реализация протокола HTTP поверх дополнительного уровня SSL, который, в свою очередь работает через TCP-сокет. На уровне SSL осуществляется проверка сертификата сервера и обмен ключами шифрования. После этого - начинается обычное взаимодействие по протоколу HTTP в текстовом виде, но это заимодейтвие передается по сети в зашифрованном виде.\r\n\r\nАналогом `telnet` для работы поверх SSL является инструмент `s_client` из состава OpenSSL:\r\n\r\n```\r\n$ openssl s_client -connect yandex.ru:443\r\n```\r\n\r\n### Утилита cURL\r\n\r\nУниверсальным инструментом для взаимодействия по HTTP в Linux считается [curl](https://curl.haxx.se), которая входит в базовый состав всех дистрибутивов. Работает не только по протоколу HTTP, но и HTTPS.\r\n\r\nОсновные опции `curl`:\r\n * `-v` - отобразить взаимодействие по протоколу HTTP;\r\n * `-X КОМАНДА` - отправить вместо `GET` произвольную текстовую команду в запросе;\r\n * `-H \"Ключ: значение\"` - отправить дополнительный заголовок в запросе; таких опций может быть несколько;\r\n * `--data-binary \"какой-то текст\"` - отправить строку в качестве данных (например, для `POST`);\r\n * `--data-binary @имя_файла` - отправить в качестве данных содержимое указанного файла.\r\n\r\n## Библиотека `libcurl`\r\n\r\nУ утилиты `curl` есть программный API, который можно использовать в качестве библиотеки, не запуская отдельный процесс.\r\n\r\nAPI состоит из двух частей: полнофункциональный асинхронный интерфейс (`multi`), и упрощённый с блокирующим вводом-выводом (`easy`).\r\n\r\nПример использования упрощённого интерфейса:\r\n```\r\n#include <curl/curl.h>\r\n\r\nCURL *curl = curl_easy_init();\r\nif(curl) {\r\n  CURLcode res;\r\n  curl_easy_setopt(curl, CURLOPT_URL, \"http://example.com\");\r\n  res = curl_easy_perform(curl);\r\n  curl_easy_cleanup(curl);\r\n}\r\n```\r\n\r\nЭтот код эквивалентен команде\r\n```\r\n$ curl http://example.com\r\n```\r\n\r\nДополнительные параметры, эквивалентные отдельным опциям команды `curl`, определяются функцией [`curl_easy_setopt`](https://curl.haxx.se/libcurl/c/curl_easy_setopt.html).\r\n\r\nВыполнение HTTP-запроса приводит к записи результата на стандартный поток вывода, но обычно бывает нужно получить данные для дальнейшей обработки.\r\n\r\nЭто делается установкой одной из callback-функций, которая ответственна за вывод:\r\n\r\n```\r\n#include <curl/curl.h>\r\n\r\ntypedef struct {\r\n  char   *data;\r\n  size_t length;\r\n} buffer_t;\r\n\r\nstatic size_t  \r\ncallback_function(\r\n            char *ptr, // буфер с прочитанными данными\r\n            size_t chunk_size, // размер фрагмента данных\r\n            size_t nmemb, // количество фрагментов данных\r\n            void *user_data // произвольные данные пользователя\r\n            )\r\n{\r\n  buffer_t *buffer = user_data;\r\n  size_t total_size = chunk_size * nmemb;\r\n\r\n  // в предположении, что достаточно места\r\n  memcpy(buffer->data, ptr, total_size);\r\n  buffer->length += total_size;\r\n  return total_size;\r\n}            \r\n\r\nint main(int argc, char *argv[]) {\r\n  CURL *curl = curl_easy_init();\r\n  if(curl) {\r\n    CURLcode res;\r\n\r\n    // регистрация callback-функции записи\r\n    curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, callback_function);\r\n\r\n    // указатель &buffer будет передан в callback-функцию\r\n    // параметром void *user_data\r\n    buffer_t buffer;\r\n    buffer.data = calloc(100*1024*1024, 1);\r\n    buffer.length = 0;\r\n    curl_easy_setopt(curl, CURLOPT_WRITEDATA, &buffer);\r\n\r\n    curl_easy_setopt(curl, CURLOPT_URL, \"http://example.com\");\r\n    res = curl_easy_perform(curl);\r\n\r\n    // дальше можно что-то делать с данными,\r\n    // прочитанными в buffer\r\n\r\n    free(buffer.data);\r\n    curl_easy_cleanup(curl);\r\n  }\r\n}\r\n\r\n\r\n```\r\n\r\n"
  },
  {
    "path": "practice/ieee754/README.md",
    "content": "# Представление вещественных чисел\r\n\r\nСуществует два способа представления вещественных чисел: с фиксированным количеством разрядов (fixed-point) под дробную часть, и с переменным числом разрядов (floating-point).\r\n\r\nПредставление чисел с фиксированной точкой часто используется там, где требуется гарантированная точность до определенного разряда, например, в финансовой сфере.\r\n\r\nПредставление в формате с плавающей точкой является более универсальным, и все современные архитектуры процессоров работают именно в этом формате.\r\n\r\n\r\n## Числа с плавающей точкой в формате IEE754\r\n\r\nДва основных типа вещественных с плавающей точкой, которые определены стандартом языка Си, - это `float` (используется 4 байта для хранения) и `double` (используется 8 байт).\r\n\r\nСамый старший бит в представлении числа - это признак отрицательного значения. Далее, по старшинству бит, хранится значения *смещенной экспоненциальной* части (8 бит для `float` или 11 бит для `double`), а затем - значение *мантиссы* (23 или 52 бит).\r\n\r\nСмещение экспоненциальной части необходимо для того, чтобы можно было в таком представлении хранить значения с отрицательной экспонентой. Смещение для типа `float` равно `127`, для типа `double` - `1023`.\r\n\r\nТаким образом, итоговое значение может быть получено как:\r\n\r\n```\r\nValue = (-1)^S * 2^(E-B) * ( 1 + M / (2^M_bits - 1) )\r\n```\r\nгде `S` - бит знака, `E` - значение смещенной экспоненты, `B` - смещение (127 или 1023), а `M` - значение мантиссы, `M_bits` - количество бит в экспоненте.\r\n\r\n\r\n## Как получить отдельные биты вещественного числа\r\n\r\nПоразрядные операции относятся к целочисленной арифметике, и не предусмотрены для типов `float` и `double`. Таким образом, нужно сохранить вещественное число в памяти, и затем прочитать его, интерпретируя как целое число. В случае с языком C++ для этого предназначен оператор `reinterpret_cast`. Для языка Си есть два способа: использовать аналог `reinterpret_cast` - приведение указателей, либо использовать тип `union`.\r\n\r\n### Приведение указателей\r\n```\r\n// У нас есть некоторое целое вещественное число, которое хранится в памяти\r\ndouble a = 3.14159;\r\n\r\n// Получаем указатель на это число\r\ndouble* a_ptr_as_double = &a;\r\n\r\n// Теряем информацию о типе, приведением его к типу void*\r\nvoid* a_ptr_as_void = a_ptr_as_void;\r\n\r\n// Указатель void* в языке Си можно присваивать любому указателю\r\nuint64_t* a_ptr_as_uint = a_ptr_as_void;\r\n\r\n// Ну а дальше просто разыменовываем указатель\r\nuint64_t b = *a_as_uint;\r\n```\r\n\r\n### Использование типа `union`\r\n\r\nТип `union` - это тип данных, который синтаксически очень похож на тип `struct`, то есть там можно перечислить несколько именованных полей, но концептуально - это совершенно разные типы данных! Если в структуре или классе, для хранения каждого поля для предусмотрено отдельное место в памяти, то для `union` этого не происходит, и все поля накладываются друг на друга при размещении в памяти.\r\n\r\nОбычно тип `union` используется в качестве вариантного типа данных (в С++ начиная с 17-го стандарта для этого предусмотрен `std::variant`), но в качестве побочного эффекта - его удобно использовать приведения типов в стиле `reinterpret_cast`, не используя при этом указатели.\r\n\r\n```\r\n// У нас есть некоторое целое вещественное число, которое хранится в памяти\r\ndouble a = 3.14159;\r\n\r\n// Используем тип union\r\ntypedef union {\r\n    double     real_value;\r\n    uint64_t   uint_value;\r\n} real_or_uint;\r\n\r\nreal_or_uint u;\r\nu.real_value = a;\r\nuint64_t b = u.uint_value;\r\n```\r\n\r\n## Специальные значения в формате IEEE754\r\n\r\n * Бесконечность: `E=0xFF...FF`, `M=0`\r\n * Минус ноль (результат деления 1 на минус бесконечность): `S=1`, `E=0`, `M=0`\r\n * NaN (Not-a-Number): `S` - любое, `E=0xFF...FF`, `M <> 0`\r\n\r\nНекоторые процессоры, например архитектуры x86, поддерживают расширение стандарта, позволяющее более эффективно представлять множество чисел, значения которых близко к нулю. Такие числа называются *денормализованными*.\r\n\r\nПризнаком денормализованного числа является значение смещенной экспоненты `E=0`. В этом случае, численное значение получается следующим образом:\r\n\r\n```\r\nValue = (-1)^S * ( M / (2^M_bits - 1) )\r\n```\r\n\r\n### Значения Not-a-Number\r\n\r\nНекоторые процессоры, например Intel x86, различают два вида чисел `NaN` - невалидное значение.\r\n\r\n\r\n#### sNaN - Signaling NaN\r\n\r\nЗначения `sNaN` возникают при выполнении операций, которые сигнализируют об ошибке на уровне прерывания процессора. Например, деление на `0`. Обычно, чтобы получить такие значения, необходимо собирать программу с опцией `-fno-signaling-nans`. Более подробно - см. [FloatingPointMath - GCC Wiki](https://gcc.gnu.org/wiki/FloatingPointMath)\r\n\r\nПример битовой маски для типа `double` на x86_64, определяющей значение `sNaN`:\r\n\r\n```\r\nНомера битов    6         5         4         3         2         1         0\r\n             3210987654321098765432109876543210987654321098765432109876543210\r\n             ----------------------------------------------------------------\r\nЗначения     0111111111110100000000000000000000000000000000000000000000000000\r\n             ----------------------------------------------------------------\r\nРегион       SEEEEEEEEEEEMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n```\r\n\r\n\r\n####  qNaN - Quiet NaN\n\nВ отличии от `sNaN`, значения Quiet NaN, которые получаются в результате вычислений, не приводят к прерыванию процессора и вызове обработчика исплючительной ситуации.\n\nПримером является попытка сложить `+inf` и `-inf`.\n\nПример битовой маски для типа `double` на x86_64, определяющей значение `qNaN`:\n\n```\nНомера битов    6         5         4         3         2         1         0\n             3210987654321098765432109876543210987654321098765432109876543210\n             ----------------------------------------------------------------\nЗначения     0111111111111000000000000000000000000000000000000000000000000000\n             ----------------------------------------------------------------\nРегион       SEEEEEEEEEEEMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\n```\n"
  },
  {
    "path": "practice/integers/README.md",
    "content": "# Целочисленная арифметика\n\n## Целочисленные типы данных\n\nМинимально адресуемым размером данных является, какправило, один байт (8 бит). Как правило - это значит, что не всегда, и бывают разные экзотические архитектуры, где \"байт\" - это 9 бит (PDP-10), или специализированные сигнальные процессоры с минимально адресуемым размером данных 16 бит (TMS32F28xx).\n\nПо стандарту языка Си определена константа `CHAR_BIT` (в заголовочном файле `<limits.h>`), для которой гарантируется, что `CHAR_BIT >= 8`.\n\nТип данных, представляющий один байт, исторически называется \"символ\" - `char`, который содержит ровно `CHAR_BIT` количество бит.\n\nЗнаковость типа `char` по стандарту не определена. Для\nархитектуры x86 это знаковый тип данных, а, например,\nдля ARM - беззнаковый. Опции компилятора gcc `-fsigned-char`\nи `-funsigned-char` определяют это поведение.\n\nДля остальных целочисленных типов данных: `short`, `int`,\n`long`, `long long`, стандарт языка Си определяет минимальную разрядность:\n\n| Тип данных | Разрядность                    |\n| -----------| -------------------------------|\n| `short`    | не менее 16 бит                |\n| `int`      | не менее 16 бит, обычно 32 бит |\n| `long`     | не менее 32 бит                |\n| `long long`| не менее 64 бит, обычно 64 бит |\n\nТаким образом, полагаться на количество разрядов в базовых\nтипах данных нельзя, и это нужно проверять с помощью\nоператора `sizeof`, который возвращает \"количество байт\", то\nесть, в большинстве случает - сколько блоков размером\n`CHAR_BIT` помещается в типе данных.\n\nС особой осторожностью нужно относиться к типу данных `long`: на 64-разрядной системе Unix он является 64-битным, а, например, на 64-битной Windows - 32-битным. Поэтому, во избежание путаницы, использовать этот тип данных запрещено.\n\n## Знаковые и беззнаковые типы данных\n\nПеред целочисленными типами данных могут стоять модификаторы\n`unsigned` или `signed`, которые указывают допустимость отрицательных чисел.\n\nДля знаковых типов, старший бит определяет знак числа: значение `1` является признаком отрицательности.\n\n Способ внутреннего представления отрицательных чисел не регламентирован  [стандартом языка Си](http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570), однако все современные компьютеры используют обратный дополнительный код. Более того, п.6.3.1.3.2 стандарта языка Си определяет способ приведения типов от знакового к беззнаковому таким способом, которые приводит к кодированию обратным дополнительным кодом.\n\nТаким образом, значение `-1` представляется как целое число,\nвсе биты которого равны единице.\n\nС точки зрения низкоуровневого программирования, и языка Си в частности, знаковость типов данных определяет только способ применения различных операций.\n\n## Типы данных с фиксированным количеством бит\n\nВ заколовочных файлах файле `<stdint.h>` (для Си99+) и\n`<cstdint>` (для C++11 и новее) определены типы данных, для\nкоторых гарантируется фиксированное количесвто разрядов:\n`int8_t`, `int16_t`, `int32_t`, `int64_t`, - для знаковых, и\n`uint8_t`, `uint16_t`, `uint32_t`, `uint64_t` - для беззнаковых.\n\n# Переполнение\n\nСитуация целочисленного переполнения возникает, когда тип данных результата не имеет достаточно разрядов для того, чтобы хранить итоговый результат. Например, при сложении беззнаковых 8-разрядных целых чисел 255 и 1, получается результат, который не может быть представим 8-разрядным значением.\n\nДля **беззнаковых чисел** ситуация переполнения является штатной, и эквивалентна операции \"сложение по модулю\".\n\nДля **знаковых** типов данных - приводит к ситуации *неопределенного поведения* (Undefined Behaviour). В корректных программах такие ситуации встречаться не могут.\n\nПример:\n```\nint some_func(int x) {\n    return x+1 > x;\n}\n```\n\nС точки зрения здравого смысла, такая программа должна всегда возвращать значение `1` (или `true`), поскольку мы знаем, что `x+1` всегда больше, чем `x`. Компилятор может использовать этот факт для оптимизации кода, и всегда возвращать истинное значение. Таким образом, поведение программы зависит от того, какие опции оптимизации были использованы.\n\n## Контроль неопределенного поведения\n\nСвежие версии компиляторов `clang` и `gcc` (начиная с 6-й версии) умеют контролировать ситуации неопределенного поведения.\n\nМожно включить генерацию *управляемого* кода программы, который использует дополнительные проверки во время выполнения. Естественно, это происходит ценой некоторого снижения производительности.\n\nТакие инструменты называются *ревизорами* (sanitizers), предназначенными для разных целей.\n\nДля включения ревизора, контролирующего ситуацию неопределенного поведения, используется опция `-fsanitize=undefined`.\n\n## Контроль переполнения, независимо от знаковости\n\nЦелочисленное переполнение означает перенос старшего разряда, и многие процессоры, включая семейство x86, позволяют это диагностировать. Стандартами языков Си и C++ эта возможность не предусмотрена, однако компилятор gcc (начиная с 5-й версии) предоставляет **нестандартные** встроенные функции для выполнения операций с контролем переполнения.\n\n```\n// Операция сложения\nbool __builtin_sadd_overflow (int a, int b, int *res);\nbool __builtin_saddll_overflow (long long int a, long long int b, long long int *res);\nbool __builtin_uadd_overflow (unsigned int a, unsigned int b, unsigned int *res);\nbool __builtin_uaddl_overflow (unsigned long int a, unsigned long int b, unsigned long int *res);\nbool __builtin_uaddll_overflow (unsigned long long int a, unsigned long long int b, unsigned long long int *res);\n\n// Операция вычитания\nbool __builtin_ssub_overflow (int a, int b, int *res)\nbool __builtin_ssubl_overflow (long int a, long int b, long int *res)\nbool __builtin_ssubll_overflow (long long int a, long long int b, long long int *res)\nbool __builtin_usub_overflow (unsigned int a, unsigned int b, unsigned int *res)\nbool __builtin_usubl_overflow (unsigned long int a, unsigned long int b, unsigned long int *res)\nbool __builtin_usubll_overflow (unsigned long long int a, unsigned long long int b, unsigned long long int *res)\n\n// Операция умножения\nbool __builtin_smul_overflow (int a, int b, int *res)\nbool __builtin_smull_overflow (long int a, long int b, long int *res)\nbool __builtin_smulll_overflow (long long int a, long long int b, long long int *res)\nbool __builtin_umul_overflow (unsigned int a, unsigned int b, unsigned int *res)\nbool __builtin_umull_overflow (unsigned long int a, unsigned long int b, unsigned long int *res)\nbool __builtin_umulll_overflow (unsigned long long int a, unsigned long long int b, unsigned long long int *res)\n\n```\n"
  },
  {
    "path": "practice/linux_basics/README.md",
    "content": "# Инструменты разработки на Си/С++\n\nПредполагается, что на уровне пользователя вы знаете, что такое Linux. Если это не так, то необходимо прочитать [основы работы в Linux](intro.md).\n\n## Компиляторы gcc и clang\n\nВ стандартную поставку современных UNIX-систем входит один из компиляторов: либо `gcc`, либо `clang`. В случае с Linux, по умолчанию обычно используется `gcc`, а в BSD-системах - `clang`. Далее будет описана работа с компилятором `gcc`, имея ввиду, что работа с `clang` ничем принципиально не отличается: у обоих компиляторов много общего, в том числе опции командной строки.\n\nКроме того, существует команда `cc`, которая является символической ссылкой на используемый по умолчанию компилятор языка Си (`gcc` или `clang`), и команда `c++`, - символическая ссылка на используемый по умолчанию компилятор для C++.\n\nРассмотрим простейшую программу на языке C++:\n```\n// файл hello.cpp\n#include <iostream>\n\nint main() {\n  std::cout << \"Hello, World!\" << std::endl;\n  return 0;\n}\n```\n\nСкомпилировать эту программу можно с помощью команды:\n```\n> c++ -o program.jpg hello.cpp\n```\n\nОпция компилятора `-o ИМЯ_ФАЙЛА` указывает имя выходного файла, который нужно создать. По умолчанию используется имя `a.out`. Обратите внимание, что файл `program.jpg` является обычным выполняемым файлом!\n\n### Стадии сборки программы на Си или C++\n\nПри выполнении команды `c++ -o program.jpg hello.cpp` выполняется достаточно сложная цепочка действий:\n\n 1. Выполняется *препроцессинг* текстового файла `hello.cpp`. На этом этапе обрабатываются *директивы препроцессора* (которые начинаются с символа `#`), и получается новый текст программы. Если запустить компилятор с опцией `-E`, то будет выполнен только этот шаг, и на стандартный поток вывода будет выведен преобразованный текст программы.\n\n 2. Выполняется *трансляция* одного или нескольких текстов на Си или C++ в объектные модули, содержащие машинный код. Если указать опцию `-c`, то на этом сборка программы будет приостановлена, и будут созданы объектные файлы с суффиксом `.o`. Объектные файлы содержат *бинарный* исполняемый код, которому в точности соотвествует некоторый текст на языке ассемблера. Этот текст можно получить с помощью опции `-S`, - в этом случае, вместо объектных файлов будут созданы текстовые файлы с суффиксом `.s`.\n\n 3. Компоновка одного или нескольких объектных файлов в исполняемый файл, и связываение его со стандартной библиотекой Си/С++ (ну и другими библиотеками, если требуется). Для выполнения компоновки компилятор вызывает стороннюю программу `ld`.\n\n### Программы на Си v.s. программы на C++\n\nКомпилятор `gcc` имеет опцию `-x ЯЗЫК`, для указания языка исходного текста программы: Си (`c`), C++ (`c++`) или Фортран (`fortran`). По умолчанию, язык исходного текста определяется в соответствии с именем файла: `.c`, - это программы на языке Си, а файлы, оканчивающиеся на `.cc`, `.cpp` или `.cxx`, - это тексты на языке C++. Таким образом, имя файла является существенным.\n\nЭто относится к стадиям препроцессинга и трансляция, но может вызвать проблемы на стадии компоновки. Например, используя команду `gcc` вместо `g++` (или `cc` вместо `c++`), можно успешно скомпилировать исходный текст программы на C++, но при этом возникнут ошибки на стадии связывания, поскольку компоновщику `ld` будут переданы опции, подразумевающие связывание только со стандартной библиотекой Си, но не C++. Поэтому, при сборке программ на C++ нужно использовать команду `c++` или `g++`.\n\n### Указание стандартов\n\nОпция компилятора `-std=ИМЯ` позволяет явным образом указать используемый стандарт языка. Рекомендуется явным образом указывать используемый стандарт, поскольку поведение по умолчанию зависит от используемого дистрибутива Linux. Допустимые имена:\n * `c89`, `c99`, `c11`, `gnu99`, `gnu11` для языка Си;\n * `c++03`, `c++11`, `c++14`, `c++17`, `gnu++11`, `gnu++14`, `gnu++17` для языка C++.\n\nДвузначное число в имени стандарта указывает на его год. Если в имени стандарта присутствует `gnu`, то подразумеваются GNU-расширения компилятора, специфичные для UNIX-подобных систем, и кроме того, считается определенным макрос `#define _DEFAULT_SOURCE`, который в некоторых случаях меняет поведение отдельных функций стандартной библиотеки.\n\nВ дальнейшем мы будем ориентироваться на стандарт `c11`, а в некоторых задачах, где будет про это явно указано - его расширением `gnu11`.\n\n### Директивы препроцессора\n\nЧерез опции командной строки можно определять константы, которые обрабатывается на стадии препроцессинга текстового файла. Для этого используется опция `-DИМЯ=ЗНАЧЕНИЕ`. В процессе компиляции эти имена эквивалентны конструкциям `#define`.\n\n```\n#ifdef PREDEFINED_CONSTANT\nstatic const char * StringConstant = PREDEFINED_CONSTANT;\n#else\nstatic const char * StringConstant = \"Default value\";\n#endif\n```\n\nДанный код может быть скомпилирован с опцией, которая доопределит `PREDEFINED_CONSTANT`:\n\n```\n> cc -DPREDEFINED_CONSTANT='\"Hello, World!\"'\n```\n\nОбратите внимание, что если необходимо использовать символы пробелов или кавычек, то их необходимо либо экранировать, либо заключить аргумент в одинарные кавычки.\n\n## Объектные файлы, библиотеки и исполняемые файлы\n\n### Модуль ctypes интерпретатора Python\n\nРассмотрим программу на языке Си:\n```\n/* my-first-program.c */\n#include <stdio.h>\n\nstatic void do_something()\n{\n    printf(\"Hello, World!\\n\");\n}\n\nextern void do_something_else(int value)\n{\n    printf(\"Number is %d\\n\", value);\n}\n\nint main()\n{\n    do_something();\n}\n```\n\nСкомпилируем эту программу в объектный файл, а затем - получим из него: (1) выполняемую программу; (2) разделяемую библиотеку. Обратите внимание на опцию `-fPIC`, предназначенную для генерации позиционно-независимого кода, о чем будет рассказано на одном из последующих семинарах.\n\n```\n> gcc -c -fPIC my-first-program.c\n> gcc -o program.jpg my-first-program.o\n> gcc -shared -o library.so my-first-program.o\n```\n\nВ результате мы получим программу `program.jpg`, которая\nвыводит на экран строку `Hello, World!`, и *библиотеку* с именем `library.so`, которую можно использовать как из Си/C++ программы, так и динамически подгрузить для использования интерпретатором Python:\n\n```\n> python3\nPython 3.6.5 (default, Mar 31 2018, 19:45:04) [GCC] on linux\nType \"help\", \"copyright\", \"credits\" or \"license\" for more information.\n>>> from ctypes import cdll\n>>> lib = cdll.LoadLibrary(\"./library.so\")\n>>> lib.do_something_else(123)\n>>> retval = lib.do_something_else(123)\nNumber is 123\n>>> print(retval)\n14\n```\nОбратите внимание, что результатом работы функции `do_something_else` является какое-то загадочное число `14` (возможно, будет какое-то другое при попытке воспроизвети этот эксперимент), хотя функция возвращает `void`.\n\nПричина заключается в том, что разделяемые библиотеки хранят только **имена** функций, но не их сигнатуры (типы параметров и возвращаемого значения).\n\nПопытка вызвать функцию `do_something` не увенчается успехом:\n```\n>>> lib.do_something()\nTraceback (most recent call last):\n  File \"<stdin>\", line 1, in <module>\n  File \"/usr/lib64/python3.6/ctypes/__init__.py\", line 361, in __getattr__\n    func = self.__getitem__(name)\n  File \"/usr/lib64/python3.6/ctypes/__init__.py\", line 366, in __getitem__\n    func = self._FuncPtr((name_or_ordinal, self))\nAttributeError: ./library.so: undefined symbol: do_something\n```\n\nВ этом случае имя `do_something` не найдено, поскольку в исходном тексте на языке Си модификатор `static` перед именем функции явно запрещает использование функции где-либо вне текущего исходного текста.\n\n### Просмотр таблицы символов\n\nДля исследования объектных файлов, в том числе и скомпонованных, используется утилита `objdump`.\n\nОпция `--syms` или `-t` отображает отдельные секции исполняемого объектного файла, которым присвоены имена - *символы*.\n\nНекоторые имена имеют пометку `*UND*`, - это означает, что имя используется в объектном файле, но его расположение неизвестно. Задача компоновщика состоит как раз в том, чтобы найти требуемые имена в разных объектных файлах или динамических библиотеках, а затем - подставить правильный адрес.\n\nНекоторые символы помечены как глобальные (символ `g` во втором столбце вывода), а некоторые - как локальные (символ `l`). Те символы, которые не являются глобальными, считаются *не экспортируемыми*, то есть (теоретически) не должны быть доступны извне.\n\n## Автоматизация сборки с помощью Makefile\n\nПри разработке больших проектов, состоящих из нескольких файлов, возникает необходимость в автоматизации процесса сборки. Автоматизацией сборки занимается утилита `make`, которая использует описание сборки `Makefile`, специфичный для целевой и операционной системы и заведомо известной конфигурации. В простых случаях эти описания можно написать самостоятельно, но в более сложных, когда файлов много, или процесс сборки зависит от параметров, используется предварительная генерация файла `Makefile` с помощью более высокоуровневого инструмента, например `cmake`.\n\nОбщий формат минимального `Makefile`:\n\n```\n# могут быть комментарии\nимя_цели: список зависимостей\n\tкоманда 1\n\tкоманда 2\n\t...\n\tкоманда N\n```\n\nОбратите внимание, что отступы перед именами команд - это строго символы табуляции, а не последовательности пробелов. Текстовые редакторы, ориентированные на написание исходного кода, обычно при работе с `Makefile` игнорируют пользовательские настройки, связанные с отступами, и используют символ табуляции.\n\nЗависимостями могут быть определенные файлы исходных текстов, либо файлы промежуточного этапа компиляции, которые сами являются другими целям сборки. При запуске утилиты `make` проверяются даты модификации файлов, перечисленных в списке зависимостей, и выполняются команды только для тех целей, которые действительно требуют пересборки.\n\nЦелей может быть несколько, причем имена целей не обязаны совпадать с именами генерируемых ими файлов. При запуске `make` без параметров выполняется сборка той цели, которая описана самой первой, поэтому часто выделяют отдельную цель под названием `first`. Кроме того, предусматривают отдельную цель под названием `clean`, которая должна оставить после себя только конечный результат, удалив все временные файлы.\n\nКоманда `make` может использовать разные компиляторы и дополнительные параметры, которые можно переопределять через аргументы командной строки. В самом файле эти параметры используются как переменные с синтаксисом `$(имя_переменной)`.\n\nПример `Makefile` для программы и бибилиотеки, которые были приведены выше:\n\n```\nCC=gcc        # компилятор по умолчанию\nLINK=gcc      # линковщик по умолчанию\nCFLAGS=-fPIC  # флаги компилятора по умолчанию\n\n# первая цель для сборки\nfirst: all\n\n# виртуальная цель для сборки всего\nall: library.so program.jpg\n\n# библиотека library.so зависит от my-first-program.o\nlibrary.so: my-first-program.o\n\t$(LINK) -shared -o library.so my-first-program.o\n\n# программа тоже зависит от объектного файла\nprogram.jpg: my-first-program.o\n\t$(LINK) -o program.jpg my-first-program.o\n\n# правило для сборки объектного файла\nmy-first-program.o: my-first-program.c\n\t$(CC) -c $(CFLAGS) my-first-program.c\n\t\n# правило для очистки проекта\nclean:\n\t# обратите внимание на || true\n\t#  - подавляет ошибку, если файлы не существуют\n\trm -rf *.o || true\n\t\n# правило для очистки всего\ndistclean: clean\n\trm -rf program.jpg library.so || true\n\t\n```\n\nЕсли требуется во время сборки определить какие-либо параметры окружения, то можно вызывать внешние команды, и использовать их вывод в качестве переменных. Для этого используется конструкция `ПЕРЕМЕННАЯ=$(shell КОМАНДА)`. Кроме того, все реализации `make` поддерживают примитивные конструкции для проверки условий `ifeq (A, B) ... endif`.\n\n```\n# пример сборки с учетом имени операционной системы\n\n# команда uname возвращает имя операционной системы\nUNAME=$(shell uname -s)\nifeq ($(UNAME), Darwin)\n\tOS=APPLE\nendif\nifeq ($(UNAME), Linux)\n\tOS=LINUX\nendif\n\nplatform_specific.o: platform_specific.c\n\t$(CC) -c $(CFLAGS) -D__$(OS)__ platform_specific.c\n```\n\n```\n// platform_specific.c\n#ifdef __APPLE__\n// эта часть будет компилироваться только под Mac\n#endif\n#ifdef __LINUX__\n// эта часть будет компилироваться только под Linux\n#endif\n```\n\n\n\n## Проверка стиля кода\n\nПеред публикацией кода, не важно, куда - в репозиторий или при отправке решений, необходимо следовать единому стилю кода, про который есть договоренности с теми, кто этот код будет изучать. Для автоматизации этого процесса используется утилита `clang-format`.\n\nДля языков семейства Си нет единого стандарта, как должен быть отформатирован код. Есть несколько сложившихся в Open Source стандартов, которые задаются опцией `--style=ИМЯ`: `LLVM`, `GNU`, `Google`, `Chromium`, `Microsoft`, `Mozilla`, `WebKit`. Если указать опцию `--style=file`, то стандарт оформления будет использован из локального файла `.clang-format` (имя начинается с символа точки). Файл `.clang-format` может располагаться не только в одном  каталоге с исходным файлом, но и в любом каталоге выше по иерархии, - в этом случае будет использован первый найденный файл.\n\nПо умолчанию утилита `clang-format` выводит преобразованный исходный файл на стандартный поток вывода, опция `-i` (inplace) указывает, что необходимо перезаписать исходный файл.\n\nНекоторые тектовые редакторы и среды разработки позволяют интегрировать `clang-format` для переопределения стиля форматирования текста.\n\nДля редактора Emacs необходимо установить  пакет `clang-format`, и дописать в конфигурационный файл `~/.emacs` строчки:\n```\n(require 'clang-format)          ;; загрузка пакета clang-format\n(setq clang-format-style \"file\") ;; опция --style=file\n(global-auto-revert-mode t)      ;; автозагрузка изменений файла\n```\nПосле этого код можо переформатировать командой `cla-b`.\n\nСреда разработки CLion начиная с версии 2021.2 определяет наличие файла `.clang-format`, и позволяет подключить переопределение стиля кода.\n\nДля этого нужно в правом нижнем углу окна кликнуть на количество пробелов, и выбрать из всплывающего меню элемент \"ClangFormat\".\n\n<img src=\"clion-clang-format.png\" alt=\"clion-clang-format\"  />\n\nПереформатирование кода в CLion выполняется из меню Code > Reformat Code.\n\n## Использование отладчика\n\nЕсли скомпилировать программу с опцией `-g`, то размер программы увеличится, поскольку в ней появляются дополнительные секции, которые содержат *отладочную информацию*.\n\nОтладочная информация содержит сведения о соответствии отдельных фрагментов программы исходным текстам, и включает в себя номера строк, имена исходных файлов, имена типов, функций и переменных.\n\nЭти информация используется только отладчиком, и почти никак не влияет на поведение программы. Таким образом, отладочную информацию можно совмещать с оптимизацией, в том числе достаточно агрессивной (опция компилятора `-O3`).\n\nДля запуска программы под отладчиком используется команда `gdb`, в качестве аргумента к которой указывается имя выполняемого файла или команды.\n\nОсновные команды `gdb`:\n* `run` - запуск программы, после `run` можно указать аргументы;\n* `break` - установить точку останова, параметрами этой команды может имя функции или пара `ИМЯ_ФАЙЛА:НОМЕР_СТРОКИ`;\n* `ni`, `si` - шаг через строку или шаг внутрь функции соответственно;\n* `return` - выйти из текущей функции наверх;\n* `continue` - продолжить выполнение до следующей точки останова или исключительной ситуации;\n* `print` - вывести значение переменной из текущего контекста выполнения;\n* `layout src|regs|split` - переключение вида консольного отладчика;\n* `focus src|regs` - переключениние фокуса клавиш стрелок между разными окнами.\n\nВзаимодействие с отладчиком производится в режиме командной строки. Различные интегрированные среды разработки (CLion, CodeBlocks, QtCreator) являются всего лишь графической оболочкой, использующей именно этот отладчик, и визуализируя взаимодействие с ним.\n\nБолее подробный список команд можно посмотреть в [CheatSheet](https://www.cheatography.com/fristle/cheat-sheets/closed-source-debugging-with-gdb/).\n\n### Удаленная отладка\n\nОтладчик `gdb` может запускаться без интерфейса, что удобно в следующих случаях:\n\n* явное разделение ввода-вывода и команд отладчика;\n* запуск программы на удаленном компьютере через SSH, и управление отладчиком с минимальными задержками на медленном соединении;\n* использование GUI для управления отладчиком.\n\nДля запуска программы под удаленным отладчиком используется команда `gdbserver`:\n\n```\n> gdbserver localhost:12345 ./my_program ARG1 ARG2 <input >output\n```\n\nПервым параметром `gdbserver` указывается `ХОСТ:ПОРТ`, где `localhost` означает имя компьютера в сети (его может и не быть, но имя `localhost` определено всегда), а номер порта должен быть в диапазоне от 1000 до 65535, и порт должен быть не занят чем-то еще. \n\nПосле запуска `gdbserver` программа НЕ запускается, а только загружается отладчиком, и сразу же переходит в режим паузы.\n\nДля подключения к серверу отладки нужно запустить отладчик `gdb` и выполнить команду:\n\n```\n(gdb) target remote localhost:12345\n```\n\nДля того, чтобы лишний раз не передавать бинарные файлы с отладочной информацией по сети, их можно предварительно загрузить из локальной копии с помощью команды `file`:\n\n```\n(gdb) file ./my_program\n```\n\nПосле того, как установлено подключение к `gdbserver`, программа все еще не выполняется и находится в режиме паузы, и запустить ее на выполнение можно с помощью команды `continue`.\n\nВ случае использования другого компьютера, а не локального хоста, дополнительный порт часто бывает не доступен, например он закрыт файрволом. В этом случае можно (и даже нужно по соображениям безопасности) использовать утилиту `ssh` для туннелирования дополнительных портов:\n\n```\n# подключаемся по ssh к remote-server\n# удаленный порт 12345 будет доступен как локальный порт 56789\nlocal-pc> ssh -L 56789:12345 user@remote-server \n\n# запускаем gdbserver на порту 12345\nremote-server> gdbserver localhost:12345 ./my_program\n```\n\n```\n# запускаем интерфейс gdb на локальном компьютере\nlocal-pc> gdb\n\n# подключаемся к локальному порту 56789\n(gdb) target remote localhost:56789\n```\n\n### Автоматизация рутинных действий .gdbinit\n\nЧасто требуется запускать отладчик `gdb` многократно, например после внесения каких-либо изменений. Для того, чтобы не вводить команды каждый раз при запуске, можно положить рядом с программой текстовый файл `.gdbinit`, который будет выполнять команды при запуске отладчика:\n\n```\n# файл .gdbinit рядом с программой\n\n# загружаем файл с программой\nfile my_program\n# ставим точку останова в функцию main\nbreak main\n# переключаем интерфейс в отображение исходников\nlayout src\n# подключаемся к удаленному серверу :12345\ntarget remote localhost:12345\n# запускаем программу до первой точки останова\ncontinue\n```\n\nПо соображениям безопасности, `gdb` не будет выполнять произвольные скрипты, которые лежат где угодно. Для этого необходимо указать отладчику каталог, который считается надежным, и тем самым сделать исключение. Для этого нужно дописать строчки в файл `~/.gdbinit`:\n\n```\n# разрешить загрузку локальных скриптов .gdbinit\n# из каталога ~/i-love-akos/ и всех его подкаталогов\nset auto-load safe-path ~/i-love-akos/\n```\n\n\n\n"
  },
  {
    "path": "practice/linux_basics/cmake.md",
    "content": "# Система сборки `cmake`\n\nИспользование сторонних библиотек усложняет процесс воспроизводимости сборки. В случае, когда целевая операционная система одна, это не доставляет особых проблем и достаточно простого `Makefile`, но если предполагается разработка кросс-платформенного продукта, то возникают неоднозначности:\n * какой компилятор используется для сборки (gcc, clang, cl.exe);\n * расположение include-файлов библиотек для компиляции;\n * рссположение файлов библиотек для компоновки.\n\nПо этой причине часто практикуется не распространение `Makefile`, написанного для конкретного стека инструментов, а его генерация по декларативному описанию в процессе сборки: `./configure`, `qmake`, или `cmake`.\n\nНаиболее гибкой системой сборки, и при этом относительно простой, является [CMake](https://cmake.org/cmake/help/v3.2/), которая реализована с поддержкой не только UNIX-подобных систем, но и Windows.\n\n## Проект CMake и его сборка\n\nОписание проекта находится в файле `CMakeLists.txt` и имеет примерно следующий вид:\n\n```cmake\n# признак того, что это файл для cmake\n# номер версии - это минимально требуемый для сборки проекта\n# не стоит злоупотребять указанием самой свежей версии\n# CMake, поскольку в консервативных Linux-дистрибутивах\n# может быть что-то более старое\ncmake_minimum_required(VERSION 3.2)\n\n# имя проекта - не обязательно, обычно используется IDE\nproject(my_great_project)\n\n# команда `set` устанавливает значение переменной\n# некоторые переменные (их имена начинаются с CMAKE_)\n# имеют специальное значение\n\n# дополнительные опции компилятора Си\nset(CMAKE_C_FLAGS \"-std=gnu11\")\n\n# дополнительные опции компилятора C++\nset(CMAKE_CXX_FLAGS \"-std=gnu14\")\n\n# в переменной SOURCES будет храниться список файлов;\n# если файлов не много, то можно этого не делать,\n# но некоторым IDE это требуется для навигации по проекту\nset(SOURCES\n  file1.c\n  file2.cpp\n  file3.cpp\n)\n\n# добавление цели для сборки - бинарного файла;\n# синтаксис ${...} означает использование значения\n# переменной, которая в данном примере будет раскрыта\n# в список файлов, из которых собирается программа\nadd_executable(my_cool_program ${SOURCES})\n```\n\nДля сборки CMake-проекта необходимо выполнить две стадии:\n 1. Сгенерировать `Makefile`из `CMakeLists.txt`\n 2. Собрать проект обычнычным инструментом `make`.\n\nОбычно в процессе генерации `Makefile` и при сборке проекта создается много временных файлов. По этой причине сборку принято проводить в отдельном каталоге, - чтобы не засорять каталог с исходными текстами.\n\n```bash\n$ mkdir build     # создаем каталог для сборки\n$ cd build        # переходим в него\n$ cmake ../       # генерируем Makefile\n                  # аргумент cmake - это каталог, который\n                  # содержит файл CMakeLists.txt\n$ make            # запуск компиляции                   \n```\n\n## Использование сторонних библиотек, для который есть готовое описание CMake\n\nДля многих OpenSource библиотек в стандартной поставке CMake уже готовы модули поддержки, которые выполняют поиск библиотеки. В случае c UNIX этот поиск осуществляется с помощью запуска команд конфигурации, либо проверки различных вариантов написания имен файлов в `/usr/include` и `/usr/lib`. Для Windows просматривается системный реестр.\n\nСписок поддерживаемых библиотек можно найти в поставке CMake, для Linux это может быть каталог (в разных дистрибутивах они разные) `/usr/share/cmake/Modules`. Все файлы модулей имеют название `FindИМЯБИБЛИОТЕКИ.cmake`.\n\nПодключение библиотеки, которая поддерживается \"из коробки\", осуществляется с помощью команды `find_package`. В случае, если необходимые файлы присутствуют, то определяются переменные:\n\n* `ИМЯБИБЛИОТЕКИ_FOUND` - переменная, значение которой устанавливается в `1`, если библиотека не отмечена как `REQUIRED`;\n* `ИМЯБИБЛИОТЕКИ_INCLUDE_DIRS`  - список дополнительных каталогов, в которых нужно искать заголовочные файлы (опции компилятора `-I...`);\n*  `ИМЯБИБЛИОТЕКИ_LIBRARIES` - список дополнительных библиотек и каталоги к ним (опции компилятора `-l...` и `-L...`);\n\nПример для curl:\n```cmake\n# найти библиотеку CURL; опция REQUIRED означает,\n# что библиотека является обязательной для сборки проекта,\n# и если необходимые файлы не будут найдены, cmake\n# завершит работу с ошибкой\nfind_package(CURL REQUIRED)\n\nadd_executable(my_cool_program ${SOURCES})\n\n# добавляет в список каталогов для цели my_cool_program, \n# которые превратятся в опции -I компилятора для всех \n# каталоги, которые перечислены в переменной CURL_INCLUDE_DIRECTORIES\ntarget_include_directories(my_cool_program ${CURL_INCLUDE_DIRECTORIES})\n\n# для цели my_cool_program указываем библиотеки, с которыми\n# программа будет слинкована\ntarget_link_libraries(my_cool_program ${CURL_LIBRARIES})\n```\n\nЕсли необходимо использовать библиотеку для всех целей проекта, а не для отдельных, то можно использовать команды `include_directories` и `link_libraries`.\n\n## Использование сторонних библиотек, для которых есть описания `pkg-config`\n\nДля многих GNU-библиотек существуют описания их использования, которые можно использовать утилитой [`pkg-config(1)`](https://linux.die.net/man/1/pkg-config). Эти описания можно использовать в проектах CMake в том случае, если для них не реализованы описания CMake, но существуют описания pkg-config. Обычно эти файлы `*.pc` располагаются в каталоге `/usr/lib[64]/pkgconfig`.\n\nПример для fuse3:\n\n```cmake\n# подключаем модуль интеграции с pkg-config\nfind_package(PkgConfig REQUIRED)\n\npkg_check_modules(\n  FUSE         # имя префикса для названий выходных переменных\n  REQUIRED     # если библиотека является обязательной\n  fuse3        # имя библиотеки, должен существовать файл fuse3.pc\n)\n\n# можно использовать переменные FUSE_INCLUDE_DIRECTORIES \n# и FUSE_LIBRARIES\ntarget_include_directories(my_cool_program ${FUSE_INCLUDE_DIRECTORIES})\ntarget_link_libraries(my_cool_program ${FUSE_LIBRARIES})\n\n# дополнительные флаги компиляции, например определения -D...,\n# которые не указывают на каталоги для поиска заголовочных файлов,\n# перечислены в переменной FUSE_CFLAGS_OTHER\ntarget_compile_options(my_cool_program ${FUSE_CFLAGS_OTHER})\n```\n\n## Использование сторонних библиотек, для которых вообще ничего нет\n\nВ случае, если для библиотеки не подготовлено никаких описаний, то можно попытаться найти необходимые файлы, используя перебор различных комбинаций имен и стандартных каталогов:\n\n```cmake\n# поиск файла динамической библиотеки\nfind_library(\n  SOME_LIBRARY     # переменная, в которую будет записан результат\n  NAMES            # перечисляются различные варианты имен для поиска\n    some\n    something\n    somelib0\n)\n\n# поиск пути к заголовочным файлам\nfind_path(\n  SOME_INCLUDE_DIRECTORY  # переменная, в которую будет записан результат\n  NAMES                   # имена файлов, которые могут содержаться в каталоге\n    somelib.h\n    somelib_common.h\n  PATH_SUFFIXES           # возможные имена подкаталогов в .../include/\n    somelib\n    somelib-1.0\n)\n\ntarget_include_directories(my_cool_program ${SOME_INCLUDE_DIRECTORY})\ntarget_link_libraries(my_cool_program ${SOME_LIBRARY})\n```\n\n"
  },
  {
    "path": "practice/linux_basics/devtools.md",
    "content": "# Инструменты разработчика\n\n## Компиляторы `gcc` и `clang`\n\nВ стандартную поставку современных UNIX-систем входит один из компиляторов: либо `gcc`, либо `clang`. В случае с Linux, по умолчанию обычно используется `gcc`, а в BSD-системах - `clang`. Далее будет описана работа с компилятором `gcc`, имея ввиду, что работа с `clang` ничем принципиально не отличается: у обоих компиляторов много общего, в том числе опции командной строки.\n\nКроме того, существует команда `cc`, которая является символической ссылкой на используемый по умолчанию компилятор языка Си (`gcc` или `clang`), и команда `c++`, - символическая ссылка на используемый по умолчанию компилятор для C++.\n\nРассмотрим простейшую программу на языке C++:\n```\n// файл hello.cpp\n#include <iostream>\n\nint\nmain() {\n  std::cout << \"Hello, World!\" << std::endl;\n  return 0;\n}\n```\n\nСкомпилировать эту программу можно с помощью команды:\n```\n> c++ -o program.jpg hello.cpp\n```\n\nОпция компилятора `-o ИМЯ_ФАЙЛА` указывает имя выходного файла, который нужно создать. По умолчанию используется имя `a.out`. Обратите внимание, что файл `program.jpg` является обычным выполняемым файлом!\n\n### Стадии сборки программы на Си или C++\n\nПри выполнении команды `c++ -o program.jpg hello.cpp` выполняется достаточно сложная цепочка действий:\n\n 1. Выполняется *препроцессинг* текстового файла `hello.cpp`. На этом этапе обрабатываются *директивы препроцессора* (которые начинаются с символа `#`), и получается новый текст программы. Если запустить компилятор с опцией `-E`, то будет выполнен только этот шаг, и на стандартный поток вывода будет выведен преобразованный текст программы.\n\n 2. Выполняется *трансляция* одного или нескольких текстов на Си или C++ в объектные модули, содержащие машинный код. Если указать опцию `-c`, то на этом сборка программы будет приостановлена, и будут созданы объектные файлы с суффиксом `.o`. Объектные файлы содержат *бинарный* исполняемый код, которому в точности соотвествует некоторый текст на языке ассемблера. Этот текст можно получить с помощью опции `-S`, - в этом случае, вместо объектных файлов будут созданы текстовые файлы с суффиксом `.s`.\n\n 3. Компоновка одного или нескольких объектных файлов в исполняемый файл, и связываение его со стандартной библиотекой Си/С++ (ну и другими библиотеками, если требуется). Для выполнения компоновки компилятор вызывает стороннюю программу `ld`.\n\n### Программы на Си v.s. программы на C++\n\nКомпилятор `gcc` имеет опцию `-x ЯЗЫК`, для указания языка исходного текста программы: Си (`c`), C++ (`c++`) или Фортран (`fortran`). По умолчанию, язык исходного текста определяется в соответствии с именем файла: `.c`, - это программы на языке Си, а файлы, оканчивающиеся на `.cc`, `.cpp` или `.cxx`, - это тексты на языке C++. Таким образом, имя файла является существенным.\n\nЭто относится к стадиям препроцессинга и трансляция, но может вызвать проблемы на стадии компоновки. Например, используя команду `gcc` вместо `g++` (или `cc` вместо `c++`), можно успешно скомпилировать исходный текст программы на C++, но при этом возникнут ошибки на стадии связывания, поскольку компоновщику `ld` будут переданы опции, подразумевающие связывание только со стандартной библиотекой Си, но не C++. Поэтому, при сборке программ на C++ нужно использовать команду `c++` или `g++`.\n\n### Указание стандартов\n\nОпция компилятора `-std=ИМЯ` позволяет явным образом указать используемый стандарт языка. Рекомендуется явным образом указывать используемый стандарт, поскольку поведение по умолчанию зависит от используемого дистрибутива Linux. Допустимые имена:\n * `c89`, `c99`, `c11`, `gnu99`, `gnu11` для языка Си;\n * `c++03`, `c++11`, `c++14`, `c++17`, `gnu++11`, `gnu++14`, `gnu++17` для языка C++.\n\nДвузначное число в имени стандарта указывает на его год. Если в имени стандарта присутствует `gnu`, то подразумеваются GNU-расширения компилятора, специфичные для UNIX-подобных систем, и кроме того, считается определенным макрос `#define _DEFAULT_SOURCE`, который в некоторых случаях меняет поведение отдельных функций стандартной библиотеки.\n\nВ дальнейшем мы будем ориентироваться на стандарт `c11`, а в некоторых задачах, где будет про это явно указано - его расширением `gnu11`.\n\n## Объектные файлы, библиотеки и исполняемые файлы\n\n### Модуль `ctypes` интерпретатора Python\n\nРассмотрим [программу](my-first-program.c) на языке Си:\n```\n/* my-first-program.c */\n#include <stdio.h>\n\nstatic void\ndo_something()\n{\n    printf(\"Hello, World!\\n\");\n}\n\nextern void\ndo_something_else(int value)\n{\n    printf(\"Number is %d\\n\", value);\n}\n\nint\nmain()\n{\n    do_something();\n}\n```\n\nСкомпилируем эту программу в объектный файл, а затем - получим из него: (1) выполняемую программу; (2) разделяемую библиотеку. Обратите внимание на опцию `-fPIC`, предназначенную для генерации позиционно-независимого кода, о чем будет рассказано на одном из последующих семинарах.\n\n```\n> gcc -c -fPIC my-first-program.c\n> gcc -o program.jpg my-first-program.o\n> gcc -shared -o library.so my-first-program.o\n```\n\nВ результате мы получим программу `program.jpg`, которая\nвыводит на экран строку `Hello, World!`, и *библиотеку* с именем `library.so`, которую можно использовать как из Си/C++ программы, так и динамически подгрузить для использования интерпретатором Python:\n\n```\n> python3\nPython 3.6.5 (default, Mar 31 2018, 19:45:04) [GCC] on linux\nType \"help\", \"copyright\", \"credits\" or \"license\" for more information.\n>>> from ctypes import cdll\n>>> lib = cdll.LoadLibrary(\"./library.so\")\n>>> lib.do_something_else(123)\n>>> retval = lib.do_something_else(123)\nNumber is 123\n>>> print(retval)\n14\n```\nОбратите внимание, что результатом работы функции `do_something_else` является какое-то загадочное число `14` (возможно, будет какое-то другое при попытке воспроизвети этот эксперимент), хотя функция возвращает `void`.\n\nПричина заключается в том, что разделяемые библиотеки хранят только **имена** функций, но не их сигнатуры (типы параметров и возвращаемого значения).\n\nПопытка вызвать функцию `do_something` не увенчается успехом:\n```\n>>> lib.do_something()\nTraceback (most recent call last):\n  File \"<stdin>\", line 1, in <module>\n  File \"/usr/lib64/python3.6/ctypes/__init__.py\", line 361, in __getattr__\n    func = self.__getitem__(name)\n  File \"/usr/lib64/python3.6/ctypes/__init__.py\", line 366, in __getitem__\n    func = self._FuncPtr((name_or_ordinal, self))\nAttributeError: ./library.so: undefined symbol: do_something\n```\n\nВ этом случае имя `do_something` не найдено, поскольку в исходном тексте на языке Си модификатор `static` перед именем функции явно запрещает использование функции где-либо вне текущего исходного текста.\n\n### Просмотр таблицы символов\n\nДля исследования объектных файлов, в том числе и скомпонованных, используется утилита `objdump`.\n\nОпция `--syms` или `-t` отображает отдельные секции исполняемого объектного файла, которым присвоены имена - *символы*.\n\nНекоторые имена имеют пометку `*UND*`, - это означает, что имя используется в объектном файле, но его расположение неизвестно. Задача компоновщика состоит как раз в том, чтобы найти требуемые имена в разных объектных файлах или динамических библиотеках, а затем - подставить правильный адрес.\n\nНекоторые символы помечены как глобальные (символ `g` во втором столбце вывода), а некоторые - как локальные (символ `l`). Те символы, которые не являются глобальными, считаются *не экспортируемыми*, то есть (теоретически) не должны быть доступны извне.\n\n\n## Отладчик\n\nЕсли скомпилировать программу с опцией `-g`, то размер программы увеличится, поскольку в ней появляются дополнительные секции, которые содержат *отладочную информацию*.\n\nОтладочная информация содержит сведения о соответствии отдельных фрагментов программы исходным текстам, и включает в себя номера строк, имена исходных файлов, имена типов, функций и переменных.\n\nЭти информация используется только отладчиком, и почти никак не влияет на поведение программы. Таким образом, отладочную информацию можно совмещать с оптимизацией, в том числе достаточно агрессивной (опция компилятора `-O3`).\n\nДля запуска программы под отладчиком используется команда `gdb`, в качестве аргумента к которой указывается имя выполняемого файла или команды.\n\nОсновные команды `gdb`:\n * `run` - запуск программы, после `run` можно указать аргументы;\n * `break` - установить точку останова, параметрами этой команды может имя функции или пара `ИМЯ_ФАЙЛА:НОМЕР_СТРОКИ`;\n * `ni`, `si` - шаг через строку или шаг внутрь функции соответственно;\n * `return` - выйти из текущей функции наверх;\n * `continue` - продолжить выполнение до следующей точки останова или исключительной ситуации;\n * `print` - вывести значение переменной из текущего контекста выполнения.\n\nВзаимодействие с отладчиком производится в режиме командной строки. Различные интегрированные среды разработки (CLion, CodeBlocks, QtCreator) являются всего лишь графической оболочкой, использующей именно этот отладчик, и визуализируя взаимодействие с ним.\n\nБолее подробный список команд можно посмотреть в [CheatSheet](https://www.cheatography.com/fristle/cheat-sheets/closed-source-debugging-with-gdb/).\n"
  },
  {
    "path": "practice/linux_basics/intro.md",
    "content": "# Введение в ОС Linux\n\n## Это не Windows. Забудьте то, что вы знали раньше\n\n### Используемая терминология\n\n * Файловая система представляет собой иерархию файлов и *каталогов*. Не нужно называть каталоги \"папками\".\n * В отличии от Windows, все файлы в UNIX являются равнозначными, независимо от их имени. Понятия \"расширение файла\" не существует, но для удобства восприятия имени файла пользователем, их снабжают *суффиксами имени*, отделяемые от основного имени точкой. Суффиксов у имени файла может быть несколько, например `.tar.gz`.\n * В системе выполняется огромное количество *процессов*, а не \"задач\". Процессы могут быть запущены как непосредственно пользователем, так и одним из *демонов*, которые запускаются при загрузке системы, и сами по себе являются процессами.\n\n\n### Принятые обозначения клавиатурных сокращений\n\nПри работе с консольными UNIX-программами обычно используются следующие, исторически\n сложившиеся, обозначения клавиатурных сочетаний:\n * `C-Буква` - одновременное нажатие `Ctrl` и буквенной клавиши. Вниманию пользователей\n MacOS! `Ctrl` - это именно клавиша `Ctrl`, а не `Command`.\n * `M-Буква` - одноваременное нажатие `Alt` и буквенной клавиши. Сокращение \"M\" - от слова\n \"Meta\". Такая клавиша была на старых рабочих станциях Sun и SGI.\n * `C-Буква1 Буква2` - сначала одновременное нажатие `Ctrl` и `Буква1`, затем отпустить клавишу\n `Ctrl` и нажать `Буква2`. Аналогично для клавиши `Alt`. Сочетание `C-Буква1` называется *префиксом*\n клавиатурного сочетания, обычно под одинаковыми префиксами группируются клавиатурные сочетания для\n действий одного характера\n * `C-Буква1 C-Буква2`. Нажать `Ctrl`, затем нажать и отпустить `Буква1`, нажать и отпустить `Буква2`.\n После этого можно отпустить клавишу `Ctrl`.\n * Клавиши `F13`...`F15`. На PC-клавиатуре их нет. Их нажатие обеспечивается клавишей `Shift` и одной\n функциональных клавиш с номером `F...` меньше, в зависимости от терминала, на 10 или на 12. Например,\n во многих графических терминалах, `Shift+F5` означает нажатие клавиши `F15`.\n\n\n## Начало работы\n\nLinux, как и любые другие операционные системы семейства UNIX, является\n**многопользовательской** операционной системой. Для начала работы необходимо\nзнать свое имя пользователя и пароль.\n\nВ зависимости от целей использования, вход в систему может быть осуществлен\nразличными способами.\n\n\n### Локальный вход с графическим интерфейсом\nЭтот вариант обычно используется при установке Linux в качестве Desktop'а.\nКак правило, в большинстве дистрибутивов Linux предусмотрен автоматический\nвход в систему, если при установке был указан только один пользователь-человек\n(существует еще и другой вид пользователей -- системные). Если пользователей несколько,\nто вход в систему мало чем отличается от такового в ОС Windows или Mac.\n\nПосле входа в систему, отображается графическая оболочка (GNOME, Unity или KDE).\nКомандная строка, с которой мы преимущественно будем работать, доступна с\nпомощью приложения \"Терминал\".\n\n### Локальный вход без графического интерфейса\nЭтот вариант обычно используется при начальной настройке серверов (графический\nстек является потенциальной \"дырой\" в безопасности, и обычно не устанавливается),\nа также при работе со встраиваемыми системами.\n\nПосле загрузки системы или подключения терминала отображается текстовое\nприглашение с предложением ввести имя пользователя и пароль, а после входа\nв систему управление передается **командному интерпретатору**.\n\n### Удаленный вход через SSH\nДля подключения по SSH необходимо использовать команду (для Linux/Mac)\n\n```\nssh ИМЯ_ПОЛЬЗОВАТЕЛЯ@ИМЯ_ХОСТА\n```\n\nДля подключения по SSH из Windows существуют специальные программы, например\n[PuTTY](http://www.chiark.greenend.org.uk/~sgtatham/putty/download.html).\n\nПосле подключения нужно ввести пароль для входа в систему. В некоторых случаях\nвводить пароль не потребуется, например, если настроена авторизация с\nиспользованием SSH-ключей.\n\nПосле входа в систему, управление передается командному интерпретатору.\n\n\n## Основы работы с командной строкой\n### Навигация по файловой системе\n\nПриглашение командной строки обычно имеет вид, зависящий от состояния:\n\n```\nИМЯ_ПОЛЬЗОВАТЕЛЯ@ИМЯ_ХОСТА:ТЕКУЩИЙ_КАТАЛОГ>\n```\n\nКорневой каталог в иерархии файловой системы -- это `/`. После входа\nв систему, текущим является *домашний каталог* текущего пользователя, --\nэто каталог, который доступен как для чтения, так и для записи.\n\nДомашние каталоги обычных пользователей располагаются в:\n * `/home/` -- для Linux\n * `/usr/local/home` -- для FreeBSD\n * `/Users` -- для MacOS\n\nНезависимо от используемой операционной системы, имя `~` (символ\n\"тильда\", на одной клавише с буквой \"Ё\"), является синонимом домашнего\nкаталога *текущего пользователя*.\n\nЛексемы `.` и `..` означают, соответственно, текущий каталог и каталог\nна один уровень выше в иерархии.\n\nДля навигации по каталогам используется команда `cd`. Примеры:\n\n```\ncd ..          # Перейти на уровень выше\ncd ../..       # Перейти на два уровня выше\ncd ../src      # Перейти на уровень выше, затем в подкаталог src\ncd /           # Перейти в корневой каталог\ncd /usr/lib64  # Перейти в каталог /usr/lib64\ncd ~/projects  # Перейти в каталог /home/ИМЯРЕК/projects\n```\n\n**Замечание**. При вводе имен файлов или каталогов, клавиша `TAB`\nвызывает функцию автодополнения имени.\n\n### Запуск выполняемых файлов\n\nВыполняемый файл -- это любой файл (в том числе и текстовый), обладающий\nспециальным аттрибутом.\n\nЗапуск выполняемого файла осуществяется двумя способами:\n * Вводом имени этого файла в том случае, если файл располагается в одном\n из каталогов, перечисленных в *переменной окружения* `PATH`. Все стандартные\n программы, входящие в поставку UNIX-системы запускаются таким способом.\n * Вводом *полного имени* файла. Полное имя может быть как абсолютным (то есть\n начинаться с символа `/`), так и относительным (начиначется с символа `.`).\n Таким способом обычно запускаются программы, находящиеся в домашнем каталоге.\n\n\n### Стандартные программы для управления файлами\n\n * `cp` -- копирование файла или каталога (с опцией `-R`)\n * `mv` -- переименование (перемещение) файла или каталога (с опцией `-R`)\n * `rm` -- удаление файла или каталога (с опцией `-r`)\n * `ls` -- вывод содержимого текущего каталога\n\nВсе эти команды являются обычными программами, которые располагаются в каталоге\n`/usr/bin`.\n\n**Вопрос**. Почему не существует программы с именем `cd`?\n<!--\nПравильный ответ: потому что текущий каталог -- это состояние интерпретатора.\nКаждая команда запускается в отдельном процессе, который не может менять состояние процесса-родителя.\n-->\n\n\n### Форматы исполняемых файлов\n\n * Бинарный файл начинается с последовательности байт `0x7F 0x45 0x4C 0x46`.\n Этот формат называется ELF (Executable and Linkable Format).\n * Произвольный файл, в том числе текстовый, который начинается с текстовой\n строки вида `#!ИМЯ_ИНТЕРПРЕТАТОРА\\n`. В этом случае, система запускает\n указанный интерпретатор, и передает ему выполняемый файл в качестве аргумента.\n\n Пример выполняемого файла:\n\n ```\n #!/usr/bin/python\n\n print(\"Hello, UNIX!\")\n ```\n\n### Midnight Commander\n\nДля навигации по файловой системы, использование командной строки, -- это не всегда удобно.\n\n**Замечание**. При использовании предоставляемого образа ВМ, файловая система также\nдоступна по FTP: [ftp://student@192.168.56.105/](ftp://student@192.168.56.105/).\n\n*Midnight Commander* -- это двухпанельный файловый менеджер, доступный почти для всех\nUNIX-подобных операционных систем (включая MacOS). Запускается командой `mc` и\nработа с ним аналогична работе с FAR Manager или Total Commander. Исключение составляют некоторые клавиатурные сочетания.\n\nОсновные операции:\n * `F3` -- просмотр файла\n * `F4` -- редактирование файла\n * `Shift+F4` -- создание и редактирование нового файла\n * `F5` -- копирование\n * `F6` -- перемещение\n * `F7` -- создание каталога\n * `F8` -- удаление\n * `F10` -- выход из Midnight Commander\n * `C-x c` -- редактирование аттрибутов файла\n * `C-x o` -- редактирование пользователя файла\n * `C-x s` -- создание символической ссылки на файл\n\n**Замечание.** Выход из редактирования или просмотра файла осуществляется нажатием клавиши\n`Esc` **два раза**. Это связано с тем, что клавиша `Esc` в классических терминалах предназначена\nдля префиксного ввода управляющих символов.\n\n\n### Иерархия файловой системы\n\nВ отличии от Windows, где каждому физическому диску или разделу на диске соответствует определенная буква,\nнапример `C:\\`, дерево файловой системы в UNIX-системах имеет общий корень `/`. Отдельные диски или разделы\n*монтируются* в подкаталоги основной файловой системы.\n\nФайловая система всех дистрибутивов Linux имеет следующую иерархию:\n * `/bin` -- выполняемые программы, предоставляющие минимально необходимый набор команд\n * `/boot` -- файлы, необходимые для загрузки операционной системы\n * `/dev` -- псевдо-файлы устройств\n * `/etc` -- текстовые файлы настроек\n * `/home` -- домашние каталоги пользователей\n * `/lib` или `/lib64`, либо оба этих каталога -- минимально необходимый для работоспособности системы набор разделяемых библиотек. Каталог `/lib64` присутствует на 64-битных системах и содержит варианты библиотек для\nx86_64, в то время как `/lib` -- их аналоги для i386.\n * `/lost+found` -- файлы, которые по каким-либо причинам (например, неправильное выключение компьютера, или\nсбой диска) оказались вне какого-либо каталога, но их содержимое доступно\n * `/media` -- каталог для монтирования сменных носителей, доступных всем пользователям\n * `/mnt` -- каталог для монтирования общедоступных сетевых файловых систем или инородных разделов\n * `/opt` -- каталог для установки сторонних приложений не из репозитория дистрибутива, например Google Chrome\nили Яндекс.Браузер\n * `/proc` -- здесь примонтирована виртуальная файловая система с информацией о запущенных в системе процессах\n * `/root` -- домашний каталог пользователя `root`\n * `/run` -- содержит *именованные сокеты* и текстовые файлы с *индентификаторами процесса* для запущенных демонов\n * `/sbin` -- выполняемые файлы для запуска пользователем `root`; у других пользователей этот каталог не включен\nв переменную окружения `PATH`, и для их запуска необходимо указывать полный путь\n * `/srv` -- файлы для хранения данных сетевыми службами\n * `/sys` -- виртуальная файловая система для просмотра и изменения параметров ядра\n * `/tmp` -- каталог для временных файлов\n * `/usr` -- содержит иерархию, аналогичную корневой; там находятся файлы большинства программ, которые\nустанавливаются из *репозиториев* дистрибутива\n * `/usr/local` -- аналогично `/usr`, но предназначен для установки программ самостоятельно из исходных текстов\n * `/var` -- содержит данные различных демонов, например базы данных.\n\n## Консольные текстовые редакторы\n\n### Встроенный редактор Midnight Commander\n\nВызывается нажатием клавиши `F4` из файлового менеджера, либо командой `mcedit ИМЯ_ФАЙЛА` как\nсамостоятельная программа.\n\nОсновные клавиши:\n * `F2` -- сохранить файл\n * `Esc Esc` -- выход\n * `F3` -- начало/конец выделения текста\n * `F5` -- копирование выделенного текста в текущую позицию\n * `F6` -- перемещение выделенного текста в текущую позицию\n * `F8` -- удаление выделенного текста; если текст не выделен, -- удаление текущей строки\n\n### Редактор VI\n\nПоскольку Midnight Commander, и соответственно, редактор `mcedit` не всегда установлены по умолчанию,\nиногда возникает необходимость использования редактора `vi`, который входит в базовый состав почти\nвсех дистрибутивов Linux.\n\nЗапускается редактор командой `vi ИМЯ_ФАЙЛА`, либо в результате какого-нибудь действия, которое требует\nредактирования текста (например, командой `git commit` -- для редактирования комментария к коммиту).\n\nОпознать редактор `vi` можно по черному экрану, в левом столбце терминала при этом во всех пустых строках\nв конце текста присутствует символ `~`.\n\nПосле запуска, редактор находится в **командном режиме**. Не нужно нажимать буквенно-цифровые клавиши для ввода текста в этом режиме. Если это все-же произошло, нужно нажать `C-[` для возврата в командный режим.\n\nВ командном режиме навигация по тексту осуществляется клавишами стрелок, а также клавишами `h`, `j`, `k`, и `l`.\nПомимо текстового редактора `vi`, многие среды разработки, например QtCreator и IntelliJ IDEA, а также браузеры имеют Chrome и Firefox имеют плагины, позволяющие использовать навигацию в стиле VIM, так что назначение этих\nклавиш лучше запомнить.\n\nДля перехода в **режим вставки**, привычному по GUI-редакторам, нужно нажать клавишу `i`. Выход из этого режима\n-- сочетанием `C-[`. Для перехода в **режим замены** -- клавиша `o`, выход аналогичен.\n\nОсновные команды, которые нужно запомнить:\n * `:w` -- сохранить файл\n * `:e ИМЯ_ФАЙЛА` -- открыть или создать файл с указанным именем\n * `:q` -- выход из редактора, возможен только если нет не сохраненных изменений\n * `:q!` -- принудительный выход из редактора без сохранения\n * `!КОМАНДА` -- запуск UNIX-команды без выхода из редактора\n\nБолее подробное руководство по `vi` можно получить, запустив программу `vimtutor`\n\n### Редактор nano\n\nВ некоторых дистрибутивах, например Ubuntu, по умолчанию вместо `vi` установлен редактор `nano`.\nЭто простой в использовании текстовый редактор. Опознать его можно по тексту `GNU nano` в заголовке вверху\nтерминала, и подсказкам о сочетаниях клавиш вида `^G Get Help` в подвале. Символ `^` означает клавишу `Ctrl`.\n"
  },
  {
    "path": "practice/linux_basics/my-first-program.c",
    "content": "/* my-first-program.c */\n#include <stdio.h>\n\nstatic void\ndo_something()\n{\n    printf(\"Hello, World!\\n\");\n}\n\nextern void\ndo_something_else(int value)\n{\n    printf(\"Number is %d\\n\", value);\n}\n\nint\nmain()\n{\n    do_something();\n}\n"
  },
  {
    "path": "practice/math/README.md",
    "content": "# Целочисленная и вещественная арифметика\n\n## Целочисленные типы данных\n\nМинимально адресуемым размером данных является, какправило, один байт (8 бит). Как правило - это значит, что не всегда, и бывают разные экзотические архитектуры, где \"байт\" - это 9 бит (PDP-10), или специализированные сигнальные процессоры с минимально адресуемым размером данных 16 бит (TMS32F28xx).\n\nПо стандарту языка Си определена константа `CHAR_BIT` (в заголовочном файле `<limits.h>`), для которой гарантируется, что `CHAR_BIT >= 8`.\n\nТип данных, представляющий один байт, исторически называется \"символ\" - `char`, который содержит ровно `CHAR_BIT` количество бит.\n\nЗнаковость типа `char` по стандарту не определена. Для\nархитектуры x86 это знаковый тип данных, а, например,\nдля ARM - беззнаковый. Опции компилятора gcc `-fsigned-char`\nи `-funsigned-char` определяют это поведение.\n\nДля остальных целочисленных типов данных: `short`, `int`,\n`long`, `long long`, стандарт языка Си определяет минимальную разрядность:\n\n| Тип данных | Разрядность                    |\n| -----------| -------------------------------|\n| `short`    | не менее 16 бит                |\n| `int`      | не менее 16 бит, обычно 32 бит |\n| `long`     | не менее 32 бит                |\n| `long long`| не менее 64 бит, обычно 64 бит |\n\nТаким образом, полагаться на количество разрядов в базовых\nтипах данных нельзя, и это нужно проверять с помощью\nоператора `sizeof`, который возвращает \"количество байт\", то\nесть, в большинстве случает - сколько блоков размером\n`CHAR_BIT` помещается в типе данных.\n\nС особой осторожностью нужно относиться к типу данных `long`: на 64-разрядной системе Unix он является 64-битным, а, например, на 64-битной Windows - 32-битным. Поэтому, во избежание путаницы, использовать этот тип данных запрещено.\n\n## Знаковые и беззнаковые типы данных\n\nПеред целочисленными типами данных могут стоять модификаторы\n`unsigned` или `signed`, которые указывают допустимость отрицательных чисел.\n\nДля знаковых типов, старший бит определяет знак числа: значение `1` является признаком отрицательности.\n\nСпособ внутреннего представления отрицательных чисел не регламентирован  [стандартом языка Си](http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570), однако все современные компьютеры используют обратный дополнительный код. Более того, п.6.3.1.3.2 стандарта языка Си определяет способ приведения типов от знакового к беззнаковому таким способом, которые приводит к кодированию обратным дополнительным кодом.\n\nТаким образом, значение `-1` представляется как целое число,\nвсе биты которого равны единице.\n\nС точки зрения низкоуровневого программирования, и языка Си в частности, знаковость типов данных определяет только способ применения различных операций.\n\n## Типы данных с фиксированным количеством бит\n\nВ заколовочных файлах файле `<stdint.h>` (для Си99+) и\n`<cstdint>` (для C++11 и новее) определены типы данных, для\nкоторых гарантируется фиксированное количесвто разрядов:\n`int8_t`, `int16_t`, `int32_t`, `int64_t`, - для знаковых, и\n`uint8_t`, `uint16_t`, `uint32_t`, `uint64_t` - для беззнаковых.\n\n# Переполнение\n\nСитуация целочисленного переполнения возникает, когда тип данных результата не имеет достаточно разрядов для того, чтобы хранить итоговый результат. Например, при сложении беззнаковых 8-разрядных целых чисел 255 и 1, получается результат, который не может быть представим 8-разрядным значением.\n\nДля **беззнаковых чисел** ситуация переполнения является штатной, и эквивалентна операции \"сложение по модулю\".\n\nДля **знаковых** типов данных - приводит к ситуации *неопределенного поведения* (Undefined Behaviour). В корректных программах такие ситуации встречаться не могут.\n\nПример:\n```\nint some_func(int x) {\n    return x+1 > x;\n}\n```\n\nС точки зрения здравого смысла, такая программа должна всегда возвращать значение `1` (или `true`), поскольку мы знаем, что `x+1` всегда больше, чем `x`. Компилятор может использовать этот факт для оптимизации кода, и всегда возвращать истинное значение. Таким образом, поведение программы зависит от того, какие опции оптимизации были использованы.\n\n## Контроль неопределенного поведения\n\nСвежие версии компиляторов `clang` и `gcc` (начиная с 6-й версии) умеют контролировать ситуации неопределенного поведения.\n\nМожно включить генерацию *управляемого* кода программы, который использует дополнительные проверки во время выполнения. Естественно, это происходит ценой некоторого снижения производительности.\n\nТакие инструменты называются *ревизорами* (sanitizers), предназначенными для разных целей.\n\nДля включения ревизора, контролирующего ситуацию неопределенного поведения, используется опция `-fsanitize=undefined`.\n\n## Контроль переполнения, независимо от знаковости\n\nЦелочисленное переполнение означает перенос старшего разряда, и многие процессоры, включая семейство x86, позволяют это диагностировать. Стандартами языков Си и C++ эта возможность не предусмотрена, однако компилятор gcc (начиная с 5-й версии) предоставляет **нестандартные** встроенные функции для выполнения операций с контролем переполнения.\n\n```\n// Операция сложения\nbool __builtin_sadd_overflow (int a, int b, int *res);\nbool __builtin_saddll_overflow (long long int a, long long int b, long long int *res);\nbool __builtin_uadd_overflow (unsigned int a, unsigned int b, unsigned int *res);\nbool __builtin_uaddl_overflow (unsigned long int a, unsigned long int b, unsigned long int *res);\nbool __builtin_uaddll_overflow (unsigned long long int a, unsigned long long int b, unsigned long long int *res);\n\n// Операция вычитания\nbool __builtin_ssub_overflow (int a, int b, int *res)\nbool __builtin_ssubl_overflow (long int a, long int b, long int *res)\nbool __builtin_ssubll_overflow (long long int a, long long int b, long long int *res)\nbool __builtin_usub_overflow (unsigned int a, unsigned int b, unsigned int *res)\nbool __builtin_usubl_overflow (unsigned long int a, unsigned long int b, unsigned long int *res)\nbool __builtin_usubll_overflow (unsigned long long int a, unsigned long long int b, unsigned long long int *res)\n\n// Операция умножения\nbool __builtin_smul_overflow (int a, int b, int *res)\nbool __builtin_smull_overflow (long int a, long int b, long int *res)\nbool __builtin_smulll_overflow (long long int a, long long int b, long long int *res)\nbool __builtin_umul_overflow (unsigned int a, unsigned int b, unsigned int *res)\nbool __builtin_umull_overflow (unsigned long int a, unsigned long int b, unsigned long int *res)\nbool __builtin_umulll_overflow (unsigned long long int a, unsigned long long int b, unsigned long long int *res)\n\n```\n\n## Числа с плавающей точкой в формате IEE754\n\nДва основных типа вещественных с плавающей точкой, которые определены стандартом языка Си, - это `float` (используется 4 байта для хранения) и `double` (используется 8 байт).\n\nСамый старший бит в представлении числа - это признак отрицательного значения. Далее, по старшинству бит, хранится значения *смещенной экспоненциальной* части (8 бит для `float` или 11 бит для `double`), а затем - значение *мантиссы* (23 или 52 бит).\n\nСмещение экспоненциальной части необходимо для того, чтобы можно было в таком представлении хранить значения с отрицательной экспонентой. Смещение для типа `float` равно `127`, для типа `double` - `1023`.\n\nТаким образом, итоговое значение может быть получено как:\n\n```\nValue = (-1)^S * 2^(E-B) * ( 1 + M / (2^M_bits - 1) )\n```\nгде `S` - бит знака, `E` - значение смещенной экспоненты, `B` - смещение (127 или 1023), а `M` - значение мантиссы, `M_bits` - количество бит в экспоненте.\n\n\n## Как получить отдельные биты вещественного числа\n\nПоразрядные операции относятся к целочисленной арифметике, и не предусмотрены для типов `float` и `double`. Таким образом, нужно сохранить вещественное число в памяти, и затем прочитать его, интерпретируя как целое число. В случае с языком C++ для этого предназначен оператор `reinterpret_cast`. Для языка Си есть два способа: использовать аналог `reinterpret_cast` - приведение указателей, либо использовать тип `union`.\n\n### Приведение указателей\n```\n// У нас есть некоторое целое вещественное число, которое хранится в памяти\ndouble a = 3.14159;\n\n// Получаем указатель на это число\ndouble* a_ptr_as_double = &a;\n\n// Теряем информацию о типе, приведением его к типу void*\nvoid* a_ptr_as_void = a_ptr_as_void;\n\n// Указатель void* в языке Си можно присваивать любому указателю\nuint64_t* a_ptr_as_uint = a_ptr_as_void;\n\n// Ну а дальше просто разыменовываем указатель\nuint64_t b = *a_as_uint;\n```\n\n### Использование типа `union`\n\nТип `union` - это тип данных, который синтаксически очень похож на тип `struct`, то есть там можно перечислить несколько именованных полей, но концептуально - это совершенно разные типы данных! Если в структуре или классе, для хранения каждого поля для предусмотрено отдельное место в памяти, то для `union` этого не происходит, и все поля накладываются друг на друга при размещении в памяти.\n\nОбычно тип `union` используется в качестве вариантного типа данных (в С++ начиная с 17-го стандарта для этого предусмотрен `std::variant`), но в качестве побочного эффекта - его удобно использовать приведения типов в стиле `reinterpret_cast`, не используя при этом указатели.\n\n```\n// У нас есть некоторое целое вещественное число, которое хранится в памяти\ndouble a = 3.14159;\n\n// Используем тип union\ntypedef union {\n    double     real_value;\n    uint64_t   uint_value;\n} real_or_uint;\n\nreal_or_uint u;\nu.real_value = a;\nuint64_t b = u.uint_value;\n```\n\n## Специальные значения в формате IEEE754\n\n * Бесконечность: `E=0xFF...FF`, `M=0`\n * Минус ноль (результат деления 1 на минус бесконечность): `S=1`, `E=0`, `M=0`\n * NaN (Not-a-Number): `S` - любое, `E=0xFF...FF`, `M <> 0`\n\nНекоторые процессоры, например архитектуры x86, поддерживают расширение стандарта, позволяющее более эффективно представлять множество чисел, значения которых близко к нулю. Такие числа называются *денормализованными*.\n\nПризнаком денормализованного числа является значение смещенной экспоненты `E=0`. В этом случае, численное значение получается следующим образом:\n\n```\nValue = (-1)^S * ( M / (2^M_bits - 1) )\n```\n\n### Значения Not-a-Number\n\nНекоторые процессоры, например Intel x86, различают два вида чисел `NaN` - невалидное значение.\n\n\n#### sNaN - Signaling NaN\n\nЗначения `sNaN` возникают при выполнении операций, которые сигнализируют об ошибке на уровне прерывания процессора. Например, деление на `0`. Обычно, чтобы получить такие значения, необходимо собирать программу с опцией `-fno-signaling-nans`. Более подробно - см. [FloatingPointMath - GCC Wiki](https://gcc.gnu.org/wiki/FloatingPointMath)\n\nПример битовой маски для типа `double` на x86_64, определяющей значение `sNaN`:\n\n```\nНомера битов    6         5         4         3         2         1         0\n             3210987654321098765432109876543210987654321098765432109876543210\n             ----------------------------------------------------------------\nЗначения     0111111111110100000000000000000000000000000000000000000000000000\n             ----------------------------------------------------------------\nРегион       SEEEEEEEEEEEMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\n```\n\n\n####  qNaN - Quiet NaN\n\nВ отличии от `sNaN`, значения Quiet NaN, которые получаются в результате вычислений, не приводят к прерыванию процессора и вызове обработчика исплючительной ситуации.\n\nПримером является попытка сложить `+inf` и `-inf`.\n\nПример битовой маски для типа `double` на x86_64, определяющей значение `qNaN`:\n\n```\nНомера битов    6         5         4         3         2         1         0\n             3210987654321098765432109876543210987654321098765432109876543210\n             ----------------------------------------------------------------\nЗначения     0111111111111000000000000000000000000000000000000000000000000000\n             ----------------------------------------------------------------\nРегион       SEEEEEEEEEEEMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\n```\n\n"
  },
  {
    "path": "practice/mmap/README.md",
    "content": "# Страницы памяти в виртуальном адресном пространстве\n\n## Инструменты\n\nКроме пошагового отладчика `gdb`, при работе с памятью существуют дополнительные инструменты, предназначенные для выявления проблем.\n\n### Интерпретируемое выполнение с помощью `valgrind`\n\nНабор инструментов `valgrind` использует контролируемое выполнение инструкций программы, модифицируя её код перед выполнением на физическом процессоре.\n\nОсновные инструменты:\n * `memcheck` - диагностика проблем с памятью: неверные указатели на кучу, повторное освобождение, чтение неинициализированных данных и забытое освобождение памяти.\n * `callgrind` - диагностика производительности выполняемой программы.\n\nДля запуска программы под valgrind необходимо собрать программу с отладочной информацией (опция компиляции `-g`), в противном случае вывод valgrind будет не информативным.\n\nЗапуск:\n```\n> valgrind --tool=ИНСТРУМЕНТ program.jpg ARG1 ARG2 ... ARGn\n```\n\nВ случае использования инструмента `callgrind`, после выполнения программы генерируется файл `callgrind.out` в формате XML, который можно визуализировать с помощью KCacheGrind (из KDE во всех современных дистрибутивах Linux), либо его кросс-платформенном аналоге [QCacheGrind](https://sourceforge.net/projects/qcachegrindwin/).\n\n### Runtime-проверки ошибок с помощью sanitizer'ов\n\nТребует для своей работы свежие версии `clang` или `gcc`, и позволяют выполнять инструментальный контроль во время выполнения программы значительно быстрее, чем `valgrind`.\n\nРеализуются на уровне генерации кода и замены некоторых функций, например `malloc`/`free` на реализации с дополнительными проверками.\n\nОсновные санитайзеры:\n * AddressSanitizer (`-fsanitize=address`) - диагностирует ситуации утечек памяти, двойного освобождения памяти, выхода за границу стека или кучи, и использования указателей на стек после завершения работы функции.\n * MemorySanitizer (`-fsanitize=memory`) - диагностика ситуаций чтения неинициализированных данных. Требует, чтобы программа, и как и все используемые ею библиотеки, были скомпилированы в позиционно-независимый код.\n * UndefinedBehaviourSanitizer (`-fsanitize=undefined`) - диагностика неопределенного поведения в целочисленной арифметике: битовые сдвиги, знаковое переполнение, и т.д.\n\n\n## Системный вызов mmap\n\n```\n#include <sys/mman.h>\n\nvoid *mmap(\n    void *addr,    /* рекомендуемый адрес отображения */\n    size_t length, /* размер отображения */\n    int prot,      /* аттрибуты доступа */\n    int flags,     /* флаги совместного отображения */\n    int fd,        /* файловый декскриптор фала */\n    off_t offset   /* смещение относительно начала файла */\n  );\n\nint munmap(void *addr, size_t length) /* освободить отображение */\n```\n\nСистемный вызов `mmap` предназначен для создания в виртуальном адресном пространстве процесса доступной области по определенному адресу. Эта область может быть как связана с определенным файлом (ранее открытым), так и располагаться в оперативной памяти. Второй способ использования обычно реализуется в функциях `malloc`/`calloc`.\n\nПамять можно выделять только постранично. Для большинства архитектур размер одной страницы равен 4Кб, хотя процессоры архитектуры x86_64 поддерживают страницы большего размера: 2Мб и 1Гб.\n\nВ общем случае, никогда нельзя полагаться на то, что размер страницы равен 4096 байт. Его можно узнать с помощью команды `getconf` или функции `sysconf`:\n\n```\n# Bash\n> getconf PAGE_SIZE\n4096\n\n/* Си */\n#include <unistd.h>\nlong page_size = sysconf(_SC_PAGE_SIZE);\n```\n\nПараметр `offset` (если используется файл) обязан быть кратным размеру страницы; параметр `length` - нет, но ядро системы округляет это значение до размера страницы в большую сторону. Параметр `addr` (рекомендуемый адрес) может быть равным `NULL`, - в этом случае ядро само назначает адрес в виртуальном адресном пространстве.\n\nПри использовании отображения на файл, параметр `length` имеет значение длины отображаемых данных; в случае, если размер файла меньше размера страницы, или отображается его последний небольшой фрагмент, то оставшаяся часть страницы заполняется нулями.\n\nСтраницы памяти могут флаги аттрибутов доступа:\n * чтение `PROT_READ`;\n * запись `PROT_WRITE`;\n * выполнение `PROT_EXE`;\n * ничего `PROT_NONE`.\n\nВ случае использования отображения на файл, он должен быть открыт на чтение или запись в соответствии с требуемыми аттрибутами доступа.\n\nФлаги `mmap`:\n * `MAP_FIXED` - требует, чтобы память была выделена по указаному в первом аргументе адресу; без этого флага ядро может выбрать адрес, наиболее близкий к указанному.\n * `MAP_ANONYMOUS` - выделить страницы в оперативной памяти, а не связать с файлом.\n * `MAP_SHARED` - выделить страницы, разделяемые с другими процессами; в случае с отображением на файл - синхронизировать изменения так, чтобы они были доступны другим процессам.\n * `MAP_PRIVATE` - в противоположность `MAP_SHARED`, не делать отображение доступным другим процессам. В случае отображения на файл, он доступен для чтения, а созданные процессом изменения, в файл не сохраняются.\n"
  },
  {
    "path": "practice/mutex-condvar-atomic/README.md",
    "content": "# Синхронизация потоков\r\n\r\n## Проблема гонки данных\r\n\r\nНа всех современных процессорных архитектурах операции чтения и записи в память выровненных данных, размер которых не превышает машинного слова, являются атомарными. Однако, с точки зрения строгому следованию стандартам Си и C++, такое предположение неверно, и необходимо использовать специальные типы данных и атомарные операции.\r\n\r\nКроме того, операции над типами данных, размер которых превышает размер машинного слова, заведомо являются неатомарными. В первую очередь, такая проблема проявляется для 64-разрядных типов данных (`double` и `int64_t`) на 32-разрядных архитектурах.\r\n\r\nЕсли несколько потоков или процессов обращаются к одной области памяти, причем один из потоков (процессов) производит запись, то такая ситуация называется *гонкой данных* (data race), и приводит к ситуации неопределенного поведения.\r\n\r\nПример:\r\n```\r\nint64_t balance = 0;\r\n\r\n// Thread-1\r\nvoid* thread_func_one(void *arg) {\r\n    balance += (int64_t) arg;\r\n}\r\n\r\n// Thread-2\r\nvoid* yet_another_thread_func(void *arg) {\r\n    if (balance > 0) {\r\n        // use balance value\r\n    }\r\n    else {\r\n        // fail\r\n    }\r\n}\r\n```\r\n\r\nВ данной программе гонка данных возникает по причине того, что поток `Thread-2` полагается на неизменность значения переменной `balance` во время работы функции. В то же время, поток `Thread-1` совершенно независимо меняет это значение.\r\n\r\n\r\n## Критические секции\r\n\r\nЧасть программы, которая подразумевает монопольное использование какого-либо набора переменных, называется *критической секцией*. Начало критической секции подразумевает установки блокировки, которая будет препятствовать выполнению остальных потоков до тех пор, пока блокировка не будет снята.\r\n\r\nСтандартным инструментом для обозначения началала критической секции является захват *мьютекса*.\r\n\r\nМьютекс - это примитив синхронизации, который, в большинстве случаев, имеет два состояния. Исключение - рекурсивные мьютексы, которые один поток может захватывать N раз, но остальные потоки не смогут захватить мьютекс до тех пор, пока он не будет N раз освобожден.\r\n\r\nМьютекс объявлен в заголовочном файле `<pthread.h>`, и функции работы с ним требуют линковки с библиотекой `-pthread`.\r\n\r\n * `pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *attr)` - инициализация мьютекса для его последующего использования. Если второй параметр `NULL`, то инициализируется обычный (не рекурсивный) мьютекс. Для создания нового инициализированного мьютекса с параметрами по умолчанию можно использовать макрос `PTHREAD_MUTEX_INITIALIZER`.\r\n * `pthread_mutex_destroy(pthread_mutex_t *mutex)` - уничтожить ранее созданный мьютекс.\r\n * `pthread_mutex_lock(pthread_mutex_t *mutex)` - захватить мьютекс. Если другой поток уже захватил его, то текущий поток приостанавливает свою работу.\r\n * `pthread_mutex_trylock(pthread_mutex_t *mutex)` - пытается захватить мьютекс. В случае успеха возвращает значение `0`, а если мьютекс уже занят, то значение `EBUSY`.\r\n * `pthread_mutex_unlock(pthread_mutex_t *mutex)` - освободить ранее захваченный мьютекс. В отличии от семафоров, освободить мьютекс может только тот поток, которые его захватил, в противном случае это приведет к ошибке `EPERM`.\r\n\r\n\r\n ## Условные переменные\r\n\r\n Условная переменная - это некоторый примитив синхронизации, используемый для нотификации одним из потоков о наступлении некоторого события, например, готовности данных. Использование условных переменных позволяет отказаться от необходимости применения операций ввода-вывода, и тем самым, исключают необходимость испоьзования системных вызовов.\r\n\r\n С условной переменной связан определенный мьютекс, который разблокируется на время ожидания.\r\n\r\n  * `pthread_cond_init(pthread_cond_t *c, const pthread_condattr_t *attr)` - инициализации условной переменной. Второй параметр может быть `NULL` - в этом случае подразумевается использование переменной только в рамках одного процесса. Для инициализации условной переменной с параметрами по умолчанию используется макрос `PTHREAD_COND_INITIALIZER`.\r\n  * `pthread_cond_destroy(pthread_cond_t *c)` - уничтожить условную переменную.\r\n  * `pthread_cond_wait(pthread_cond_t *c, pthread_mutex_t *m)` - ожидает нотификации условной переменной переменной `c`, временно разблокируя мютекс `m`. Перед вызовом мьютекс должен находиться в заблокированном состоянии, в противном случае - неопределенное поведение. После наступления события нотификации, мьютекс опять блокируется.\r\n  * `pthread_cond_timedwait(pthread_cond_t *c, pthread_mutex_t *m, const struct timespec *timeout)` - то же, что и `pthread_cond_wait`, но ожидание прекращается по истечению указанного периода времени.\r\n  * `pthread_cond_signal(pthread_cond_t *c)` - уведомляет один поток, для которого выполняется ожидание нотификации. В общем случае, поток выбирается случайным образом, если их несколько.\r\n  * `pthread_cond_broadcast(pthread_cond_t *c)` - уведомляет все потоки, для которых выполняется ожидание нотификации.\r\n\r\nВ случае, если ни один поток не вызвал `pthread_cond_wait`, то нотификация условной переменной проходит незамеченной.\r\n\r\n\r\n## Атомарные переменные и неблокирующие структуры данных\r\n\r\nНеобходимость блокировки мьютекса, защищающего критическую секцию, может приводить к простою потоков, которые не собираются ничего модифицировать. В ситуации, когда потоков много, это существенно сказывается на производительности, и становится более выгодным использование lock-free структур данных, например односвязных списков.\r\n\r\nОсновная идея lock-free структур заключается в том, что любая модификация данных проводится каким-либо потоком в области памяти, которая не разделяется с другими потоками. В тот момент времени, когда данные подгтовлены и должны стать доступны остальным потокам, указатель на них записывается в достижимую другим потокам переменную, причем это происходит атомарным образом. Атомарность достигается за счёт того, что размер указателя в памяти не превышает размера машинного слова.\r\n\r\nНачиная со 2011 года, в языке Си появлилось новое глючевое слово `_Atomic` - модификатор типа, регламентирующий атомарный тип данных. В отличии от языка C++, где атомарным может быть произвольный объект (не совсем честно, поскольку для составных типов используется мьютекс), множество допустимых атомарных типов данных для языка Си ограничивается только типами, которые умещаются в машинное слово. В противном случае, ключевое слово `_Atomic` и атомарные операции не имеют никакого значения. Атомарные операции над типами, объявленными как `_Atomic`, реализуются в Си11 как макросы:\r\n\r\n * `void atomic_store(T* object, T value)`,\r\n * `void atomic_store_explicit(T* object, T value, memory_order order)` - сохранить значение в атомарную переменную.\r\n * `T atomic_load(T* object)`,\r\n * `T atomic_load_explicit(T* object, memory_order order)` - загрузить значение из переменной.\r\n * `T atomic_exchange(T* object, T new_value)`,\r\n * `T atomic_exchange_explicit(T* object, T new_value, memory_order order)` - заменить значение и вернуть предыдущее.\r\n * `_Bool atomic_compare_exchange_strong(T* object, T* expected, T new_value)`,\r\n * `_Bool atomic_compare_exchange_strong_explicit(T* object, T* expected, T new_value, memory_order success, memory_order failure)`,\r\n * `_Bool atomic_compare_exchange_weak(T* object, T* expected, T new_value)`,\r\n * `_Bool atomic_compare_exchange_weak_explicit(T* object, T* expected, T new_value, memory_order success, memory_order failure)` - сравнить два значения, в случае их равенства - заменить на новое, в противном случае - записать в `expected` значение `object`.\r\n * `T atomic_fetch_MOD(T* object, T operand)`,\r\n * `T atomic_fetch_MOD_explicit(T* object, T operand, memory_order order)` - получить значение переменной, после чего - модифицировать её. `MOD` можеть быть:\r\n  - `add` - инкремент\r\n  - `sub` - декремент\r\n  - `and` - поразрядное \"и\"\r\n  - `or` - поразрядное \"или\"\r\n  - `xor` - поразрядное \"исключающее или\".\r\n\r\nВ операции `compare_exchange` вариант `weak` обычно работает быстрее, но может ложно возвращать значение `0` даже при равенстве значений в `object` и `expected`. В некоторых алгоритмах это бывает допустимо, например, если значение проверяется в цикле.\r\n\r\n\r\n## Модели памяти\r\n\r\nКомпиляторы могут выполнять нетривиальные преобразования программ, применяя различные эвристики о том, как можно увеличить производительность программ. Компилятор имеет полное право выносить некоторые повторяющиеся дейсвтия из тела цикла, хранить значения переменных в регистрах, а не в памяти, и переставлять отдельные операции местами, если это не противоречит семантике исходной *однопоточной* программы.\r\n\r\nВсе это приводит к тому, что фактически наблюдаемый порядок изменения значений переменных в памяти для одного потока может отличаться от того порядка, которые предполагает другой поток.\r\n\r\nПример:\r\n```\r\n// Thread-1\r\ny = 0;\r\nx = 0;\r\n...\r\ny = 2;\r\nx = 1;\r\n\r\n// Thread-2\r\nif (2 == y) {\r\n    z = x;  // 0 или 1?\r\n}\r\n```\r\n\r\nВ данном случае переменная `z` может иметь значения как `0`, так и `1`, поскольку компилятор мог переставить местами инструкции `y=2` и `x=1`, считая такую перестановку не влияющей на выполнение кода функции, выполняемой в потоке `Thread-1`.\r\n\r\nАтомарные операции обычно окружены какими-то соседними инструкциями над обычными (не атомарными) переменными.\r\n\r\n*Модель памяти (memory order)* определяет, какие ограничения накладываются на перестановку обычных операций вокруг атомарных.\r\n\r\nОграничения `memory_order`:\r\n * `memory_order_relaxed` - отсутствие каких-либо ограничений на оптимизацию.\r\n * `memory_order_consume` - на существующих современных архитектурах совпадает с `memory_order_relaxed`.\r\n * `memory_order_acquire` - запрещает перестановку операций работы с памятью вперед данной операции.\r\n * `memory_order_release` - запрещает перестановку операций работы с памятью после данной операции.\r\n * `memory_order_acq_rel` - одновременно `memory_order_acquire` и `memory_order_release`.\r\n * `memory_order_seq_cst` - самые сильные ограничения на перестановку инструкций; все нити видят операции в том порядке, как они были прописаны в коде программы.\r\n\r\nПо умолчанию (то есть без явного указания модели памяти) подразумевается самая строгая модель `seq_cst`.\r\n"
  },
  {
    "path": "practice/openssl/README.md",
    "content": "# Шифрование с использованием OpenSSL/LibreSSL\n\n## Основы шифрования в Linux\n\nКриптография в Linux, как и во многих других UNIX-подобных системах реализована с помощью пакета `openssl` или совместимого с ним форка `libressl`.\n\nПакет предоставляет:\n * команду `openssl` для выполнения операций в командной строке\n * библиотеку `libcrypto` с реализацией алгоритмов шифрования\n * библиотеку `libssl` с реализацией взаимодействия по протоколам SSL и TLS.\n\n### Вычисление хеш-значений\n\nКоманды:\n * `openssl md5`\n * `openssl sha256`\n * `openssl sha512`\n\nвычисляют хеш-значение для указанного файла и выводят его в читаемом виде на стандартный поток выввода. Дополнительная опция `-binary` указывает вывод в бинарном формате. Если имя файла не указано, то вычисляется хеш-значение для данных со стандартного потока ввода.\n\n### Симметричное шифрование\n\nКоманда:\n\n```\nopenssl enc -ШИФР -in ИМЯФАЙЛА -out ВЫХФАЙЛ\n```\n\nВыполяет шифрование *симметричным ключем*, то есть некоторым \"паролем\", который является одинаковым как для шифрования, так и для обратной операции дешифрования.\n\nПолный список поддерживаемых шифров отображается командой `openssl enc -ciphers`. Наиболее часто используемые:\n\n * `des` - достаточно старый алгоритм с использованием 56-битного ключа;\n * `aes256` или `aes-256-cbc` - более надежный и достаточно быстрый;\n * `base64` - без шифрования (ключ не требуется); удобный способ конвертировать бинарные файлы в текстовое представление и обратно.\n\nОпция `-d` означает обратное преобразование, то есть *дешифрование*. Опция `-base64` подразумевает, что зашифрованные данные дополнительно преобразуются в кодировку Base64, например, для передачи данных в виде текста.\n\nПосле запуска команды будет запрошен пароль и его подтверждение. В случае, когда нужно автоматизировать запуск команды, используется опция `-pass`, после которой передается, каким именно образом задается пароль:\n * `pass:ПАРОЛЬ` - пароль задается обычным текстом в виде аргумента командной строки; жутко небезопасно;\n * `env:ПЕРЕМЕННАЯ` - пароль задается определенной переменной окружения; немного лучше, но можно выяснить через `/proc/.../environ`;\n * `file:ИМЯФАЙЛА` - пароль берется из файла;\n * `fd:ЧИСЛО` - пароль берется из файлового дескриптора с указанным номером; используется при запуске через `fork`+`exec`.\n\nПоскольку алгоритмы симметричного шифрования подразумевают использование ключа фиксированного размера, текстовый пароль произвольной длины предварительно преобразуется с помощью хеш-функции. По умолчанию используется SHA-256, но это можно задавать с помощью опции `-md АЛГОРИТМ`.\n\nПомимо пароля, в ключ входит ещё одна составляющая - *соль* размером 8 байт, которая хранится в самом зашифрованном файле. Это значение генерируется случайным образом, но для возпроизводимости результата может быть явным образом задана с помощью опции `-S HEX`, где `HEX` - восьмибайтное значение в шестнадцатеричной записи.\n\n\n### Шифрование с использованием пары ключей\n\nСтандартным алгоритмом для шифрования с использованием пары ключей считается RSA.\n\nГенерация ключей осуществляется командой:\n```\nopenssl genrsa -out ФАЙЛ РАЗРЯДНОСТЬ\n```\n\nЕсли имя выходного файла не указано, то ключ в текстовом формате будет сохранен на стандартный поток вывода. Обычно  ключи RSA хранят в файлах с суффиксом имени `.pem`.\n\nРазрядность определяет стойкость ключа, по умолчанию - 2048 бит.\n\nПоскольку приватный ключ где-то должен храниться, причем безопасным методом, хорошей практикой считается его хранение в зашифрованном виде, для этого используется шифрование с симметричным ключем:\n```\nopenssl genrsa -aes256 -passout ОПЦИИ_ПАРОЛЯ\n```\n\nПри использовании зашифрованного закрытого ключа, необходимо будет каждый раз указывать пароль, заданный при его создании.\n\nИзвлечение публичного ключа из приватного осуществляется командой:\n```\nopenssl rsa -in ПРИВАТНЫЙ_КЛЮЧ -out ПУБЛИЧНЫЙ_КЛЮЧ -pubout\n```\n\nЕсли при создании пары ключей использовалось шифрование, то необходимо ввести пароль, либо задать его через `-passin`.\n\nШифрование с использованием открытого ключа:\n```\nopenssl rsautl -encrypt -pubin -inkey ПУБЛИЧНЫЙ_КЛЮЧ -in ФАЙЛ -out ВЫХОД\n```\n\nОбратная операция с использованием закрытого ключа:\n```\nopenssl rsautl -decrypt -inkey ПРИВАТНЫЙ_КЛЮЧ -in ФАЙЛ -out ВЫХОД\n```\n\nОграничением алгоритма RSA является то, что размер шифруемых данных не может превышать размер ключа. С этим можно бороться следующими способами:\n 1. Делить исходные данные на блоки размером по 2 или 4 Кбайт и шифровать их по-отдельности\n 2. Генерировать случаным образом одноразовый *сеансовый ключ*, который будет использован в паре с алгоритмом симметричного шифрования, но сам будет зашифрован с помощью RSA.\n\n```\n# Генерируем случайный ключ длиной 30 байт и сохраняем\n# его текстовое Base64 представление в переменной $KEY\nKEY=`openssl rand -base64 30`\n\n\n# Шифруем симметричный ключ с помощью открытого ключа RSA\necho $KEY | openssl rsautl -encrypt -pubin \\\n                           -inkey public.pem \\\n                           -out symm_key_encrypted.bin\n\n# Шифруем данные из большого файла README.md симметричным\n# ключем из переменной $KEY, которая предварительно\n# экспортируется, чтобы быть доступной дочернему процессу\nexport KEY\nopenssl enc -aes256 -in README.md \\\n                    -out README_encrypted.bin \\\n                    -pass env:KEY\n\n# Забываем сеансовый ключ - он больше не нужен\nunset KEY\n```\n\nДалее можно смело передавать по незащищенному каналу файлы `README_encrypted.bin`, который содержит данные, и `symm_key_encrypted.bin` с зашифрованным симметричным ключем.\n\nДля расшифровки необходимо восстановить сеансовый симметричный ключ, и используя его - дешифровать данные:\n```\n# Расшифровываем сеансовый симметричный ключ с помощью\n# приватного ключа RSA и сохраняем в переменной $KEY\nKEY=`openssl rsautl -decrypt \\\n                    -inkey private.pem \\\n                    -in symm_key_encrypted.bin`\n\n# Выполняем декодирование файла с данными, используя\n# полученный сеансовый ключ\n  export KEY\n  openssl enc -d -aes256 -pass env:KEY \\\n              -in README_encrypted.bin\n\n# Забываем расшифрованный сеансовый ключ\nunset KEY\n```\n\n\n## API библиотеки `libcrypto`\n\nВ качестве онлайн-документации по API OpenSSL удобнее использовать документацию из проекта [LibreSSL](https://www.libressl.org).\n\n### Использование с CMake\n\nЕсли сторонний фреймворк состоит из нескольких библиотек, то команда `find_package` позволяет указать, какие именно необходимо использовать, указав их перечень после `COMPONENTS`:\n```\nfind_package(OpenSSL COMPONENTS Crypto REQUIRED)\n```\n\nВ случае успешного нахождения `libcrypto` из OpenSSL, будут определены переменные `${OPENSSL_INCLUDE_DIR}` и `${OPENSSL_CRYPTO_LIBRARY}`.\n\n\n### Workflow\n\nПреобразования данных криптографическими функциями подразумевает три стадии:\n 1. Инициализация - функции, заканчивающиеся на `Init`\n 2. Добавление очередной порции данных с помощью одной из функций, имя которой заканчивается на `Update`. Этот процесс можно повторять итеративно по мере поступления данных\n 3. Финализация - функции, оканчивающиеся на `Final`; на этом этапе появляется итоговый результат преобразования.\n\n\n### Функции `libcrypto`\n\nФункции, имена которых начинаются с `SHA` или `MD5` предназначены для вычисления хеш-значений. Они используют простой workflow из трех стадий.\n\nДля кодирования или декодирования с использованием симметричного ключа используется стандартный workflow, но на стадии инициализации настраивается *контекст* - переменная, которая хранит состояние шифрующего автомата.\n\nПример:\n```\n// Создание контекста\nEVP_CIPHER_CTX* ctx = EVP_CIPHER_CTX_new();\n\n// Генерация ключа и начального вектора из\n// пароля произвольной длины и 8-байтной соли\nEVP_BytesToKey(\n  EVP_aes_256_cbc(),    // алгоритм шифрования\n  EVP_sha256(),         // алгоритм хеширования пароля\n  salt,                 // соль\n  password, strlen(password), // пароль\n  1,                    // количество итераций хеширования\n  key,          // результат: ключ нужной длины\n  iv            // результат: начальный вектор нужной длины\n);\n\n// Начальная стадия: инициализация\nEVP_DecryptInit(\n  ctx,                  // контекст для хранения состояния  \n  EVP_aes_256_cbc(),    // алгоритм шифрования\n  key,                  // ключ нужного размера\n  iv                    // начальное значение нужного размера\n);\n```\n"
  },
  {
    "path": "practice/posix_dirent_time/README.md",
    "content": "# POSIX API для работы с файловой системой и временем\n\n## Каталоги\n\nКаталоги в UNIX-системах - это специальный вид файлов, который содержит набор пар `{inode, name}` для построения иерархии файловой системы.\n\nКак и обычные файлы, каталоги могут быть открыты на чтение или запись с помощью системного вызова `open`. В системе Linux существует не обязательный флаг открытия `O_DIRECTORY`, единственное назначие которого - проверить, что открываемый файл действительно является каталогом, а не файлом другого типа; в противном случае - диагностировать ошибку.\n\n### Функции для работы с каталогами\n\nФормат специально файла-каталога зависит от конкретной операционной системы, и абстракцией на уровне POSIX является функции (не системные вызовы!) стандартной библиотеки Си:\n\n```\n#include <dirent.h>\n\n// открытие каталога\nDIR *opendir(const char *dirname);\nDIR *fdopendir(int fd);\n\n// закрытие каталога\nint closedir(DIR *dirp);\n```\n\nОткрытый каталог описывается структурой `DIR`, которая при открытии каталога размещается в куче, и необходимо её освобождать с помощью функции `closedir`.\n\nЧтение из потока каталога осуществляется функцией `readdir`, единицей чтения которой является не байт, а элемент структуры `dirent`:\n```\nstruct dirent {\n    ino_t  d_ino;  // inode файла в файловой системе\n    char   d_name[NAME_MAX+1]; // имя файла\n    /* дальше могут быть ещё какие-нибудь нестандартные поля */\n};\n```\nВ системе Linux максимальное имя файла равно 255 (`NAME_MAX`)символам, но при этом, максимальная длина пути к файлу - 4Кб (`PATH_MAX`).\n\nПеремещение текущего указателя чтения записей в каталоге осуществляется с помощью функций `seekdir` и `telldir`.\n\nКаждый каталог, даже пустой, содержит, как минимум, две записи:\n * специальный файл `.` - каталог, inode которого совпадает с inode того каталога, в котором он содержится;\n * специальный файл `..` - каталог, inode которого совпадает с inode каталога на уровень выше, либо корневого каталога, если каталог уровнем выше не существует.\n\nДругие функции работы с каталогами:\n\n * `getcwd` - получить текущий рабочий каталог;\n * `chdir` - сменить текущий каталог.\n\n\n### 'at'-функции\n\nДля того, чтобы упростить работу с относительными путями файлов, в современных версиях `glibc` (Linux и FreeBSD) присуствуют функции для открытия файловых дескрипторов относительно открытого каталога:\n\n```\n// Аналоги open\nint openat(int dirfd, const char *pathname, int flags);\nint openat(int dirfd, const char *pathname, int flags, mode_t mode);\n\n// Аналог stat\nint fstatat(int dirfd, const char *pathname, struct stat *statbuf, int flags);\n```\n\n## Пользователи и группы\n\nИнформация о пользователях и группах может храниться как в локальном источнике, например в файлах `/etc/passwd` и `/etc/groups`, так и на удаленных серверах, например LDAP.\n\nИнформацию о пользователе или группе можно получить с помощью одной из функций:\n * `struct passwd *getpwnam(const char *name)` - получить информацию о пользователе по имени;\n * `struct passwd *getpwuid(uid_t uid)` - получить информацию о пользователе по его User ID;\n * `struct group *getgrnam(const char *name)` -  получить информацию о группе по имени;\n * `struct group *getgrgid(gid_t gid)` - получить информацию о группе по её Group ID.\n\n\n## Работа со временем\n\n### Текущее время\n\nВремя в UNIX-системах определяется как количество секунд, прошедшее с 1 января 1970 года, причем часы идут по стандартному гринвичскому времени (GMT) без учета перехода на летнее время (DST - daylight saving time).\n\n32-разрядные системы должны прекратить своё нормальное существование 19 января 2038 года, поскольку будет переполнение знакового целого типа для хранения количества секунд.\n\nФункция `time` возвращает количество секунд с начала эпохи. Аргументом функции (в который можно передать `NULL`) является указатель на переменную, куда требуется записать результат.\n\nВ случае, когда требуется более высокая точность, чем 1 секунда, можно использовать системный вызов `gettimeofday`, который позволяет получить текущее время в виде структуры:\n```\nstruct timeval {\n  time_t      tv_sec;  // секунды\n  suseconds_t tv_usec; // микросекунды\n};\n```\n\nВ этом случае, несмотря на то, что в структуре определено поле для микросекунд, реальная точность будет составлять порядка 10-20 миллисекунд для Linux.\n\nБолее высокую точность можно получить с помощью системного вызова `clock_gettime`.\n\n\n### Разложение времени на составляющие\n\nЧеловеко-представимое время состоит из даты (год, месяц, день) и времени суток (часы, минуты, секунды).\n\nЭто описывается структурой:\n```\nstruct tm { /* время, разбитое на составляющие */\n  int tm_sec; /* секунды от начала минуты: [0 -60] */\n  int tm_min; /* минуты от начала часа: [0 - 59] */\n  int tm_hour; /* часы от полуночи: [0 - 23] */\n  int tm_mday; /* дни от начала месяца: [1 - 31] */\n  int tm_mon; /* месяцы с января: [0 - 11] */\n  int tm_year; /* годы с 1900 года */\n  int tm_wday; /* дни с воскресенья: [0 - 6] */\n  int tm_yday; /* дни от начала года (1 января): [0 - 365] */\n  int tm_isdst; /* флаг перехода на летнее время: <0, 0, >0 */\n};\n```\nДля преобразования человеко-читаемого времени в машинное используется функция `mktime`, а в обратную сторону - одной из функций: `gmtime` или `localtime`.\n\n\n### Daylight Saving Time\n\nВо многих странах используется \"летнее время\", когда стрелки часов переводятся на час назад.\n\nИстория введения/отмены летнего времени, и его периоды хранится в [базе данных IANA](https://data.iana.org/time-zones/tz-link.html).\n\nБаза данных представляет собой набор правил в тектовом виде, которые компилируются в бинарное представление, используемое библиотекой glibc. Наборы файлов с правилами перехода на летнее время для разных регионов хранятся в `/usr/share/zoneinfo/`.\n\nКогда значение `tm_isdst` положительное, то применяется летнее время, значение `tm_isdst` - зимнее. В случае, когда значение `tm_isdst` отрицательно, - используются данные из timezone data.\n\n\n## Reentrant-функции\n\nМногие функции POSIX API разрабатывались во времена однопроцессорных систем. Это может приводить к разным неприятным последствиям:\n\n```\nstruct tm * tm_1 = localtime(NULL);\nstruct tm * tm_2 = localtime(NULL); // opps! *tm_1 changed!\n```\n\nПроблема заключается в том, что некоторые функции, например `localtime`, возвращает указатель на структуру-результат, а не скалярное значение. При этом, сами данные структуры не требуется удалять, - они хранятся в `.data`-области библиотеки glibc.\n\nПроблема решается введением *повторно входимых (reentrant)* функций, которые в обязательном порядке трубуют в качестве одного из аргументов указатель на место в памяти для размещения результата:\n```\nstruct tm tm_1; localtime_r(NULL, &tm_1);\nstruct tm tm_2; localtime_r(NULL, &tm_2); // OK\n```\n\nИспользование повторно входимых функций является обязательным (но не достаточным) условием при написании многопоточных программ.\n\nНекоторые reentrant-функции уже не актуальны в современных версиях glibc для Linux, и помечены как deprecated. Например, реализация `readdir` использует локальное для каждого потока хранение данных. \n"
  },
  {
    "path": "practice/posix_ipc/README.md",
    "content": "# Средства межпроцессного взаимодействия POSIX\r\n\r\n\r\n## Разделяемая память\r\n\r\nДля создания сегментов разделяемой памяти используется механизм [отображаемых файлов mmap](../mmap). Выполнение системного вызова `mmap` с параметрами `MAP_SHARED` и указанием файлового дескпритора позволяет использовать некоторое имя в файловой системе для взаимодействия между собой неродственных процессов.\r\n\r\nДля того, чтобы избежать создания файлов на диске (которые занимают место), предусмотрен механизм создания ортогонального пространства имен (\"ключей\") для разделяемых файлов, которые существуют только в памяти.\r\n\r\nКаждый ключ - это некоторая строка длиной до `NAME_MAX` байт, которая должна начинаться с символа `'/'`. У ключей могут быть права доступа и владелец, по аналогии с обычными файлами.\r\n\r\nТакие разделяемые объекты существуют до перезагрузки компьютера, либо до их явного удаления одним из процессов.\r\n\r\n\r\n### Функции для работы с разделяемыми файлами\r\n\r\n * `int shm_open(const char *name, int flags, mode_t mode)` - по аналогии с системным вызовом `open`, открывает ключ по имени, как обычный файл. Если среди флагов в `flags` присутствует опция `O_CREAT`, то третий аргумент подразумевает права доступа, в противном случае его значение игнорируется. Возвращает файловый дескриптор, который можно передать в системный вызов `mmap`.\r\n * `int shm_unlink(const char *path)` - удаляет имя из памяти. Если разделяемый файл был имеет отображение в одном или нескольких процессах, то он продолжает быть доступным и занимать место в памяти.\r\n\r\nПрава доступа можно настраивать с помощью системных вызовов `fchmod`  и `fchown`, которые, в отличии от команд shell'а и системных вызовов, работающих с именами, позволяют работать с файловыми дескрипторами.\r\n\r\nПри создании объекта, он имеет размер `0`, и может быть изменен с помощью `ftruncate`.\r\n\r\n\r\n### Особенности реализации в Linux\r\n\r\nВ операционной системе Linux (в отличии, например, от FreeBSD), объекты разделяемой памяти - это самые обычные файлы, которые располагаются в файловой системе `tmpfs`, примонтированной в `/dev/shm`.\r\n\r\nКроме того, есть дополнительные особенности:\r\n * для использования функций разделяемых объектов POSIX, нужно указывать опцию `-lrt`, посколько `glibc` разбита на несколько частей;\r\n * требование про символ `/` в начале имени ключа является не обязательным; тем не менее, это противоречит стандарту `POSIX` и в BSD системах приводит к ошибке `EINVAL`, а в системе `QNX` просто создаст обычный файл на диске в текущем каталоге.\r\n\r\n\r\n## Семафоры\r\n\r\nСемафор - это беззнаковая целочисленная переменная, которая обладает дополнительными свойтсвами:\r\n\r\n * существуют операции увеличения и уменьшения счетчика, которые выполняются атомарно;\r\n * при попытке уменьшить счетчик, который равен `0`, выполняется приостановка процесса (или нити), который пытается это сделать;\r\n * при увеличении счетчика, если существует какой-то приостановленный процесс, который пытался его уменьшить, работа этого процесса возобновляется.\r\n\r\nВ теоретической литературе операция *захвата семафора* (уменьшения значения) обычно обозначается буквой `P` (proberen), а операция *освобождения* (увеличение значения) - буквой `V` (verhogen), - названия операций заимствованы из нидерландского языка, т.к. семафоры изобрел Дейкстра.\r\n\r\nСемафоры часто используют для синхронизации между собой нескольких потоков выполнения, и в частности, они могут использоваться для предотвращения гонки данных.\r\n\r\n### Семафоры POSIX\r\n\r\nСемафоры POSIX определяются некотроым типом `sem_t`, объявленным в файле `<semaphore.h>`, реализация которого, в общем случае, считается неопределенной, и зависит не только от конкретной операционной системы, но и от процессора.\r\n\r\nФункции работы с семафорами обычно принимают его по указателю:\r\n * `sem_wait(sem_t *sem)` - захватить семафор (операция `P`);\r\n * `sem_post(sem_t *sem)` - освободить семафор (операция `V`);\r\n * `sem_trywait(sem_t *sem)` - попытаться захватить семафор, если он равен нулю, то процесс не блокируется, а функция вовзращает значение `-1`, прописывая значение `EAGAIN` в `errno`;\r\n * `sem_timedwait(sem_t *sem, struct timespec *timeout)` - захватывает семафор, но если за указанный промежуток времени он не был разблокирован, то функция завершает свою работу с ошибкой `ETIMEDOUT`;\r\n * `sem_getvalue(sem_t *sem, int *out_var)` - читает численное значение семафора, не блокируя его; эта функция бывает полезна при отладке.\r\n\r\nВ системе Linux для использования функций работы с семафорами необходимо компоновать программу с опцией компилятора `-pthread`.\r\n\r\nПеред использованием, семафоры должны быть корректно инициализированы. Инициализиция зависит от типа семафора.\r\n\r\n### Анонимные семафоры\r\n\r\nАнонимные семафоры - это семафоры, которые доступны только в рамках одного адресного пространства (для многопоточности), либо родственным процессам.\r\n\r\nСоздаются с попощью функции `sem_init`:\r\n\r\n```\r\nint sem_init(sem_t *sem,    // указатель на семафор в памяти\r\n             int pshared,   // 0 - если предназначен для использования\r\n                            // в рамках одного адресного пространства,\r\n                            // 1 - если разными процессами\r\n             unsinged value // начальное значение\r\n            )\r\n```\r\n\r\nУничтожаются анонимные семафоры с помощью функции `sem_destroy`. При этом ситуация, когда какие-то процессы или нити были заблокированы семафором, считается **неопределенным поведением**, и может приводить к полной блокировке.\r\n\r\nСемафоры, которые предназначены для использования разными процессами, должны находиться в разделяемой через `mmap` области памяти, доступной всем задействованным процессам. В противном случае, изменения семафора в одном процессе не будут видны остальным.\r\n\r\n### Именованные семафоры\r\n\r\nИменованные семафоры - это реализация семафоров совместно с механизмом разделяемой памяти POSIX. Имена семафоров подчиняются тем же правилам, что имена сегментов разделяемой памяти, за одним исключением: максимальная длина имени на 4 байта короче, т.к. к имени семафора автоматически приписывается (в зависимости от реализации) суффикс `.sem` или префикс `sem.`.\r\n\r\nСоздаются именованные семафоры с помощью `sem_open`:\r\n```\r\nsem_t *sem_open(const char *name, int oflag);\r\nsem_t *sem_open(const char *name, int oflag,\r\n                mode_t mode, unsigned int value);\r\n```\r\nПо аналогии с `open`, если присутствует флаг `O_CREAT`, то нужно указать права доступа, и кроме того, - начальное значение. В отличии от обычных файлов, не нужно указывать флаги, определяющие режим открытия на чтение/запись. Если открывается существующий семафор, то `oflag=0`.\r\n\r\nЗакрытие именованного семафора с помощью `sem_close`, в отличии от анонимного, никак не влияет на процессы, которые могут быть им заблокированы: значение счетчика остается неизменным, и может быть изменено после повторного открытия.\r\n\r\nПрименять операцию `sem_destroy` для именованных семафоров запрещено, так же как и операцию `sem_close` - для анонимных.\r\n\r\nДля удаления имени семафора используется функция `sem_unlink(const char *name)`. Как и в случае обычного файла, значение семафора сохраняется даже после удаления до тех пор, пока он не будет закрыт всеми использующими его процессами.\r\n"
  },
  {
    "path": "practice/pthread/README.md",
    "content": "# Многопоточность\r\n\r\n## Общие сведения\r\n\r\nПоток (нить, легковесный процесс) - единица планирования времени в рамках одного процесса.\r\n\r\nВсе потоки в рамках одного процесса разделяют общее адресное пространство и открытые файловые дескрипторы.\r\n\r\nДля каждого потока предусмотрен свой отдельный стек фиксированного размера, который располагается в общем адресном пространстве. В конце стека для каждого потока обычно присутствует небольшой участок памяти (Guad Page), предназначенный для того, чтобы предотвратить ситуацию перезаписи данных другого потока в результате, например, его переполнения.\r\n\r\nВ каждом процессе существует как минимум один поток, выпонение которого начинается с функции `_start`.\r\n\r\nВ отличии от обычных процессов, которые имеют иерархическую структуру \"родитель-ребенок\", все потоки являются равнозначными.\r\n\r\n## POSIX Threads\r\n\r\nСтандартом для UNIX-систем является POSIX Threads API. В системе Linux (как и во FreeBSD), ввиду фрагментации стандартной библиотеки, компоновка программ должна проводиться с опцией компилятора `-pthread`.\r\n\r\nВ отличии от большинства других функций POSIX, в случае ошибки, функции из pthread не прописывают их код в переменную `errno`, а возвращают различные целочисленные значения, отличные от `0`, которые соответствуют определенным ошибкам.\r\n\r\n\r\n### Создание и запуск нового потока\r\n\r\n```\r\nint pthread_create(\r\n    // указатель на переменную-результат\r\n    pthread_t *thread,\r\n\r\n    // опционально: параметры нового потока,\r\n    // может быть NULL\r\n    const pthread_attr_t *attr,\r\n\r\n    // функция, которая будет выполняться\r\n    (void*)(*function)(void*),\r\n\r\n    // аргумент, который передается в функцию\r\n    void *arg\r\n    );\r\n```\r\n\r\nФункция `pthread_create` создает новый поток, и сразу же запускает в нем на выполнение функцию, которая передана в качестве аргумента.\r\n\r\nВыполняемая функция должна принимать единственный аргумент размером с машинное слово (`void*`), и этот аргумент передается одновременно с созданием потока. Возвращаемое значение выполняемой функции можно будет получить после её выполнения о ожидания завершения потока.\r\n\r\n### Завершение работы потока и результат работы\r\n\r\nПоток завершается в тот момент, когда завершается выполнение функции, либо пока не будет вызван аналог `exit` для потока - функция `pthread_exit`.\r\n\r\nВозвращаемые значения размером больше одного машинного слова, которые являются результатом работы потока, не могут быть размещены в стеке, поскольку стек будет уничтожен при завершении работы функции.\r\n\r\nДождаться завершения потока и получить результат можно с помощью функции `pthread_join`\r\n\r\n```\r\nint pthread_join(\r\n    // поток, который нужно ждать\r\n    pthread_t thread,\r\n\r\n    // указатель на результат работы функции,\r\n    // либо NULL, если он не интересен\r\n    (void*) *retval\r\n    );\r\n```\r\n\r\nФункция `pthread_join` ожидает завершения работы определенного потока, и получает результат работы функции.\r\n\r\nВозможна ситуация, приводящая к deadlock'у, когда два потока вызывают друг для друга ожидание. Функция `pthread_join` проверяет эту ситуацию, и завершается с ошибкой (не блокируя выполнение).\r\n\r\n```\r\npthread_t a;\r\npthread_t b;\r\n\r\nvoid* thread_func_a(void *) {\r\n    sleep(1);\r\n    pthread_join(b, NULL);\r\n}\r\n\r\nvoid* thread_func_b(void *) {\r\n    sleep(1);\r\n    pthread_join(a, NULL);\r\n}\r\n\r\n// Bug: Deadlock, but detected\r\npthread_create(&a, NULL, thread_func_a, 0);\r\npthread_create(&b, NULL, thread_func_b, 0);\r\n```\r\n\r\nТакая проверка возможна только при попытке ожидать поток, который уже ожидает поток, пытающийся вызвать `pthread_join`. В случае нескольких потоков, которые косвенно ожидают друг друга, такая диагностика невозможна, и приведет к deadlock'у.\r\n\r\n### Принудительное завершение потока\r\n\r\nФункция `pthread_cancel` принудительно завершает работу потока, если поток явно это не запретил с помощью функции `pthread_setcancelstate`.\r\n\r\n```\r\nint pthread_cancel(\r\n    // поток, который нужно прибить\r\n    pthread_t thread\r\n    );\r\n```\r\n\r\nРезультатом работы функции, который будет передан в `pthread_join` будет специальное значение `PTHREAD_CANCELED`.\r\n\r\nВ системе Linux остановка потоков реализована через отправку процессом самому себе сигнала реального времени с номером `32`.\r\n\r\nПринудительное завершение потока вовсе не означает, что поток будет немедленно остановлен. Функция `pthread_cancel` только проставляет флаг остановки, и этот флаг может быть проверен только во время определенного набора системных вызовов и функций стандартной библиотеки, которые называются *Cancelation Points*.\r\n\r\nПолный список функций, которые могут быть прерваны, перечислен в разделе `7` man-страницы `pthreads`.\r\n\r\nНекоторые системы, в том числе Linux, позволяют принудительно завершить поток даже вне Cancelation Points. Для этого поток должен вызывать функцию `pthread_setcanceltype` с параметром `PTHREAD_CANCEL_ASYNCHRONOUS`. После этого завершение потока будет осуществляться на уровне планировщика заданий.\r\n\r\n\r\n### Атрибуты потока\r\n\r\nАтрибуты потока (второй параметрв в `pthread_create`) хранятся в структуре `pthread_attr_t`, объявление которой является платформо-зависимым, и не регламентируется стандартом POSIX.\r\n\r\nДля инициализации атрибутов используется функция `pthread_attr_init(pthread_attr_t *attr)`, и кроме того, после использования, структуру атрибутов необходимо уничтожать с помощью `pthread_attr_destroy`.\r\n\r\nС помощью нескольких функций-сеттеров можно задавать определенные параметры вновь создаваемого потока:\r\n\r\n * `pthread_attr_setstacksize` - установить размер стека для потока. Размер стека должен быть кратен размеру страницы памяти (обычно 4096 байт), и для него определен минимальный размер, определяемый из параметров системы `sysconf(_SC_THREAD_STACK_MIN)` или константой `PTHREAD_STACK_MIN` из `<limits.h>` (в Linux это 16384 байт);\r\n * `pthread_attr_setstackaddr` - указать явным образом адрес размещения памяти, которая будет использована для стека;\r\n * `pthread_attr_setguardsize` - установить размер защитной области после стека (Guard Page). По умолчанию в Linux этот размер равен размеру страницы памяти, но можно явно указать значение 0.\r\n"
  },
  {
    "path": "practice/python/README.md",
    "content": "# Python: расширение и внедрение\n\nОсновной источник (на английском): [Extending and Embedding](https://docs.python.org/3.6/extending/index.html).\n\nСправочная информация (на английском): [Python/C API](https://docs.python.org/3.6/c-api/index.html).\n\n## Ипользование интерпретатора Python\n\nИнтерпретатор Python реализован в разделяемой библиотеке, а исполняемый файл интерпретатора `python3` является лишь оболочкой для запуска интерпретатора.\n\nДля сборки программы можно использовать CMake, в стандартной поставке которого входит поддержка Python.\n\n```cmake\nfind_package(PythonLibs 3.6 REQUIRED)\ninclude_directories(${PYTHON_INCLUDE_DIRS})\ntarget_link_libraries(program ${PYTHON_LIBRARIES})\n```\n\nОбратите внимание на то, что необходимо указывать минимальную версию интерпретатора, поскольку в поставку многих дистрибутивах Linux входит две версии Python (2.7 и 3.x), и может возникнуть неоднозначность используемой библиотеки.\n\nТривиальная реализация своего интерпретатора, с использованием библиотеки `python` выглядит следующим образом:\n\n```c\n#include <stdio.h>\n// В этом заголовочном файле собран почти весь API Python\n#include <Python.h>\n\nint main(int argc, char *argv[])\n{\n    // Открытие файла на чтение\n    FILE* fp = fopen(argv[1], \"r\");\n    // Инициализация интерпретатора Python\n    Py_Initialize();\n    // Выполнение файла\n    PyRun_SimpleFile(fp, argv[1]);\n    // Завершение работы интерпретатора\n    Py_Finalize();\n    // Закрытие файла\n    fclose(fp);\n}\n```\n\nУказание имени файла в качестве второго аргумента является желательным по двум причинам: оно используется при генерации сообщении возникающих исключительных ситуаций, и кроме того, используется для определения пути поиска зависимых модулей. Переданный текст доступен через глобальную переменную `__file__` и может быть произвольным.\n\n```python\n/* Си */\nPyRun_SimpleFile(fp, \"abrakadabra\");\n\n# Python\nprint(__file__)\n# abrakadabra\n```\n\nСкрипт на языке Python может иметь аргументы командной строки, которые доступны через переменную-список `sys.argv`. В приведенной выше тривиальной реализации интерпретатора это значение не установлено, поэтому обращение к `sys.argv` выдаст ошибку о том, что эта переменная не определена:\n\n```\nAttributeError: module 'sys' has no attribute 'argv'\n```\n\nПеред запуском файла на выполнение можно установить список аргументов с помощью `PySys_SetArgv`:\n\n```c\nwchar_t* args[] = { L\"One\", L\"Two\", L\"Аргумент\" };\nPySys_SetArgv(3, args); // int argc, wchar_t *argv[]\n```\n\nОбратите внимание на то, что строки в Python являются многобайтовыми, поэтому многие функции API подразумевают работу с типом данных `wchar_t`.  В случае использования однобайтных цепочек символов используется системная локаль, как правило это UTF-8.\n\nПрограммой может быть не только текст программы, хранящийся в файле, но и произвольная строка текста:\n\n```c\nPyRun_SimpleString(\"a=1\\nb=2\\nprint(a+b)\");\n// будет выведено 3\n```\n\nПри выполнении текста, с которым не связан никакой файл, глобальная переменная `__file__` считается не определенной, а в случае возникновения исключений, в качестве файла-источника будет использован текст `<string>`. \n\n```c\nPyRun_SimpleString(\"print(__file__)\");\n\nTraceback (most recent call last):\n  File \"<string>\", line 1, in <module>\nNameError: name '__file__' is not defined\n```\n\n## API стандартных классов Python\n\nЯзык Python содержит реализацию стандартных контейнерных классов, которые являются встроенными типами Python, но их можно использовать и без интерпретации текста программы.\n\nБазовым классом для всех объектов является класс `object`, которому в Си API соответствует базовый класс `PyObject`. Поскольку интерптетатор реализован на языке Си, который не является объектно-ориентированным, то на пользователя API возлагается ответственноть за контролем используемых типов.\n\nВсе классы и методы в PyObject API именуются следующим образом:`PyКласс_Метод`, а указатель на объект класса передается в качестве первого аргумента.\n\nПримеры стандартных классов и методов:\n\n* `PyList_Append(PyObject *list, PyObject *item)` - эквивалент `list.append(item)` \n* `PyDict_SetItem(PyObject *p, PyObject *key, PyObject *val)` - эквивалент `p[key]=val`\n\nОбратите внимание, что для объектов всех классов используется тип `PyObject*`, поэтому необходимо предварительно проверять, какой тип имеет переменная с помощью одной из функций вида `PyКласс_Check(PyObject *p)`, которая возвращает ненулевое значение в случае принадлежности объекта к классу, и `0` в противном случае.\n\nСоответствие стандартных типов Python префиксам функций PyObject API: `list` - `PyList_`, `tuple` - `PyTuple_`, `dict` - `PyDict_`, `str` - `PyUnicode_`, `bytes` - `PyBytes_`, `file` - `PyFile_`, `int` - `PyLong_`, `float` - `PyFloat_`.\n\nОбратите внимание, что тип для строк называется `PyUnicode`, а не `PyString`. Это связано с тем, что во времена Python 2 строки были двух видов: однобайтные и юникодные, а в Python 3 остались только юникод-строки. \n\n**Пример использования API без интерпретатора**: разбить текст на лексемы, выделяя целые числа как числа, а остальные слова оставляя строками.\n\nЭтому коду соответствует программа на Python:\n\n```python\ntext = \"сейчас 23 59 не время спать\"\nresult = []\ntokens = text.split(\" \")\nfor entry in tokens:\n\ttry:\n        number = int(entry)\n        result += [number]\n    except:\n        result += [ entry.upper() ]\nprint(result)\n# ['сейчас', 23, 59, 'не', 'время', 'спать']\n```\n\n```c\nint main()\n{\n    // Если не используются wchar_t*, то по умолчанию подразумевается,\n    // что все однобайтные строки - в кодировке UTF-8\n    static const char TheText[] = \"сейчас 23 59 не время спать\";\n\n    // Инициализация API Python\n    Py_Initialize();\n\n    // Создание Python-строк из Си-строк\n    PyObject *py_text = PyUnicode_FromString(TheText);    \n    PyObject *py_space_symbol = PyUnicode_FromString(\" \");\n    \n    // Создание пустого списка\n    PyObject *py_result = PyList_New(0);\n    // str.split(py_text, py_space_symbol, maxsplit=-1)\n    PyObject *py_tokens = PyUnicode_Split(py_text, py_space_symbol, -1);\n    PyObject *py_entry = NULL;\n    PyObject *py_number = NULL;\n    \n    // Цикл по элементам списка. PyList_Size - его размер\n    for (int i=0; i<PyList_Size(py_tokens); ++i) {\n        // list.__getitem__(i) - этому методу соответствует оператор []\n        py_entry = PyList_GetItem(py_tokens, i);\n        // Попытка создать int из строки, base=10\n        // В случае не успеха устанавливается ошибка ValueError\n        py_number = PyLong_FromUnicodeObject(py_entry, 10);\n        // Проверяем, не возникло ли исключение\n        if (! PyErr_Occurred()) {\n            // OK - преобразование int(py_entry) выполнено успешно\n            PyList_Append(py_result, py_number);            \n        }\n        else {\n            // Возникло исключение, оставляем просто текст\n            PyList_Append(py_result, py_entry);\n            // Убираем флаг ошибки, так как мы её обработали.\n            // Если этого не сделать, то это исключение попадет\n            // в интерпретатор, как только он будет использован\n            PyErr_Clear();\n        }\n    }\n    // Вывод print(repr(py_result))\n    // Если последний параметр Py_PRINT_RAW вместо 0,\n    // то вместо repr() будет использована функция str() для\n    // преобразования произвольного объекта к строковому виду\n    PyObject_Print(py_result, stdout, 0);\n\n    Py_Finalize();\n}\n```\n\n## Расширениие функциональности Си-модулями\n\nСкрипты на Python, выполняемые интерпретатором, могут использовать любые модули через `import`, в этом случае выполняется их поиск в одном из каталогов, перечисленных в списке `sys.path`. Некоторые из модулей являются *встроенными* (built-in), и не загружаются из внешних файлов, а создаются самим интерпретатором.\n\nДля доступа к функциональности приложения, в который встраивается интерпретатор, можно использовать встроенные модули, которые взаимодействуют с самим приложением.\n\n```python\n# создадим модуль с названием 'app', который реализует функциональность\nimport app\n# модуль может содержать функции\napp.do_something()\n# и какие-нибудь глобальные переменные\nprint(app.some_value)\n```\n\nПри выполнении строки `import` интерпретатор выполняет поиск модуля, и в случае успеха, создает новый объект модуля с указанным именем, используя его в качестве глобального пространства имен, в котором выполняется инициализация модуля.\n\nИниализация выполняется ровно один раз, независимо от того, сколько раз импортируется модуль. Для обычных Python-модулей, его инициализация заключается в выполнении текста программы, а для встроенных модулей - в вызове функции, которая возвращает новый модуль.\n\n```c\nstatic PyObject *\ncreate_module() {\n    // NULL в качестве возвразаемого значения любой функции,\n    // которая должна возвращать PyObject*, означает \n    // исключительную ситуацию\n    PyErr_SetString(PyExc_RuntimeError, \"Not implemented yet\");\n    return NULL;\n}    \n\nint main(int argc, char *argv[]) {\n    \n    // Добавляем в таблицу имя встроенного модуля\n    PyImport_AppendInittab(\"app\", create_module);\n    \n    // Регистрация встроенных модулей должна быть сделана \n    // раньше, чем PyInitialize\n    PyInitialize();\n    ...\n}\n```\n\nСам модуль - это объект Python, который инициализируется из структуры-описания `PyModuleDef`:\n\n```c\nstatic PyModuleDef moduleDef = {\n\t// ссылка на RTTI, поскольку Си не является ООП-языком\n    .m_base = PyModuleDef_HEAD_INIT,\n    // имя модуля\n    .m_name = \"app\",\n    // размер дополнительной памяти для хранения состояния модуля в\n    // случае использования нескольких интерпретаторов, либо -1,\n    // если не планируется использование PyModule_GetState\n    .m_size = -1,\n    // указатель на список методов (функций) модуля, может быть NULL\n    .m_methods = methods,\n};\nPyObject *module = PyModule_Create(&moduleDef);\n```\n\nСписок методов модуля - это массив объектов `PyMethodDef`, признаком конца которого является \"нулевой элемент\", - структура заполненная нулями, по аналогии с признаком конца строк в языке Си.\n\n```c\nstatic PyObject *\ndo_something(PyObject *self, PyObject *args) {\n    PyErr_SetString(PyExc_RuntimeError, \"Not implemented yet\");\n    return NULL;\n}\n\nstatic PyMethodDef methods[] = {\n    {\n        // имя Python-функции \n        .ml_name = \"do_something\",\n        // указатель на Си-функцию\n        .ml_meth = do_something,\n        // флаги использования Си-функции\n        .ml_flags = METH_VARARGS,\n        // строка описания, выдается функцией help()\n        .ml_doc = \"Do something very useful\"\n    },\n    // признак конца массива описаний методов\n    {NULL, NULL, 0, NULL}\n};\n```\n\nКаждая Си-функция, которая реализует Python-функцию, должна возвращать объект `PyObject*`, и принимает минимум один аргумент - указатель на сам объект модуля.\n\nСи-функции могут иметь разные аргументы (включая из количество), в зависимости о того, как допускается вызывать метод. Это поведение определяется флагами в поле `ml_flags`.\n\n* С одним аргументом: `(PyObject *self)` - в случае, если функция не принимает никаких аргументов и значение в `.ml_flags = METH_NOARGS`\n* С двумя аргументами: `(PyObject *self, PyObject *argsTuple)`, причем второй аргумент является кортежем (возможно пустым), - в случае если функция принимает переменное количество позиционных аргументов и значение в `.ml_flags = METH_VARARGS`\n* С двумя аргументами: `(PyObject *self, PyObject *argsDict)`, причем второй аргумент является словарем (возможно пустым), - в случае если функция принимает переменное количество именованных аргументов и значение в `.ml_flags = METH_KEYWORDS`\n* С тремя аргументами: `(PyObject *self, PyObject *argsTuple, PyObject *argsDict)`, где второй аргумент - это кортеж из позиционных аргументов, а третий - это словарь именованных аргументов, - при значении `.ml_flags = METH_KEYWORDS|METH_VARARGS`\n\nВозвращаемое значение `NULL` вместо объекта `PyObject*` означает исключительную ситуацию. В языке Python любая функция должна возвращать хоть какой-нибудь объект. С точки зрения синтаксиса языка, отсутствие возвращаемого значения означает, что будет возвращен объект типа `NoneType`, который называется `None`.\n\nОбъект типа `None` существует в единственном экзепляре на весь интерпретатор, но при этом может много где ипользоваться, и к нему, как и к любому объекту, применяются обычные правила подсчета ссылок.\n\n```python\ndef a(): pass  # функция, которая \"ничего не возвращает\"\nb = a()        # b = None, причем вызов a() увеличил счетчик ссылок на None\n\na()            # возращается None, и результат отбрасывается,\n               # поскольку он ничему не присвоен. В момент вызова a()\n               # увеличивается счетчик ссылок, при отсутствии левой части\n               # присваивания счетчик сслок уменьшается\n```\n\nПри создании новых объектов из Си-кода, счетчик ссылок устанавливается равным 1, и обычно никаких действий по его увеличению не требуется, если объекты возвращаются интерпретатору:\n\n```c\nstatic PyObject *\nfunc_returning_string(PyObject *self)\n{\n    PyObject *ret = PyUnicode_FromString(\"Hello\"); // ret->ob_refcnt=1\n    return ret; // OK\n}\n\n# из Python:\na = func_returning_string()  # ret -> a, refcnt=1\nfunc_returning_string()      # del ret, refcnt=1 --> refcnt=0    \n```\n\nВ случае использования `None`, поскольку он существует в единственном экземпляре, нужно увеличить количество ссылок:\n\n```c\nstatic PyObject *\nfunc_returning_none(PyObject *self)\n{\n    // Py_None - это указатель на статический объект _Py_NoneStruct\n    Py_INCREF(Py_None);   // Py_None->ob_refcnt ++\n    return Py_None;\n}    \n```\n\n## Реализация модулей для использования штатным интерпретатором\n\nСи-модуль для языка Python - это разделяемая библиотека, которая загружается через механизм `dlopen`, и поэтому должна быть скомпилирована в позиционно-независимый код (опция `-fPIC` компилятора).\n\nБиблиотека имеет не стандартное имя файла:\n\n* `МОДУЛЬ.so` - для Mac, Linux и *BSD. Обратите внимание на отсутствие префикса `lib` в имени, и кроме того, в Mac используется суффикс `.so` вместо `.dynlib`\n* `МОДУЛЬ.pyd` или `МОДУЛЬd.pyd` - для Windows. Вместо суффикса `.dll` используется `.pyd` или `d.pyd` (для варианта сборки с отладочной информацией).\n\nЕдинственная функция, которая обязана быть реализована в библиотеке - это функция:\n\n```c\nPyObject* PyInit_МОДУЛЬ();\n```\n\nФункция должна создавать и возвращать объект модуля, по аналогии с расширением интерпретатора встроенным модулем. \n\nЕсли код модуля реализуется на языке C++, то необходимо отключить преобразование имен с помощью `extern \"C\"`, а в случае с операционной системой Windows и компилятором MSVC - ещё и объявить функцию экспортируемой: `__declspec(dllexport)`.\n\nВсе платформо-зависимые объявления спрятаны в макрос `PyMODINIT_FUNC`, значение которого определяется препроцессором. Таким образом, модуль реализуется функцией:\n\n```c\nPyMODINIT_FUNC PyInit_my_great_module() {\n\tstatic PyModuleDef modDef = {\n        .m_base = PyModuleDef_HEAD_INIT,\n        .m_name = \"my_great_module\",\n        ....\n    }\n    return PyModule_Create(&modDef);\n}\n```\n\nОбратите внимание, что имя файла с модулем, имя части функции после `PyInit_` и имя самого модуля должны совпадать, иначе интерпретатор не сможет найти и загрузить его.\n\nВсе остальные функцие модуля могут быть статическими, а не экспортироваться из библиотеки, поскольку указатели на них явным образом будут присутствовать в объекте, который вернет функция инициализации.\n\nВ CMake-пакете `PythonLibs` определяется функция для создания цели-модуля:\n\n```cmake\nfind_package(PythonLibs 3.6 REQUIRED)\n\npython_add_module(my_great_module module.c)\n```\n\nЭта цель определяет необходимые опции компиляции для сборки позиционно-независимого кода с правильным именем, независимо от используемой операционной системы.\n\nДля загрузки модуля из Python необходимо разместить его в одном из каталогов поиска модулей Python, либо рядом с файлом скрипта, который его использует. Python при этом корректно работает с символическими ссылками.\n\n## Python и отладчик GDB\n\nОтладчик GDB позволяет ставить точки останова в любой части программы, для которой существует отладочная информация. Таким образом, если собрать модуль отдельно с опцией `-g`, то вместе с ним можно использовать `gdb` даже в том случае, если для самого интерпретатора отладочная информация отсутствует. Целевой программой для `gdb` указывается исполняемый файл интерпретатора `python3` , а сам тестовый скрипт - в качестве аргумента запуска.\n\n```bash\n> gdb python3\n(gdb) b module.c:112\n(gdb) r script.py\n```\n\nСовременные версии `gdb` (начиная с 7.x) включают поддержку расширений для типов данных Python API, и использование команды отладчика `print` вызывает метод `repr` языка Python для выводимых объектов, а он, в свою очередь - подразумевает вызов функции `PyObject_Repr(PyObject *obj)` для произвольного Python-объекта. \n\nВ случае, если переменная не инициализирована, и содержит мусор по указателю, то это может приводить к ошибке нарушения сегментации, причиной которой становится сам отладчик, вызывая `print`. При использовании интегрированных сред разработки, эта команда отладчика вызывается очень часто  для обновления значений локальных переменных, что может приводить к печальным последствиям.\n\n```c\nPyObject* some_function(PyObject *self, PyObject *args) {\n    // <-- точка останова где-то здесь\n    ....\n    ....\n    PyObject * value = ...\n    ...\n}\n```\n\nВ данном примере отладчик инициирует ошибку нарушения сегментации, поскольку локальная переменная `value` ещё не инициализирована. Для того, чтобы этого избежать, есть два способа:\n\n* Отключить использования вызова `repr` для Python-объектов командой отладчика `disable pretty-printer` (в среде QtCreator это делается автоматически при снятии чекбокса \"Use Debugging Helper\" в настройках отладчика), - в этом случае все Python-объекты будут отображаться как Си-структуры.\n\n* Реорганизовать код таким образом, чтобы на момент остановки отладчиком все локальные переменные были инициализированы.\n\n  ```c\n  PyObject* some_function(PyObject *self, PyObject *args) {\n      PyObject * value = NULL; \n      ....\n      // <-- точка останова после инициализации всех PyObject*\n      ....\n      value = ...\n      ...\n  }\n  ```\n\n  \n\n "
  },
  {
    "path": "practice/signal-1/README.md",
    "content": "# Сигналы. Часть 1\n\n## Введение\nСигнал - это механизм передачи коротких сообщений (номер сигнала), как правило, прерывающий работу процесса, которому он был отправлен.\n\nСигналы могут быть посланы процессу:\n * ядром, как правило, в случае критической ошибки выполнения;\n * другим процессом;\n * самому себе.\n\nНомера сигналов начинаются с 1. Значение 0 имеет специальное назначение (см. ниже про `kill`). Некоторым номерам сигналов соответствуют стандартные для POSIX названия и назначения, которые подробно описаны `man 7 signal`.\n\nПри получении сигнала процесс может:\n 1. Игнорировать его. Это возможно для всех сигналов, кроме `SIGSTOP` и `SIGKILL`.\n 2. Обработать отдельной функцией. Кроме `SIGSTOP` и `SIGKILL`.\n 3. Выполнить действие по умолчанию, предусмотренное назначением стандартного сигнала POSIX. Как правило, это завершение работы процесса.\n\nПо умолчанию, все сигналы, кроме `SIGCHILD` (информирование о завершении дочернего процесса) и `SIGURG` (информировании о поступлении TCP-сегмента с приоритетными данными), приводят к завершению работы процесса.\n\nЕсли процесс был завершён с помощью сигнала, а не с ипользованием системного вызова `exit`, то для него считается не определенным код возврата. Родительский процесс может отследить эту ситуацию, используюя макросы `WIFSIGNALED` и `WTERMSIG`:\n\n```\npid_t child = ...\n...\nint status;\nwaitpid(child, &status, 0);\nif (WIFEXITED(status)) {\n    // дочерний процесс был завершён через exit\n    int code = WEXITSTATUS(status); // код возврата\n}\nif (WIFSIGNALED(status)) {\n    // дочерний процесс был завершёл сигналом\n    int signum = WTERMSIG(status); // номер сигнала\n}\n```\n\nОтправить сигнал любому процессу можно с помощью команды `kill`. По умолчанию отправляется сигнал `SIGTERM`, но можно указать в качестве опции, какой именно сигнал нужно отправить. Кроме того, некоторые сигналы отправляются терминалом, например Ctrl+C посылает сигнал `SIGINT`, а Ctrl+\\ - сигнал `SIGQUIT`.\n\n\n## Пользовательские сигналы\n\nИзначально в POSIX было зарезервировано два номера сигнала, которые можно было использовать на умотрение пользователя: `SIGUSR1` и `SIGUSR2`.\n\nКроме того, в Linux предусмотрен диапазон сигналов с номерами от `SIGRTMIN` до `SIGRTMAX`, которые можно использовать на усмотрение пользователя.\n\nДействием по умолчанию для всех \"пользовательских\" сигналов является завершение работы процесса.\n\n\n## Отправка сигналов программным способом\n\n### Системный вызов `kill`\n\nПо аналогии с одноимённой командой, `kill` предназначен для отправки сигнала любому процессу.\n\n```\nint kill(pid_t pid, int signum); // возврашает 0 или -1, если ошибка\n```\n\nОтправлять сигналы можно только тем процессам, которые принадлежат тому пользователю, что и пользователь, по которым выполняется системный вызов `kill`. Исключение составляет пользователь `root`, который может всё. При попытке отправить сигнал процессу другого пользователя, `kill` вернёт значение `-1`.\n\nНомер процесса может быть меньше `1` в случаях:\n * `0` - отправить сигнал всем процессам текущей группы процессов;\n * `-1` - отправить сигнал всем процессам пользователя (использовать с осторожностью!);\n * отрицательное значение `-PID` - отправить сигнал всем процессам группы `PID`.\n\nНомер сигнала может принимать значение `0`, - в этом случае никакой сигнал не будет отправлен, а `kill` вернёт значение `0` в том случае, если процесс (группа) с указанным `pid` существует, и есть права на отправку сигналов.\n\n### Функции `raise` и `abort`\n\nФункция `raise` предназначен для отправки сигнала процессом самому себе. Функция стандартной библиотеки `abort` посылает самому себе сигнал `SIGABRT`, и часто используется для генерации исключительных ситуаций, которые получилось диагностировать во время выполнения, например, функцией `assert`.\n\n### Системный вызов `alarm`\n\nСистемный вызов `alarm` запускает таймер, по истечении которого процесс сам себе отправит сигнал `SIGALRM`.\n\n```\nunsigned int alarm(unsigned int seconds);\n```\n\nОтменить ранее установленный таймер можно, вызвав `alarm` с параметром `0`. Возвращаемым значением является количество секунд предыдущего установленного таймера.\n\n## Обработка сигналов\n\nСигналы, которые можно перехватить, то есть все, кроме `SIGSTOP` и `SIGKILL`, можно обработать программным способом. Для этого необходимо зарегистрировать функцию-обработчик сигнала.\n\n### Системный вызов `signal`\n```\n#include <signal.h>\n\n// Этот тип определен только в Linux!\ntypedef void (*sighandler_t)(int);\n\nsighandler_t signal(int signum, sighandler_t handler); // для Linux\nvoid (*signal(int signum, void (*func)(int))) (int); // по стандарту POSIX\n```\n\nСистемный вызов `signal` предназначен для того, чтобы зарегистрировать функцию в качестве обработчика определенного сигнала. Первым аргументом является номер сигнала, вторым - указатель на функцию, которая принимает единственный аргумент - номер пришедшего сигнала (т.е. одну функцию можно использовать сразу для нескольких сигналов), и ничего не возвращает.\n\nДва специальных значения функции-обработчика `SIG_DFL` и `SIG_IGN` предназанчены для указания обработчика по умолчанию (т.е. отмены ранее зарегистрированного обработчика) и установки игнорирования сигнала.\n\nСистемный вызов `signal` возвращает указатель на ранее установленный обработчик.\n\n### System-V v.s. BSD\n\nВ стандартах, родоначальниками которых были UNIX System-V и BSD UNIX, используется различное поведение обработчика сигнала, зарегистрированного с помощью `signal`. При определении одного из макросов препроцессора: `_BSD_SOURCE`, `_GNU_SOURCE` или `_DEFAULT_SOURCE` (что подразумевается опцией компиляции `-std=gnu99` или `-std=gnu11`), используется семантика BSD; в противном случае (`-std=c99` или `-std=c11`) - семантика System-V.\n\nОтличия BSD от System-V:\n * В System-V обработчик сигнала выполяется один раз, после чего сбрасывается на обработчик по умолчанию, а в BSD - остается неизменным.\n * В BSD обработчик сигнала не будет вызван, если в это время уже выполняется обработчик того же самого сигнала, а в System-V это возможно.\n * В System-V блокирующие системные вызовы (например, `read`) завершают свою работу при поступлении сигнала, а в BSD большинство блокирующих системных вызовов возобновляют свою работу после того, как обработчик сигнала заверщает свою работу.\n\nПо этой причине, системный вызов `signal` считается устаревшим, и в новом коде использовать его запрещено, за исключением двух ситуаций:\n\n```\nsignal(signum, SIG_DFL); // сброс на обработчик по умолчанию\nsignal(signum, SIG_IGN); // игнорирование сигнала\n```\n\n### Системный вызов `sigaction`\n\nСистемный вызов `sigaction`, в отличии от `signal`, в качестве второго аргумента принимает не указатель на функцию, а указатель на структуру `struct sigaction`, с которой, помимо указателя на функцию, хранится дополнительная информация, описывающая семантику обработки сигнала. Поведение обработчиков, зарегистрированных с помощью `sigaction`, не зависит от операционной системы.\n\n```\nint sigaction(int signum,\n              const struct sigaction *restrict act,\n              struct sigaction *oldact);\n```\n\nТретьим аргументов является указатель на структуру, описывающую обработчик, который был зарегистрирован для этого. Если эта информация не нужна, то можно передать значение `NULL`.\n\nОсновные поля структуры `struct sigaction`:\n * `sa_handler` - указатель на функцию-обработчик с одним аргументом типа `int`, могут быть использованы значения `SIG_DFL` и `SIG_IGN`;\n * `sa_flags` - набор флагов, опиывающих поведение обработчика;\n * `sa_sigaction` - указатель на функцию-обработчик с тремя параметрами, а не одним (используется, если в флагах присутствует `SA_SIGINFO`).\n\nНекоторые флаги, которые можно передавать в `sa_flags`:\n * `SA_RESTART` - продолжать выполнение прерванных системных вызовов (семантика BSD) после завершения обработки сигнала. По умолчанию (если флаг отсутствует) используется семантика System-V.\n * `SA_SIGINFO` - вместо функции из `sa_handler` нужно использовать функцию с тремя параметрами `int signum, siginfo_t *info, void *context`, которой помимо номера сигнала, передается дополнительная информация (например PID отправителя) и пользовательский контекст.\n * `SA_RESETHAND` - после выполнения обработчика сбросить на обработчик по умолчанию (семантика System-V). По умолчанию (если флаг отсутствует) используется семантика BSD.\n * `SA_NODEFER` - при повторном приходе сигнала во время выполени обработчика он будет обработан немедленно (семантика System-V). По умолчанию (если флаг отсутствует) используется семантика BSD.\n\n## Асинхронность обработки сигналов\n\nСигнал может прийти процессу в любой момент времени. При этом, выполнение текущего кода будет прервано, и будет запущен обработчик сигнала.\n\nТаким образом, возникает проблема \"гонки данных\", которая часто встречается в многопоточном программировании.\n\nСуществует безопасный целочисленный (32-разрядный) тип данных, для которого гарантируется атомарность чтения/записи при переключении между выполнением основной программы и выполнением обработчика сигнала: `sig_atomic_t`, объявленный в `<signal.h>`.\n\nКроме того, во время выполнения обработчика сигналов запрещено использовать не потоко-безопасные функции (большинство функций стандартной библиотеки). В то же время, использование системных вызовов - безопасно.\n"
  },
  {
    "path": "practice/signal-2/README.md",
    "content": "# Сигналы. Часть 2\r\n\r\n## Механизм доставки сигналов\r\n\r\nС каждым процессом связан аттрибут, который не наследуется при `fork`, - это *маска сигналов, ожидающих доставки*. Как правило, она представляется внутри системы в виде целого числа, хотя стандартом внутреннее представление не регламентируется. Отдельные биты в этой маске соответствуют отдельным сигналам, которые были отправлены процессу, но ещё не обработаны.\r\n\r\nПоскольку одним битом можно закодировать только бинарное значение, то учитывается только сам факт поступления сигнала, но не их количество. Например, это может быть критичным, если сигналы долго не обрабатываются. Таким образом, использовать механизм стандартных сигналов для синхронизации двух процессов - нельзя.\r\n\r\nТот факт, что сигнал оказался в маске ожидающих доставки, ещё не означает, что он будет немедленно обработан. У процесса (или даже у отдельной нити) может существовать маска *заблокированных* сигналов, которая накладывается на маску ожидающих доставки с помощью поразрядной операции `И-НЕ`.\r\n\r\nВ отличии от маски ожидающих достаки, маска заблокированных сигналов наследуется при `fork`.\r\n\r\n## Множества сигналов\r\n\r\nМножества сигналов описываются типом данных `sigset_t`, объявленным в заголовочном файле `<signal.h>`.\r\n\r\nОперации над множествами:\r\n * `sigemptyset(sigset_t *set)` - инициализировать пустое множество;\r\n * `sigfillset(sigset_t *set)` - инициализировать полное множество;\r\n * `sigaddset(sigset_t *set, int signum)` - добавить сигнал к множеству;\r\n * `sigdelset(sigset_t *set, int signum)` - убрать сигнал из множества;\r\n * `sigismember(sigset_t *set, int signum)` - проверить наличие сигнала в множестве.\r\n\r\n## Блокировка достаки сигналов\r\n\r\nВременная блокировка доставки сигналов часто используется для защиты критических секций программы, когда внезапное выполнение обработчика может повредить целостности данных или нарушению логики поведения.\r\n\r\nПри этом, нельзя заблокировать сигналы `SIGSTOP` и `SIGKILL`.\r\n\r\nБлокировка реализуется установки маски блокируемых сигналов с помощью системного вызова `sigprocmask`:\r\n\r\n```\r\nint sigprocmask(int how, sigset_t *set, sigset_t *old_set);\r\n```\r\nгде `old_set` - куда записать старую маску (может быть `NULL`, если не интересно), а параметр `how` - это одно из значений:\r\n * `SIG_SETMASK` - установить множество сигналов в качестве маски блокируемых сигналов;\r\n * `SIG_BLOCK` - добавить множество к маске блокируемых сигналов;\r\n * `SIG_UNBLOCK` - убрать множество из маски блокируемых сигналов.\r\n\r\n## Отложенная обработка сигналов\r\n\r\nСигналы, которые попали в маску сигналов, ожидающих доставки, остаются там до тех пор, пока не будут доставлены (а в дальнейшем - либо игнорированы, либо обработаны). Если сигнал был заблокирован, то его обработчик будет вызван сразу после разблокировки.\r\n\r\n```\r\n#include <signal.h>\r\n#include <unistd.h>\r\n\r\nstatic void\r\nhandler(int signum) {\r\n    static const char Message[] = \"Got Ctrl+C\\n\";\r\n    write(1, Message, sizeof(Message)-1);\r\n}\r\n\r\nint main() {\r\n    sigaction(SIGINT,\r\n              &(struct sigaction)\r\n              {.sa_handler=handler, .sa_flags=SA_RESTART},\r\n              NULL);\r\n    sigset_t mask;\r\n    sigemptyset(&mask);\r\n    sigaddset(&mask, SIGINT);\r\n\r\n    while (1) {\r\n        sigprocmask(SIG_BLOCK, &mask, NULL);\r\n        sleep(10);\r\n        sigprocmask(SIG_UNBLOCK, &mask, NULL);\r\n    }\r\n}\r\n```\r\n\r\nВ данном примере [sigprocmask.c](sigprocmask.c) обработчик сигнала `SIGINT` всё равно будет выполнен, даже несмотря на длительную паузу.\r\n\r\n## Временная замена маски заблокированных сигналов\r\n\r\nМаска сигналов может быть временно заменена.\r\n\r\n### Системный вызов `sigsuspend`\r\n\r\nСистемный вызов `sigsuspend(sigset_t *temp_mask)` временно приостанавливает работу программы до тех пор, пока не прийдёт один из сигналов, отсутсвующий в множестве `temp_mask`. Сигналы, отсутсвующие в новом временном множестве, будут доставлены даже в том случае, если они ранее были заблокированы.\r\n\r\nСразу после завершения работы `sigsuspend`, маска заблокированных сигналов вернется в исходную.\r\n\r\n### Во время обработки сигнала, зарегистрированного `sigaction`\r\n\r\nОдно из полей структуры `sigaction` определяет маску сигналов, доставка которых будет заблокирована на время выполнения обработчика. Дополнительные флаги при этом не требуются.\r\n\r\n```\r\nstruct sigaction act;\r\nmemset(&act, 0, sizeof(act));\r\nact.sa_handler = handler;\r\nact.sa_flags = SA_RESTART;\r\nsigfillset(&act.sa_mask); // блокировать все сигналы\r\n```\r\n\r\n## Сигналы реального времени\r\n\r\nСигналы реального времени - это расширение POSIX, которые, в отличии от стандартных UNIX-сигналов могут быть обработаны используя очередь доставки, и таким образом:\r\n * учитывается их количество и порядок прихода;\r\n * вместе с сигналом сохраняется дополнительная метаинформация, включая одно челочисленное поле, которое может быть использовано произвольным образом.\r\n\r\nСигналы реального времени задаются значениями от `SIGRTMIN` до `SIGRTMAX`, и могут быть использованы с помощью `kill` как дополнительные стандартные UNIX-сигналы. Действие по умолчанию аналогично `SIGTERM`.\r\n\r\nДля использования очереди сигналов, необходимо отправлять их с помощью функции `sigqueue`:\r\n\r\n```\r\n#include <signal.h>\r\nunion sigval {\r\n    int    sival_int;\r\n    void*  sival_ptr;\r\n};\r\nint sigqueue(pid_t pid, int signum, const union sigval value);\r\n```\r\n\r\nЭта функция может завершиться с ошибкой `EAGAIN` в том случае, если исчерпан лимит на количество сигналов в очереди. Опциональное значение, передаваемое в качестве третьего параметра, может быть извлечено получателем из поля `si_value` структуры `siginfo_t`, если использовать вариант обработчика `sigaction` с тремя аргументами.\r\n"
  },
  {
    "path": "practice/signal-2/sigprocmask.c",
    "content": "#include <signal.h>\n#include <unistd.h>\n\nstatic void\nhandler(int signum) {\n    static const char Message[] = \"Got Ctrl+C\\n\";\n    write(1, Message, sizeof(Message)-1);\n}\n\nint main() {\n    sigaction(SIGINT,\n              &(struct sigaction)\n              {.sa_handler=handler, .sa_flags=SA_RESTART},\n              NULL);\n    sigset_t mask;\n    sigemptyset(&mask);\n    sigaddset(&mask, SIGINT);\n\n    while (1) {\n        sigprocmask(SIG_BLOCK, &mask, NULL);\n        sleep(10);\n        sigprocmask(SIG_UNBLOCK, &mask, NULL);\n    }\n}\n"
  },
  {
    "path": "practice/sockets-tcp/README.md",
    "content": "# Сокеты с установкой соединения\n\n## Сокет\n\nСокет - это файловый дескриптор, открытый как для чтения, так и для записи. Предназначен для взаимодействия:\n * разных процессов, работающих на одном компьютере (*хосте*);\n * разных процессов, работающих на разных *хостах*.\n\nСоздается сокет с помощью системного вызова `socket`:\n\n```\n#include <sys/types.h>\n#include <sys/socket.h>\n\nint socket(\n  int domain,    // тип пространства имён\n  int type,      // тип взаимодействия через сокет\n  int protocol   // номер протокола или 0 для авто-выбора\n)\n```\n\nМеханизм сокетов появился ещё в 80-е годы XX века, когда не было единого стандарта для сетевого взаимодействия, и сокеты являлись абстракцией поверх любого механизма сетевого взаимодействия, поддерживая огромное количество разных протоколов.\n\nВ современных системах используемыми можно считать несколько механизмов, определяющих пространство имен сокетов; все остальное - это legacy, которое мы дальше рассматривать не будем.\n\n * `AF_UNIX` (`man 7 unix`) - пространство имен локальных UNIX-сокетов, которые позволяют взаимодействовать разным процессам в пределах одного компьютера, используя в качестве адреса уникальное имя (длиной не более 107 байт) специального файла.\n * `AF_INET` (`man 7 ip`) - пространство кортежей, состоящих из 32-битных IPv4 адресов и 16-битных номеров портов. IP-адрес определяет хост, на котором запущен процесс для взаимодействия, а номер порта связан с конкретным процессом на хосте.\n * `AF_INET6` (`man 7 ipv6`) - аналогично `AF_INET`, но используется 128-разрядная адресация хостов IPv6; пока этот стандарт поддерживается не всеми хостерами и провайдерами сети Интернет.\n * `AF_PACKET` (`man 7 packet`) - взаимодействие на низком уровне.\n\nЧерез сокеты обычно происходит взаимодействие одним из двух способов (указывается в качестве второго параметра `type`):\n * `SOCK_STREAM` - взаимодействие с помощью системных вызовов `read` и `write` как с обычным файловым дескриптором. В случае взаимодействия по сети, здесь подразумевается использование протокола `TCP`.\n * `SOCK_DGRAM` - взаимодейтсвие без предвариательной установки взаимодействия для отправки коротких сообщений. В случае взаимодействия по сети, здесь подразумевается использование протокола `UDP`.\n\n\n## Пара сокетов\n\nИногда сокеты удобно использовать в качестве механизма взаимодействия между разными потоками или родственными процессами: в отличии от каналов, они являются двусторонними, и кроме того, поддерживают обработку события \"закрытие соединения\". Пара сокетов создается с помощью системного вызова `socketpair`:\n\n```\nint socketpair(\n  int domain,    // В Linux поддерживатся только AF_UNIX\n  int type,      // SOCK_STREAM или SOCK_DGRAM\n  int protocol,  // Только значение 0 в Linux\n  int sv[2]      // По аналогии с pipe, массив из двух int\n)\n```\n\nВ отличии от неименованных каналов, которые создаются системным вызовом `pipe`, для пары сокетов не имеет значения, какой элемент массива `sv` использовать для чтения, а какой - для записи, - они являются равноправными.\n\n## Использование сокетов в роли клиента\n\nСокеты могут участвовать во взаимодействии в одной из двух ролей. Процесс может быть *сервером*, то есть объявить некоторый адрес (имя файла, или кортеж из IP-адреса и номера порта) для приема входящих соединений, либо выступать в роли *клиента*, то есть подключиться к какому-то серверу.\n\nСразу после создания сокета, он ещё не готов к взамиодействию с помощью системных вызовов `read` и `write`. Установка взаимодействия с сервером осуществляется с помощью системного вызова `connect`. После успешного выполнения этого системного вызова - взаимодействие становится возможным до выполнения системного вызова `shutdown`.\n\n```\nint connect(\n  int sockfd,                  // файловый дескриптор сокета\n\n  const struct sockaddr *addr, // указатель на *абстрактную*\n                               // структуру, описывающую\n                               // адрес подключения\n\n  socklen_t addrlen            // размер реальной структуры,\n                               // которая передается в\n                               // качестве второго параметра\n)\n```\n\nПоскольку язык Си не является объектно-ориентированным, то нужно в качестве адреса передавать:\n 1. Структуру, первое поле которой содержит целое число со значением, совпадающим с `domain` соответствующего сокета\n 2. Размер этой структуры.\n\nКонкретными стурктурами, которые \"наследуются\" от абстрактной структуры `sockaddr` могут быть:\n\n1. Для адресного пространства UNIX - стрктура `sockaddr_un`\n\n```\n#include <sys/socket.h>\n#include <sys/un.h>\n\nstruct sockaddr_un {\n  sa_family_t   sun_family;    // нужно записать AF_UNIX\n  char          sun_path[108]; // путь к файлу сокета\n};\n```\n\n2. Для адресации в IPv4 - структура `sockaddr_in`:\n\n```\n#include <sys/socket.h>\n#include <netinet/in.h>\n\nstruct sockaddr_in {\n  sa_family_t    sin_family; // нужно записать AF_INET\n  in_port_t      sin_port;   // uint16_t номер порта\n  struct in_addr sin_addr;   // структура из одного поля:\n                             // - in_addr_t s_addr;\n                             //   где in_addr_t - это uint32_t\n};\n```\n\n3. Для адресации в IPv6 - структура `sockaddr_in6`:\n\n```\n#include <sys/socket.h>\n#include <netinet/in.h>\n\nstruct sockaddr_in6 {\n  sa_family_t    sin6_family; // нужно записать AF_INET6\n  in_port_t      sin6_port;   // uint16_t номер порта\n  uint32_t       sin6_flowinfo; // дополнительное поле IPv6\n  struct in6_addr sin6_addr;  // структура из одного поля,\n                              // объявленного как union {\n                              //     uint8_t  [16];\n                              //     uint16_t [8];\n                              //     uint32_t [4];\n                              // };\n                              // т.е. размер in6_addr - 128 бит\n  uint32_t       sin6_scope_id; // дополнительное поле IPv6\n};\n```\n\n\n## Адреса в сети IPv4\n\nАдрес хоста в сети IPv4 - это 32-разрядное беззнаковое целое число в *сетевом порядке байт*, то есть Big-Endian. Для номеров портов - аналогично.\n\nКонвертация порядка байт из сетевого в системный и наоборот осуществляется с помощью одной из функций, объявленных в `<arpa/inet.h>`:\n * `uint32_t htonl(uint32_t hostlong)` - 32-битное из системного в сетевой порядок байт;\n * `uint32_t ntohl(uint32_t netlong)` - 32-битное из сетевого в системный порядок байт;\n * `uint16_t htons(uint16_t hostshort)` - 16-битное из системного в сетевой порядок байт;\n * `uint16_t ntohs(uint16_t netshort)` - 16-битное из сетевого в системный порядок байт.\n\nIPv4 адреса обычно записывают в десятичной записи, отделяя каждый байт точкой, например: `192.168.1.1`. Такая запись может быть конвертирована из текста в 32-битный адрес с помощью функций `inet_aton` или `inet_addr`.\n\n\n## Закрытие сетевого соединения\n\nСистемный вызов `close` предназначен для закрытия *файлового дескриптора*, и его нужно вызывать для того, чтобы освободить запись в таблице файловых дескрипторов. Это является необходимым, но не достаточным требованием при работе с TCP-сокетами.\n\nПомимо закрытия файлового дескриптора, хорошим тоном считается уведомление противоположной стороны о том, что сетевое соединение закрывается\n\nЭто уведомление осуществляется с помощью системного вызова `shutdown`.\n\n\n## Использование сокетов в роли сервера\n\nДля использования сокета в роли сервера, необходимо выполнить следующие действия:\n\n 1. Связать сокет с некоторым адресом. Для этого используется системный вызов `bind`, параметры которого точно такие же, как для системного вызова `connect`. Если на компьютере более одного IP-адреса, то адрес `0.0.0.0` означает \"все адреса\". Часто при отладке и возникает проблема, что порт с определенным номером уже был занят на предыдущем запуске программы (и, например, не был корректно закрыт). Это решается принудительным повторным использованием адреса:\n\n```\n// В релизной сборке такого обычно быть не должно!\n#ifdef DEBUG\nint val = 1;\nsetsockopt(lfd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val));\nsetsockopt(lfd, SOL_SOCKET, SO_REUSEPORT, &val, sizeof(val));\n#endif\n```\n\n 2. Создать очередь, в которой будут находиться входящие, но ещё не принятые подключения. Это делается с помощью системного вызова `listen`, который принимает в качестве параметра максимальное количество ожидающих подключений. Для Linux это значение равно 128, определено в константе `SOMAXCONN`.\n\n 3. Принимать по одному соединению с помощью системного вызова `accept`. Второй и третий параметры этого системного вызова могуть быть `NULL`, если нас не интересует адрес того, кто к нам подключился. Системный вызов `accept` блокирует выполнение до тех пор, пока не появится входящее подключение. После чего - возвращает файловый дескриптор нового сокета, который связан с конкретным клиентом, который к нам подключился.\n"
  },
  {
    "path": "practice/sockets-udp/README.md",
    "content": "# Сетевой взаимодействие без установки соединения\r\n\r\n## Протокол UDP\r\n\r\n### Схема взаимодействия TCP/IP\r\n\r\nПосле создания сокета типа `SOCK_STREAM`, он должен быть подключен к противоположной стороне с помощью системного вызова `connect`, либо принять входящее подключение с помощью системного выхова `accept`.\r\n\r\nПосле этого становится возможным сетевое взаимодействие с использованием операций ввода-вывода.\r\n\r\nСетевое взаимодействие по TCP/IP (создание сокета с параметрами `AF_INET` и `SOCK_STREAM`) подразумевает, что ядро операционной системы преобразует непрерывный поток данных в последовательность TCP-сегментов, упакованных в IP-пакеты, и наоборот.\r\n\r\n### Сокеты UDP\r\n\r\nМеханизм отправки сообщений по UDP подразумевает передачу данных без предварительной установки соединения. Сокет, ориентированный на отправку UDP-сообщений имеет тип `SOCK_DGRAM` и используется совместно с адресацией IPv4 (`AF_INET`) либо IPv6 (`AF_INET6`).\r\n\r\n```c\r\n// Создание сокета для работы по UDP/IP\r\nint sockfd = socket(AF_INET, SOCK_DGRAM, 0);\r\n```\r\n\r\nКак и в случае с TCP, для адресация UDP подразумевает, что помимо IP-адреса хоста необходимо определиться с номером порта, который обслуживает отдельный процесс.\r\n\r\n### Системные вызовы для передачи и приема данных без установки соединения\r\n\r\n```c\r\n// Отправить пакет данных\r\nssize_t sendto(int sockfd,                  // сокет\r\n               const void *buf, size_t len, // данные и размер\r\n               int flags,                   // дополнительные опции\r\n               // адрес назначения (и его размер как для bind/connect)\r\n               const struct sockaddr *dest_addr, socklen_t addrlen);\r\n\r\n// Получить пакет данных\r\nssize_t recvfrom(int sockfd,             // сокет\r\n                 void *buf, size_t len,  // данные и размер\r\n                 int flags,              // дополнительные опции\r\n                 // адрес отправителя (и размер как для accept)\r\n                 const struct sockaddr *src_addr, socklen_t *addrlen);               \r\n```\r\n\r\nCистемный вызов `sendto` предназначен для отправки сообщения. Поскольку предварительно соединение не было установлено, то обязательным является указание адреса назначения: IP-адрес хоста и номер порта.\r\n\r\nСистемный вызов `recvfrom` предназначен для приема сообщения, и является блокирующим до тех пор, пока придет хотя бы одно сообщение UDP.\r\n\r\nРазмер буфера, в который `recvfrom` должен записать данные, должен быть достаточного размера для хранения сообщения, в противном случае данные, которые не влезли в буфер, будут потеряны.\r\n\r\nДля того, чтобы иметь возможность принимать данные по UDP, необходимо анонсировать прослушивание определенного порта с помощью системного вызова `bind`; параметры адреса для `recvfrom` предназначены только для получения информации об отправителе, и являются опциональными (эти значения могут быть NULL).\r\n\r\n\r\n## Инструменты в Linux для отладки сетевого взаимодействия\r\n\r\n### Сетевой ввод-вывод\r\n\r\nКоманда `nc` (сокращение от `netcat`) работает аналогично команде `cat`, но в\r\nкачестве аргумента принимает не имя файла для вывода потока данных, а пару\r\n`хост порт`. Параметр `-u` означает отправку UDP-пакета.\r\n\r\nЕсли предполагается использовать только адресацию IPv4, но не IPv6, то\r\nиспользуется опция `-4`.\r\n\r\n```bash\r\n# Пример: передать данные из in.dat в UDP-сокет на localhost\r\n# порт 3000 и записать вывод в файл out.dat\r\n> cat in.dat | nc -4 -u localhost 3000 >out.dat\r\n```\r\n\r\n### Режим Бога\r\n\r\nУтилита `wireshark` позволяют просматривать абсолютно все пакеты на уровне от `Ethernet`, которые проходят через систему. Для этого требуются права `root`, либо настройка `Linux Capabilities` для команды `/usr/bin/dumpcap`, которая является частью `wireshark`:\r\n\r\n```bash\r\nsudo /usr/sbin/setcap cap_net_raw,cap_net_admin+eip /usr/bin/dumpcap\r\n```\r\n\r\nКроме того, в некоторых дистрибутивах, например Debian/Ubuntu, необходимо, чтобы пользователь входил в группу `wireshark`.\r\n\r\nПоскольку через систему проходит много сетевых пакетов, то для поиска только интересующих пакетов необходимо настроить фильтр.\r\n\r\n\r\n### Python\r\n\r\nСтандартная библиотека Python содержит средства работы с сокетами, которые в точности соответствуют их аналогам для POSIX.\r\n\r\nПример отправки UDP-сообщения:\r\n```python\r\nfrom socket import socket, AF_INET, SOCK_DGRAM\r\n\r\nIP = \"127.0.0.1\"\r\nPORT = 3000\r\n\r\nsock = socket(AF_INET, SOCK_DGRAM)   # создание UDP-сокета\r\n# Соединение не требуется\r\nsock.sendto(\"Hello!\\n\", (IP, PORT))  # отправка сообщения\r\n```\r\n\r\nПрием UDP-сообщений:\r\n```python\r\nfrom socket import socket, AF_INET, SOCK_DGRAM\r\n\r\nIP = \"127.0.0.1\"\r\nPORT = 3000\r\nMAX_SIZE = 1024\r\n\r\nsock = socket(AF_INET, SOCK_DGRAM)       # создание UDP-сокета\r\nsock.bind((IP, PORT))                    # нужно анонсировать порт  \r\n\r\nwhile True:\r\n    data, addr = sock.recvfrom(MAX_SIZE) # получить сообщение\r\n    print(\"Got {} from {}\", data, addr)\r\n```\r\n\r\n## Linux Capabilities\r\n\r\nВ классических UNIX-системах права процессов на выполнение привилегированных действий разграничиваются только на уровне доступа к файлам, либо на уровне \"обычный пользователь\" - \"администратор\". В современных ядрах Linux существует ортогональный механизм для предоставления отдельным программам определенных прав, не связанных с доступом к файлам, который называется [capabilities(7)](http://man7.org/linux/man-pages/man7/capabilities.7.html). \r\n\r\nОтдельному исполняемому файлу можно назначить маску привилегированных разрешений, которые распространяются только на отдельную программу (но не дочерние процессы) с помощью утилиты `setcap` (требуются права root для запуска).\r\n\r\nФормат вызова:\r\n\r\n```bash\r\n> sudo setcap CAPABILITIES+FLAGS EXECUTABLE_FILE\r\n```\r\n\r\nЗдесь `CAPABILITIES` - одно, либо несколько, разделенных запятыми, полномочий. `FLAGS` - это комбинация флагов:\r\n\r\n* `p` - (Permitted) - полномочие разрешено для исполняемого файла;\r\n* `i` - (Inherited) - может наследоваться при вызове `exec`;\r\n* `e` - (Effective) - полномочие включено сразу при запуске программы.\r\n\r\nПри этом, установленные атрибуты `capabilities` не сохраняются:\r\n\r\n* во время модификации файла (например, в результате перекомпиляции);\r\n* при копировании или переименовании файла.\r\n\r\nТаким образом, чтобы иметь возможность создавать и отлаживать программу, требующую дополнительные полномочия, необходимо обеспечить вызов `setcap` на этапе установки или сборки.\r\n\r\nТак как `capabilities` это атрибуты файлов, то для их работы требуется поддержка со стороны файловой системы.\r\nВ стандартных для linux файловых системах с этим проблем нет,\r\nно если файл находится на примонтированном разделе с неподдерживающей `capabilities` файловой системой,\r\nто попытка установки закончится ошибкой `Failed to set capabilities on file './executable' (Operation not supported)`.\r\n\r\nТакже бывает удобным (для отладки) поставить необходимый набор полномочий на отладчик `gdb`; для корректной работы это требует дополнительно установки того же набора полномочий на командный интерпретатор `bash`. \r\n\r\n## Взаимодействие на уровне сетевого интерфейса\r\n\r\n### Пакетные сокеты\r\n\r\nСистема Linux позволяет взаимодействовать с сетевыми устройствами на низком уровне, используя специальный тип сокетов: пакетные сокеты `AF_PACKET`.\r\n\r\nБолее подробно работа с сокетами на низком уровне рассмотрена в статье [Introduction to RAW Sockets](https://api.semanticscholar.org/CorpusID:136157623)\r\n\r\nДля создания таких сокетов требуются либо права `root`, либо настройка `cap_net_raw`, в противном случае системный вызов `socket` вернет значение `-1`.\r\n\r\nПри работе с обычными TCP или UDP сокетами, ядро операционной системы полностью абстрагирует пользовательский процесс от дополнительной информации, связанной с доставкой сетевых данных. \r\n\r\nПри работе с пакетными сокетами необходимо самостоятельно реализовывать обработку требуемых заголовков.\r\n\r\nСуществует два уровня абстракции для пакетных сокетов: передача данных, которые заворачиваются в стандартный фрейм Ethernet `(AF_PACKET, SOCK_DGRAM)`, там и полностью произвольный поток данных `(AF_PACKET, SOCK_RAW)`, который может быть использован, например, для отправки широковещательных Ethernet-фреймов.\r\n\r\n![](raw-sockets-figure.png)\r\n\r\n### Бинарные заголовки сетевых протоколов\r\n\r\nДля работы с заголовками сетевых протоколов средствами языков Си/C++ можно использовать обычные структуры.\r\n\r\nПорядок, в котором объявлены поля структуры в тексте программы, является при этом существенным, поскольку он соответствует тому, в каком порядке хранятся данные. Кроме того, необходимо учитывать тот факт, что компиляторы оптимизируют код, выравнивания поля структур в соответствии с особенностями архитектур процессоров, и необходимо явным образом указывать использование \"упакованных\" структур.\r\n\r\n**Пример:** заголовок Ethernet-кадра может быть представлен следующим образом.\r\n\r\n```c\r\ntypedef struct {\r\n    /* MAC-адрес получателя, 6 байт */\r\n    uint8_t destination[6];\r\n    /* MAC-адрес отправителя, 6 байт */\r\n    uint8_t source[6];\r\n    /* Тип передаваемого пакета */\r\n    uint16_t type;\r\n} __attribute__((__packed__)) ethernet_header_t;\r\n```\r\n\r\nКроме того, необходимо помнить о том, что большинство сетевых протоколов подразумевают использование сетевого порядка байт, поэтому нужно использовать функции `htons`, `ntohs`, и др., для того, чтобы правильно представлять целочисленные значения.\r\n\r\n### Адресация без IP-адреса\r\n\r\nУ каждого сетевого интерфейса есть имя в системе, например `eth0` или `wlan0`, которое можно посмотреть в выводе команды `ifconfig`, и *порядковый номер* (индекс). У каждого, даже не настроенного, сетевого интерфейса есть свой аппаратный адрес (MAC-адрес), размер которого обычно 6 байт.\r\n\r\nПри адресации через семейство протоколов `AF_PACKET` используется структура `sockaddr_ll`:\r\n\r\n```c\r\nstruct sockaddr_ll {\r\n\tunsigned short sll_family;   /* Always AF_PACKET */\r\n\tunsigned short sll_protocol; /* Physical-layer protocol */\r\n\tint            sll_ifindex;  /* Interface number */\r\n\tunsigned short sll_hatype;   /* ARP hardware type */\r\n\tunsigned char  sll_pkttype;  /* Packet type */\r\n\tunsigned char  sll_halen;    /* Length of address */\r\n\tunsigned char  sll_addr[8];  /* Physical-layer address */\r\n};\r\n```\r\n\r\nПоле `sll_family` должно иметь значение `AF_PACKET` (поскольку необходимо отделять этот тип адресов от других возможных `struct sockaddr`).\r\n\r\nДля отправки низкоуровневых пакетов определенному устройству с использованием протокола Ethernet, когда используется комбинация `(AF_PACKET, SOCK_DGRAM)`, необходимо заполнять поля:\r\n\r\n* `sll_protocol` - значение константы из `<linux/if_ether.h>`, которая определят тип пакета данных (протокол), который содержится внутри Ethernet-фрейма;\r\n* `sll_halen` - длина адреса в байтах; для современных реализаций Ethernet это значение равно `6` (константа `ETH_ALEN` из `<linux/if_ether.h>`) ;\r\n* `sll_ifindex` - индекс сетевого устройства; нумерация начинается с `1`, специальное значение `0` может быть использовано только для чтения (признак того, что интересуют данные из любого устройства);\r\n* `sll_addr` - значение MAC-адреса.\r\n* Все остальные поля заполняются драйвером устройства и должны быть инициализированы нулями.\r\n\r\nЕсли используется отправка пакетов без заголовка Ethernet, то есть, используется комбинация `(AF_PACKET, SOCK_RAW)`, то достаточно указать только порядковый индекс сетевого интерфейса `sll_ifindex`.\r\n\r\n### Управление устройствами ввода-вывода\r\n\r\nДля управления файло-подобными устройствами ввода-вывода используется системный вызов `ioctl`, сигнатура которого такая же, как для `fcntl`: первый аргумент - это файловый дескриптор, затем целочисленная команда, а потом возможны аргументы произвольного типа, в зависимости от команды.\r\n\r\nНабор команд для работы с сетевыми интерфейсами описан в [man 7 netdevice](http://man7.org/linux/man-pages/man7/netdevice.7.html). Многие из них могут быть выполнены только при наличии соответствующих прав (если модифицируют параметры сетевого интерфейса). С помощью GET-команд, отправляемых через системный вызов `ioctl`, можно выяснить индекс устройства по его имени, связанный с ним MAC-адрес, IP-адрес, если устройство настроено, и т. д.\r\n\r\n"
  },
  {
    "path": "practice/stat_fcntl/README.md",
    "content": "# Свойства файлов\n\n## Сведения о файле\n\n### Структура `stat`\n\nС каждым файлом в файловой системе связана метаинформация (status), которая определяется структурой `struct stat`:\n\n```\nstruct stat {\n   dev_t     st_dev;         /* ID of device containing file */\n   ino_t     st_ino;         /* Inode number */\n   mode_t    st_mode;        /* File type and mode */\n   nlink_t   st_nlink;       /* Number of hard links */\n   uid_t     st_uid;         /* User ID of owner */\n   gid_t     st_gid;         /* Group ID of owner */\n   dev_t     st_rdev;        /* Device ID (if special file) */\n   off_t     st_size;        /* Total size, in bytes */\n   blksize_t st_blksize;     /* Block size for filesystem I/O */\n   blkcnt_t  st_blocks;      /* Number of 512B blocks allocated */\n\n   struct timespec st_atim;  /* Time of last access */\n   struct timespec st_mtim;  /* Time of last modification */\n   struct timespec st_ctim;  /* Time of last status change */\n\n   /* Backward compatibility */\n   #define st_atime st_atim.tv_sec      \n   #define st_mtime st_mtim.tv_sec\n   #define st_ctime st_ctim.tv_sec\n};\n\n```\n\nМетаинформацию о файле получается с помощью команды `stat ИМЯ_ФАЙЛА` или одного из системных вызовов:\n * `int stat(const char *file_name, struct stat *stat_buffer)` - получение информации о файле по его имени;\n * `int fstat(int fd, struct stat *stat_buffer)` - то же самое, но для открытого файлового дескриптора;\n * `int lstat(const char *path_name, struct stat *stat_buffer)` - аналогично `stat`, но в случае, если имя файла указывает на символическую ссылку, то возвращается информация о самой ссылке, а не о файле, на который она ссылается.\n\n\n### Режим доступа и типы файлов в POSIX\n\nВ POSIX есть несколько основных типов файлов:\n\n * Регулярный файл (`S_IFREG = 0100000`). Занимает место на диске, содержит самые обычные данные.\n * Каталог (`S_IFDIR = 0040000`). Файл специального типа, который хранит список имен файлов.\n * Символическая ссылка (`S_IFLNK = 0120000`). Файл, который ссылается на другой файл (в том числе в другом каталоге или даже на другой файловой системе), и с точки зрения фукнций ввода-вывода ничем не отличается от того файла, на который он ссылается.\n * Блочные (`S_IFBLK = 0060000`) и символьные (`S_IFCHR = 0020000`) устройства. Используются как удобный способ взаимодействия с оборудованием.\n * Именованные каналы (`S_IFIFO = 0010000`) и сокеты (`S_IFSOCK = 0140000`), предназначенные для межпроцессного взаимодействия.\n\n\nТип файла закодирован в одном поле структуры с режимом доступа (`rwxrwxrwx`) - целочисленном `.st_mode`.\n\nДля выделения отдельных типов файлов применяются поразрядные операции с помощью одного из макросов: `S_ISREG(m)`, `S_ISDIR(m)`, `S_ISCHR(m)`, `S_ISBLK(m)`, `S_ISFIFO(m)`, `S_ISLNK(m)` и `S_ISSOCK(m)`, которые возвращают `0` в качестве false, и произвольное ненулевое значение в качестве true.\n\nДля выделения режима доступа, который закодирован в младших битах `.st_mode`, которые можно извлекать, применяя поразрядные операции с помощью констант `S_IWUSR`, `S_IRGRP`, `S_IXOTH` и др. Полный список констант можно посмотреть в `man 7 inode`.\n\n### Доступ к файлу\n\nУ каждого файла, помимо режима доступа (`rwx` для владельца, группы и остальных) есть два идентификатора, - целых положительных числа:\n * `.st_uid` - идентификатор пользователя-владельца файла;\n * `.st_gid` - идентификатор группы-владельца файла.\n\nПрава доступа \"для владельца\" применяются при совпадении идентификатора текущего пользователя (можно получить с помощью `getuid()`) с полем `.st_uid`. Аналогично - для группы при совпадении `getgid()` с `.st_gid`. В остальных случаях действуют права доступа \"для остальных\".\n\nУдобным способом определения прав у текущего пользователя является использование системного вызова `access`:\n```\nint access(const char *path_name, int mode)\n```\n\nЭтот системный вызов принимает в качестве параметра `mode` поразрядную комбинацию из флагов `R_OK`, `W_OK`, `X_OK` и `F_OK`, - соответственно способность чтения, записи, выполнения файла, и его существование. Возвращает 0 в случае, если перечисленные аттрибуты допустимы для текущего пользователя, и -1 в противном случае.\n\n\n## Маска создания файлов\n\nПри создании новых файлов с помощью системного вызова `open` (и всех высокоуровневых функций, которые используют `open`), нужно обязательно указывать режим доступа для вновь созданных файлов.\n\nВ реальности режим доступа может отличаться от запрошенного: для вновь созданного файла (или каталога) применяется *маска создания файлов* с помощью поразрядной операции \"и-не\":\n```\n /* Пусть umask = 0222 */\n open(\"new_file\", O_WRONLY|O_CREAT, 0666); // OK\n\n /* Создан файл с аттрибутами 0666 & ~0222 = 0444 */\n```\n\nПо умолчанию маска создания файлов равна `0000`, то есть не накладывает никаких ограничений. Системный вызов `umask` позволяет задать явным образом новую маску, которая может быть использована для предотвращения случайного создания файлов со слишком слабозащищенными правами доступа.\n\n\n\n## Ссылки и файловые дескрипторы\n\nПара значений, хранящихся в полях `.st_dev` и `.st_ino`, позволяют однозначно идентифицировать любой файл в виртуальной файловой системе до её перезагрузки или отмонтирования каких-то частей файловой системы.\n\nПоле `.st_nlink` хранит количество имен, связанных с данным именем файла, причем они могут находиться в разных каталогах одной физической файловой системы. Для файла, который существует в файловой системе, `.st_nlink` всегда не меньше `1`, при удалении файла это число становится равным `0`. Но если файл открыт хотя бы одним процессом в системе, то физически он не удаляется (хотя по имени его уже не найти), и доступен по файловому дескриптору до момента закрытия файла.\n\nУдалить программным спосособом файл можно с помощью системного вызова `unlink`. Как следует из названия, этот системный вызов уменьшает количество ссылок `.st_nlink`.\n\nДля предотвращения *состояния гонки* (race condition), у многих системных вызовов для работы с файлами существуют аналоги, подразумевающие работу с файловыми дескрипторами, а не с именами файлов.\n\nПример состояния гонки:\n```\nstruct stat st;\nassert(0==stat(\"my_file.txt\", &st)); // OK\n\n/* теперь кто-нибудь извне успевает удалить или\n   переименовать файл между моментами этих двух вызовов */\n\nint fd = open(\"my_file.txt\", O_RDONLY); // ERR: файл не найден\n/* ??? как же так? Только что был здесь, а теперь недоступен */\n```\n\nЕсли немного переделать этот пример с использованием `fstat`, то проблема решена:\n```\nint fd = open(\"my_file.txt\", O_RDONLY);\nif (-1!=fd) {\n\n    /* Теперь кто-нибудь удаляет файл, или переименовывает\n    его. Но нас это уже не должно волновать: файл существует\n    до того момента, пока мы его не закроем */\n\n    struct stat st;    \n    fstat(fd, &st);\n    off_t file_size = st.st_size; // размер доступных данных\n}\n```\n\n## Разные полезные системные вызовы\n\n* Добавление нового имени (увеличение ссылок) - `man 2 link`\n* Удаление (уменьшение ссылок) - `man 2 unlink`\n* Создание символической ссылки - `man 2 symlink`\n* Чтение значения символической ссылки - `man 2 readlink`\n* Создание каталога - `man 2 mkdir`\n* Удаление пустого каталога - `man 2 rmdir`\n* Изменение режима доступа - `man 2 chmod`\n* Перемещение (переименование) файла - `man 2 rename`\n\n\n## Аттрибуты файловых дескрипторов\n\nСистемный вызов `fcntl` предназначен для управления открытыми файлыми дескрипторами.\n\n```\nint fcntl(int fd, int get_command);\nint fcntl(int fd, int set_command, void *set_value);\n```\n\nДля открытых файлов можно командами `F_GETFL` и `F_SETFL` получать и менять аттрибуты открытия: `O_APPEND`, `O_ASYNC`, `O_NONBLOCK`. В Linux не возможно изменить режим открытия файлов (например поменять `O_RDONLY` на `O_RDRW`), хотя в некоторых UNIX системах это допускается.\n\n## Блокировки файлов\n\nПроблема состояния гонки относится не только к возможным изменениям аттрибутов файлов, но и к проблеме *гонки данных* (data race) между различными процессами, которые хотят одновременно обращаться к одним и тем же файлам.\n\nВ UNIX-подобных системах существует два основных механизма для блокировки файлов.\n\n### Блокировки BSD flock\n\nПоддерживается \\*BSD и Linux. Системный вызов `flock` имеет\nсигнатуру:\n```\nint flock(int fd, int operation);\n```\n`fd` - открытый файловый дескриптор, возможные операции:\n * `LOCK_SH` - получить разделяемую блокировку. Разделяемую блокировку на файл могут накладывать разные процессы, не мешая друг другу, когда им требуется только читать данные из файла.\n * `LOCK_EX` - получить эксклюзивную блокировку. Только один процесс может это сделать.\n * `LOCK_UN` - разблокировать файла.\n\nБлокировка накладывается не на файловые дескрипторы, а на сами файлы, с с которыми они связаны. При попытке получить блокировку (любого типа) к файлу, который кем-то эксклюзивно заблокирован, приведет к приостановке текущего процесса до тех пора, пока блокировка не будет снята.\n\n### Блокировки POSIX file lock\n\nИспользует команды `F_GETLK` (получить блокировки), `F_SETLK` (установить блокиовку) и `F_SETLKW` (установить блокировку, но сначала дождаться, когда это возможно) системного вызова `fcntl` для управления блокировками.\n\nВ качестве третьего аргумента `fcntl` передается структура:\n```\nstruct flock {\n    off_t   l_start;  /* starting offset\t*/\n    off_t   l_len;    /* len = 0\tmeans until end\tof file\t*/\n    pid_t   l_pid;    /* lock owner */\n    short   l_type;   /* lock type: read/write, etc. */\n    short   l_whence; /* type of\tl_start\t*/\n    int     l_sysid;  /* remote system id or zero for local */\n};\n```\n\nВ отличии от BSD `flock`, этот способ является более гибким, и позволяет управлять блокировками отдельных частей файла.\n\nПример:\n```\nstruct flock fl;\nmemset(&fl, 0, sizeof(fl));\n\n// Установить блокировку только для чтения (не эксклюзивно)\nfl.l_type = F_RDLCK;\n\n// Блокировка на весь файл\nfl.l_whence = SEEK_SET; // по аналогии с lseek\nfl.l_start = 0;         // по аналогии с lseek\nfl.l_len = 0;           // 0 - до конца, либо размер области                        \n\n// Установить блокировку для чтения. Если какой-то процесс\n// уже захватил файл для записи, то ожидание, пока освободится\nfcntl(fd, F_SETLKW, &fl);\n\n// Блокировка на запись с 10 по 15 байты файла\nfl.l_type = F_WRLCK;\nfl.l_start = 10;\nfl.l_len = 5;\nfcntl(fd, F_SETLK, &fl);\n\n// Снять блокировку с этого же диапазона\nfl.l_type = F_UNLCK;\nfcntl(fd, F_SETLK, &fl);\n\n// При закрытии файла процессом все блокировки недействительны\nclose(fd);\n```\n\n### Блокировка BSD lockf\n\nУпрощенным аналогом для блокировки отдельных частей файла является системный вызов `lockf`; для указания начала области блокировки нужно переместить указатель `lseek`.\n\n```\nint lockf(int fd, int cmd, off_t len);\n```\nВ отличии от `fcntl`, этот системный вызов подразумевает только эксклюзивную блокировку (для записи).\n\n### Команды flock и lslocks\n\nКоманда `flock` позоляет установить блокировку на произвольный файл для запуска какой-либо программы, и снимает блокировку при завершении работы дочернего процесса.\n\nКоманда `lslocks` отображает таблицу файлов с блокировками.\n"
  },
  {
    "path": "practice/time/README.md",
    "content": "# Время в UNIX\n\n## API для работы со временем\n\n### Текущее время\n\nВремя в UNIX-системах определяется как количество секунд, прошедшее с 1 января 1970 года, причем часы идут по стандартному гринвичскому времени (GMT) без учета перехода на летнее время (DST - daylight saving time).\n\n32-разрядные системы должны прекратить своё нормальное существование 19 января 2038 года, поскольку будет переполнение знакового целого типа для хранения количества секунд.\n\nФункция `time` возвращает количество секунд с начала эпохи. Аргументом функции (в который можно передать `NULL`) является указатель на переменную, куда требуется записать результат.\n\nВ случае, когда требуется более высокая точность, чем 1 секунда, можно использовать системный вызов `gettimeofday`, который позволяет получить текущее время в виде структуры:\n\n```\nstruct timeval {\n  time_t      tv_sec;  // секунды\n  suseconds_t tv_usec; // микросекунды\n};\n```\n\nВ этом случае, несмотря на то, что в структуре определено поле для микросекунд, реальная точность будет составлять порядка 10-20 миллисекунд для Linux.\n\nБолее высокую точность можно получить с помощью системного вызова `clock_gettime`.\n\n\n### Разложение времени на составляющие\n\nЧеловеко-представимое время состоит из даты (год, месяц, день) и времени суток (часы, минуты, секунды).\n\nЭто описывается структурой:\n\n```\nstruct tm { /* время, разбитое на составляющие */\n  int tm_sec; /* секунды от начала минуты: [0 -60] */\n  int tm_min; /* минуты от начала часа: [0 - 59] */\n  int tm_hour; /* часы от полуночи: [0 - 23] */\n  int tm_mday; /* дни от начала месяца: [1 - 31] */\n  int tm_mon; /* месяцы с января: [0 - 11] */\n  int tm_year; /* годы с 1900 года */\n  int tm_wday; /* дни с воскресенья: [0 - 6] */\n  int tm_yday; /* дни от начала года (1 января): [0 - 365] */\n  int tm_isdst; /* флаг перехода на летнее время: <0, 0, >0 */\n};\n```\n\nДля преобразования человеко-читаемого времени в машинное используется функция `mktime`, а в обратную сторону - одной из функций: `gmtime` или `localtime`.\n\n\n### Daylight Saving Time\n\nВо многих странах используется \"летнее время\", когда стрелки часов переводятся на час назад.\n\nИстория введения/отмены летнего времени, и его периоды хранится в [базе данных IANA](https://data.iana.org/time-zones/tz-link.html).\n\nБаза данных представляет собой набор правил в текстовом виде, которые компилируются в бинарное представление, используемое библиотекой glibc. Наборы файлов с правилами перехода на летнее время для разных регионов хранятся в `/usr/share/zoneinfo/`.\n\nКогда значение `tm_isdst` положительное, то применяется летнее время, значение `tm_isdst` - зимнее. В случае, когда значение `tm_isdst` отрицательно, - используются данные из timezone data.\n\n\n## Reentrant-функции\n\nМногие функции POSIX API разрабатывались во времена однопроцессорных систем. Это может приводить к разным неприятным последствиям:\n\n```\nstruct tm * tm_1 = localtime(NULL);\nstruct tm * tm_2 = localtime(NULL); // opps! *tm_1 changed!\n```\n\nПроблема заключается в том, что некоторые функции, например `localtime`, возвращает указатель на структуру-результат, а не скалярное значение. При этом, сами данные структуры не требуется удалять, - они хранятся в `.data`-области библиотеки glibc.\n\nПроблема решается введением *повторно входимых (reentrant)* функций, которые в обязательном порядке требуют в качестве одного из аргументов указатель на место в памяти для размещения результата:\n\n```\nstruct tm tm_1; localtime_r(NULL, &tm_1);\nstruct tm tm_2; localtime_r(NULL, &tm_2); // OK\n```\n\nИспользование повторно входимых функций является обязательным (но не достаточным) условием при написании многопоточных программ.\n\nНекоторые reentrant-функции уже не актуальны в современных версиях glibc для Linux, и помечены как deprecated. Например, реализация `readdir` использует локальное для каждого потока хранение данных. \n\n## Виды часов\n\nВремя в UNIX системе представляется в виде двух чисел: количества секунд и количества наносекунд, но это не означает, что точность часов сопоставимо с одной наносекундой. В современных компьютерах архитектуры несколько типов часов:\n\n* аппаратные часы, которые работают от отдельной батарейки даже при отключения питания;\n* счетчик в ядре операционной системы, который периодически обновляется отдельным аппаратным таймером;\n* счетчик тактов процессора.\n\n### Аппаратные часы\n\nАппаратные часы обычно работают на базе стандартного часового кварца, обеспечивающего частоту 32.768КГц, и имеющие точность, сопоставимую с точностью обычных бытовых часов. Эти часы могут хранить дату как в формате UTC (стандарт, принятый в UNIX-системах), так и локальное время (принято в Windows). \n\nВ Linux аппаратные часы доступны в виде символьного устройства `/dev/rtc`, доступ к которому есть только у пользователя `root`.\n\nЭто устройство может быть открыто только для чтения, после чего из него можно читать 32-битные значения - информацию о прерываниях. Настройка поведения часов осуществляется с помощью системного вызова `ioctl` и передачей одной из команд, относящихся к [`rtc(4)`](http://man7.org/linux/man-pages/man4/rtc.4.html).\n\nПрерывания могут быть:\n\n* каждую секунду, если часы настроены на ежесекундное срабатывание `RTC_UIE`\n* с частотой от 2 до 8192 Гц, причем частота должна быть степенью двойки `RTC_PIE`\n* срабатывание в определенное время `RTC_AIE`.\n\nЕжесекундное прерывание с использованием часов реального времени:\n\n```c\n#include <sys/ioctl.h>       // системный вызов ioctl\n#include <linux/rtc.h>       // константы RTC_*\n\nint rtc = open(\"/dev/rtc\", O_RDONLY);\nif (-1==rtc) { perror(\"open /dev/rtc\"); exit(1); } // только root может открыть\n\nioctl(rtc, RTC_UIE_ON, 0);   // включаем прерывания каждую секунду\nwhile (1) {\n    int interrupt_mask;\n    // системный вызов read блокируется до следующего прерывания\n    read(rtc, &interrupt_mask, sizeof(interrupt_mask));\n    puts(\"Tick\");\n}\n```\n\nАппаратные часы хранят информацию о текущей дате и текущем времени с точностью до секунды, причем это время может отличаться от системного. \n\nПолучение времени из аппаратных часов:\n\n```c\n#include <sys/ioctl.h>       // системный вызов ioctl\n#include <linux/rtc.h>       // константы RTC_*, а ещё структура rtc_time\n\nint rtc = open(\"/dev/rtc\", O_RDONLY);\nif (-1 == rtc) { perror(\"open /dev/rtc\"); exit(1); }\n\nstruct rtc_time t = {};\n// чтение текущего времени из аппаратных часов\nioctl(rtc, RTC_RD_TIME, &t);\n\nprintf(\"RTC time: %02d : %02d : %02d \\n\",\n       t.tm_hour, t.tm_min, t.tm_sec);\n```\n\nСинхронизация системного времени с аппаратными часами осуществляется при загрузке системы и завершении работы (выключении или перезагрузке).\n\nКоманда `hwclock`  позволяет взаимодействовать с часами реального времени, в том числе и для синхронизации.\n\n```bash\n> hwclock -r     # прочитать и вывести время из RTC\n> hwclock -w     # сохранить системное время в RTC (обычно при выключении)\n> hwclock -s     # установить системное время из RTC (при загрузке)\n```\n\nПо умолчанию подразумевается, что аппаратные часы используют время UTC, но можно если компьютер используется совместно с системой Windows (двойная загрузка), то можно синхронизировать локальное время, для этого используется опция `-l`. Многие дистрибутивы Linux на этапе установки позволяют указать, какое именно время хранится в часах реального времени.\n\n### Системное время и источники времени\n\nОтсчет времени начинается с момента загрузки ядра, и хранится в виде целого количества *тиков* (jiffies), продолжительность которых определяется параметром компиляции ядра `CONFIG_HZ`, и может принимать одно из значений: 100, 250, 300 или 1000 Гц. Для текущего ядра это можно выяснить в файле `/boot/config-ВЕРСИЯ_ЯДРА`.\n\nБолее высокая частота подразумевает большую нагрузку на процессор, но бывает полезна в некоторых применениях, когда требуется повысить отзывчивость системы.\n\nДоступные источники времени зависят от архитектуры процессора и конфигурации ядра, узнать в Linux их можно командой:\n\n```bash\n> cat /sys/devices/system/clocksource/clocksource0/available_clocksource\ntsc hpet acpi_pm\n#^   ^    ^ \n#|   |    Legacy-драйвер           \n#|   Системный таймер высокой точнсти, обычно работает на частоте от 10Мгц\n#Регистр Time-Step Counter в самом процессоре\n```\n\nТекущий способ определения точного времени хранится в `current_clocksource`:\n\n```bash\n> cat /sys/devices/system/clocksource/clocksource0/current_clocksource\ntsc\n```\n\nНаиболее точным источником времени, в то же время с минимальным временем доступа, - это счетчик тактов в самом процессоре Time-Step Counter, значение которого, для архитектуры x86 можно получить с помощью команды `RDTSC`. Поскольку получение высокоточных значений времени используется при эксплуатации уязвимостей процессоров Meltdown и Spectre, то этот способ может быть принудительно отключен в системе.\n\nДля получения текущего времени из источника времени используется системный вызов `clock_gettime`:\n\n```c\n#include <time.h>\n\nstruct timespec {\n    time_t tv_sec;  // время в секундах\n    long   tv_nsec; // доля времени в наносекундах\n};\n\nint clock_gettime(clockid_t id, /* out: */ struct timespec *tp);\n```\n\nПервый параметр системного вызова - это целочисленное значение, определяющее, какой именно счетчик или таймер нужно использовать. Для большинства UNIX-систем определены таймеры:\n\n* `CLOCK_REAL` - значение астрономического времени, где за точку отсчета принимается *начало эпохи* - 1 января 1970 года;\n* `CLOCK_MONOTONIC` - значение времени с момента загрузки ядра, исключая то время, пока система находилась в спящем режиме;\n* `CLOCK_PROCESS_CPUTIME_ID` - значение времени, затраченного на выполнение текущего процесса;\n* `CLOCK_THREAD_CPUTIME_ID` - значение времени, затраченного на выполнение текущего потока.\n\nЭтот системный вызов в FreeBSD, и ядре Linux до версии 2.6.21, для стандартных таймеров возвращает значение, которое было обновлено в момент предыдущего аппаратного прерывания от системного таймера, то есть точность времени не превышает продолжительности одного тика. \n\nВ современных версиях Linux происходит обращение к регистру TSC, либо опрос системного таймера, который возвращает текущее значение с высокой точностью. Часы `CLOCK_REAL_COARSE` и `CLOCK_MONOTONIC_COARSE` возвращают время с точностью до одного тика, как в старых версиях.\n\nВ системе FreeBSD предусмотрены два вида часов - точные, с суффиксом `_PRECISE`, которые опрашивают системный таймер, и быстрые, с суффиксом `_FAST`, которые возвращают значения с точностью до тика. POSIX-совместимым названиям часов соответствуют `_FAST`-версии.\n\nСистемный вызов `clock_gettime` реализован в виде [`vdso(7)`](http://man7.org/linux/man-pages/man7/vdso.7.html)-функции, которая доступна в адресном пространстве пользователя. В случае, если происходит опрос часов с низкой точностью (например `CLOCK_REAL_COARSE` в Linux или `CLOCK_REAL_FAST` в FreeBSD), то время вычисляется в адресном пространстве пользователя по значению из счетчика, ранее проставленного планировщиком задач. Если же требуется получить время с высокой точностью и не используется TSC, то может потребоваться настоящий системный вызов для опроса системного таймера.\n\n"
  },
  {
    "path": "practice/x86-64/README.md",
    "content": "# Ассемблер архитектуры x86/x86-64\n\n## Внешние ссылки\n\nОсновной reference по набору команд [преобразованный в HTML](https://www.felixcloutier.com/x86/).\n\nReference по наборам команд MMX, SSE и \nAVX [на сайте Intel](https://software.intel.com/sites/landingpage/IntrinsicsGuide/).\n**Доступ из РФ закрыт, требуется VPN!**\n\nТо же самое [из неофициального источника](https://www.laruence.com/sse/), доступ пока есть.\n\nНеплохой учебник по ассемблеру x86 [на WikiBooks](https://en.wikibooks.org/wiki/X86_Assembly)\n\n## Синтаксис AT&T и Intel\n\nИсторически сложилось два синтаксиса языка ассемблера x86: синтаксис AT&T,\nиспользуемый в UNIX-системах, и синтаксис Intel, используемый в DOS/Windows.\n\nРазличие, в первую очередь, относится к порядку аргументов команд.\n\nКомпилятор gcc по умолчанию использует синтаксис AT&T, но с указанием опции\n`-masm=intel` может переключаться в синтаксис Intel.\n\nКроме того, можно указать используемый синтаксис первой строкой в тексте\nсамой программы:\n```nasm\n.intel_syntax noprefix\n```\n\nЗдесь параметр `noprefix` после `.intel_syntax` указывает на то, что помимо порядка аргументов, соответствующих синтаксису Intel, ещё и имена регистров не должны начинаться с символа `%`, а константы - с символа `$`, как это принято в синтаксисе AT&T.\n\nМы будем использовать именно этот синтаксис, поскольку с его использованием\nнаписано большинство доступной документации и примеров, включая документацию\nот производителей процессоров.\n\n## Регистры процессора общего назначения\n\nИсторически семество процессоров x86 унаследовало набор 8-битных регистров\nобщего назначения\nсемества 8080/8085, которые назывались `a`, `b`, `c` и `d`. Но поскольку\nпроцессор 8086 стал 16-битным, то регистры стали назваться `ax`, `bx`, `cx`\nи `dx`.\nВ 32-битных процессорах они называются `eax`, `ebx`, `ecx` и `edx`, в\n64-битных `rax`, `rbx`, `rcx` и `rdx`.\n\nКроме того, в x86 есть регистры \"двойного назначения\", которые можно использовать, в том числе, в качестве регистров общего назначения, если пользоваться ограниченным подмножеством команд процессора:\n\n * `rbp` - верхняя граница стека;\n * `rsi` - индекс элемента массива, из которого выполняется копирование;\n * `rdi` - индекс элемента массива, в который выполняется копирование.\n\nРегистр `rsp` содержит указатель на нижнюю границу стека, поэтому произвольным образом его использовать не рекомендуется.\n\n### Регистры x86-64\n\n64-разрядные регистры для архитектуры x86-64 именуются начиная с буквы `r`. Помимо регистров `rax`...`rsi`, `rdi` можно использовать регистры общего назначение `r9`...`r15`. Указатель стека хранится в `rsp`, верхняя граница стекового фрейма - в `rbp`.\n\nМладшие 32-разрядные части регистров `rax`...`rsi`,`rdi`,`rsp`,`rbp` можно адресовать по именам `eax`...`esi`,`edi`,`esp`,`ebp`. При записи значений по 32-битным именам регистров, старшие 32 разряда обнуляются, что приемлемо для операций над 32-разрядными беззнаковыми значениями.\n\nДля работы со знаковыми 32-разрядными значениями, например типом `int`, необходимо предварительно выполнять операции *знакового расширения* с помощью команды `movslq`\n\n## Некоторые инструкции\n\n**Для синтаксиса Intel** первым аргументов команды является тот, значение которого\nбудет модифицировано, а вторым - которое остается неизменным.\n\n```nasm\nadd     DST, SRC        /* DST += SRC */\nsub     DST, SRC        /* DST -= SRC */\ninc     DST             /* ++DST */\ndec     DST             /* --DST */\nneg     DST             /* DST = -DST */\nmov     DST, SRC        /* DST = SRC */\nimul    SRC             /* (eax,edx) = eax * SRC - знаковое */\nmul     SRC             /* (eax,edx) = eax * SRC - беззнаковое */\nand     DST, SRC        /* DST &= SRC */\nor      DST, SRC        /* DST |= SRC */\nxor     DST, SRC        /* DST ^= SRC */\nnot     DST             /* DST = ~DST */\ncmp     DST, SRC        /* DST - SRC, результат не сохраняется, */\ntest    DST, SRC        /* DST & SRC, результат не сохраняется  */\nadc     DST, SRC        /* DST += SRC + CF */\nsbb     DST, SRC        /* DST -= SRC - CF */\n```\n\n**Для синтаксиса AT&T** порядок аргументов - противоположный, то есть команда\n`add %rax, %rbx` вычислит сумму `%rax` и `%rbx`, после чего сохранит результат\nв регистр `%rbx`, который указан вторым аргументом.\n\n## Флаги процессора\n\nВ отличии от процессоров ARM, где обновление регистра флагов производится\nтолько при наличии специального флага в команде, обозначаемого суффиксом\n`s`, в процессорах Intel флаги обновляются всегда большинстом инструкций.\n\nФлаг `ZF` устанавливается, если в результате операции был получен нуль.\n\nФлаг `SF` устанавливается, если в результате операции было получено\nотрицательное число.\n\nФлаг `CF` устанавливается, если в результате выполнения операции произошел\nперенос из старшего бита результата. Например, для сложения `CF` устанавливается\nесли результат сложения двух беззнаковых чисел не может быть представлен\n64-битным беззнаковым числом.\n\nФлаг `OF` устанавливается, если в результате выполняния операции произошло\nпереполнение знакового результата. Например, при сложении `OF` устанавливается,\nесли результат сложения двух знаковых чисел не может быть представлен\n64-битным знаковым числом.\n\nОбратите внимание, что и сложение `add`, и вычитание `sub` устанавливают\nодновременно и флаг `CF`, и флаг `OF`. Сложение и вычитание знаковых и\nбеззнаковых чисел выполняется совершенно одинаково, и поэтому используется одна\nинструкция и для знаковой, и для беззнаковой операции.\n\nИнструкции `test` и `cmp` не сохраняют результат, а только меняют флаги.\n\n## Управление ходом программы\n\nБезусловный переход выполняется с помощью инструкции `jmp`\n```nasm\njmp label\n```\n\nУсловные переходы проверяют комбинации арифметических флагов:\n```nasm\njz      label   /* переход, если равно (нуль), ZF == 1 */\njnz     label   /* переход, если не равно (не нуль), ZF == 0 */\njc      label   /* переход, если CF == 1 */\njnc     label   /* переход, если CF == 0 */\njo      label   /* переход, если OF == 1 */\njno     label   /* переход, если OF == 0 */\njg      label   /* переход, если больше для знаковых чисел */\njge     label   /* переход, если >= для знаковых чисел */\njl      label   /* переход, если < для знаковых чисел */\njle     label   /* переход, если <= для знаковых чисел */\nja      label   /* переход, если > для беззнаковых чисел */\njae     label   /* переход, если >= (беззнаковый) */\njb      label   /* переход, если < (беззнаковый) */\njbe     label   /* переход, если <= (беззнаковый) */\n```\n\nВызов функции и возврат из неё осуществляются командами `call` и `ret`\n```nasm\ncall    label   /* складывает в стек адрес возврата, и переход на label */\nret             /* вытаскивает из стека адрес возврата и переходит к нему */\n```\n\nКроме того, есть составная команда для организации циклов, которая\nподразумевает, что в регистре `ecx` находится счётчик цикла:\n```nasm\nloop    label   /* уменьшает значение ecx на 1; если ecx==0, то\n                   переход на следующую инструкцию, в противном случае\n                   переход на label */\n```\n\n\n## Адресация памяти\n\nВ отличии от RISC-процессоров, x86 позволяет использовать в качестве\n**один из аргументов** команды как адрес в памяти.\n\n**В синтаксисе AT&T** такая адресация записывается в виде:\n`OFFSET(BASE, INDEX, SCALE)`, где `OFFSET` - это константа, `BASE` и `INDEX` -\nрегистры, а `SCALE` - одно из значений: `1`, `2`, `4` или `8`.\n\nАдрес в памяти вычисляется как `OFFSET+BASE+INDEX*SCALE`. Параметры `OFFSET`,\n`INDEX` и `SCALE` являются опциональными. При их отсутсвтвии подразумевается,\nчто `OFFSET=0`, `INDEX=0`, `SCALE` равен размеру машинного слова.\n\n**В синтаксисе Intel** используется более очевидная нотация:\n`[BASE + INDEX * SCALE + OFFSET]`.\n\n## Соглашения о вызовах для 64-разрядной архитектуры SystemV AMD64 ABI\n\nЦелочисленные аргументы передаются последовательно в регистрах: `rdi`, `rsi`, `rdx`, `rcx`, `r8`, `r9`. Если передается более 6 аргументов, то оставшиеся - через стек.\n\nВещественные аргументы передаются через регистры `xmm0`...`xmm7`.\n\nВозвращаемое значение целочисленного типа должно быть сохранено в `rax`,  вещественного - в `xmm0`.\n\nВызываемая функция обязана сохранять на стеке значения регистров общего назначения `rbx`, `rbp`, и регистры `r12`...`r15`.\n\nКроме того, при вызове функции для 64-разрядной архитектуры есть дополнительное требование - перед вызовом функции стек должен быть выровнен по границе 16 байт, то есть необходимо уменьшить значение `rsp` таким образом, оно было кратно 16. Если кроме регистров задействуется стек для передачи параметров, то они должны быть прижаты к нижней выровненной границе стека.\n\nДля функций гарантируется 128-байтная \"красная зона\" в стеке ниже регистра `rsp` - область, которая не будет затронута внешним событием, например, обработчиком сигнала. Таким образом, можно задействовать для адресации локальных переменных память до `rsp-128`.\n"
  },
  {
    "path": "projects/README.md",
    "content": "# Семестровые проекты\n\n## Общие требования\n\n 1. Проект должен быть реализован на языке Си или C++\n 2. В поставке проекта должны быть проектные файлы для его сборки с помощью `cmake`. Все использованные сторонние библиотеки должны быть надлежащим образом оформлены в качестве зависимостей, проверяемых при конфигурировании проекта\n 3. Проект должен быть опубликован в **приватном** репозитории на GitHub или BitBucket. Доступ должен быть предоставлен лектору курса АКОС и своему семинаристу\n 4. При выполнении проекта допускается использование ограниченного набора сторонних библиотек и технологий, которые перечислены в описании проектов\n 5. В репозитории должен быть файл `README.md` с описанием проекта и файлы с документацией, достаточной для сборки, настройки и использования полученной программы.\n\n## Описания проектов\n\n * [Компилятор языка Оберон](compiler.md)\n * [Веб-сервер с поддержкой динамической генерации страниц](httpd.md)\n * [HTTP-прокси сервер](proxy.md)\n * [Сетевая игра стрелялка](task_doom.pdf)\n * [Модельный Шелл](shell.pdf)\n * [Ассемблерные макросы](assembler_macroces.md)\n \n"
  },
  {
    "path": "projects/assembler_macroces.md",
    "content": "# Написание набора ассемблерных  макросов\n\nТребуется написать набор макросов для gnu assembler в спецификации .intel_syntax noprefix\nв соответствии со спецификацией\n[здесь](https://github.com/intel-arch-assembler-course/masm/blob/master/doc/macroces_description_ru.md).\nПри этом примеры [здесь](https://github.com/intel-arch-assembler-course/gnu-assembler/tree/master/examples) \nдолжны работать с минимальными переделками. Примеры написаны для Macroassembler (masm) -\nассемблер используемый microsoft.\n\nТак же полезно написать макросы и переписать примеры под 64-х битный ассемблер.\n\nТакже былог бы интересно реализовать всё это под arm.\n\n\n"
  },
  {
    "path": "projects/compiler.md",
    "content": "# Компилятор языка Оберон\n\nЦель - реализовать полноценный компилятор для языка программирования [Оберон](https://en.wikipedia.org/wiki/Oberon_(programming_language) первой версии (без поддержки объектно-ориентированного программирования).\n\nВ качестве эталонной реализации можно рассматривать компилятор [XDS Oberon](https://www.excelsior.ru/products/xdsdl).\n\nНа вход компилятор должен принимать текст программы, на выходе - создавать выполняемый файл для операционной системы Linux. Архитектура процессора (ARM, x86 или x86_64) - остается на усмотрение студента.\n\nФормальное описание языка: [http://www.ethoberon.ethz.ch/oreport.html](http://www.ethoberon.ethz.ch/oreport.html)\n\nEBNF языка: [http://www.ethoberon.ethz.ch/EBNF.html](http://www.ethoberon.ethz.ch/EBNF.html)\n\n# Пререквизиты, необходимые для выполнения проекта\n\n 1. Теория формальных языков и трансляций\n 2. Ассемблер целевого языка программирования\n 3. Системные вызовы, необходимые для запуска других команд\n 4. Системные вызовы, необходимые для реализации базовой функциональности стандартной библиотеки\n\n\n# Стек технологий\n\nКомпилятор должен быть реализован на языке Си или С++.\n\nЗапрещено использовать:\n * Библиотеки LLVM\n * Готовые инструменты для парсинга (`yacc`, `bison` и пр.)\n\nРазрешается использовать сторонние библиотеки, реализующие регулярные выражения.\n\n# Критерии оценивания\n\n * **3 балла.** За этот проект не ставится\n * **4 балла.** За этот проект не ставится\n * **5 баллов.** Реализован разбор текстов программ без поддержки структур и массивов и его преобразование в текст программы на ассемблере\n * **6 баллов.** Реализована поддержка массивов или структур\n * **7 баллов.** Полная реализация языка программирования\n * **+1 балл.** Реализована цепочка запуска инструментов `gcc` для создания выполняемого файла\n * **+1 балл.** Реализована стандартная библиотека\n * **+1 балл.** Выполняемые программы не связываются со стандартной библиотекой языка Си, и могут работать без неё.\n"
  },
  {
    "path": "projects/httpd.md",
    "content": "# Веб-сервер с поддержкой динамической генерации страниц \n\nЦель - реализовать веб-сервер для протокола `HTTP/1.1`, который умеет не только выдавать статические страницы, но и выполнять произвольные программы, взаимодействуя по протоколу [CGI](https://en.wikipedia.org/wiki/Common_Gateway_Interface).\n\nВ качестве эталонной реализации можно рассматривать сервер [Apache](http://httpd.apache.org).\n\nРеализованный сервер, помимо реализации основной функциональности, должен предполагать, что он работает без пользовательского интерфейста, то есть должны быть предусмотрены механизмы для управления запущенным сервером.\n\n# Пререквизиты, необходимые для выполнения проекта\n\n 1. Работа со строками\n 2. Работа с процессами\n 3. Работа с сигналами\n 4. Работа с файловыми дескрипторами\n 5. Работа с сокетами\n\n\n# Стек технологий\n\nРазрешается использовать только стандартную библиотеку Си или C++.\n\n# Критерии оценивания\n\n * **3 балла.** Реализован сервер, который выдает статические страницы. Должна быть реализована функциональность для конфигурирования, запуска и остановки сервера\n * **5 баллов.** Сервер умеет выполнять CGI-скрипты и выдавать результат их работы\n * **8 баллов.** Реализована возможность запускать потенциально опасные скрипты с ограничением их возможностей\n * **+1 балл.** Реализована передача данных скриптам через POST-запросы\n * **+1 балл.** Поддерживается запись логов сервера, информативность которой можно настраивать\n"
  },
  {
    "path": "projects/proxy.md",
    "content": "# HTTP-прокси сервер\n\nЦель - реализовать веб-сервер для протокола `HTTP/1.1`, который умеет обслуживать с минимальными задержками 100К одновременных соединений, выдавать статические страницы, и перенаправлять запросы на другие веб-серверы, кэшируя их выдачу.\n\nВ качестве эталонной реализации можно рассматривать сервер [Nginx](http://nginx.org).\n\nРеализованный сервер, помимо реализации основной функциональности, должен предполагать, что он работает без пользовательского интерфейста, то есть должны быть предусмотрены механизмы для управления запущенным сервером.\n\n# Пререквизиты, необходимые для выполнения проекта\n\n 1. Работа со строками\n 2. Работа с нитями\n 3. Работа с сигналами\n 4. Работа с файловыми дескрипторами\n 5. Работа с сокетами\n 6. Мультиплексирование ввода-вывода\n\n\n# Стек технологий\n\nРазрешается использовать только стандартную библиотеку Си или C++.\n\n# Критерии оценивания\n\n * **3 балла.** Реализован сервер, который выдает статические страницы. Должна быть реализована функциональность для конфигурирования, запуска и остановки сервера\n * **5 баллов.** Сервер умеет перенапралять запросы другим серверам\n * **8 баллов.** Реализованы кеширование ответов пенераправленных запросов и балансировка нагрузки\n * **+1 балл.** Корректно используется несколько ядер процессора\n * **+1 балл.** Поддерживается запись логов сервера, информативность которой можно настраивать\n"
  },
  {
    "path": "projects/shell.tex",
    "content": "\\documentclass[11pt, a4paper]{article}\n\\usepackage[utf8]{inputenc}\n\\usepackage[english,russian]{babel}\n\\usepackage[colorlinks=true,linkcolor=blue]{hyperref}\n\\usepackage{listings}\n\\usepackage{color}\n\\usepackage{verbatim}\n\n%\\usepackage{tocloft} % it requires package texlive-latex-extra in Debian\n\n\\usepackage{indentfirst}\n\n%\n% for tocloft\n%\n%\\renewcommand{\\cftsecaftersnum}{.}\n%\\renewcommand{\\cftsubsecaftersnum}{.}\n\n\\renewcommand{\\thesection}{\\arabic{section}.}\n\n\n\n\\definecolor{mygreen}{rgb}{0,0.6,0}\n\\definecolor{mygray}{rgb}{0.5,0.5,0.5}\n\\definecolor{mymauve}{rgb}{0.58,0,0.82}\n\n\\lstset{ %\n  backgroundcolor=\\color{white},   % choose the background color; you must add \\usepackage{color} or \\usepackage{xcolor}\n  basicstyle=\\footnotesize,        % the size of the fonts that are used for the code\n  breakatwhitespace=false,         % sets if automatic breaks should only happen at whitespace\n  breaklines=true,                 % sets automatic line breaking\n  captionpos=b,                    % sets the caption-position to bottom\n  commentstyle=\\color{mygreen},    % comment style\n  deletekeywords={...},            % if you want to delete keywords from the given language\n  escapeinside={\\%*}{*)},          % if you want to add LaTeX within your code\n  extendedchars=true,              % lets you use non-ASCII characters; for 8-bits encodings only, does not work with UTF-8\n  frame=single,                    % adds a frame around the code\n  keepspaces=true,                 % keeps spaces in text, useful for keeping indentation of code (possibly needs columns=flexible)\n  keywordstyle=\\color{blue},       % keyword style\n  language=C,                      % the language of the code\n  morekeywords={*,...},            % if you want to add more keywords to the set\n  numbers=left,                    % where to put the line-numbers; possible values are (none, left, right)\n  numbersep=5pt,                   % how far the line-numbers are from the code\n  numberstyle=\\tiny\\color{mygray}, % the style that is used for the line-numbers\n  rulecolor=\\color{black},         % if not set, the frame-color may be changed on line-breaks within not-black text (e.g. comments (green here))\n  showspaces=false,                % show spaces everywhere adding particular underscores; it overrides 'showstringspaces'\n  showstringspaces=false,          % underline spaces within strings only\n  showtabs=false,                  % show tabs within strings adding particular underscores\n  stepnumber=2,                    % the step between two line-numbers. If it's 1, each line will be numbered\n  stringstyle=\\color{mymauve},     % string literal style\n  tabsize=2,                       % sets default tabsize to 2 spaces\n  title=\\lstname                   % show the filename of files included with \\lstinputlisting; also try caption instead of title\n}\n\n\n\\title{Задание практикума: командная оболочка операционной системы}\n\\author{Сальников А.Н.}\n\\date{}\n\n\\begin{document}\n\n\\maketitle\n\n\\tableofcontents\n\n\\section{Общее описание}\n\nТребуется написать программу на языке \\textbf{\\textit{Си}}, осуществляющую частичную \nэмуляцию командной оболочки (shell). Примеры программных оболочек:\n{\\selectlanguage{english}\nWindows Console (cmd)\\cite{cmd}, Windows Power Shell\\cite{Power_Shell}, bash\\cite{guide-eng},\ntcsh\\cite{tcsh}, zsh\\cite{zsh}.}\n\nКомандная оболочка представляет собой интерактивную программу. Поток команд берется со \nстандартного потока ввода. Команды shell-у задаются пользователем через командую строку. \nСчитывать команды нужно в цикле до тех пор, пока на очередной итерации не будет получен конец \nфайла (детектируется при помощи константы EOF или проверкой соответствующих возвращаемых значений \nфункции чтения из файлов/потоков) либо не будет введена команда \\textbf{exit}. Перед считыванием \nнужно вывести на экран приглашение к набору команд. Например приглашение может быть таким: \n\\textit{``vasia''} далее символ \\textit{\\$} и следующий за ним пробел).\nДлина считываемой строки ограничена только размером\nвиртуальной памяти доступной процессу.\n\nПример приглашения ввода команд (здесь пользователь уже набрал некторорую команду):\n{\\small\n\\begin{verbatim}\nvasia$ echo \"We found: \"; find /home -name \\*${USER}\\* 2>/dev/null| \\\n       wc -l; echo \" files\"\n\\end{verbatim}\n}\n\nЗавершение shell происходит либо по закрытию стандартного потока ввода, либо в случае \nвыполнения встроенной команды  \\textbf{exit} (Реализованы должны быть оба варианта).\n\nПрограмма должна корректно обрабатывать все ошибки, выдавая осмысленные информационные сообщения о них\nв стандартный поток ошибок. В том числе обрабатывать ошибки неправильного синтаксиса командной строки. \n\n\\section{Описание языка команд shell}\n\nКомандная оболочка shell выполняет группу работ, где каждая работа отделяется от другой \nсимволами перевода строки (`{\\textbackslash}n` в языке \\textbf{\\textit{Си}}) и символом `;'.\nВнутри работы могут встречаться команды, их аргументы и перенаправления ввода/вывода, \nкоторые могут разделятся символами конвейера '|'. \n\nВ строке могут встречаться следующие конструкции:\n\\begin{itemize}\n\\item текст в кавычках (позволяют вставить пробел в аргумент программы). Кавычки бывают двух видов: \n      двойные и одинарные. Отличие текста в двойных ковычках от текста в одинарных заключается в \n      том, что в двойных кавычках производится подстановка переменных, а в одинарных нет.\n\n       Например:\\newline\n       \\verb#echo \"I am ${USER}\"# напечатает для пользователя c именем <<vasia>> \\newline\n         \\verb#I am vasia# \\newline\n       \\verb#echo 'I am ${USER}'# напечатает \\newline \\verb#I am ${USER}#\n\n    \\item  \\textit{экранирование символа}. Осуществляется при помощи символа `\\textbackslash'. \n       Символ после обратного слэш не будет иметь <<служебного>> смысла. Например обратный слэш \n       позволяет поставить кавычку внутри кавычек. Последовательность `\\textbackslash\\textbackslash'\n       позволяет ввести обычный одинарный слэш. Символ `\\textbackslash' может так же наоборот \n       означать наличие специального смысла символа в ситуации, когда без этой конструкции особого\n       смысла небыло. Если символ `\\textbackslash' стоит в конце строки (следующий символ в файле \n       перевод строки), то это означает, что перевод строки <<съедается>>, а строка на новой \n       строчке на самом деле является продолжением текущей.\n\n   \\item  \\textit{Коментарии}. Если во входной строке встречается символ \\textit{`\\#'}, который \n       находится не внутри кавычек одинарных или двойных и не экранированн обратным слэшом,\n       то все символы, стоящие после решётки до конца строки игнорируются.\n\n   \\item  \\textit{Подстановка значений переменных}. Перед тем как интерпретировать команду необходимо \n       подставить в командную строку значения переменных. Могут быть служебные переменные, а могут быть \n       пользовательские переменные. Данное задание предполагает только подстановку служебных переменных.\n       Значение переменной --- это некотороый текст, который сопоставлен имени пременной.  \n       В случае встречи в строке конструкции вида:\n       {\\small \n       \\begin{verbatim}\n          ${ИМЯ_ПЕРЕМЕННОЙ}\n       \\end{verbatim}\n       }\n       вместо этой конструкции, в строку, будет подставлена строка, являющаяся значением переменной.\n\\end{itemize}\n\n\\section{Команды и запуск команд}\n\nКоманды можно разделить на 2 группы. Первая -- встроенные команды shell. Часто они не требуют запуска\nотдельных процессов (их реализация является частью кода shell\\footnote{Однако они могут присутствовать \nв конвейере, тогда для них необходимо создавать отдельные процессы, перенаправлять ввод/вывод и т.п., \nно вместо вызова exec, нужно вызвать функцию в коде shell, реализующую встроенную команду.}),\nнапример команда \\textbf{cd} или \\textbf{pwd}. Внешние команды shell --- это обычные\nпрограммы, которые запускаются так, что каждая программа оказывается в своём отдельном процессе.\nНа всторенные команды не может быть вызван exec.\n\nКоманде можно указать список её аргументов. Например:\n{\\small \n\\begin{verbatim}\n          ls -l -a ../.. #конец аргументов\n\\end{verbatim}\n}\nпередаст программе ls после её запуска:\n\\newline в argv[0] ``ls'',\n\\newline в argv[1] ``-l'',\n\\newline в argv[2] ``-a'',\n\\newline в argv[3] ``../..''.\n\nПосле списка аргументов прогаммы допускеается указывать символы \\verb#<# и \\verb#>#, которые позволяют \nсчитать стандартный ввод из файла или вывести стандартный вывод в файл, а также последовательность\nсимволов \\verb#>>#, что позволяет дописать вывод программы в конец указанного файла. \nНапример:\\newline\n{\\small\n\\begin{verbatim}\n        # Перенаправим ввод команде cat \n        # из файла file.in и допишем \n        # файл file.out\n        cat < file.in >> file.out\n\\end{verbatim}\n}\n\nПосле перенаправлений ввода/вывода допускается указать символ \\verb#&#, который означает запуск \nкоманды в фоновом режиме. Подробнее про фоновый режим далее.\n\nПри помощи символа \\verb#|# организуется конвейер. Смысл символа таков, что команда слева от символа\nделает свой стандартный поток вывода стандартным потоком ввода для команды справа от символа. \nИ так продолжается дальше по цепочке, пока <<вертикальные палки>> не закончатся. В случае, если имело \nместо перенаправление ввода/вывода, то приоритет отдаётся именно перенаправлению, а не конвейеру. В\nрезультате роцесс подключённый к конвейеру справа получит себе конец файла, либо процесс слева \nбудет некому <<слушать>> из конвейера и он вероятно получит себе \\textit{SIGPIPE}.\n\nСимвол \\verb#&# допускается указывать только в конце определения исполняемой работы shell. То есть \nдо символа задающего конвейер \\verb#&# указать нельзя.\n\n\\section{Режим переднего плана и фоновый режим}\n\nВ режиме переднего плана (foreground) работы исполняются последовательно одна за другой. \nКаждая работа на время своего выполнения получает терминал в свой монопольный доступ. \nПосле исполнения одной или нескольких работ (в случае, если они разделены точкой с запятой)\nпользователю выдаётся стандартное приглашение ко вводу следующего набора команд. \nПри этом, нажатие \\textit{Ctrl+c} должно приводить к посылке сигнала текущей выполняющейся работе,\nно не самому процессу реализующему shell. По нажатию \\textit{Ctrl+z} работа должна \nприостанавливаться, после чего выдаётся приглашение shell, которое позволяет запустить новую порцию\nкоманд или продолжить выполнение приостановленной ранее работы путём указания её номера во \nвстроенной команде \\textbf{fg} или \\textbf{bg}. Список имеющихся работ можно посмотреть при \nпомощи команды \\textbf{jobs}.\n\nРабота запущенная в фоновом режиме (background) не должна обращаться к терминалу. В случае обращения \nк терминалу процесс из работы запущенной в фоновом режиме должен быть приостановлен (не завершён). \nЕсли работа запускается в фоновом режиме, то процессы не должны получать \\textit{SIGINT} \nв случае нажатия \\textit{Ctrl+c} на клавиатуре. В случае запуска работы в фоновом режиме shell \nне вместо ожидания завершения такойц работы, сразу либо выдаёт приглашение ко вводу следующего \nнабора команд, либо выполняет следующую работу (например они были разделены точкой с запятой). \nВ случае завершения фоновой работы shell информирует об этом пользователя, выдавая соответствующее\nсообщение в стандартный поток ошибок основным процессом shell\n(тот, который был изначально при запуске приложения).\n\n\\section{Запуск команд из истории}\n\nШеллы позволяют использовать короткое мнемоническое имя для запуска команды из истории запусков.\nДля этого используется конструкция вида \\verb#!100500#, здесь 100500 - это номер команды в истории команд. \nКонструкция должна сработать как подстановка переменной, тоесть в то место, где встретилась конструкция подствляется \nвводившаяся когда-то команда. Дальше могут идти конструкции с точкой-запятой, перенаправление ввода-вывода\nи т.п. Пример:\n\\begin{verbatim}\nvasia$ history | grep gcc \n 1022  gcc -E  test_abc.c\n 1023  gcc -E  test_abc.c > /tmp/filo.c\n 1025  gcc -S  test_abc.c\n 1027  gcc -c  test_abc.c\n 1029  gcc -o test_abc test_abc.c\n 1032  gcc -o test_abc test_abc.c -lm\n 1035  gcc -o test_abc test_abc.c -lm\n 1206  gcc -g redactor.c\n 1229  gcc -g ilya.c \n 1476  gcc prog32.c \n 1852  gcc terminal_manipulation.c \n 1855  gcc term_mode_change.c \n 1859  gcc term_mode_change.c \n 1862  gcc term_mode_change.c \n 1964  gcc -Wall -ansi -pedantic -g main9.c \n 1966  gcc -Wall -ansi -pedantic -g main9.c \n 1968  gcc -Wall -ansi -pedantic -g main9.c \n 1994  if gcc foo ; then echo \"OK\"; fi\n 1995  if gcc foo ; then echo \"OK\"; else echo \"fooo\";  fi\n 2005  history | grep gcc\n\nvasia$ !1862 ; !1966 \ngcc term_mode_change.c  ; gcc -Wall -ansi -pedantic -g main9.c  \n\\end{verbatim}\n\n\\section{Дополнительные требования}\n\nТребуется реализовать встроенные команды:\n\\begin{itemize}\n\\item  \\textbf{cd} -- смена текущего каталога\n\\item  \\textbf{pwd} -- выдача текущего каталога в стандартный поток вывода\n\\item  \\textbf{jobs}, \\textbf{fg}, \\textbf{bg} -- выдача списка активных в текущий момент работ\n\\item  \\textbf{exit} -- выход из shell\n\\item  \\textbf{history} -- команда показывающая историю команд\n\\item export -- передача переменных окружения в запускаемые процессы из текщего shell\n\\end{itemize}\n\nТребуется реализовать как встроенные команды следующие внешние программы:\n\\begin{itemize}\n\\item\n    \\textbf{cat} --  с именем \\textbf{mcat}, где в качестве параметра либо ничего не указывается,\n    либо указывается имя файла (полный набор параметров реализовывать не нужно).\n\\item \n    \\textbf{sed} -- c именем \\textbf{msed}. Реализовать минимальный вариант \n    подстановки по образцу, где в качестве шаблона используется просто текст \n    без спецсимволов, которые позволяют задавать синтаксис регулярных выражений \n    \\cite{regex}. Первый аргумент задаёт строку образец, второй ааргумент -- \n    строка на которую будет заменён образец. Необходимо осуществлять замену \n    всех вхождений образца в строке. Символ `\\textasciicircum' означает, что второй аргумент \n    программы необходимо приписать вначало всех строк. Cимвол `\\$' означает, что второй аргумент \n    программы необходимо приписать в конец всех строк. Внутри строк, на которые заменяем \n    может встречаться перевод строки, который задаётся '\\\\n'.\n\\item\n    \\textbf{grep} -- с именем \\textbf{mgrep} Реализовать минимальный вариант. Формат шаблона такой \n        же как для предыдущей команды \\textbf{msed}. Дополнительно возможны конструкции \\textit{.*}\n        -- означает произвольное количесттво любых символов, в том числе пустое; и \\textit{.+} --\n        означает произвольное количество любых символов, но не пустое.\n\\end{itemize}\n\nТребуется реализовать подстановку любых переменных, которые перед запуском выставлены в переменных \nокружения, в том числе служебных:\n\\begin{itemize}\n\\item \\verb#$цифра# -- подстановка соответствующего аргумента командной строки самого shell.\n\\item \\verb@$#@ -- число параметров переданное shell\n\\item \\verb@$?@ -- значение статуса последнего завершившегося процесса в последней выполнившейся\n                   работе переднего плана.\n\\item \\verb#${USER}# -- login пользователя.\n\\item \\verb#${HOME}# -- домашний каталог пользователя.\n\\item \\verb#${SHELL}# -- имя shell. Путь до того места в файловой системе, где находится \n                         исполняемый файл с ним. (В качестве плохо работающего <<костыля>> \n                         допускается реализация в виде подстановки argv[0]).\n\\item \\verb#${UID}# -- идентификатор пользователя\n\\item \\verb#${PWD}# -- текущий каталог\n\\item \\verb#${PID}# -- pid shell\n\\item \\verb#$HOSTNAME# -- имя машины на которой запускается shell   \n\\end{itemize}\n%О работе с переменными окружения можно прочитать здесь:~\\cite{getenv}.\n\n\\newpage\n\\section{Рекомендации по написанию кода задания}\n\nНа начальном этапе при выполнении задания необходимо научиться считывать командные строки \nшелл и сохранять их в оперативной памяти (не выполняя команды). \nРекомендуется данные в памяти хранить как массив структур следующего вида:\n\n\\begin{lstlisting}\n\nstruct program\n{\n    char* name;\n    int number_of_arguments;\n    char** arguments;\n    char *input_file, *output_file; /* NULL - not redirected */\n    int output_type; /* 1 - rewrite, 2 - append */\n};\n\nstruct job\n{\n    int state; /* stopped, background, foreground */\n    struct program* programs;\n    int number_of_programs;\n    pid_t process_group;\n    pid_t *pids;\n};\n\n\\end{lstlisting}\n\nПосле того, как убедились, что разбор команд происходит правильно можно приступать к \nреазлизации запуска действий связанных с командами.\nДля реализации команды \\textbf{history}\nцелесообразно использовать очередь с ограничением на её длину. \n\nДля корректного отслеживания завершающихся процессов полезно определить действие на приход \nсигнала \\textit{SIGCHLD}.\n\nНе стоит надеятся, что служебные переменные, такие как \\verb#${HOME}#  будут для вас выставлены.\nЗадача Shell как раз заключается в том, что Shell эти переменные определяет сам и выставляет для \nзапускаемых из него программ.\n\n\n\\begin{thebibliography}{50}\n\\bibitem{cmd}\n    Официальная документация на команду cmd на сайте Microsft:\n    \\url{htps://www.microsoft.com/resources/documentation/windows/xp/all/proddocs/en-us/ntcmds.mspx?mfr=true}.\n\n\\bibitem{Power_Shell}\n    Сайт проекта Microsoft Power Shell:  \\url{https://msdn.microsoft.com/en-us/powershell}.\n\n\\bibitem{tcsh}\n    Сайт tcsh: \\url{http://www.tcsh.org/Welcome}.\n\n\\bibitem{zsh}\n    Сайт одного из наиболее полного всем чем только можно шелла:\n    \\url{http://zsh.sourceforge.net/Doc/}.\n\n\\bibitem{man}\n    Справочная страница с описанием реализации shell /bin/bash: ``man bash''.\n\n\\bibitem{guide-rus}\n    Перевод руководства по bash скриптам: \\url{http://www.opennet.ru/docs/RUS/bash\\_scripting\\_guide}.\n\n\\bibitem{guide-eng}\n    Более полное руководство по bash \\url{http://www.gnu.org/software/bash/manual/bashref.html}\n    \n\\bibitem{regex}\n    Справочная страница по регулярным выражениям: ``man 7 regex''\n\n\\bibitem{getenv}\n    Справочная страница по функции getenv ``man getenv''\n\n\\end{thebibliography}\n\n\\end{document}\n\n"
  },
  {
    "path": "projects/task_doom.tex",
    "content": "\\documentclass[russian,a4paper]{article}\n\\usepackage[russian]{babel}\n\\usepackage[utf8]{inputenc}\n\\usepackage{amsmath}\n\\usepackage{color}\n\\usepackage{verbatim}\n\\usepackage{hyperref}\n\n\\hypersetup{pdftex, \n            colorlinks=true,\n            linkcolor=blue,\n            citecolor=blue,\n            filecolor=blue,\n            urlcolor=blue,\n            pdftitle=,\n            pdfauthor=Alexey Salnikov,\n            pdfsubject=,\n            pdfkeywords=}\n\n\\pagestyle{empty}\n\n\\textwidth=17cm\n\\hoffset=0cm\n\\voffset=0cm\n\\headheight=0cm\n\\topmargin=0cm\n\\oddsidemargin=0cm\n\n\n\\title{Задание практикума: сетевая игра, консольный Doom}\n\\author{Алексей Сальников}\n\\date{}\n\n\\begin{document}\n\n\\maketitle\n\n%\n% Вариант 1\n%\n\\section{Общее описание}\n\nТребуется реализовать игровой сервер, игровой клиент, отображатель статистики.\nСервер и клиенты должны взаимодействовать через сеть путём установки TCP\nсоединения.\n\nПри своём запуске сервер читает файл с картой (имя файла указывается в\nпараметрах при запуске). Сервер должен быть реализован как демон в\nUnix, тем самым вся диагностическая информация должна выдаваться в\nспециальный файл журнала (Можно в syslog). Для имени файла карты и \nфайла журнала в программе должны быть предусмотрены имена по умолчанию, \nкоторые будут подставлены если имена файлов не были указаны в командной \nстроке при запуске. \nТакже специальными параметрами указываются номер TCP порта \nи имя файла, где будет записан pid сервера (это необходимо чтобы можно \nбыло посылать сигналы именно этому экземпляру сервера), \nпо умолчанию: /var/run/как\\_там\\_мой\\_сервер\\_называется). Также \nнеобходимо иметь возможность запустить серевер не как демон, \nа как обычную программу.\n\nКлиент соединятся с сервером (hostname сервера и номер TCP порта\nсервера указываются клиенту в аргументах main при запуске\n(если номер порта небыл указан, использовать некоторый номер порта по\nумолчанию)). Клиент должен отображать игровое пространство, позволять\nигроку делать ход, выходить из игры по желанию игрока. (В том числе\nкорректно завершаться по нажатию на Ctrl+c и Ctrl+d).\n\n%Отображатель статистики, это отдельная программа, которая может \n%обращаться к серверу с применением IPC и показывать сведения о ведущих-ся \n%на сервере играх.\n\nИгроки на сервере могут выступать в 2-х ролях: в роли создателя команды игроков,\nв роли участника команды игроков. В начальный момент, перед тем, как начать игру\nодним из игроков создаётся команда, и он ожидает, пока нужное количество других \nучастников присоединится к этой команде, чтобы начать игру. Именно создатель команды \nопределяет число участников игры и момент начала игры. При подключении клиент может \nпросмотреть список уже имеющихся команд. При подсоединении или создании команды \nигрок создаёт себе <<имя>> login. Размер имени не может привышать 60 символов.\n\n\\section{Описание игры}\n\nИгра начинается после того, как создатель команды игроков объявил старт игры. \nЕсли игра началась, то присоединиться к ней ещё одному игроку нельзя до тех пор,\nпока она не будет закончена. Создатель команды игроков может по своему желанию \nв любой момент завершить игру. Создатель не может принимать участия в игре, \nно зато по запросу может узнать координаты игроков и их уровень здоровья.\n\nИгра происходит в прямоугольном лабиринте, представленным как набор\nточек некоторой матрицы размера MxN. Лабиринт вместе с его размером\nдолжны быть заданы в файле карты, при этом сам файл карты должен иметь\nтекстовое представление легко читаемое человеком. Цель игры оказаться\nвыжившим в лабиринте с максимальным уровнем здоровья.\n\nЛабиринт состоит из стенок, аптечек/потравлялок, и проходов. По точке,\nсодержащей аптечку/потравлялку можно двигаться, по стенкам и за\nграницами лабиринта двигаться нельзя. Аптечка обладает\nлечебным/отравляющим эффектом определённой силы. Игрок может съесть\nаптечку/потравлялку, при этом к его здоровью прибавляется/отнимается\nчисленное значение эффекта. По внешнему виду аптечки/потравлялки нельзя\nничего сказать о силе её воздействия и о знаке её воздействия. После\nупотребления аптечки соответствующая клетка считается просто проходом\n(повторно съесть аптечку нельзя). В лабиринте могут встречаться другие\nигроки, которые всегда занимают одну точку пространства (в стенке игрок\nне может находиться).\n\nИгроку сопоставляется некоторый уровень здоровья. Который с каждым\nсделанным им ходом уменьшается на определённое значение (указывается в\nпараметрах серверу). C течением времени, если игрок не перемещается,\nзначение здоровья уменьшается, но не так активно, как в случае\nперемещения. Игрок помещается в одну\nиз точек с координатами (i,j), два игрока не могут одновременно\nнаходится в одной и той же точке. В этой точке он может видеть\nсостояние лабиринта на 10 точек в каждом направлении. То есть будет\nвиден прямоугольник с координатами (i-10,j-10,i+10,j+10). Прямоугольник\nи всё что в нём есть нужно уметь показывать в консоли в текстовом\nрежиме.\n\nЗа один ход можно произвести одну из следующих операций:\n\\begin{itemize}\n\\item съесть аптечку, находящуюся в текущей точке,\n\\item применить боевой заряд,\n\\item переместиться на 1 соседнюю клетку,\n\\item закладывать мину.\n\\end{itemize}\n\nБоевой заряд применяется следующим образом. Вычисляется кратчайшее \nрасстояние, до другого игрока по пути через клетки (если игрок оказался за\nстенкой, то стенки надо обходить). Радиус действия заряда не\nпревышает 10. Интенсивность удара убывает с расстоянием от игрока,\nкоторый заряд применяет. Игрока, применившего заряд, удар от этого\nзаряда не травмирует. \n\nИгроку предоставляется 10 мин, каждой из которых он может заминировать\nклетку в лабиринте. Мины не отображаются другим игрокам. В случае попадания \nигрока на мину к нему применяется ущерб эквивалентный применению боевого \nзаряда на соседней клетке.\n\nВ случае применения заряда есть время необходимое на перезарядку,\nв случае минирования игрок на определённое время обездвиживается.\n\nВ начальный момент игроки расставляются серверм на карту случайным образом. Они\nдолжны обязательно оказаться в допустимой точке (то есть не на стенке и \nне на одной клетке с другим игроком). С начала игры объявляется \nмораторий на применение оружия определённой длительности. Длительность моратория \nзадаётся в файле с картой и измеряется в секундах.\n\nМощность заряда и скорость убывания здоровья при движении\nможет быть задана в файле с картой.\nЗначения действий аптечек и потравлялок так же указываются в файле с картой.\n\n\\newpage\n\\section{Формат файла скартой}\n\nДалее приведён пример файла с картой:\n\\begin{verbatim}\nMap 10x20\n######################\n# #  #   #           #\n# #   #  #  #####    #\n# #####  #      #    #\n#        ######### ###\n#  # ##      #       #\n#  ########  # ##### #\n## ##        # #     #\n## ### ############# #\n#  #        #     #  #\n#       #      #     #\n######################\n\ninitial_health       = 500\nhit_value            = 50\nrecharge_duration    = 3\nmining_time          = 6\nstay_health_drop     = 1\nmovement_health_drop = 4\nstep_standard_delay  = 0.1\nmoratory_duration    = 5\n\nitems:\n1  1     10\n20 20   -10\n2  3     8\n\\end{verbatim}\n\nВ файле карты, собственно карта обрамлена\nконтуром из решёток, что необходимо для\nнаглядности.\n\nКоординаты в карте нумеруются начиная с \nединицы.\n\n%\\section{Статистика и файл журнала}\n%\n%при обращении из программы по сбору статистики должен выдаваться список \n%команд игроков, где для каждой команды выдаётся:\n%\\begin{itemize}\n%\\item статус игры: идёт, окончилась, не начата;\n%\\item дата начала игры, если начата;\n%\\item дата конца игры, если закончилась;  \n%\\item победитель, если определён;\n%\\item список игроков, их текущий уровень здоровья и координаты.\n%\\end{itemize}\n%\n%В файл журнала нужно сохранять сведения о начавшихся и закончившихся играх, \n%а так же выводить имена хостов подключившихся клиентов. Если клиен отключился, \n%с использованием соответствующей команды, отправляемой серверу, то факт отключения клиента, \n%так же нужно зафиксировать в журнале.\n\n%Сервер, в случае достаточности ресурсов должен обеспечивать возможность подключения \n%до 1000 клиентов одновременно. \n\n\\end{document}\n\n"
  }
]