mirror of https://github.com/t1meshift/os_labs.git
Initial commit
commit
847b90dee7
|
@ -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
|
|
@ -0,0 +1,3 @@
|
||||||
|
.idea
|
||||||
|
cmake-build-*
|
||||||
|
build
|
|
@ -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)
|
|
@ -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 ()
|
|
@ -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`.
|
|
@ -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;
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
|
@ -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"
|
|
@ -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
|
Loading…
Reference in New Issue