commit 847b90dee7986200fc31590ccc06900d5efa7275 Author: Yury Kurlykov Date: Thu Apr 16 15:15:52 2020 +1000 Initial commit diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..e7e0de8 --- /dev/null +++ b/.clang-format @@ -0,0 +1,66 @@ +# Generated from CLion C/C++ Code Style settings +BasedOnStyle: LLVM +AccessModifierOffset: -1 +AlignAfterOpenBracket: Align +AlignConsecutiveAssignments: false +AlignOperands: false +AllowAllArgumentsOnNextLine: false +AllowAllConstructorInitializersOnNextLine: false +AllowAllParametersOfDeclarationOnNextLine: false +AllowShortBlocksOnASingleLine: Always +AllowShortCaseLabelsOnASingleLine: true +AllowShortFunctionsOnASingleLine: All +AllowShortIfStatementsOnASingleLine: Always +AllowShortLambdasOnASingleLine: All +AllowShortLoopsOnASingleLine: true +AlwaysBreakAfterReturnType: None +AlwaysBreakTemplateDeclarations: Yes +BreakBeforeBraces: Custom +BraceWrapping: + AfterCaseLabel: false + AfterClass: false + AfterControlStatement: false + AfterEnum: false + AfterFunction: false + AfterNamespace: false + AfterUnion: false + BeforeCatch: false + BeforeElse: false + IndentBraces: false + SplitEmptyFunction: false + SplitEmptyRecord: true +BreakBeforeBinaryOperators: NonAssignment +BreakBeforeTernaryOperators: false +BreakConstructorInitializers: BeforeColon +BreakInheritanceList: BeforeColon +ColumnLimit: 0 +CompactNamespaces: false +ContinuationIndentWidth: 4 +IndentCaseLabels: true +IndentPPDirectives: None +IndentWidth: 2 +KeepEmptyLinesAtTheStartOfBlocks: true +MaxEmptyLinesToKeep: 1 +NamespaceIndentation: None +ObjCSpaceAfterProperty: false +ObjCSpaceBeforeProtocolList: false +PointerAlignment: Right +ReflowComments: false +SpaceAfterCStyleCast: true +SpaceAfterLogicalNot: false +SpaceAfterTemplateKeyword: false +SpaceBeforeAssignmentOperators: true +SpaceBeforeCpp11BracedList: false +SpaceBeforeCtorInitializerColon: true +SpaceBeforeInheritanceColon: true +SpaceBeforeParens: ControlStatements +SpaceBeforeRangeBasedForLoopColon: true +SpaceInEmptyParentheses: false +SpacesBeforeTrailingComments: 0 +SpacesInAngles: false +SpacesInCStyleCastParentheses: false +SpacesInContainerLiterals: false +SpacesInParentheses: false +SpacesInSquareBrackets: false +TabWidth: 4 +UseTab: Never diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b28ecc6 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +.idea +cmake-build-* +build \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..9950096 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,16 @@ +cmake_minimum_required(VERSION 3.16) +project(os_labs C) + +set(CMAKE_C_STANDARD 11) + +function(define_lab lab_name) + add_subdirectory("${lab_name}") + add_custom_target("RUN_${lab_name}" ALL + "${PROJECT_SOURCE_DIR}/scripts/run_all.sh" "${PROJECT_BINARY_DIR}/${lab_name}" + USES_TERMINAL + DEPENDS "${lab_name}" + ) + message("${lab_name} defined.") +endfunction() + +define_lab(lab2) \ No newline at end of file diff --git a/lab2/CMakeLists.txt b/lab2/CMakeLists.txt new file mode 100644 index 0000000..6e284fe --- /dev/null +++ b/lab2/CMakeLists.txt @@ -0,0 +1,27 @@ +cmake_minimum_required(VERSION 3.16) +set(CMAKE_C_STANDARD 11) + +# Lab name +set(LAB_NAME "lab2") + +# Lab tasks +list(APPEND SOURCE_FILES + task1.c + task2.c + task3.c + task4.c + task7.c + task8.c + ) + + +### Here goes the template + +project("${LAB_NAME}" C) + +add_custom_target("${LAB_NAME}") + +foreach (file IN LISTS SOURCE_FILES) + add_executable("${LAB_NAME}_${file}_run" "${file}") + add_dependencies("${LAB_NAME}" "${LAB_NAME}_${file}_run") +endforeach () \ No newline at end of file diff --git a/lab2/README.md b/lab2/README.md new file mode 100644 index 0000000..7827947 --- /dev/null +++ b/lab2/README.md @@ -0,0 +1,96 @@ +# Лабораторная работа №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`. \ No newline at end of file diff --git a/lab2/task1.c b/lab2/task1.c new file mode 100644 index 0000000..ac80fb0 --- /dev/null +++ b/lab2/task1.c @@ -0,0 +1,40 @@ +#include +#include +#include +#include +#include +#include +#include + +// A macro to simplify my life +#define FORK_TASK(IN_FORK, IN_THIS) { \ + int f = fork(); \ + switch (f) { \ + case -1: perror("fork() returned an error!"); exit(1); \ + case 0: IN_FORK; exit(0); \ + default: IN_THIS; \ + } \ +} + +int main() { + // Task 1 + printf("\n\nTask 1\n"); + int x = 100; + FORK_TASK( + { + // `x' is copied from parent process + assert(x == 100); + x = 50; + }, + { + printf("Fork executed, pid: %d\n", f); + x = 20; + assert(x == 20); + int kk = wait(NULL); + printf("AFTER wait, pid: %d\n", kk); + assert(x == 20); + } + ) + alarm(2); + return 0; +} diff --git a/lab2/task2.c b/lab2/task2.c new file mode 100644 index 0000000..3b2adeb --- /dev/null +++ b/lab2/task2.c @@ -0,0 +1,38 @@ +#include +#include +#include +#include +#include +#include +#include + +// A macro to simplify my life +#define FORK_TASK(IN_FORK, IN_THIS) { \ + int f = fork(); \ + switch (f) { \ + case -1: perror("fork() returned an error!"); exit(1); \ + case 0: IN_FORK; exit(0); \ + default: IN_THIS; \ + } \ +} + + +int main() { + // Task 2 + printf("\n\nTask 2\n"); + FORK_TASK( + { + printf("hello\n"); + // Continue parent process execution + kill(getppid(), SIGCONT); + }, + { + printf("Fork executed, pid: %d\n", f); + // Stop parent execution + raise(SIGSTOP); + printf("goodbye\n"); + } + ) + + return 0; +} diff --git a/lab2/task3.c b/lab2/task3.c new file mode 100644 index 0000000..e7b96b2 --- /dev/null +++ b/lab2/task3.c @@ -0,0 +1,41 @@ +#include +#include +#include +#include +#include +#include +#include + +// A macro to simplify my life +#define FORK_TASK(IN_FORK, IN_THIS) { \ + int f = fork(); \ + switch (f) { \ + case -1: perror("fork() returned an error!"); exit(1); \ + case 0: IN_FORK; exit(0); \ + default: IN_THIS; \ + } \ +} + + +int main() { + // Task 3 + printf("\n\nTask 3\n"); + FORK_TASK( + { + printf("Goodbye, fork, you are `ls' now\n"); + if (execlp("ls", "ls", "-lah", NULL) == -1) { + perror("exec() error"); + }; + }, + { + printf("Fork executed, pid: %d\n", f); + //int status = -1488; + int kk = wait(NULL); + printf("AFTER wait, pid: %d\n", kk); + //waitpid(f, &status, 0); + //printf("Execution status is %d\n", status); + } + ) + + return 0; +} diff --git a/lab2/task4.c b/lab2/task4.c new file mode 100644 index 0000000..9d23ef6 --- /dev/null +++ b/lab2/task4.c @@ -0,0 +1,39 @@ +#include +#include +#include +#include +#include +#include +#include + +// A macro to simplify my life +#define FORK_TASK(IN_FORK, IN_THIS) { \ + int f = fork(); \ + switch (f) { \ + case -1: perror("fork() returned an error!"); exit(1); \ + case 0: IN_FORK; exit(0); \ + default: IN_THIS; \ + } \ +} + + +int main() { + // Task 4 + printf("\n\nTask 4\n"); + FORK_TASK( + { + printf("Fork before wait\n"); + wait(NULL); + printf("Fork AFTER wait\n"); + exit(228); + }, + { + printf("Parent before wait, pid of fork: %d\n", f); + int status = -1488; + int kk = waitpid(f, &status, 0); + printf("pid %d, Execution status is %d\n", kk, WEXITSTATUS(status)); + } + ) + + return 0; +} diff --git a/lab2/task7.c b/lab2/task7.c new file mode 100644 index 0000000..25e94c6 --- /dev/null +++ b/lab2/task7.c @@ -0,0 +1,40 @@ +#include +#include +#include +#include +#include +#include +#include + +// A macro to simplify my life +#define FORK_TASK(IN_FORK, IN_THIS) { \ + int f = fork(); \ + switch (f) { \ + case -1: perror("fork() returned an error!"); exit(1); \ + case 0: IN_FORK; exit(0); \ + default: IN_THIS; \ + } \ +} + + +int main() { + // Task 7 (whatever it means) + printf("\n\nTask 7\n"); + FORK_TASK( + { + fclose(stdout); + if (printf("Test\n") == -1) { + perror("Task 7, printf() in fork error"); + fflush(stderr); + } + }, + { + printf("Test1\n"); + int kk = wait(NULL); + printf("AFTER wait, pid: %d\n", kk); + printf("Test2\n"); + } + ) + + return 0; +} diff --git a/lab2/task8.c b/lab2/task8.c new file mode 100644 index 0000000..faa32bf --- /dev/null +++ b/lab2/task8.c @@ -0,0 +1,52 @@ +#include +#include +#include +#include +#include +#include +#include + +// A macro to simplify my life +#define FORK_TASK(IN_FORK, IN_THIS) { \ + int f = fork(); \ + switch (f) { \ + case -1: perror("fork() returned an error!"); exit(1); \ + case 0: IN_FORK; exit(0); \ + default: IN_THIS; \ + } \ +} + + +int main() { + // Task 8 + printf("\n\nTask 8\n"); + int pipefd[2]; + if (pipe(pipefd) == -1) { + perror("Couldn't create pipe!"); + exit(1); + } + char buff[256]; + + FORK_TASK( + { + close(pipefd[0]); + printf("In fork\n"); + dup2(pipefd[1], STDOUT_FILENO); + snprintf(buff, 256, "Written from fork, pid %d\n", getpid()); + //printf("FORK: Message = %s, Len = %lu\n", buff, strlen(buff)); + write(pipefd[1], buff, strlen(buff)); + close(pipefd[1]); + }, + { + wait(NULL); + close(pipefd[1]); + printf("Fork responded:\n"); + while (read(pipefd[0], buff, 1) > 0) { + printf("%c", buff[0]); + } + close(pipefd[0]); + } + ) + + return 0; +} diff --git a/run_lab.sh b/run_lab.sh new file mode 100755 index 0000000..28c0195 --- /dev/null +++ b/run_lab.sh @@ -0,0 +1,7 @@ +#!/usr/bin/env bash + +set -euo pipefail +IFS=$'\n\t' + +cmake -Bbuild -DCMAKE_BUILD_TYPE=Debug -G "CodeBlocks - Unix Makefiles" . +cmake --build build --target "RUN_$1" \ No newline at end of file diff --git a/scripts/run_all.sh b/scripts/run_all.sh new file mode 100755 index 0000000..cee40ce --- /dev/null +++ b/scripts/run_all.sh @@ -0,0 +1,16 @@ +#!/usr/bin/env bash + +set -euo pipefail +IFS=$'\n\t' + +echo -e "\nTesting script started...\n" + +while IFS= read -r -d '' FILE +do + echo "Executing $FILE..." + $FILE +done< <(find "$1" -name '*_run' -print0 | sort -z) + + +echo "Fin." +echo \ No newline at end of file