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