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`. |