diff --git a/README.md b/README.md
new file mode 100644
index 0000000..a52a00a
--- /dev/null
+++ b/README.md
@@ -0,0 +1,5 @@
+# Системное программное обеспечение
+
+## Лабораторные работы
+
+2. [Системное программирование в Linux](labs/lab02)
diff --git a/labs/lab02/README.md b/labs/lab02/README.md
new file mode 100644
index 0000000..a49b590
--- /dev/null
+++ b/labs/lab02/README.md
@@ -0,0 +1,843 @@
+---
+title: Системное программирование в Linux
+lang: ru
+---
+
+Вы должны:
+
+* Уметь выполнять команды в командной строке Linux.
+* Владеть языком C.
+
+Вы научитесь:
+
+* Находить и читать документацию по системным функциям.
+* Использовать системные функции в программах на C.
+* Собирать программы из командной строки Linux.
+* Пользоваться отладчиком GDB.
+* Пользоваться программой `strace` для диагностики системных вызовов.
+
+# Системное программирование
+
+Системное программирование связано с взаимодействием с ядром ОС.
+Это может быть использование системных вызовов в пространстве пользователя
+или написание кода, который исполняется в ядре (само ядро и драйверы).
+Первое значительно проще и чаще встречается.
+Технически работа с системными вызовами мало отличается
+от работы с любыми другими библиотеками и их API.
+Однако нужно понимать концепции и возможности данной ОС.
+Например, для Linux это иерархия файловой системы (ФС)
+и специальные ФС (`procfs`, `sysfs`) для доступа к объектам ядра,
+разграничение прав доступа к файлам,
+конкретные возможности, которые ОС предоставляет для ряда задач.
+Большая часть системного программирования — это не написание кода,
+а изучение документации на системные вызовы ОС и на её возможности.
+
+# Чтение документации
+
+Из `man man` можно узнать, что документация состоит из разделов, в частности:
+
+* 1 — исполняемые программы и команды оболочки;
+* 2 — системные вызовы (специфичные для Linux);
+* 3 — функции библиотек (не специфичные для Linux);
+* 7 — общая информация, не связанная с конкретной функцией.
+
+Страница с одним и тем же названием может быть в разных разделах.
+Например, `man stat` открывает страницу раздела 1 и описывает программу `stat`.
+Номер раздела виден в левом верхнем углу страницы: `STAT(1)`.
+Функция же `stat()` описана в разделе 3, открывается как `man 3 stat`.
+В разделе 2 тоже есть страница про `stat`, причем она больше, чем в разделе 3.
+Разница в том, что *stat(3)* описывает стандартные функции для всех POSIX-систем,
+а *stat(2)* описывает все, что поддерживает Linux (больше, чем в стандарте).
+
+Документацию не обязательно читать в терминале:
+,
+.
+Однако `man` соответствует конкретному дистрибутиву Linux и версиям его пакетов,
+а часть сведений с сайтов может быть неприменима к данной системе.
+
+Документацию лучше читать в оригинале (обычно на английском),
+чтобы не ждать перевода и не страдать из-за ошибок переводчика.
+Автоматические переводчики тоже иногда ошибаются и искажают смысл.
+
+Узнать о том, что какая-то функция вообще есть, можно из учебников и новостей.
+Популярные источники новостей:
+ (на русском),
+ (на английском).
+
+# Выполнение лабораторной работы
+
+Чтобы изучить на примере, как программировать с использованием системных API,
+напишем программу `example1`, которая печатает размеры файлов,
+имена которых передаются ей как аргументы при запуске:
+
+```sh
+$ ./example1 example1.c example1
+Size of 'example1.c' is 259 bytes
+Size of 'example1' is 87340 bytes
+```
+
+## Написание кода
+
+В ходе ЛР предлагается писать код в `vim`
+или в Midnight Commander (`mc`) по клавише `F4`.
+Преимущество в том, что ими можно пользоваться, просто подключившись к серверу.
+Это простые редакторы, но для маленьких лабораторных программ их достаточно.
+
+Вообще говоря, работа с Linux не ограничивается терминалом.
+Под Linux есть графические окружения, в том числе редакторы кода
+и интегрированные среды разработки (IDE).
+Популярные: Visual Studio Code (не путать с Visual Studio), Eclipse,
+QtCreator, CLion (платная), CodeBlocks и много других.
+На виртуальные серверы их ставить не нужно,
+так как они потребляют много ресурсов.
+Однако, если лабораторная работа выполняется на локальной машине,
+можно установить, настроить и использовать то, что удобнее.
+
+Предлагается писать код на C, а не на C++.
+Это связано с тем, что системные API описаны в терминах C,
+примеры в документации на языке C, а средства C++ в задачах этого курса
+менее востребованы, чем в прикладных программах.
+Фактически это значит, что `vector` и `string` недоступны,
+а вместо `cin` и `cout` из ``
+нужно пользоваться `scanf()` и `printf()` из ``.
+На практике C++ для системного программирования активно используют,
+но только в пространстве пользователя.
+
+## Работа с аргументами программы
+
+Создайте файл `example1.c`, введите в него следующий код и сохраните:
+
+```c
+#include
+
+int
+main(int argc, char** argv) {
+ int i;
+
+ for (i = 1; i < argc; i++) {
+ printf("Size of '%s' is %d bytes\n", argv[i], 0);
+ }
+ return 0;
+}
+```
+
+Заготовка делает почти то же самое, что должна делать программа в итоге,
+но вместо размеров печатаются нули. Если вы забыли, как работать с аргументами
+программы и функцией `printf()`,
+обратитесь к [документации](https://en.cppreference.com/w/c/language/main_function)
+и примеру в ней.
+
+**Задание.**
+Ответьте на вопросы для самопроверки:
+
+* Чему будет равно `argc` при запуске программы как `./example1 foo bar`?
+* Какие значения находятся в массиве `argv` при таком запуске?
+* Почему в коде выше цикла начинается с 1, а не с 0?
+* Как называется первый параметр функции `printf()`?
+* Что означают `%s` и `%d` в нем?
+* Почему выбраны именно `%s` и `%d`?
+
+## Компиляция
+
+Соберите исполняемый файл `example1` из исходного кода в `example1.c`:
+
+```sh
+gcc example1.c -o example1
+```
+
+Запустите программу:
+
+```sh
+./example1 example1.c example1
+```
+
+## Изучение документации
+
+Получить сведения о файле, в частности, его размер, можно функцией `stat()`.
+
+```
+NAME
+ stat, fstat, lstat, fstatat - get file status
+```
+
+Одна `man`-страница часто содержит описание семейства функций,
+в данном случае четырех функций для получения статуса файла (сведений о нем).
+
+```
+SYNOPSIS
+ #include
+
+ int stat(const char *restrict pathname,
+ struct stat *restrict statbuf);
+ ...
+```
+
+Раздел-сводка описывает, как объявлена функция и какие заголовочные файлы
+нужно подключить, чтобы использовать её.
+
+В объявлениях часто встречается ключевое слово `restrict` с указателями.
+Оно означает, что если есть два параметра-указателя, то они содержат адреса
+разных, не пересекающихся блоков памяти. В случае `stat()` это значит,
+что нельзя записать в массив символов имя файла
+и передать указатель на этот массив и как `pathname`, и как `statbuf`,
+ожидая, что функция сначала прочитает из массива имя файла,
+а потом перезапишет содержимое массива сведениями о файле.
+На практике так в любом случае стараются не делать, чтобы не запутывать код.
+
+Первый параметр, `pathname`, имеет тип `const char*`, то есть это строка.
+Второй параметр, `statbuf`, имеет тип `struct stat*`, указатель на структуру.
+В отличие от C++, в C, если объявлена структура `struct Example { ... }`,
+то экземпляр `ex` этой структуры надо объявлять как `struct Example ex`,
+а не `Example ex`.
+
+```
+DESCRIPTION
+ These functions return information about a file,
+ in the buffer pointed to by statbuf...
+
+ The stat structure
+ All of these system calls return a stat structure,
+ which contains the following fields:
+
+ struct stat {
+ dev_t st_dev; /* ID of device containing file */
+ ...
+```
+
+Раздел с описанием раскрывает, что именно делает функция,
+что передается во входных параметрах и что возвращается в выходных.
+Функция `stat()` записывает информацию о файле в поля структуры,
+адрес которой передан в параметре-указателе `statbuf`.
+Далее описывается, что будет записано в разные поля этой структуры.
+
+Нас интересует поле с размером файла:
+
+```
+ st_size
+ This field gives the size of the file
+ (if it is a regular file or a symbolic link) in bytes.
+ The size of a symbolic link
+ is the length of the pathname it contains,
+ without a terminating null byte.
+```
+
+У многих функций после описания идет раздел `EXAMPLES` (примеры),
+где приводятся небольшие программы, демонстрирующие, как пользоваться функцией.
+
+Большая часть `man`-страниц заканчивается разделом,
+где перечислены связанные страницы.
+Например, к теме получения сведений о файле относятся команды `ls` и `stat`,
+функции `access()`, `readlink()` и другие:
+
+
+```
+SEE ALSO
+ ls(1), stat(1), access(2), chmod(2), chown(2), readlink(2),
+ statx(2), utime(2), capabilities(7), inode(7), symlink(7)
+```
+
+Итак, необходимо:
+
+* Объявить переменную типа `struct stat`.
+* Передать функции `stat()` имя файла и адрес объявленной переменной.
+* Напечатать размер из поля `st_size` объявленной переменной.
+
+**Задание.**
+Модифицируйте код, соберите программу и запустите её так же, как в прошлый раз.
+
+```c
+#include
+#include
+
+int
+main(int argc, char** argv) {
+ struct stat st;
+ int i;
+
+ for (i = 1; i < argc; i++) {
+ stat(argv[i], &st);
+ printf("Size of '%s' is %d bytes\n", argv[i], st.st_size);
+ }
+ return 0;
+}
+```
+
+
+
+**Задание.**
+Попробуйте вызвать программу без аргументов,
+с несколькими аргументами-именами существующих файлов
+и с аргументом-именем файла, которого не существует.
+Объясните результаты.
+
+## Обработка ошибок при вызове функций
+
+Как понять, что функция не смогла выполнить свою работу,
+например, если файла не существует?
+
+Продолжим чтение документации:
+
+```
+RETURN VALUE
+ On success, zero is returned.
+ On error, -1 is returned, and errno is set to indicate the error.
+
+ERRORS
+ EACCES Search permission is denied for one of the directories
+ in the path prefix of pathname.
+ (See also path_resolution(7).)
+ ...
+ ENOENT A component of pathname does not exist
+ or is a dangling symbolic link.
+ ...
+```
+
+Функция `stat()` возвращает целое число: 0 при успехе, (-1) при ошибках.
+Числовой код ошибки, по которому можно понять причину,
+записывается в глобальную переменную `errno`.
+В частности, если файла не существует, будет `errno == ENOENT`
+и аналогично с другими кодами для других причин ошибок.
+
+Значит, при вызове функции нужно проверить возвращаемое значение,
+Если оно отрицательное, то обработать ошибку,
+например, напечатать сообщение о ней с кодом:
+
+```c
+int ret = stat(...);
+if (ret < 0) {
+ printf("stat(): errno=%d\n", errno);
+ continue;
+}
+```
+
+**Задание.**
+Добавьте в программу обработку ошибок. Соберите программу.
+Проверьте, как программа реагирует на несуществующий файл.
+
+Коды ошибок трудно и не нужно помнить наизусть.
+Есть функции *strerror(3)*, *perror(3)* и другие, чтобы по коду ошибки
+получить или напечатать её текстовое описание для пользователя.
+
+Программа *errno(1)* позволяет получить описание ошибки по коду
+(эта программа по умолчанию не установлена):
+
+```sh
+% errno 12
+ENOMEM 12 Cannot allocate memory
+```
+
+## Исправление ошибок и предупреждений компилятора
+
+Заменим `for (i = 1; i < argc…` на `for (i = 1; i < argd…` (`argc` → `argd`),
+как будто допущена опечатка, и повторим сборку:
+
+```sh
+$ gcc example1.c -o example1
+```
+
+Возникнет ошибка, GCC даже подсказывает, в чем она может заключаться:
+
+```
+example1.c: In function ‘main’:
+example1.c:9:21: error: ‘argd’ undeclared (first use in this function); did you mean ‘argv’?
+ 9 | for (i = 1; i < argd; i++) {
+ | ^~~~
+ | argv
+example1.c:9:21: note: each undeclared identifier is reported only once for each function it appears in
+```
+
+В начале сообщения указана строка с ошибкой (9).
+VIM позволяет перейти к строке с заданным номером так: `:9`, Enter.
+
+**Задание.**
+Исправьте ошибку и убедитесь, что программа снова собирается успешно.
+
+По умолчанию компилятор обнаруживает только ошибки.
+Современные компиляторы могут также обнаруживать потенциальные проблемы,
+которые не являются ошибками с точки зрения языка C,
+но могут привести к неправильной работе программы.
+Показ дополнительных предупреждений включается флагами компилятора:
+
+```sh
+$ gcc example1.c -Wall -Wextra -o example1
+```
+
+```
+example1.c: In function ‘main’:
+example1.c:11:34: warning: format ‘%d’ expects argument of type ‘int’, but argument 3 has type ‘__off_t’ {aka ‘long int’} [-Wformat=]
+ 11 | printf("Size of '%s' is %d bytes\n", argv[i], st.st_size);
+ | ~^ ~~~~~~~~~~
+ | | |
+ | unsigned int __off_t {aka long int}
+ | %ld
+```
+
+Действительно, `st.st_size` имеет тип `long int` (обычно 64 бита),
+поэтому для него нужен спецификатор `%ld`.
+Спецификатор `%d` — для `int` (обычно 32 бита, значения до ~2 млрд.).
+
+**Задание.**
+Исправьте ошибку и убедитесь, что предупреждения больше не возникает.
+
+**Задание.**
+В каком случае проблема проявилась бы?
+
+**Внимание.**
+В этой и последующих работах все программы нужно собирать с такими же
+или более строгими флагами, исправляя все возникающие предупреждения.
+
+## Отладка программ
+
+*Отладкой (debugging)* называется поиск и устранение ошибок в программе.
+Так как системные программы обычно пишутся на C,
+и при ошибке не показывается место в коде, где она возникла,
+полезна *пошаговая отладка (step-by-step debugging)* —
+выполнение программы «строка за строкой» с возможностью остановиться
+и просмотреть значения переменных.
+*Отладчик (debugger)* — это специальная программа,
+которая запускает отлаживаемую и позволяет управлять ходом её выполнения.
+
+Отладчиком часто пользуются из удобного графического интерфейса IDE.
+Технически на том же сервере, что и отлаживаемая программа,
+достаточно установить `gdbserver`, чтобы подключаться к нему и отлаживать в IDE.
+Зачем тогда знать командную строку GDB?
+
+* Установить на удаленный сервер GDB может быть намного проще,
+ чем согласовать подключение к нему при работе в корпоративных сетях.
+ Текстовые команды можно продиктовать или переслать тому,
+ кто может подключиться к серверу.
+
+* Текст сеанса работы с GDB (все команды и их вывод) можно сохранить
+ и поделиться им с другими специалистами, в отличие от интерактивного GUI.
+
+* Язык команд GDB позволяет широкую автоматизацию.
+ Как минимум, команды можно записать и затем быстро повторить действия.
+
+### Работа с GNU Debugger
+
+Создайте файл `example2.c` со следующей программой:
+
+```c
+#include
+
+void
+print(const char* message) {
+ puts(message);
+}
+
+int
+main(int argc, char** argv) {
+ print(argv[0]);
+ print(argv[1]);
+ print(argv[2]);
+}
+```
+
+Отладчику нужны сведения о том, как машинный код в исполняемом файле
+соотносится с исходным текстом, где в памяти размещены переменные и т. п.
+По умолчанию эта отладочная информация не включается в исполняемый файл.
+Чтобы компилятор добавил ее, нужен ключ `-g` при компиляции:
+
+```sh
+$ gcc example2.c -g -o example2
+```
+
+Запустите программу:
+
+```sh
+$ ./example2
+```
+
+```
+./example2
+Segmentation fault (core dumped)
+```
+
+Программа аварийно завершается («падает»).
+Первую строку, `./example2`, программа напечатала сама — это `argv[0]`.
+Вторую строку напечатала оболочка, её смысл мы разберем далее.
+
+Запустим программу под отладчиком, чтобы найти причину аварийного завершения:
+
+```sh
+$ gdb -q --args ./example2
+```
+
+```
+Reading symbols from example2...
+(gdb)
+```
+
+Вместо приглашения командной оболочки
+появилось приглашение командной строки отладчика, обозначаемое `(gdb)`.
+
+Отладчик не запускает программу сразу, это нужно сделать командой `run`
+(GDB также воспринимает сокращения команд, например, `r` вместо `run`):
+
+```gdb
+(gdb) run
+```
+
+```
+Starting program: /home/user/example2
+Missing separate debuginfo for /lib64/ld-linux-x86-64.so.2
+Try to install the hash file /usr/lib/debug/.build-id/89/c19f7bc64e27bdd0c57a4b08e124f8c95799ee.debug
+Missing separate debuginfo for /lib64/libc.so.6
+Try to install the hash file /usr/lib/debug/.build-id/41/259207a6a4da6595a09db78fa98a4118ae0d0e.debug
+/home/user/example2
+
+Program received signal SIGSEGV, Segmentation fault.
+0x00007f4ef9e7ec59 in ?? () from /lib64/libc.so.6
+```
+
+Строки в начале (`Missing separate debuginfo...`) говорят о том,
+что отладочной информации для системных библиотек недоступно.
+По этой причине в последней строке написано, что аварийный останов случился,
+когда выполнялся машинный код в библиотеке `/lib64/libc.so.6`,
+но соотнести его с местом в исходном коде библиотеки невозможно (`in ?? ()`).
+
+Запросим у отладчика *стек вызовов (stack trace),*
+то есть из какой функции был вызван код, приведший к аварийному останову,
+из какой функции была вызвана эта функция и так далее вплоть до `main()`.
+Это делается командой `backtrace` (`bt`):
+
+```gdb
+(gdb) bt
+```
+
+```
+#0 0x00007f58f4c25c59 in ?? () from /lib64/libc.so.6
+#1 0x00007f58f4b38e80 in puts () from /lib64/libc.so.6
+#2 0x00005578019f114d in print (message=0x0) at example2.c:5
+#3 0x00005578019f1181 in main (argc=1, argv=0x7ffc86ac2d88) at example2.c:11
+```
+
+У стека вызовов в данном случае четыре уровня, или *фрейма (frame).*
+Нижние два (0 и 1) находятся в библиотеке `/lib64/libc.so.6`.
+Для фрейма 1 известно, что это функция `puts()`,
+то есть аварийный останов вызван каким-то неизвестным кодом (фрейм 0),
+который был вызван функцией `puts()`.
+
+Больше информации есть о фреймах 2 и 3, относящихся к отлаживаемой программе.
+Фрейм 2 находится в функции `print()`,
+конкретно это вызов `puts()` на строке 5 файла `example2.c`.
+Также показано, что функция вызвана с параметром `message`, который равен 0.
+Аналогично для фрейма 3 показано, с какими аргументами вызвана `main()`,
+и откуда конкретно в ней сделан вызов `print()`.
+
+Можно переместиться к нужному фрейму, указав его номер:
+
+```gdb
+(gdb) frame 2
+```
+
+```
+#2 0x00005578019f114d in print (message=0x0) at example2.c:5
+5 puts(message);
+```
+
+Можно также перемещаться вверх (up) и вниз (down) по стеку вызовов:
+
+```gdb
+(gdb) up
+```
+
+```
+#3 0x00005578019f1181 in main (argc=1, argv=0x7ffc86ac2d88) at example2.c:11
+11 print(argv[1]);
+```
+
+Пока программа остановлена, можно исследовать значения переменных в ней:
+
+```gdb
+(gdb) print argv[0]
+```
+
+```
+$1 = 0x7ffc86ac4edc "/home/user/example2"
+```
+
+```gdb
+(gdb) print argv[1]
+```
+
+```
+$2 = 0x0
+```
+
+Итак, проблема вызвана тем, что `puts()` передан нулевой указатель как аргумент,
+а взялся он из `argv[1]`, который равен `NULL`, потому что программа
+запущена без аргументов (`argc=1`, заполнен только `argv[0]`).
+
+Выполните следующую команду, её результат понадобится в дальнейшем:
+
+```gdb
+(gdb) gcore coredump
+```
+
+```
+warning: Memory read failed for corefile section, 4096 bytes at 0xffffffffff600000.
+Saved corefile coredump
+```
+
+Отладку можно завершить:
+
+```gdb
+(gdb) quit
+```
+
+```
+A debugging session is active.
+
+ Inferior 1 [process 648] will be killed.
+```
+
+`Quit anyway? (y or n)` **`y`**
+
+### Отладка post-mortem
+
+Имея исполняемый файл с отладочной информацией,
+можно многое узнать об используемых программой алгоритмах.
+Это нежелательно, если исходный код программы закрыт.
+
+Отладочная информация также существенно увеличивает размер исполняемого файла:
+
+```sh
+$ ./example1 example2
+```
+
+```
+Size of 'example2' is 26536 bytes
+```
+
+По этим причинам разработчики обычно удаляют отладочную информацию,
+как минимум, чтобы сэкономить место на диске:
+
+```sh
+$ gcc example2.c -s -o example2-stripped
+$ ./example1 example2-stripped
+```
+
+```
+Size of 'example2-stripped' is 17184 bytes
+```
+
+**Задание.**
+Запустите исполняемый файл `example2-stripped` под отладчиком.
+Что изменилось в тексте, который GDB выводит при старте?
+Начините выполнение, и когда управление будет возвращено отладчику,
+выполните команду `bt`.
+Что изменилось в её выводе?
+
+Можно заметить, что отладить `example2-stripped` фактически невозможно:
+отладчик работает, но не получится узнать, к какому месту в исходном коде
+относится фрейм, чему равны локальные переменные и параметры функций.
+
+Как отлаживать программу, которая аварийно завершилась у конечного пользователя?
+
+Вернемся к тому, что печатала оболочка, когда программа запускалась без GDB:
+
+```
+Segmentation fault (core dumped)
+```
+
+Слова «segmentation fault» означают, что аварийное завершение случилось
+по причине некорректного доступа к памяти. Могут быть и другие причины.
+
+Слова «core dumped» означают, что система записала слепок памяти процесса
+на момент сбоя (core dump, «корку»). Будет ли слепок записан и куда именно,
+зависит от настроек системы и процесса (`man core`).
+Так или иначе, этот файл можно получить от пользователя,
+который столкнулся с проблемой.
+
+На учебных стендах получить файл, записанный системой, не получится.
+Однако точно такой же файл был создан ранее командой `gcore` в GDB.
+Он сохранен в текущей директории в файл `coredump` (аргумент команды).
+
+Имея исполняемый файл *с отладочной информацией*
+и файл core dump, полученный от пользователя,
+можно начать отладку:
+
+```sh
+$ gdb -q example2 coredump
+```
+
+**Задание.**
+Какие известные вам команды GDB работают в таком режиме?
+Какую информацию они выдают (с отладкой `example2` и `example2-stripped`)?
+Почему некоторые не работают?
+
+Иногда нужно отладить программу, установленную из пакета.
+В большинстве дистрибутивов отладочная информация в пакеты не входит,
+но есть возможность установить отдельные пакеты с ней.
+Например, для ALT Linux: .
+
+## strace
+
+Иногда программы недостаточно подробно сообщают о том,
+что пытаются сделать и какие ошибки возникают.
+Утилита `strace` позволяет проследить все системные вызовы,
+которые совершает процесс.
+Например, для программы `true` (ничего не делает и завершается успешно):
+
+```sh
+$ strace /bin/true
+```
+
+```
+execve("/bin/true", ["/bin/true"], 0x7ffe0f8e18f8 /* 10 vars */) = 0
+exit_group(0) = ?
++++ exited with 0 +++
+```
+
+Процесс совершил два системных вызова:
+
+1) `execve()` для запуска своего исполняемого файла
+2) `exit_group()` для своего завершения с заданным кодом (0 — успех)
+
+**Задание.**
+По документации `execve()` выясните, что означают первые два аргумента.
+Почему второй показан в квадратных скобках?
+
+Большинство программ в начале делают много системных вызовов,
+чтобы загрузить и инициализировать используемые библиотеки,
+поэтому вывод `strace` от одного их старта занимает десятки строк.
+
+На самом деле, вызов `execve()` — это не прямой интерфейс системных вызовов,
+а вызов функции из библиотеки `libc`, которая делает системный вызов.
+Часто на один системный вызов приходится несколько функций `libc`
+схожего назначения. Например, помимо `execve()` для запуска процессов
+могут использоваться упрощенные формы: `exec()`, `execv()` и другие.
+Так как `execve()` наиболее общая и имеет в точности те же параметры,
+что и системный вызов, `exec()` и прочие реализованы через нее,
+и `strace` не может различить, вызвана `execve()` напрямую или изнутри `exec()`.
+Получив имя системного вызова с помощью `strace`, имеет смысл
+сразу уточнить в документации к нему, нет ли более простой функции.
+
+Почему нужны обертки из `libc`?
+Системный вызов — это не просто вызов функции.
+Вызов функции представляет собой передачу управления коду функции в памяти.
+Но код функции, которая реализует системный вызов, находится в ядре,
+а пользовательские процессы не имеют доступа к памяти ядра.
+Системные вызовы реализуются специальной командой процессора,
+которая принимает не адрес перехода, а номер системного вызова;
+параметры передаются особым образом; есть нюансы обработки ошибок.
+Обертки скрывают эти сложности.
+Для некоторых новых функций оберток нет.
+Их требуется вызывать с помощью специальной функции `syscall()`,
+как описано на соответствующих `man`-страницах.
+
+**Задание.**
+Получите справку по `strace` и попробуйте использовать флаги `-v` и `-o`.
+
+**Задание.**
+Попробуйте использовать `-e`, чтобы скрыть `execve()`.
+
+Программа `ltrace` позволяет аналогично отследить вызовы библиотечных функций.
+Она не рассматривается в лабораторных работах.
+
+# Общее контрольное задание
+
+1. Установить на виртуальном сервере компилятор, отладчик
+ и программу для наблюдения системных вызовов, которые совершает процесс.
+
+1. Узнать, какой функцией программа `rm` удаляет файлы
+ (документировать в отчете, как это сделано).
+ Написать программу `lab01-rm`, которая удаляет все файлы,
+ переданные ей как аргументы (обрабатывать каталоги не нужно).
+
+1. Написать программу `lab01-ls`, которая печатает список файлов в каталогах.
+ Каталоги передаются в аргументах программы.
+ Если их не передано, подразумевается текущий каталог.
+ Формат списка: `дата_создания_ГГГГ-ММ-ДД размер_в_байтах имя_файла`.
+ Если файл является каталогом, в конце имени нужно приписать `/`.
+ Некоторые из нужных функций: `opendir()`, `strftime()`.
+
+# Контрольные вопросы
+
+1. Чем отличаются ошибки от предупреждений?
+
+1. Зачем включать дополнительные предупреждения компилятора?
+
+1. Известен код ошибки, как узнать его смысл?
+
+1. Как получить список стандартных кодов ошибок, чтобы выбрать подходящий?
+
+1. Программа завершается сразу после запуска с ошибкой "не найден файл настроек".
+ В документации не сказано, где и какой файл она ожидает.
+ Как выяснить это?
+
+1. Разработчик собрал программу и запустил под отладчиком.
+ Однако при останове отладчик вместо имен функций показывает `??` и адреса.
+ В чем проблема и как ее исправить?
+
+1. Разработчик собрал программу и запустил под отладчиком.
+ Однако, когда происходит ошибка и управление передается отладчику,
+ он показывает, что якобы выполнялся не тот код, который вызвал ошибку.
+ В выводе отладчика есть слова "stack frame corrupted".
+ В чем дело и как искать ошибку в этом случае?
+
+# Индивидуальные контрольные задания
+
+Написанная программа не должна запускать другие программы,
+если об этом не сказано явно.
+То есть, если нужно повторить работу `id`, например,
+написанная программа не должна делать `system("id")` или подобное.
+
+Если команде, работу которой нужно повторить, передаются аргументы (`FILE`),
+они будут передаваться и написанной программе.
+Опции (`-a`) передаваться не будут, программа просто должна работать так,
+как работает команда с указанной опцией.
+
+Если используемая функция выделяет ресурсы (память, дескриптор),
+которые требуется освободить в вызывающем коде, это должно быть сделано.
+
+Если любая из задействованных функций из системных библиотек вернет ошибку,
+нужно напечатать сообщение, которое начинается с `ERROR: функция:`
+и завершить программу с кодом 1.
+
+Если программа вызвана неправильно,
+например, предполагается вызов `program ARG1 ARG2`,
+а указан только один аргумент,
+нужно напечатать сообщение, начинающееся на `ERROR:`,
+и завершить программу с кодом 2.
+
+## Варианты
+
+1. Написать программу, которая работает как команда `id` (без параметров).
+
+1. Написать программу, которая работает как `uname -a`.
+
+1. Написать программу, которая работает как `mv FILE1 FILE2`.
+
+1. Написать программу, которая работает как `truncate FILE SIZE`.
+
+1. Написать программу, которая печатает значение переменной окружения,
+ имя которой передается программе как параметр.
+
+1. Написать программу, которая работает как `date %Y-%m-%d %H:%M:%S`.
+
+1. Написать программу, которая работает как `sleep SECONDS`
+ (достаточно поддержать целое `SECONDS`).
+
+1. Написать программу, которая получает как `mkdir DIR`.
+ В созданный каталог должна быть возможность перейти.
+
+1. Написать программу, которая выполняет команду, состоящую из аргументов
+ (например, `program ls -l` выполняет `ls -l`).
+
+1. Написать программу, которая работает как `pwd`.
+
+1. Написать программу, которая принимает параметр-целое число
+ и печатает время в наносекундах, которое занимает
+ вычисление квадратного корня из этого числа.
+
+1. Написать программу, которая работает как `realpath FILE`.
+
+1. Написать программу, которая работает как `env`,
+ но печатает только переменные, начинающиеся на `P`.
+
+1. Написать программу, которая работает как `ln -s FROM TO`.
+
+1. Написать программу, которая выводит максимально возможное количество
+ процессов (аналог `prlimit -u`; «жесткий» лимит; `UNLIMITED`, если нет).