mirror of https://github.com/t1meshift/os_labs.git
Add 4th and 5th labs
parent
1de33e9192
commit
bbebc58c2b
|
@ -15,3 +15,5 @@ endfunction()
|
||||||
|
|
||||||
define_lab(lab2)
|
define_lab(lab2)
|
||||||
define_lab(lab3)
|
define_lab(lab3)
|
||||||
|
define_lab(lab4)
|
||||||
|
define_lab(lab5)
|
|
@ -0,0 +1,46 @@
|
||||||
|
cmake_minimum_required(VERSION 3.16)
|
||||||
|
set(CMAKE_C_STANDARD 11)
|
||||||
|
|
||||||
|
# Lab name
|
||||||
|
set(LAB_NAME "lab4")
|
||||||
|
|
||||||
|
# Lab tasks
|
||||||
|
list(APPEND SOURCE_FILES
|
||||||
|
aspace.c
|
||||||
|
task2.c
|
||||||
|
null.c
|
||||||
|
task6.c
|
||||||
|
task7.c
|
||||||
|
task8.c
|
||||||
|
)
|
||||||
|
list(APPEND NON_COMPILABLE_SRC
|
||||||
|
# .execme
|
||||||
|
)
|
||||||
|
|
||||||
|
set_source_files_properties(aspace.c PROPERTIES COMPILE_FLAGS -O0)
|
||||||
|
#set_source_files_properties(null.c PROPERTIES COMPILE_FLAGS -DHANDLE_SIGSEGV)
|
||||||
|
set_source_files_properties(null.c PROPERTIES COMPILE_FLAGS -g)
|
||||||
|
set_source_files_properties(task6.c PROPERTIES COMPILE_FLAGS -g)
|
||||||
|
set_source_files_properties(task7.c PROPERTIES COMPILE_FLAGS -g)
|
||||||
|
set_source_files_properties(task8.c PROPERTIES COMPILE_FLAGS -g)
|
||||||
|
|
||||||
|
### 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 ()
|
||||||
|
|
||||||
|
foreach (file IN LISTS NON_COMPILABLE_SRC)
|
||||||
|
add_custom_command(
|
||||||
|
TARGET "${LAB_NAME}" POST_BUILD
|
||||||
|
DEPENDS "${file}"
|
||||||
|
COMMAND ${CMAKE_COMMAND} -E copy
|
||||||
|
"${CMAKE_CURRENT_SOURCE_DIR}/${file}"
|
||||||
|
"${CMAKE_CURRENT_BINARY_DIR}/${file}"
|
||||||
|
)
|
||||||
|
endforeach ()
|
|
@ -0,0 +1,92 @@
|
||||||
|
# Лабораторная работа №4
|
||||||
|
|
||||||
|
## Задание 1
|
||||||
|
|
||||||
|
Было выяснено, что если не инициализировать глобальные (статические) переменные,
|
||||||
|
то порядок их расположения в памяти не определён. На trunk-версии GCC глобальные
|
||||||
|
переменные располагаются в памяти в порядке их объявления. На trunk-версии Clang
|
||||||
|
статические переменные располагаются в порядке их использования.
|
||||||
|
|
||||||
|
Обратный порядок расположения переменных на стеке соблюдается только при компилировании
|
||||||
|
с помощью Clang. (Проверено на GCC 9.3.0, GCC 6.5.0, Clang 10.0.0)
|
||||||
|
|
||||||
|
Если выделять 256 Кб и выше, то память выделяется в mmap-области.
|
||||||
|
|
||||||
|
Адрес указателя можно получить с помощью оператора "амперсанд": `&pointer`.
|
||||||
|
|
||||||
|
`&p` вернёт адрес, где лежит указатель, в то время как `p` вернёт сам указатель.
|
||||||
|
|
||||||
|
## Задание 2
|
||||||
|
|
||||||
|
`name` будет перезаписан после второго вызова `scanf()`, но если второй ввод строки
|
||||||
|
будет меньше, чем первый, то в памяти останется часть первого ввода (с учётом нуль-терминала).
|
||||||
|
|
||||||
|
При попытке записи в `name1` (`name1[0]=name1[3]`) программа даст сигнал SIGSEGV. Согласно
|
||||||
|
стандарту, запись в переменные типа `char* a = "abc"` вызывает неопределённое поведение.
|
||||||
|
Но GCC хранит переменные, проинициализированные подобным образом, в секции `.rodata`.
|
||||||
|
|
||||||
|
## Задание 3
|
||||||
|
|
||||||
|
Разыменовывание нулевого указателя даёт неопределённое поведение. Как правило, это аварийное
|
||||||
|
завершение программы. В качестве примера были использованы получение значения по адресу 0x0
|
||||||
|
и вызов функции по тому же адресу. Второй пример вызывает аварийное завершение программы не
|
||||||
|
на всех платформах -- на AVR-микроконтроллерах, например, это вызовет перезагрузку.
|
||||||
|
|
||||||
|
## Задание 4
|
||||||
|
|
||||||
|
GDB остановится на сигнале SIGSEGV, в момент разыменовывания. Для получения значения по адресу
|
||||||
|
0x0 вывод будет выглядеть так:
|
||||||
|
```text
|
||||||
|
Program received signal SIGSEGV, Segmentation fault.
|
||||||
|
0x0000555555555129 in main () at /home/sh1ft/CLionProjects/os-labs/lab4/null.c:25
|
||||||
|
25 int a = *p;
|
||||||
|
```
|
||||||
|
|
||||||
|
Для вызова по нулевому адресу вывод будет следующим:
|
||||||
|
```text
|
||||||
|
Program received signal SIGSEGV, Segmentation fault.
|
||||||
|
0x0000000000000000 in ?? ()
|
||||||
|
```
|
||||||
|
|
||||||
|
Стоит обратить внимание, что для второго случая был выведен лишь адрес аварийного завершения
|
||||||
|
работы, так как для неизвестной функции по этому адресу компилятор не сгенерировал символы
|
||||||
|
отладки.
|
||||||
|
|
||||||
|
## Задание 5
|
||||||
|
|
||||||
|
Valgrind с ключами `--leak-check=yes` сообщит, что программа попыталась обратиться к адресу,
|
||||||
|
не находящемуся в mapped-области, укажет номер строки в файле, где это произошло. Также Valgrind
|
||||||
|
выведет, что утечек памяти в программе нет, и завершит работу с кодом SIGSEGV.
|
||||||
|
|
||||||
|
## Задание 6
|
||||||
|
|
||||||
|
Valgrind с ключами `--vgdb-error=0 --leak-check=yes` даст возможность подключить отладчик GDB.
|
||||||
|
После присоединения к процессу Valgrind (`target remote | /usr/lib/valgrind/../../bin/vgdb`)
|
||||||
|
программа будет приостановлена на точке входа `_start`. Ввод команды `continue` продолжит
|
||||||
|
дальнейшее исполнение программы. Также можно поставить брейкпоинт на определённую строчку кода
|
||||||
|
командой `break <srcfile>:<lineno>`, условный -- `break <srcfile>:<lineno> if <condition>`.
|
||||||
|
Программа без освобождения памяти успешно завершит работу, не вызвав остановки в отладчике,
|
||||||
|
но Valgrind заметит, что был утерян один блок памяти.
|
||||||
|
|
||||||
|
## Задание 7
|
||||||
|
|
||||||
|
Ошибка в коде видна невооружённым глазом -- выход за границы массива. Но программа успешно завершит
|
||||||
|
работу во всех случаях, за исключением граничных случаев, когда по адресу сразу после массива идёт
|
||||||
|
другая страница памяти. Но Valgrind следит за памятью приложения строже, чем операционная система --
|
||||||
|
некорректное обращение к памяти будет сразу замечено, о чём будет сообщено:
|
||||||
|
```text
|
||||||
|
==2088068== Invalid write of size 4
|
||||||
|
==2088068== at 0x109159: main (task7.c:7)
|
||||||
|
==2088068== Address 0x4a7c1d0 is 0 bytes after a block of size 400 alloc'd
|
||||||
|
==2088068== at 0x483977F: malloc (vg_replace_malloc.c:309)
|
||||||
|
==2088068== by 0x10914A: main (task7.c:6)
|
||||||
|
```
|
||||||
|
|
||||||
|
## Задание 8
|
||||||
|
|
||||||
|
Use-After-Free -- классическая ошибка при работе с динамической памятью. Они не всегда заметны,
|
||||||
|
особенно если программа успешно выполняется в большинстве случаев (как в этом, например). К
|
||||||
|
счастью, Valgrind определяет и такие ошибки. В IDE CLion встроена графическая обёртка для данного
|
||||||
|
инструмента, которая и была использована при выполнении этого задания.
|
||||||
|
|
||||||
|
![Valgrind GUI in CLion](valgrind_clion.png)
|
|
@ -0,0 +1,50 @@
|
||||||
|
/* Example code for Think OS.
|
||||||
|
|
||||||
|
Copyright 2015 Allen Downey
|
||||||
|
License: Creative Commons Attribution-ShareAlike 3.0
|
||||||
|
|
||||||
|
*/
|
||||||
|
#define PRINT_DISTANCE(a, b) printf("Distance between " #a " and " #b ": %ld\n", (void *) (a) - (void *) (b))
|
||||||
|
|
||||||
|
#include <malloc.h>
|
||||||
|
#include <mcheck.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
int global;
|
||||||
|
int another_global;
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
char malloc_trace_path[4096];
|
||||||
|
getcwd(malloc_trace_path, 4096);
|
||||||
|
strcat(malloc_trace_path, "/malloc_trace.txt");
|
||||||
|
setenv("MALLOC_TRACE", malloc_trace_path, 0);
|
||||||
|
mtrace();
|
||||||
|
|
||||||
|
int local = 5;
|
||||||
|
int another_local = 7;
|
||||||
|
void *p = malloc(128);
|
||||||
|
void *another_p = malloc((1 << 18));
|
||||||
|
|
||||||
|
printf("Address of main is %p\n", main);
|
||||||
|
printf("Address of global is %p\n", &global);
|
||||||
|
printf("Address of local is %p\n", &local);
|
||||||
|
printf("Address of another_local is %p\n", &another_local);
|
||||||
|
printf("Address of p is %p\n", p);
|
||||||
|
printf("Address of another_p is %p\n\n", another_p);
|
||||||
|
|
||||||
|
PRINT_DISTANCE(&global, &main);
|
||||||
|
PRINT_DISTANCE(&global, &another_global);
|
||||||
|
PRINT_DISTANCE(&local, &another_local);
|
||||||
|
PRINT_DISTANCE(p, another_p);
|
||||||
|
|
||||||
|
malloc_stats();
|
||||||
|
|
||||||
|
FILE *minfo = fopen("malloc_info.xml", "w");
|
||||||
|
malloc_info(0, minfo);
|
||||||
|
fclose(minfo);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
|
@ -0,0 +1,28 @@
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <signal.h>
|
||||||
|
|
||||||
|
#ifdef HANDLE_SIGSEGV
|
||||||
|
void on_sigsegv() {
|
||||||
|
printf("SIGSEGV\nNull pointer dereference gives UB.\n");
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
#ifdef HANDLE_SIGSEGV
|
||||||
|
struct sigaction action;
|
||||||
|
memset(&action, 0, sizeof(action));
|
||||||
|
action.sa_flags = SA_SIGINFO;
|
||||||
|
action.sa_sigaction = on_sigsegv;
|
||||||
|
sigaction(SIGSEGV, &action, NULL);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Dereference NULL
|
||||||
|
int *p = NULL;
|
||||||
|
//(*p)(); // Calling a function from 0x0, yay!
|
||||||
|
int a = *p;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
|
@ -0,0 +1,35 @@
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <signal.h>
|
||||||
|
|
||||||
|
void on_sigsegv() {
|
||||||
|
printf("SIGSEGV\nWrite access to `char* a = \"abc\"` gives UB.\n");
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
struct sigaction action;
|
||||||
|
memset(&action, 0, sizeof(action));
|
||||||
|
action.sa_flags = SA_SIGINFO;
|
||||||
|
action.sa_sigaction = on_sigsegv;
|
||||||
|
sigaction(SIGSEGV, &action, NULL);
|
||||||
|
|
||||||
|
/* Assume name shorter than 20 chars. */
|
||||||
|
char name[20];
|
||||||
|
puts("Enter name: ");
|
||||||
|
scanf("%19s", name);
|
||||||
|
printf("Hello, %s.\n\n\tNice to see you.\n", name);
|
||||||
|
|
||||||
|
puts("Enter name: ");
|
||||||
|
scanf("%19s", name);
|
||||||
|
printf("Hello, %s.\n\n\tNice to see you.\n", name);
|
||||||
|
|
||||||
|
char *name1 = "Anna";
|
||||||
|
char a_letter = name1[0];
|
||||||
|
name1[0] = name1[3];
|
||||||
|
name1[3] = a_letter;
|
||||||
|
puts(name1);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <sys/mman.h>
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
char *a = malloc(1024);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
|
@ -0,0 +1,10 @@
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <sys/mman.h>
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
int *data = malloc(100 * sizeof(int));
|
||||||
|
data[100] = 0;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
|
@ -0,0 +1,12 @@
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <sys/mman.h>
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
int *data = malloc(100 * sizeof(int));
|
||||||
|
free(data);
|
||||||
|
|
||||||
|
printf("%d\n", data[96]);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
Binary file not shown.
After Width: | Height: | Size: 364 KiB |
|
@ -0,0 +1,7 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
IFS=$'\n\t'
|
||||||
|
|
||||||
|
echo "TODO!"
|
||||||
|
exit 1
|
|
@ -0,0 +1,34 @@
|
||||||
|
cmake_minimum_required(VERSION 3.16)
|
||||||
|
set(CMAKE_C_STANDARD 11)
|
||||||
|
|
||||||
|
# Lab name
|
||||||
|
set(LAB_NAME "lab5")
|
||||||
|
|
||||||
|
# Lab tasks
|
||||||
|
list(APPEND SOURCE_FILES
|
||||||
|
task1.c
|
||||||
|
)
|
||||||
|
list(APPEND NON_COMPILABLE_SRC
|
||||||
|
.execme
|
||||||
|
)
|
||||||
|
|
||||||
|
### 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 ()
|
||||||
|
|
||||||
|
foreach (file IN LISTS NON_COMPILABLE_SRC)
|
||||||
|
add_custom_command(
|
||||||
|
TARGET "${LAB_NAME}" POST_BUILD
|
||||||
|
DEPENDS "${file}"
|
||||||
|
COMMAND ${CMAKE_COMMAND} -E copy
|
||||||
|
"${CMAKE_CURRENT_SOURCE_DIR}/${file}"
|
||||||
|
"${CMAKE_CURRENT_BINARY_DIR}/${file}"
|
||||||
|
)
|
||||||
|
endforeach ()
|
|
@ -0,0 +1,21 @@
|
||||||
|
#include <assert.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <sys/wait.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
/*
|
||||||
|
1. Используя creat(), write(), fflush(), close(), gettimeofday(), разработать программу, которая
|
||||||
|
открывает файл,
|
||||||
|
записывает туда 300 KB,
|
||||||
|
очищает все записи,
|
||||||
|
закрывает и
|
||||||
|
удаляет файл,
|
||||||
|
а также измеряет и выводит время, затраченное на каждое действие.
|
||||||
|
*/
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
#error "The task is not done!"
|
||||||
|
}
|
Loading…
Reference in New Issue