Initial commit

master
Yury Kurlykov 2020-04-16 15:15:52 +10:00
commit 847b90dee7
Signed by: t1meshift
GPG Key ID: B133F3167ABF94D8
13 changed files with 481 additions and 0 deletions

66
.clang-format Normal file
View File

@ -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

3
.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
.idea
cmake-build-*
build

16
CMakeLists.txt Normal file
View File

@ -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)

27
lab2/CMakeLists.txt Normal file
View File

@ -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 ()

96
lab2/README.md Normal file
View File

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

40
lab2/task1.c Normal file
View File

@ -0,0 +1,40 @@
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <assert.h>
#include <sys/wait.h>
// 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;
}

38
lab2/task2.c Normal file
View File

@ -0,0 +1,38 @@
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <assert.h>
#include <sys/wait.h>
// 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;
}

41
lab2/task3.c Normal file
View File

@ -0,0 +1,41 @@
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <assert.h>
#include <sys/wait.h>
// 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;
}

39
lab2/task4.c Normal file
View File

@ -0,0 +1,39 @@
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <assert.h>
#include <sys/wait.h>
// 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;
}

40
lab2/task7.c Normal file
View File

@ -0,0 +1,40 @@
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <assert.h>
#include <sys/wait.h>
// 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;
}

52
lab2/task8.c Normal file
View File

@ -0,0 +1,52 @@
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <assert.h>
#include <sys/wait.h>
// 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;
}

7
run_lab.sh Executable file
View File

@ -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"

16
scripts/run_all.sh Executable file
View File

@ -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