mirror of https://github.com/t1meshift/os_labs.git
96 lines
7.6 KiB
Markdown
96 lines
7.6 KiB
Markdown
|
# Лабораторная работа №2
|
|||
|
|
|||
|
## Задание 1
|
|||
|
|
|||
|
> В основной программе переменной x присвоить значение 100,
|
|||
|
> а затем вызвать fork(). Каково значение x в порождённом процессе?
|
|||
|
> Что происходит, когда основной и порожденный процессы меняют значение x?
|
|||
|
|
|||
|
Значение `x` в порождённом процессе равно 100, так как при вызове `fork()` память
|
|||
|
процесса копируется в форк.
|
|||
|
|
|||
|
Изменение значения `x` в порождённом или основном процессе никак не влияет на
|
|||
|
значение в соседнем, так как форки не имеют общую память.
|
|||
|
|
|||
|
## Задание 2
|
|||
|
|
|||
|
> Порожденный процесс, созданный с помощью fork(), должен напечатать “hello”;
|
|||
|
> основной - “goodbye”. Нужно обеспечить, чтобы порожденный процесс печатал
|
|||
|
> первым без использования wait() в основном.
|
|||
|
|
|||
|
Это можно реализовать по меньшей мере двумя способами:
|
|||
|
|
|||
|
1. `sysctl -w kernel.sched_child_runs_first=1`
|
|||
|
2. "Поднять" SIGSTOP в основном процессе перед выводом `goodbye`, а в форке
|
|||
|
послать родительскому процессу SIGCONT после вывода `hello`.
|
|||
|
3. `SIGCHLD`?
|
|||
|
|
|||
|
В данной лабораторной работе был использован второй способ.
|
|||
|
|
|||
|
## Задание 3
|
|||
|
|
|||
|
> Сначала вызывается fork(), а затем какой-либо вариант exec()
|
|||
|
> (execl(), execle(), execlp(), execv(), execvp(), и execvpe())
|
|||
|
> для вызова /bin/ls. Для чего предусмотрено столько много вариантов exec()?
|
|||
|
|
|||
|
Постфиксы в exec-функциях имеют следующие значения:
|
|||
|
|
|||
|
- `v` — передача аргументов программе нуль-терминированным массивом;
|
|||
|
- `l` — передача аргументов программе через переменные аргументы функции (variadic arguments),
|
|||
|
последний аргумент должен быть NULL;
|
|||
|
- `p` — передача программе текущей переменной PATH;
|
|||
|
- `e` — передача программе собственных переменных окружения массивом строк вида `var=value`
|
|||
|
|
|||
|
## Задание 4
|
|||
|
|
|||
|
> В основной программе использовать wait() для того, чтобы дождаться завершения
|
|||
|
> порожденного процесса? Что возвращает wait()? Что произойдет, если использовать
|
|||
|
> wait() в порожденном процессе? Использовать waitpid() вместо of wait(). Когда
|
|||
|
> использование waitpid() целесообразно?
|
|||
|
|
|||
|
`wait()` возвращает `-1` при ошибке (код ошибки при этом лежит в `errno`),
|
|||
|
`0`, если передан флаг `WNOHANG` и ни один дочерний процесс к моменту возврата управления
|
|||
|
не был завершён, и PID завершённого процесса в остальных случаях.
|
|||
|
|
|||
|
Если вызвать `wait()` в процессе, не порождающем другие, управление тут же будет возвращено,
|
|||
|
а функция вернёт `-1`.
|
|||
|
|
|||
|
`waitpid()` целесообразно использовать, когда необходимо дождаться завершения процесса с определённым
|
|||
|
PID, либо одного из процессов из определённой группы (для этого в `waitpid()` в качестве pid передаётся
|
|||
|
-pgid). `-1` обозначает ожидание любого дочернего процесса, а `0` обозначает ожидание любого дочернего
|
|||
|
процесса, чей pgid равен таковому у текущего процесса.
|
|||
|
|
|||
|
Также у `wait()` и `waitpid()` есть аргумент `int *status`, куда будет записан статус дочернего
|
|||
|
процесса. Для получения информации из этого буфера, нужно использовать следующие макросы:
|
|||
|
```text
|
|||
|
WIFEXITED(status)
|
|||
|
не равно нулю, если дочерний процесс успешно завершился.
|
|||
|
WEXITSTATUS(status)
|
|||
|
возвращает восемь младших битов значения, которое вернул завершившийся дочерний процесс. Эти биты
|
|||
|
могли быть установлены в аргументе функции exit() или в аргументе оператора return функции main().
|
|||
|
Этот макрос можно использовать, только если WIFEXITED вернул ненулевое значение.
|
|||
|
WIFSIGNALED(status)
|
|||
|
возвращает истинное значение, если дочерний процесс завершился из-за необработанного сигнала.
|
|||
|
WTERMSIG(status)
|
|||
|
возвращает номер сигнала, который привел к завершению дочернего процесса. Этот макрос можно использовать,
|
|||
|
только если WIFSIGNALED вернул ненулевое значение.
|
|||
|
WIFSTOPPED(status)
|
|||
|
возвращает истинное значение, если дочерний процесс, из-за которого функция вернула управление, в настоящий
|
|||
|
момент остановлен; это возможно, только если использовался флаг WUNTRACED или когда подпроцесс отслеживается
|
|||
|
(см. ptrace(2)).
|
|||
|
WSTOPSIG(status)
|
|||
|
возвращает номер сигнала, из-за которого дочерний процесс был остановлен. Этот макрос можно использовать, только
|
|||
|
если WIFSTOPPED вернул ненулевое значение.
|
|||
|
|
|||
|
Некоторые версии Unix (например Linux, Solaris, но не AIX, SunOS) также определяют макрос WCOREDUMP(status) для
|
|||
|
проверки того, не вызвал ли дочерний процесс ошибку ядра. Используйте это только в структуре
|
|||
|
#ifdef WCOREDUMP ... #endif.
|
|||
|
```
|
|||
|
|
|||
|
## Задание 7
|
|||
|
|
|||
|
> Программа порождает процесс и в нем закрывает стандартный вывод (STDOUT FILENO). Что произойдет, если осуществить
|
|||
|
> вызов printf() для того, чтобы что-то вывести в основном и дочернем процессах?
|
|||
|
|
|||
|
Закрытие файлового дескриптора stdout в дочернем процессе никак не повлияет на родителя. Но в форке после закрытия
|
|||
|
stdin вызов `printf()` вернёт `-1`, а в `errno` будет лежать значение `EBADF`, обозначающее `Bad file descriptor`.
|