Compare commits

...

8 Commits

Author SHA1 Message Date
Yury Kurlykov ae84f722a8
Update 5th lab 2020-06-11 15:20:55 +10:00
Yury Kurlykov f6cf3ad3eb
Add 11th lab 2020-06-11 15:16:58 +10:00
Yury Kurlykov 9be2f2302e
Update README.md 2020-06-11 15:05:03 +10:00
Yury Kurlykov 4ad7a6d3ab
Add 9th and 10th labs 2020-06-11 15:00:19 +10:00
Yury Kurlykov 9e82285e43
Update 5th lab 2020-06-11 14:06:56 +10:00
Yury Kurlykov 037a74ed69
Update 6th lab 2020-06-11 13:22:10 +10:00
Yury Kurlykov e69a88c336
Add 6th lab 2020-06-10 20:30:09 +10:00
Yury Kurlykov ea3de372a4
Add 5th lab 2020-05-29 02:43:15 +10:00
46 changed files with 2680 additions and 1 deletions

View File

@ -16,4 +16,8 @@ endfunction()
define_lab(lab2)
define_lab(lab3)
define_lab(lab4)
define_lab(lab7)
define_lab(lab5)
define_lab(lab6)
define_lab(lab7)
define_lab(lab9)
define_lab(lab10)

View File

@ -5,12 +5,19 @@
- [Лабораторная работа 2](lab2/README.md)
- [Лабораторная работа 3](lab3/README.md)
- [Лабораторная работа 4](lab4/README.md)
- [Лабораторная работа 5](lab5/README.md)
- [Лабораторная работа 6](lab6/README.md)
- [Лабораторная работа 7](lab7/README.md)
- [Лабораторная работа 9](lab9/README.md)
- [Лабораторная работа 10](lab10/README.md)
- [Лабораторная работа 11](lab11/README.md)
## Запуск
Требуется CMake 3.16+ (более ранние версии не проверялись) и компилятор с поддержкой C11.
Для лабораторной №11 требуются make и gcc.
```bash
./run_lab.sh <lab_directory>
```

7
lab10/.execme Executable file
View File

@ -0,0 +1,7 @@
#!/usr/bin/env bash
set -euo pipefail
IFS=$'\n\t'
echo "TODO!"
exit 1

35
lab10/CMakeLists.txt Normal file
View File

@ -0,0 +1,35 @@
cmake_minimum_required(VERSION 3.16)
set(CMAKE_C_STANDARD 11)
# Lab name
set(LAB_NAME "lab10")
# Lab tasks
list(APPEND SOURCE_FILES
killer.c
reader.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 ()

76
lab10/README.md Normal file
View File

@ -0,0 +1,76 @@
# Лабораторная работа №10
## Задание 1
С операцией чтения ничего не произошло (она завершилась успешно), так как согласно документации:
> if any process has the file open when this happens, deletion is postponed until all processes have closed the file.
> (https://www.gnu.org/software/libc/manual/html_node/Deleting-Files.html#Deleting-Files)
## Задание 2
Герц = 1 с^(-1)
2 000 000 * 4 = 8 000 000 - количество операций, необходимых для вычисления программы процессором.
Тогда 8 000 000 / 8 000 000 Гц = 1 с.
Т.е. для проведения данных вычислений нам нужна 1 секунда.
## Задание 3
Так как виртуальный адрес состоит из номера страницы и смещения в ней, то:
размер виртуального адреса в битах (обозначим за n) = количеству бит под номер страницы (p)
+ количество бит под смещение в странице (d)
n = p + d
p = n - d
n = 20 (по условию)
d = log2 (размера страницы) = log2 (1KB) = 10
p = 20 - 10 = 10 - количество бит отделяемых в виртуальном адресе под номер страницы.
Тогда возможное число страниц:
2^p = 2^10 = 1024
Ответ: 1024 элемента в таблице страниц.
## Задание 4
[https://www.intuit.ru/studies/courses/641/497/lecture/11286?page=1](https://www.intuit.ru/studies/courses/641/497/lecture/11286?page=1)
Рассмотрим алгоритмы диспетчеризации по следующим критериям:
1. время ожидания
2. starvation
3. Время обработки процесса (turnaround time)
4. Разница во времени выполнения процесса
### Алгоритмы:
1. First-Come-First-Served (FCFS)
1. Зависит от сложившейся ситуации. В большинстве случаев является не минимальным, так как не оптимизируется.
2. Отсутствует
3. Зависит от сложившейся ситуации.
4. Зависит от порядка входа.
2. Shortest Job First (SJF)
1. практически минимальное среднее время ожидания.
2. возможно, для тяжёлых процессов.
3. для длительных процессов время выполнение гораздо больше, чем в FCFS.
4. отсутствует.
3. Shortest-Remaining-Time-First (SRTF) - SJF с прерыванием, если пришедший процесс выполнять быстрее чем текущий, то мы останавливаем его и берём новый.
1. минимальное среднее время ожидания.
2. возможно, для тяжёлых процессов, но не так сильно, как в SJF.
3. для длительных процессов время выполнение гораздо больше, чем в SJF, так требует затраты на переключение, во время выполнения процесса.
4. отсутствует.
4. Round Robin (RR) - даём всем процессам одинаковое время по очереди.
1. для всех одинаковое.
2. отсутствует.
3. зависит от выбранного кванта, если он достаточно большой, то ситуация напоминает FCFS, если же сильно мал, то становятся значительными затраты на переключение процессов.
4. зависит от выбранного кванта.

10
lab10/killer.c Normal file
View File

@ -0,0 +1,10 @@
#include <stdio.h>
#include <unistd.h>
int main() {
if (unlink("test.txt") != 0) {
printf("Unlink error.");
return -1;
}
return 0;
}

21
lab10/reader.c Normal file
View File

@ -0,0 +1,21 @@
#include <stdio.h>
int main() {
char c;
FILE *f = fopen("test.txt", "r"); // открыли файл
if (!f) { // проверили успешность открытия
printf("File not found.");
return -1;
}
while (1) {
fseek(f, 0, SEEK_SET);
while ((c = fgetc(f)) !=
EOF) { // считали символ, и проверили что он не конечный
printf("%c", c); // добавили XOR к ответу символа
}
}
fclose(f);
return 0;
}

24
lab11/Makefile Normal file
View File

@ -0,0 +1,24 @@
ALL = vector-deadlock vector-global-order vector-try-wait vector-avoid-hold-and-wait vector-nolock
COMMON = vector-header.h main-common.c main-header.h
all: $(ALL)
clean:
rm -f $(ALL) *~
vector-deadlock: vector-deadlock.c $(COMMON)
gcc -o vector-deadlock vector-deadlock.c -Wall -pthread -O
vector-global-order: vector-global-order.c $(COMMON)
gcc -o vector-global-order vector-global-order.c -Wall -pthread -O
vector-try-wait: vector-try-wait.c $(COMMON)
gcc -o vector-try-wait vector-try-wait.c -Wall -pthread -O
vector-avoid-hold-and-wait: vector-avoid-hold-and-wait.c $(COMMON)
gcc -o vector-avoid-hold-and-wait vector-avoid-hold-and-wait.c -Wall -pthread -O
vector-nolock: vector-nolock.c $(COMMON)
gcc -o vector-nolock vector-nolock.c -Wall -pthread -O

65
lab11/README.md Normal file
View File

@ -0,0 +1,65 @@
# Лабораторная работа №11
## Задание 1
Программа печатает в каждом потоке начальные данные перед `vector_add` и результат выполнения данной функции.
Результат может меняться от вызова к вызову, так как весь worker не покрыт мьютексами, но на практике на такой небольшой
программе это маловероятно достижимо.
При добавлении `-d` взаимная блокировка возникает не всегда, а только при попадании переключения потоков между мьютексами.
В случае, если число потоков = 1, взаимной блокировки не возникнет.
## Задание 2
Программа избегает мёртвой блокировки за счёт упорядочивания по адресам, что позволяет постоянно сохранять порядок
блокировки.
В случае если адреса совпадают, то это один мьютекс, и для корректной работы программы его надо блокировать 1 раз.
В случае увеличения числа циклов и потоков, время выполнения растёт.
В случае включения `-p` время уменьшается, так как разрешается параллелизм.
## Задание 3
Вызовы pthread_mutex_trylock необходимы для создания порядка блокировки, для того чтобы избежать дедлока.
С увеличением числа потоков происходит рост повторных попыток, что является логичным, так как переключение между потоками
становится более частым.
## Задание 4
Данный подход защищает уязвимое место дедлока, созданием глобального мьютекса, но при этом не даёт различным векторам
выполняться параллельно.
При использовании `-p` время уменьшается.
## Задание 5
Указав memory, мы дожидаемся завершения всех операцией с памятью, что своего рода позволяет заменить мьютексы.
(https://ru.wikipedia.org/wiki/GCC_Inline_Assembly)
Сравним время выполнения следующих команд:
```text
./vector-nolock -t -n 2 -l 1000000 -d = 4.08
./vector-nolock -t -n 2 -l 1000000 -d -p = 0.65
```
```text
./vector-avoid-hold-and-wait -t -n 2 -l 1000000 -d = 2.98
./vector-avoid-hold-and-wait -t -n 2 -l 1000000 -d -p = 0.45
```
```text
./vector-try-wait -t -n 2 -l 1000000 -d = 1.30
./vector-try-wait -t -n 2 -l 1000000 -d -p = 0.18
```
```text
./vector-global-order -t -n 2 -l 1000000 -d = 0.69
./vector-global-order -t -n 2 -l 1000000 -d -p = 0.19
```
Таким образом видно, что vector-nolock работает медленнее других в любом случае.

89
lab11/README_FROM_SRC Normal file
View File

@ -0,0 +1,89 @@
This homework lets you play around with a number of ways to implement a small,
deadlock-free vector object in C. The vector object is quite limited (e.g., it
only has add() and init() functions) but is just used to illustrate different
approaches to avoiding deadlock.
Some files that you should pay attention to are as follows. They, in
particular, are used by all the variants in this homework.
- mythreads.h
The usual wrappers around many different pthread (and other) library calls,
so as to ensure they are not failing silently
- vector-header.h
A simple header for the vector routines, mostly defining a fixed vector size
and then a struct that is used per vector (vector_t)
- main-header.h
A number of global variables common to each different program
- main-common.c
Contains the main() routine (with arg parsing) that initializes two vectors,
starts some threads to access them (via a worker() routine), and then waits
for the many vector_add()'s to complete
The variants of this homework are found in the following files. Each takes a
different approach to dealing with concurrency inside a "vector addition"
routine called vector_add(); examine the code in these files to get a sense of
what is going on. They all use the files above to make a complete runnable
program.
- vector-deadlock.c
This version blithely grabs the locks in a particular order (dst then
src). By doing so, it creates an "invitation to deadlock", as one thread
might call vector_add(v1, v2) while another concurrently calls
vector_add(v2, v1).
- vector-global-order.c
This version of vector_add() grabs the locks in a total order, based on
address of the vector.
- vector-try-wait.c
This version of vector_add() uses pthread_mutex_trylock() to attempt to grab
locks; when the try fails, the code releases any locks it may hold and goes
back to the top and tries it all over again.
- vector-avoid-hold-and-wait.c
This version of vector_add() ensures it can't get stuck in a hold and wait
pattern by using a single lock around lock acquisition.
- vector-nolock.c
This version of vector_add() doesn't even use locks; rather, it uses an
atomic fetch-and-add to implement the vector_add() routine. Its semantics
(as a result) are slightly different.
Type "make" (and read the Makefile) to build each of five executables.
prompt> make
Then you can run a program by simply typing its name:
prompt> ./vector-deadlock
Each program takes the same set of arguments (see main-common.c for details):
-d
This flag turns on the ability for threads to deadlock.
When you pass -d to the program, every other thread calls vector_add()
with the vectors in a different order, e.g., with two threads, and -d enabled,
Thread 0 calls vector_add(v1, v2) and Thread 1 calls vector_add(v2, v1)
-p
This flag gives each thread a different set of vectors to call add()
upon, instead of just two vectors. Use this to see how things perform
when there isn't contention for the same set of vectors.
-n num_threads
Creates some number of threads; you need more than one to deadlock.
-l loops
How many times should each thread call vector_add()?
-v
Verbose flag: prints out a little more about what is going on.
-t
Turns on timing and shows how long everything took.

139
lab11/main-common.c Normal file
View File

@ -0,0 +1,139 @@
vector_t v[2 * MAX_THREADS];
// used to ensure print outs make sense
pthread_mutex_t print_lock = PTHREAD_MUTEX_INITIALIZER;
void usage(char *prog) {
fprintf(stderr, "usage: %s [-d (turn on deadlocking behavior)] [-l loops] [-n num_threads] [-t (do timing)] [-v (for verbose)]\n", prog);
exit(1);
}
// only called by one thread (not thread-safe)
void vector_init(vector_t *v, int value) {
int i;
for (i = 0; i < VECTOR_SIZE; i++) {
v->values[i] = value;
}
Pthread_mutex_init(&v->lock, NULL);
}
// only called by one thread (not thread-safe)
void vector_print(vector_t *v, char *str) {
int i;
for (i = 0; i < VECTOR_SIZE; i++) {
printf("%s[%d] %d\n", str, i, v->values[i]);
}
}
void print_info(int call_return, int thread_id, int v0, int v1) {
if (verbose == 0)
return;
Pthread_mutex_lock(&print_lock);
int j;
for (j = 0; j < thread_id; j++) printf(" ");
if (call_return)
printf("<-add(%d, %d)\n", v0, v1);
else
printf("->add(%d, %d)\n", v0, v1);
Pthread_mutex_unlock(&print_lock);
}
typedef struct __thread_arg_t {
int tid;
int vector_add_order;
int vector_0;
int vector_1;
} thread_arg_t;
void *worker(void *arg) {
thread_arg_t *args = (thread_arg_t *) arg;
int i, v0, v1;
for (i = 0; i < loops; i++) {
if (args->vector_add_order == 0) {
v0 = args->vector_0;
v1 = args->vector_1;
} else {
v0 = args->vector_1;
v1 = args->vector_0;
}
print_info(0, args->tid, v0, v1);
vector_add(&v[v0], &v[v1]);
print_info(1, args->tid, v0, v1);
}
return NULL;
}
int main(int argc, char *argv[]) {
opterr = 0;
int c;
while ((c = getopt (argc, argv, "l:n:vtdp")) != -1) {
switch (c) {
case 'l':
loops = atoi(optarg);
break;
case 'n':
num_threads = atoi(optarg);
break;
case 'v':
verbose = 1;
break;
case 't':
do_timing = 1;
break;
case 'd':
cause_deadlock = 1;
break;
case 'p':
enable_parallelism = 1;
break;
default:
usage(argv[0]);
}
}
assert(num_threads < MAX_THREADS);
pthread_t pid[MAX_THREADS];
thread_arg_t args[MAX_THREADS];
int i;
for (i = 0; i < num_threads; i++) {
args[i].tid = i;
if (enable_parallelism == 0) {
args[i].vector_0 = 0;
args[i].vector_1 = 1;
} else {
args[i].vector_0 = i * 2;
args[i].vector_1 = i * 2 + 1;
}
if (cause_deadlock && i % 2 == 1)
args[i].vector_add_order = 1;
else
args[i].vector_add_order = 0;
}
for (i = 0; i < 2 * MAX_THREADS; i++)
vector_init(&v[i], i);
double t1 = Time_GetSeconds();
for (i = 0; i < num_threads; i++)
Pthread_create(&pid[i], NULL, worker, (void *) &args[i]);
for (i = 0; i < num_threads; i++)
Pthread_join(pid[i], NULL);
double t2 = Time_GetSeconds();
fini();
if (do_timing) {
printf("Time: %.2f seconds\n", t2 - t1);
}
//vector_print(&v[0], "v1");
//vector_print(&v[1], "v2");
return 0;
}

13
lab11/main-header.h Normal file
View File

@ -0,0 +1,13 @@
#ifndef __main_header_h__
#define __main_header_h__
#define MAX_THREADS (100)
int loops = 1;
int verbose = 0;
int num_threads = 2;
int do_timing = 0;
int cause_deadlock = 0;
int enable_parallelism = 0;
#endif // __main_header_h__

52
lab11/mythreads.h Normal file
View File

@ -0,0 +1,52 @@
#ifndef __MYTHREADS_h__
#define __MYTHREADS_h__
#include <pthread.h>
#include <assert.h>
#include <stdlib.h>
#include <sys/time.h>
void *Malloc(size_t size) {
void *rc = malloc(size);
assert(rc != NULL);
return rc;
}
double Time_GetSeconds() {
struct timeval t;
int rc = gettimeofday(&t, NULL);
assert(rc == 0);
return (double) ((double)t.tv_sec + (double)t.tv_usec / 1e6);
}
void Pthread_mutex_init(pthread_mutex_t *mutex,
const pthread_mutexattr_t *attr) {
int rc = pthread_mutex_init(mutex, attr);
assert(rc == 0);
}
void Pthread_mutex_lock(pthread_mutex_t *m) {
int rc = pthread_mutex_lock(m);
assert(rc == 0);
}
void Pthread_mutex_unlock(pthread_mutex_t *m) {
int rc = pthread_mutex_unlock(m);
assert(rc == 0);
}
void Pthread_create(pthread_t *thread, const pthread_attr_t *attr,
void *(*start_routine)(void*), void *arg) {
int rc = pthread_create(thread, attr, start_routine, arg);
assert(rc == 0);
}
void Pthread_join(pthread_t thread, void **value_ptr) {
int rc = pthread_join(thread, value_ptr);
assert(rc == 0);
}
#endif // __MYTHREADS_h__

View File

@ -0,0 +1,32 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "mythreads.h"
#include "main-header.h"
#include "vector-header.h"
// use this to make lock acquisition ATOMIC
pthread_mutex_t global = PTHREAD_MUTEX_INITIALIZER;
void vector_add(vector_t *v_dst, vector_t *v_src) {
// put GLOBAL lock around all lock acquisition...
Pthread_mutex_lock(&global);
Pthread_mutex_lock(&v_dst->lock);
Pthread_mutex_lock(&v_src->lock);
Pthread_mutex_unlock(&global);
int i;
for (i = 0; i < VECTOR_SIZE; i++) {
v_dst->values[i] = v_dst->values[i] + v_src->values[i];
}
Pthread_mutex_unlock(&v_dst->lock);
Pthread_mutex_unlock(&v_src->lock);
}
void fini() {}
#include "main-common.c"

24
lab11/vector-deadlock.c Normal file
View File

@ -0,0 +1,24 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "mythreads.h"
#include "main-header.h"
#include "vector-header.h"
void vector_add(vector_t *v_dst, vector_t *v_src) {
Pthread_mutex_lock(&v_dst->lock);
Pthread_mutex_lock(&v_src->lock);
int i;
for (i = 0; i < VECTOR_SIZE; i++) {
v_dst->values[i] = v_dst->values[i] + v_src->values[i];
}
Pthread_mutex_unlock(&v_dst->lock);
Pthread_mutex_unlock(&v_src->lock);
}
void fini() {}
#include "main-common.c"

View File

@ -0,0 +1,35 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "mythreads.h"
#include "main-header.h"
#include "vector-header.h"
void vector_add(vector_t *v_dst, vector_t *v_src) {
if (v_dst < v_src) {
Pthread_mutex_lock(&v_dst->lock);
Pthread_mutex_lock(&v_src->lock);
} else if (v_dst > v_src) {
Pthread_mutex_lock(&v_src->lock);
Pthread_mutex_lock(&v_dst->lock);
} else {
// special case: src and dst are the same
Pthread_mutex_lock(&v_src->lock);
}
int i;
for (i = 0; i < VECTOR_SIZE; i++) {
v_dst->values[i] = v_dst->values[i] + v_src->values[i];
}
Pthread_mutex_unlock(&v_src->lock);
if (v_dst != v_src)
Pthread_mutex_unlock(&v_dst->lock);
}
void fini() {}
#include "main-common.c"

13
lab11/vector-header.h Normal file
View File

@ -0,0 +1,13 @@
#ifndef __vector_header_h__
#define __vector_header_h__
#define VECTOR_SIZE (100)
typedef struct __vector {
pthread_mutex_t lock;
int values[VECTOR_SIZE];
} vector_t;
#endif // __vector_header_h__

31
lab11/vector-nolock.c Normal file
View File

@ -0,0 +1,31 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "mythreads.h"
#include "main-header.h"
#include "vector-header.h"
// taken from https://en.wikipedia.org/wiki/Fetch-and-add
int fetch_and_add(int * variable, int value) {
asm volatile("lock; xaddl %%eax, %2;"
:"=a" (value)
:"a" (value), "m" (*variable)
:"memory");
return value;
}
void vector_add(vector_t *v_dst, vector_t *v_src) {
int i;
for (i = 0; i < VECTOR_SIZE; i++) {
fetch_and_add(&v_dst->values[i], v_src->values[i]);
}
}
void fini() {}
#include "main-common.c"

36
lab11/vector-try-wait.c Normal file
View File

@ -0,0 +1,36 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "mythreads.h"
#include "main-header.h"
#include "vector-header.h"
int retry = 0;
void vector_add(vector_t *v_dst, vector_t *v_src) {
top:
if (pthread_mutex_trylock(&v_dst->lock) != 0) {
goto top;
}
if (pthread_mutex_trylock(&v_src->lock) != 0) {
retry++;
Pthread_mutex_unlock(&v_dst->lock);
goto top;
}
int i;
for (i = 0; i < VECTOR_SIZE; i++) {
v_dst->values[i] = v_dst->values[i] + v_src->values[i];
}
Pthread_mutex_unlock(&v_dst->lock);
Pthread_mutex_unlock(&v_src->lock);
}
void fini() {
printf("Retries: %d\n", retry);
}
#include "main-common.c"

7
lab5/.execme Executable file
View File

@ -0,0 +1,7 @@
#!/usr/bin/env bash
set -euo pipefail
IFS=$'\n\t'
echo "TODO!"
exit 1

39
lab5/CMakeLists.txt Normal file
View File

@ -0,0 +1,39 @@
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
task2.c
mytail.c
mystat.c
myls.c
task6.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 ()

158
lab5/README.md Normal file
View File

@ -0,0 +1,158 @@
# Лабораторная работа №5
## Задание 1
Результат выполнения:
```text
creat() call: 0.000350s
write 300KiB: 0.000346s
fsync() call: 0.005649s
close() call: 0.000011s
```
## Задание 2
Результат выполнения:
```text
creat() call: 0.000262s
write() call: 0.000275s
close() call: 0.000357s
POSIX API: 0.001043s
fopen() call: 0.000206s
fwrite() call: 0.000258s
fclose() call: 0.000455s
stdio.h API: 0.000968s
```
При многократном выполнении время сильно разнится, порой не в пользу POSIX API. Для повышения
точности таймера следует значительно увеличить размер файла.
## Задание 3
Была реализована упрощённая версия команды `tail`. По умолчанию она читает последние 10 строк,
но это поведение можно изменить параметром `-n`, где `n` -- количество строк (например, `-24`)
## Задание 4
Была реализована упрощённая версия команды `stat`. Пример вывода для пути `/dev/null`:
```text
File: /dev/null
Size: 0 Blocks: 0 IO Block: 4096 character device
Device: [0000:0006] Inode: 5 Links: 1
Mode: 020666 Uid: 0 Gid: 0
Access (atime): Tue Jun 9 20:25:43 2020
Modify (mtime): Tue Jun 9 20:25:43 2020
Change (ctime): Tue Jun 9 20:25:43 2020
```
Для `/home`:
```text
File: /home
Size: 10 Blocks: 0 IO Block: 4096 directory
Device: [0000:001d] Inode: 264 Links: 1
Mode: 040755 Uid: 0 Gid: 0
Access (atime): Fri Apr 5 22:15:25 2019
Modify (mtime): Mon Dec 4 18:26:29 2017
Change (ctime): Fri Sep 7 03:19:26 2018
```
При изменении содержимого директории число ссылок не изменяется.
## Задание 5
Была реализована упрощённая версия команды `ls`.
Были реализованы следующие ключи:
- `-l` для подробного вывода (long-listing)
- `--` для явного завершения приёма параметров (на случай, если файл начинается с дефиса)
Пример для параметров `-l -- /usr`:
```text
drwxr-xr-x 1 root root 104 2020-Jun-09 03:13:52 +10 .
drwxr-xr-x 1 root root 254 2020-May-22 07:15:49 +10 ..
drwxr-xr-x 1 root root 127286 2020-Jun-09 03:13:51 +10 bin
drwxr-xr-x 1 root root 35922 2020-Jun-09 03:13:51 +10 include
drwxr-xr-x 1 root root 287352 2020-Jun-09 03:13:51 +10 lib
drwxr-xr-x 1 root root 45012 2020-Jun-08 12:29:17 +10 lib32
drwxr-xr-x 1 root root 72 2017-Oct-26 22:22:02 +10 local
drwxr-xr-x 1 root root 7954 2020-Jun-08 13:31:41 +10 share
drwxr-xr-x 1 root root 70 2020-May-26 13:59:54 +10 src
drwxr-xr-x 1 root root 6 2019-Dec-17 13:10:37 +10 games
drwxr-xr-x 1 root root 40 2020-Apr-06 17:00:36 +10 libexec
lrwxrwxrwx 1 root root 3 2020-May-21 17:30:57 +10 lib64
lrwxrwxrwx 1 root root 3 2020-May-21 17:30:57 +10 sbin
```
Пример для параметров `/bin/sh`:
```text
/bin/sh
```
Пример для `-l /bin/sh`:
```text
lrwxrwxrwx 1 root root 4 2020-May-27 05:32:57 +10 /bin/sh
```
## Задание 6
Была реализована утилита, напоминающая упрощённую версию `find`.
Были реализованы следующие ключи:
- `-d <number>` для ограничения глубины рекурсивного прохода по директориям
(`<number>` -- неотрицательное)
- `-L` для перехода по символическим ссылкам (по умолчанию директории-симлинки игнорируются)
- `--` для явного завершения приёма параметров (на случай, если файл начинается с дефиса)
Примеры:
```text
$ ./lab5_task6.c_run -L -d 2 ../lab5  ✔
../lab5
../lab5/CMakeFiles
../lab5/CMakeFiles/lab5_task1.c_run.dir
../lab5/CMakeFiles/lab5.dir
../lab5/CMakeFiles/CMakeDirectoryInformation.cmake
../lab5/CMakeFiles/lab5_benchmark.h_run.dir
../lab5/CMakeFiles/lab5_task2.c_run.dir
../lab5/CMakeFiles/lab5_mytail.c_run.dir
../lab5/CMakeFiles/lab5_mystat.c_run.dir
../lab5/CMakeFiles/lab5_myls.c_run.dir
../lab5/CMakeFiles/lab5_task6.c_run.dir
../lab5/CMakeFiles/progress.marks
../lab5/cmake_install.cmake
../lab5/task2_posix.txt
../lab5/task2_stdio.txt
../lab5/lab5_mytail.c_run
../lab5/lab5_myls.c_run
../lab5/Makefile
../lab5/lab5.cbp
../lab5/lab5_task1.c_run
../lab5/task1.txt
../lab5/lab5_task2.c_run
../lab5/lab5_mystat.c_run
../lab5/lab5_task6.c_run
```
```text
$ ./lab5_task6.c_run -d 0
.
```
```text
$ ./lab5_task6.c_run -L -d 1
.
./CMakeFiles
./cmake_install.cmake
./task2_posix.txt
./task2_stdio.txt
./lab5_mytail.c_run
./lab5_myls.c_run
./Makefile
./lab5.cbp
./lab5_task1.c_run
./task1.txt
./lab5_task2.c_run
./lab5_mystat.c_run
./lab5_task6.c_run
```

190
lab5/myls.c Normal file
View File

@ -0,0 +1,190 @@
#include <assert.h>
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/sysmacros.h>
#include <sys/time.h>
#include <sys/wait.h>
#include <time.h>
#include <unistd.h>
#include <pwd.h>
#include <grp.h>
/*
5. Разработать собственную версию (myls) команды ls, которая выводит список файлов в заданной директории.
С ключом -l она выводит информацию о каждом файле, включая собственника, группу, разрешения и т.д., получаемые из системного
вызова stat().
Формат: myls -l directory (или текущую директорию, если параметр не задан)
Использовать: stat(), opendir(), readdir(), getcwd(), ...
*/
#define MAX(a,b) ((a)>(b)?(a):(b))
#define STR_EQ(a, b) (!strcmp((a),(b)))
#define FATAL(fmt, ...) do {\
fprintf(stderr,fmt,##__VA_ARGS__);\
exit(1);\
} while (0)
char *path = NULL;
bool long_listing = false;
bool flag_end = false;
void cleanup_on_exit() {
free(path);
}
void print_entry(char *entry) {
if (long_listing) {
struct stat fstat;
if (lstat(entry, &fstat) == -1) {
perror("stat()");
FATAL("Couldn't stat %s\n", entry);
}
// Print permissions
char entry_type;
// setuid, setgid, sticky are not in count
const char* perm_sym[] = {"---", "--x", "-w-", "-wx", "r--", "r-x", "rw-", "rwx"};
int perm = fstat.st_mode & 0777;
struct passwd *pw_data;
struct group *gp_data;
char user_name[33] = "";
char group_name[33] = "";
char last_mtime[256] = "";
switch (fstat.st_mode & S_IFMT) {
case S_IFREG:
entry_type = '-';
break;
case S_IFDIR:
entry_type = 'd';
break;
case S_IFLNK:
entry_type = 'l';
break;
case S_IFBLK:
entry_type = 'b';
break;
case S_IFSOCK:
entry_type = 's';
break;
case S_IFCHR:
entry_type = 'c';
break;
#ifdef S_IFIFO
case S_IFIFO:
entry_type = 'p';
break;
#endif /* S_IFIFO */
default:
entry_type = '?';
break;
}
if ((pw_data = getpwuid(fstat.st_uid)) == NULL) {
perror("Could not get owner username");
errno = 0;
snprintf(user_name, 33, "%d", fstat.st_uid);
} else {
snprintf(user_name, 33, "%s", pw_data->pw_name);
}
if ((gp_data = getgrgid(fstat.st_gid)) == NULL) {
perror("Could not get owner group name");
errno = 0;
snprintf(group_name, 33, "%d", fstat.st_gid);
} else {
snprintf(group_name, 33, "%s", gp_data->gr_name);
}
time_t mtime = fstat.st_mtime;
struct tm* tm_info = localtime(&mtime);
strftime(last_mtime, 256, "%Y-%b-%d %T %Z", tm_info);
printf("%c%s%s%s\t%4lu\t%s %s\t%6ld\t%s\t",
entry_type,
perm_sym[(perm & S_IRWXU) >> 6],
perm_sym[(perm & S_IRWXG) >> 3],
perm_sym[perm & S_IRWXO],
fstat.st_nlink,
user_name,
group_name,
fstat.st_size,
last_mtime);
}
printf("%s\n", entry);
}
int main(int argc, char *argv[]) {
atexit(cleanup_on_exit);
for (int i = 1; i < argc; ++i) {
if (argv[i][0] == '-' && !flag_end) {
char *flag = argv[i] + 1;
if (STR_EQ(flag, "l")) {
long_listing = true;
}
else if (STR_EQ(flag, "-")) {
flag_end = true;
}
else {
FATAL("Usage: %s [-l] [--] [path]\n", argv[0]);
}
} else {
path = strdup(argv[i]);
break;
}
}
if (!path) {
/*
* As an extension to the POSIX.1-2001 standard, glibc's getcwd()
* allocates the buffer dynamically using malloc(3) if buf is NULL. In
* this case, the allocated buffer has the length size unless size is
* zero, when buf is allocated as big as necessary. The caller should
* free(3) the returned buffer.
*/
if ((path = getcwd(NULL, 0)) == NULL) {
perror("getcwd()");
FATAL("Couldn't get current working directory\n");
}
}
struct stat path_stat;
DIR *dir = NULL;
if (stat(path, &path_stat) == -1) {
perror("stat()");
FATAL("Couldn't stat %s\n", path);
}
bool is_dir = S_ISDIR(path_stat.st_mode);
if (is_dir) {
if ((dir = opendir(path)) == NULL) {
perror("opendir()");
FATAL("Couldn't open directory %s\n", path);
}
if (chdir(path) == -1) {
perror("chdir()");
FATAL("Could not chdir to directory %s\n", path);
}
struct dirent *ent;
while ((ent = readdir(dir)) != NULL) {
print_entry(ent->d_name);
}
closedir(dir);
} else {
print_entry(path);
}
return 0;
}

79
lab5/mystat.c Normal file
View File

@ -0,0 +1,79 @@
#include <assert.h>
#include <errno.h>
#include <fcntl.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/sysmacros.h>
#include <sys/time.h>
#include <sys/wait.h>
#include <time.h>
#include <unistd.h>
/*
4. Разработать собственную версию (mystat) команды stat, которая просто осуществляет системный вызов stat(), выводит
размер, число задействованных блоков, число ссылок и т.д. Отследить, как меняется число ссылок, когда изменяется
содержимое директории.
*/
#define MAX(a,b) ((a)>(b)?(a):(b))
#define FATAL(fmt, ...) do {\
fprintf(stderr,fmt,##__VA_ARGS__);\
exit(1);\
} while (0)
int main(int argc, char *argv[]) {
if (argc != 2) {
FATAL("Usage: %s <path>\n", argv[0]);
}
char *path = argv[1];
struct stat sb;
if (lstat(path, &sb) == -1) {
perror("lstat()");
FATAL("Couldn't stat %s\n", path);
}
printf("File: %s\n", path);
printf("Size: %ld\tBlocks: %ld\tIO Block: %ld\t", sb.st_size, sb.st_blocks, sb.st_blksize);
switch(sb.st_mode & S_IFMT) {
case S_IFBLK:
puts("block device");
break;
case S_IFCHR:
puts("character device");
break;
case S_IFDIR:
puts("directory");
break;
case S_IFIFO:
puts("FIFO/pipe");
break;
case S_IFLNK:
puts("symlink");
break;
case S_IFREG:
puts("regular file");
break;
case S_IFSOCK:
puts("socket");
break;
default:
puts("unknown?");
break;
}
printf("Device: [%04x:%04x]\t", major(sb.st_dev), minor(sb.st_dev));
printf("Inode: %lu\t", sb.st_ino);
printf("Links: %lu\n", sb.st_nlink);
printf("Mode: 0%o\tUid: %d\tGid: %d\n", sb.st_mode, sb.st_uid, sb.st_gid);
printf("Access (atime): %s", ctime(&sb.st_atime));
printf("Modify (mtime): %s", ctime(&sb.st_mtime));
printf("Change (ctime): %s", ctime(&sb.st_ctime));
return 0;
}

92
lab5/mytail.c Normal file
View File

@ -0,0 +1,92 @@
#include <assert.h>
#include <errno.h>
#include <fcntl.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/wait.h>
#include <unistd.h>
#include <stdbool.h>
/*
3. Разработать собственную версию (mytail) команды tail.
Формат: mytail -n file
Она читает блок из конца файла, просматривает его с конца до заданного количества строк n и печатает эти строки
в соответствующем порядке.
Использовать: stat(), lseek(), open(), read(), close(), ...
*/
#define MAX(a,b) ((a)>(b)?(a):(b))
#define FATAL(fmt, ...) do {\
fprintf(stderr,fmt,##__VA_ARGS__);\
exit(1);\
} while (0)
int main(int argc, char *argv[]) {
const int BUFF_SIZE = 4096;
long n = 10;
char *filename = NULL;
// Parse arguments (skip 0th arg)
for (int i = 1; i < argc; ++i) {
if ((argv[i][0] == '-') && (i < argc - 1)) {
// -n flag (e.g. -20)
n = strtol(argv[i], NULL, 10) * -1;
if (n < 1 || errno == ERANGE) {
FATAL("Usage: %s [-<number_of_lines>] <file>\n", argv[0]);
}
} else {
filename = argv[i];
break;
}
}
if (filename == NULL) FATAL("No file name specified.\n");
struct stat sb;
int fd;
if (stat(filename, &sb) == -1) {
perror("stat()");
FATAL("Couldn't stat %s\n", filename);
}
if ((fd = open(filename, O_RDONLY)) == -1) {
perror("open()");
FATAL("Couldn't open %s\n", filename);
}
char *buf = calloc(BUFF_SIZE, 1);
int lf_cnt = 0;
size_t read_sz = 0;
off_t file_offset = MAX(sb.st_size - BUFF_SIZE, 0);
bool seek_stop = false;
lseek(fd, -BUFF_SIZE, SEEK_END);
do {
int data_sz = read(fd, buf, BUFF_SIZE);
for (int i = data_sz - 1; i >= 0; --i) {
if (buf[i] == '\n') {
if (++lf_cnt >= n) {
file_offset += i + 1;
goto print_lines;
}
}
}
file_offset -= BUFF_SIZE;
lseek(fd, -BUFF_SIZE, SEEK_CUR);
} while ((lf_cnt <= n) && (file_offset > 0));
print_lines:
lseek(fd, file_offset, SEEK_SET);
do {
read_sz = read(fd, buf, BUFF_SIZE);
write(STDOUT_FILENO, buf, read_sz);
} while (read_sz == BUFF_SIZE);
free(buf);
close(fd);
return 0;
}

66
lab5/task1.c Normal file
View File

@ -0,0 +1,66 @@
#include <assert.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#include <sys/time.h>
#include <unistd.h>
#define BENCHMARK(name, payload) \
do {\
struct timeval t0, t1;\
gettimeofday(&t0, NULL);\
payload \
gettimeofday(&t1, NULL);\
struct timeval result;\
timersub(&t1, &t0, &result);\
printf(name ": %ld.%06lds\n", result.tv_sec, result.tv_usec);\
} while (0)
/*
1. Используя creat(), write(), fflush(), close(), gettimeofday(), разработать программу, которая
открывает файл,
записывает туда 300 KB,
очищает все записи,
закрывает и
удаляет файл,
а также измеряет и выводит время, затраченное на каждое действие.
*/
int main() {
int f = -1;
BENCHMARK("creat() call",{
f = creat("task1.txt", S_IWUSR);
});
if (f == -1) {
perror("creat()");
return 1;
}
int bytes_written = -1;
const size_t data_size = 307200; // bytes, 300KiB
char *random_data = malloc(data_size);
BENCHMARK("write 300KiB", {
bytes_written = write(f, random_data, data_size);
});
free(random_data);
if (bytes_written == -1) {
perror("write()");
return 1;
}
BENCHMARK("fsync() call", {
fsync(f);
});
BENCHMARK("close() call", {
close(f);
});
return 0;
}

87
lab5/task2.c Normal file
View File

@ -0,0 +1,87 @@
#include <assert.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#include <sys/time.h>
#include <unistd.h>
#define BENCHMARK(name, payload) \
do {\
struct timeval __t0, __t1;\
gettimeofday(&__t0, NULL);\
payload \
gettimeofday(&__t1, NULL);\
struct timeval __result;\
timersub(&__t1, &__t0, &__result);\
printf(name ": %ld.%06lds\n", __result.tv_sec, __result.tv_usec);\
} while (0)
/*
2. Разработать программу, которая замеряет время для 300,000 однобайтовых записей с использованием
a) напрямую POSIX: creat(), write(), close().
b) с использованием библиотеки stdio (напр., fopen(), fwrite(), and fclose()).
Сравнить и объяснить результаты.
*/
int main() {
BENCHMARK("POSIX API", {
int f = -1;
BENCHMARK("creat() call",{
f = creat("task2_posix.txt", 0644);
});
if (f == -1) {
perror("creat()");
return 1;
}
int bytes_written = -1;
const size_t data_size = 300000;
char *random_data = malloc(data_size);
BENCHMARK("write() call", {
bytes_written = write(f, random_data, data_size);
});
free(random_data);
if (bytes_written == -1) {
perror("write()");
return 1;
}
BENCHMARK("close() call", {
close(f);
});
});
BENCHMARK("stdio.h API",{
FILE *f = NULL;
BENCHMARK("fopen() call", {
f = fopen("task2_stdio.txt", "w");
});
if (f == NULL) {
perror("fopen()");
return 1;
}
int bytes_written = -1;
const size_t data_size = 300000;
char *random_data = malloc(data_size);
BENCHMARK("fwrite() call", {
bytes_written = fwrite(random_data, 1, 300000, f);
});
free(random_data);
if (bytes_written != data_size) {
perror("fwrite()");
return 1;
}
BENCHMARK("fclose() call",{
fclose(f);
});
});
return 0;
}

128
lab5/task6.c Normal file
View File

@ -0,0 +1,128 @@
#include <assert.h>
#include <ctype.h>
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <grp.h>
#include <pwd.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/sysmacros.h>
#include <sys/time.h>
#include <sys/wait.h>
#include <time.h>
#include <unistd.h>
#include <limits.h>
/*
6. Разработать программу, которая выводит имена каждого файла и директории, начиная с заданной точки в дереве каталогов.
a) Без аргументов: сначала текущая директория и ее содержимое, затем поддиректории и т.д. (пока не закончится дерево,
root в качестве CWD).
b) С одним аргументом (который есть имя директории): все поддерево, начиная с заданной директории.
c) А также еще один какой-либо интересный вариант (см. опции для find).
*/
#define USAGE_STRING "Usage: %s [-d <max_depth>] [-L] [--] [path]\n"
#define MAX(a,b) ((a)>(b)?(a):(b))
#define STR_EQ(a, b) (!strcmp((a),(b)))
#define FATAL(fmt, ...) do {\
fprintf(stderr,fmt,##__VA_ARGS__);\
exit(1);\
} while (0)
#define WARNING(fmt, ...) fprintf(stderr,fmt,##__VA_ARGS__)
bool flag_end = false;
bool follow_symlinks = false;
long max_depth = LONG_MAX;
void traverse_path(char *path, long depth) {
struct stat pstat;
DIR *dir = NULL;
struct dirent *ent;
int stat_status;
printf("%s\n", path);
if (depth > max_depth)
return;
if (follow_symlinks) {
stat_status = stat(path, &pstat);
} else {
stat_status = lstat(path, &pstat);
}
if (stat_status == -1) {
perror("stat()");
FATAL("Couldn't stat %s\n", path);
}
if (!S_ISDIR(pstat.st_mode))
return;
if ((dir = opendir(path)) == NULL) {
perror("opendir()");
FATAL("Couldn't open directory %s\n", path);
}
while ((ent = readdir(dir)) != NULL) {
if (STR_EQ(ent->d_name, ".") || STR_EQ(ent->d_name, "..")) {
continue;
}
char path_next[PATH_MAX];
strcpy(path_next, path);
if (path_next[strlen(path_next) - 1] != '/') {
strcat(path_next, "/");
}
strcat(path_next, ent->d_name);
traverse_path(path_next, depth + 1);
}
closedir(dir);
}
int main(int argc, char *argv[]) {
char *path = ".";
for (int i = 1; i < argc; ++i) {
if (argv[i][0] == '-' && !flag_end) {
char *flag = argv[i] + 1;
if (STR_EQ(flag, "d")) {
if (i + 1 < argc) {
max_depth = strtol(argv[++i], NULL, 10);
if (errno == ERANGE) {
WARNING("Max depth level is out of range, %ld is set\n", max_depth);
errno = 0;
}
if (max_depth < 0) {
FATAL("Max depth must be non-negative\n");
}
} else {
FATAL(USAGE_STRING, argv[0]);
}
}
else if (STR_EQ(flag, "L")) {
follow_symlinks = true;
}
else if (STR_EQ(flag, "-")) {
flag_end = true;
}
else {
FATAL(USAGE_STRING, argv[0]);
}
} else {
path = argv[i];
break;
}
}
traverse_path(path, 1);
}

90
lab6/.execme Executable file
View File

@ -0,0 +1,90 @@
#!/usr/bin/env bash
set -euo pipefail
IFS=$'\n\t'
function assert() {
local checksum_prg="./lab6_check-$1.c_run"
local input=$2
local expected=$3
local got
echo "Testing $1 on string \`"$input"'..."
got=$($checksum_prg -q <(printf "%s" "$input"))
if diff -q <(echo "$got") <(echo "$expected") > /dev/null; then
echo "OK!"
else
echo "Assertion failed!"
echo "Expected: $expected"
echo "Got: $got"
fi
}
function run_bench() {
local checksum_prg="./lab6_check-$1.c_run"
echo "Benchmarking $1 with file $2..."
$checksum_prg -b "$2"
}
pushd "$1" > /dev/null
# Checked with https://www.scadacore.com/tools/programming-calculators/online-checksum-calculator/
assert xor "123456789" "0x31"
assert xor "444444444444444444444444444" "0x34"
assert xor "4444444444444444444444444444" "0x00"
assert xor "444444444444444444444444444\n" "0x06"
assert xor "Test string" "0x03"
assert xor "Try not to laugh1!%&<>z" "0x0d"
# Checked with https://gchq.github.io/CyberChef/#recipe=Fletcher-16_Checksum()
assert fletcher "44" "0x9c68"
assert fletcher "Test string" "0x6e5b"
assert fletcher "4444444444444444444444444444" "0xcab5"
assert fletcher "444444444444444444444444444\n" "0x3f4c"
assert fletcher "Try not to laugh1!%&<>z" "0x137c"
assert fletcher "123456789" "0x1ede"
# Checked with https://emn178.github.io/online-tools/crc16.html
assert crc "123456789" "0xbb3d"
assert crc "4444444444444444444444444444" "0x5d9d"
assert crc "Test string" "0x5e4c"
assert crc "Try not to laugh1!%&<>z" "0x6579"
assert crc "tEsTTa" "0x0763"
echo "Allocating 512B file with random data..."
head -c 512 </dev/urandom >smallfile.bin
run_bench xor smallfile.bin
run_bench fletcher smallfile.bin
run_bench crc smallfile.bin
rm smallfile.bin
echo "Allocating 64KB file with random data..."
head -c 64K </dev/urandom >midfile.bin
run_bench xor midfile.bin
run_bench fletcher midfile.bin
run_bench crc midfile.bin
rm midfile.bin
echo "Allocating 128MB file with random data..."
head -c 128M </dev/urandom >bigfile.bin
run_bench xor bigfile.bin
run_bench fletcher bigfile.bin
run_bench crc bigfile.bin
rm bigfile.bin
echo "Create & check checksum..."
head -c 16385 <(yes 4) > testfile.bin
head -c 32766 <(yes KRIPER2004) >> testfile.bin
./lab6_create-csum.c_run testfile.bin testfile.bin.crc8maxim
echo "This should be OK..."
./lab6_check-csum.c_run testfile.bin testfile.bin.crc8maxim
echo "Spoiling the file..."
sed 's/4KRIP/HELLO/' testfile.bin > testfile_broken.bin
echo "This should NOT be OK..."
./lab6_check-csum.c_run testfile_broken.bin testfile.bin.crc8maxim
popd > /dev/null

38
lab6/CMakeLists.txt Normal file
View File

@ -0,0 +1,38 @@
cmake_minimum_required(VERSION 3.16)
set(CMAKE_C_STANDARD 11)
# Lab name
set(LAB_NAME "lab6")
# Lab tasks
list(APPEND SOURCE_FILES
check-xor.c
check-fletcher.c
check-crc.c
create-csum.c
check-csum.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 ()

59
lab6/README.md Normal file
View File

@ -0,0 +1,59 @@
# Лабораторная работа №6
Входные данные тестов находятся в файле [.execme](.execme), там же находятся и сами тесты.
## Задание 1
Был реализован алгоритм вычисления контрольной суммы с использованием XOR.
Тестовые данные были проверены онлайн-калькулятором
https://www.scadacore.com/tools/programming-calculators/online-checksum-calculator/
## Задание 2
Был реализован алгоритм вычисления контрольной суммы Флетчера.
Тестовые данные были проверены онлайн-калькулятором
https://gchq.github.io/CyberChef/#recipe=Fletcher-16_Checksum()
## Задание 3
Был реализован быстрый алгоритм вычисления контрольной суммы CRC-16-IBM (он же CRC-16).
Тестовые данные были проверены онлайн-калькулятором
https://emn178.github.io/online-tools/crc16.html
## Задание 4
Для бенчмаркинга алгоритмов в программах был реализован ключ `-b`.
Результаты можно увидеть ниже:
```text
Allocating 512B file with random data...
XOR execution time: 0.000018s
Fletcher execution time: 0.000017s
CRC execution time: 0.000015s
Allocating 64KB file with random data...
XOR execution time: 0.000621s
Fletcher execution time: 0.001165s
CRC execution time: 0.000962s
Allocating 128MB file with random data...
XOR execution time: 0.876024s
Fletcher execution time: 1.921205s
CRC execution time: 1.235906s
```
Из этого можно сделать вывод, что наилучший из представленных алгоритм вычисления
контрольной суммы -- CRC-16. XOR-сумма имеет слишком много коллизий, а также нечувствительна
к перестановкам байтов, хотя она является наиболее быстрой. CRC-16 на больших файлах оказывается
ощутимо производительнее суммы Флетчера, хотя и требует на 256 байт больше памяти.
## Задание 5
В данном задании был использован алгоритм CRC-8-Maxim/Dallas для вычисления контрольных сумм
4Кб-блоков в файле, а также их сверки.
В тестах проверяется адекватность программ (создание контрольной суммы с последующей сверкой),
а так же поведение при повреждении файла.

109
lab6/check-crc.c Normal file
View File

@ -0,0 +1,109 @@
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/time.h>
#define USAGE_STRING "Usage: %s [-q] [-b] [--] [file [file...]]\n"
#define MAX(a,b) ((a)>(b)?(a):(b))
#define STR_EQ(a, b) (!strcmp((a),(b)))
#define FATAL(fmt, ...) do {\
fprintf(stderr,fmt,##__VA_ARGS__);\
exit(1);\
} while (0)
#define WARNING(fmt, ...) fprintf(stderr,fmt,##__VA_ARGS__)
static const uint16_t crc16_tab[256] = {
0x0000, 0xc0c1, 0xc181, 0x0140, 0xc301, 0x03c0, 0x0280, 0xc241,
0xc601, 0x06c0, 0x0780, 0xc741, 0x0500, 0xc5c1, 0xc481, 0x0440,
0xcc01, 0x0cc0, 0x0d80, 0xcd41, 0x0f00, 0xcfc1, 0xce81, 0x0e40,
0x0a00, 0xcac1, 0xcb81, 0x0b40, 0xc901, 0x09c0, 0x0880, 0xc841,
0xd801, 0x18c0, 0x1980, 0xd941, 0x1b00, 0xdbc1, 0xda81, 0x1a40,
0x1e00, 0xdec1, 0xdf81, 0x1f40, 0xdd01, 0x1dc0, 0x1c80, 0xdc41,
0x1400, 0xd4c1, 0xd581, 0x1540, 0xd701, 0x17c0, 0x1680, 0xd641,
0xd201, 0x12c0, 0x1380, 0xd341, 0x1100, 0xd1c1, 0xd081, 0x1040,
0xf001, 0x30c0, 0x3180, 0xf141, 0x3300, 0xf3c1, 0xf281, 0x3240,
0x3600, 0xf6c1, 0xf781, 0x3740, 0xf501, 0x35c0, 0x3480, 0xf441,
0x3c00, 0xfcc1, 0xfd81, 0x3d40, 0xff01, 0x3fc0, 0x3e80, 0xfe41,
0xfa01, 0x3ac0, 0x3b80, 0xfb41, 0x3900, 0xf9c1, 0xf881, 0x3840,
0x2800, 0xe8c1, 0xe981, 0x2940, 0xeb01, 0x2bc0, 0x2a80, 0xea41,
0xee01, 0x2ec0, 0x2f80, 0xef41, 0x2d00, 0xedc1, 0xec81, 0x2c40,
0xe401, 0x24c0, 0x2580, 0xe541, 0x2700, 0xe7c1, 0xe681, 0x2640,
0x2200, 0xe2c1, 0xe381, 0x2340, 0xe101, 0x21c0, 0x2080, 0xe041,
0xa001, 0x60c0, 0x6180, 0xa141, 0x6300, 0xa3c1, 0xa281, 0x6240,
0x6600, 0xa6c1, 0xa781, 0x6740, 0xa501, 0x65c0, 0x6480, 0xa441,
0x6c00, 0xacc1, 0xad81, 0x6d40, 0xaf01, 0x6fc0, 0x6e80, 0xae41,
0xaa01, 0x6ac0, 0x6b80, 0xab41, 0x6900, 0xa9c1, 0xa881, 0x6840,
0x7800, 0xb8c1, 0xb981, 0x7940, 0xbb01, 0x7bc0, 0x7a80, 0xba41,
0xbe01, 0x7ec0, 0x7f80, 0xbf41, 0x7d00, 0xbdc1, 0xbc81, 0x7c40,
0xb401, 0x74c0, 0x7580, 0xb541, 0x7700, 0xb7c1, 0xb681, 0x7640,
0x7200, 0xb2c1, 0xb381, 0x7340, 0xb101, 0x71c0, 0x7080, 0xb041,
0x5000, 0x90c1, 0x9181, 0x5140, 0x9301, 0x53c0, 0x5280, 0x9241,
0x9601, 0x56c0, 0x5780, 0x9741, 0x5500, 0x95c1, 0x9481, 0x5440,
0x9c01, 0x5cc0, 0x5d80, 0x9d41, 0x5f00, 0x9fc1, 0x9e81, 0x5e40,
0x5a00, 0x9ac1, 0x9b81, 0x5b40, 0x9901, 0x59c0, 0x5880, 0x9841,
0x8801, 0x48c0, 0x4980, 0x8941, 0x4b00, 0x8bc1, 0x8a81, 0x4a40,
0x4e00, 0x8ec1, 0x8f81, 0x4f40, 0x8d01, 0x4dc0, 0x4c80, 0x8c41,
0x4400, 0x84c1, 0x8581, 0x4540, 0x8701, 0x47c0, 0x4680, 0x8641,
0x8201, 0x42c0, 0x4380, 0x8341, 0x4100, 0x81c1, 0x8081, 0x4040
};
bool flag_end = false;
bool quiet_mode = false;
bool benchmark = false;
int main(int argc, char *argv[]) {
if (argc == 1) {
FATAL(USAGE_STRING, argv[0]);
}
for (int i = 1; i < argc; ++i) {
if (!flag_end) {
if (argv[i][0] != '-') {
flag_end = true;
}
else if (STR_EQ(argv[i], "--")) {
flag_end = true;
continue;
} else {
switch (argv[i][1]) {
case 'q':
quiet_mode = true;
break;
case 'b':
benchmark = true;
break;
default:
FATAL(USAGE_STRING, argv[0]);
}
continue;
}
}
FILE *f = fopen(argv[i], "rb");
if (f == NULL) {
perror("fopen()");
FATAL("Could not open %s\n", argv[i]);
}
struct timeval t0, t1;
int c;
uint16_t s = 0;
gettimeofday(&t0, NULL);
while ((c = fgetc(f)) != EOF) {
s = crc16_tab[(s ^ (uint8_t) c) & 0xFF] ^ (s >> 8);
}
gettimeofday(&t1, NULL);
if (quiet_mode) {
printf("0x%04x\n", s);
} else {
printf("%s: checksum 0x%04x\n", argv[i], s);
}
if (benchmark) {
struct timeval result;
timersub(&t1, &t0, &result);
printf("CRC execution time: %ld.%06lds\n", result.tv_sec, result.tv_usec);
}
fclose(f);
}
}

68
lab6/check-csum.c Normal file
View File

@ -0,0 +1,68 @@
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "crc8.h"
#define USAGE_STRING "Usage: %s <input_file> <checksum_file>\n"
#define MAX(a,b) ((a)>(b)?(a):(b))
#define STR_EQ(a, b) (!strcmp((a),(b)))
#define WARNING(fmt, ...) fprintf(stderr,fmt,##__VA_ARGS__)
#define FATAL(fmt, ...) do {\
WARNING(fmt,##__VA_ARGS__);\
exit(1);\
} while (0)
int main(int argc, char *argv[]) {
if (argc != 3) {
FATAL(USAGE_STRING, argv[0]);
}
FILE *f = fopen(argv[1], "rb");
if (f == NULL) {
perror("fopen()");
FATAL("Could not open %s\n", argv[1]);
}
FILE *csfile = fopen(argv[2], "rb");
if (csfile == NULL) {
perror("fopen()");
FATAL("Could not open %s\n", argv[2]);
}
int rsize = 0;
uint8_t buf[4096] = {0};
int blocks_read = 0;
uint8_t s;
uint8_t block_cs;
while ((rsize = fread(buf, 1, 4096, f)) == 4096) {
s = 0;
for (int i = 0; i < 4096; ++i) {
CRC8_MAXIM_ITER(s, buf[i]);
}
if (!fread(&block_cs, 1, 1, csfile)) {
FATAL("Unexpected end of checksum file\n");
}
if (s != block_cs) {
WARNING("Checksum mismatch at 4K-block #%d: got 0x%02x, expected 0x%02x\n", blocks_read, s, block_cs);
}
++blocks_read;
}
if (rsize > 0) {
s = 0;
for (int i = 0; i < rsize; ++i) {
CRC8_MAXIM_ITER(s, buf[i]);
}
if (!fread(&block_cs, 1, 1, csfile)) {
FATAL("Unexpected end of checksum file\n");
}
if (s != block_cs) {
WARNING("Checksum mismatch at 4K-block #%d: got 0x%02x, expected 0x%02x\n", blocks_read, s, block_cs);
}
}
fclose(f);
fclose(csfile);
}

77
lab6/check-fletcher.c Normal file
View File

@ -0,0 +1,77 @@
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/time.h>
#define USAGE_STRING "Usage: %s [-q] [-b] [--] [file [file...]]\n"
#define MAX(a,b) ((a)>(b)?(a):(b))
#define STR_EQ(a, b) (!strcmp((a),(b)))
#define FATAL(fmt, ...) do {\
fprintf(stderr,fmt,##__VA_ARGS__);\
exit(1);\
} while (0)
#define WARNING(fmt, ...) fprintf(stderr,fmt,##__VA_ARGS__)
bool flag_end = false;
bool quiet_mode = false;
bool benchmark = false;
int main(int argc, char *argv[]) {
if (argc == 1) {
FATAL(USAGE_STRING, argv[0]);
}
for (int i = 1; i < argc; ++i) {
if (!flag_end) {
if (argv[i][0] != '-') {
flag_end = true;
}
else if (STR_EQ(argv[i], "--")) {
flag_end = true;
continue;
} else {
switch (argv[i][1]) {
case 'q':
quiet_mode = true;
break;
case 'b':
benchmark = true;
break;
default:
FATAL(USAGE_STRING, argv[0]);
}
continue;
}
}
FILE *f = fopen(argv[i], "rb");
if (f == NULL) {
perror("fopen()");
FATAL("Could not open %s\n", argv[i]);
}
struct timeval t0, t1;
int c;
uint16_t s1 = 0, s2 = 0, s;
gettimeofday(&t0, NULL);
while ((c = fgetc(f)) != EOF) {
s1 = (s1 + (uint8_t) c) % 255;
s2 = (s2 + s1) % 255;
}
s = (s2 << 8) | s1;
gettimeofday(&t1, NULL);
if (quiet_mode) {
printf("0x%04x\n", s);
} else {
printf("%s: checksum 0x%04x\n", argv[i], s);
}
if (benchmark) {
struct timeval result;
timersub(&t1, &t0, &result);
printf("Fletcher execution time: %ld.%06lds\n", result.tv_sec, result.tv_usec);
}
fclose(f);
}
}

74
lab6/check-xor.c Normal file
View File

@ -0,0 +1,74 @@
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/time.h>
#define USAGE_STRING "Usage: %s [-q] [-b] [--] [file [file...]]\n"
#define MAX(a,b) ((a)>(b)?(a):(b))
#define STR_EQ(a, b) (!strcmp((a),(b)))
#define FATAL(fmt, ...) do {\
fprintf(stderr,fmt,##__VA_ARGS__);\
exit(1);\
} while (0)
#define WARNING(fmt, ...) fprintf(stderr,fmt,##__VA_ARGS__)
bool flag_end = false;
bool quiet_mode = false;
bool benchmark = false;
int main(int argc, char *argv[]) {
if (argc == 1) {
FATAL(USAGE_STRING, argv[0]);
}
for (int i = 1; i < argc; ++i) {
if (!flag_end) {
if (argv[i][0] != '-') {
flag_end = true;
}
else if (STR_EQ(argv[i], "--")) {
flag_end = true;
continue;
} else {
switch (argv[i][1]) {
case 'q':
quiet_mode = true;
break;
case 'b':
benchmark = true;
break;
default:
FATAL(USAGE_STRING, argv[0]);
}
continue;
}
}
FILE *f = fopen(argv[i], "rb");
if (f == NULL) {
perror("fopen()");
FATAL("Could not open %s\n", argv[i]);
}
struct timeval t0, t1;
int c;
uint8_t s = 0;
gettimeofday(&t0, NULL);
while ((c = fgetc(f)) != EOF) {
s ^= (uint8_t) c;
}
gettimeofday(&t1, NULL);
if (quiet_mode) {
printf("0x%02x\n", s);
} else {
printf("%s: checksum 0x%02x\n", argv[i], s);
}
if (benchmark) {
struct timeval result;
timersub(&t1, &t0, &result);
printf("XOR execution time: %ld.%06lds\n", result.tv_sec, result.tv_usec);
}
fclose(f);
}
}

42
lab6/crc8.h Normal file
View File

@ -0,0 +1,42 @@
#pragma once
#include <stdint.h>
static const uint8_t crc8_maxim_table[256] = {
0x00, 0x31, 0x62, 0x53, 0xC4, 0xF5, 0xA6, 0x97,
0xB9, 0x88, 0xDB, 0xEA, 0x7D, 0x4C, 0x1F, 0x2E,
0x43, 0x72, 0x21, 0x10, 0x87, 0xB6, 0xE5, 0xD4,
0xFA, 0xCB, 0x98, 0xA9, 0x3E, 0x0F, 0x5C, 0x6D,
0x86, 0xB7, 0xE4, 0xD5, 0x42, 0x73, 0x20, 0x11,
0x3F, 0x0E, 0x5D, 0x6C, 0xFB, 0xCA, 0x99, 0xA8,
0xC5, 0xF4, 0xA7, 0x96, 0x01, 0x30, 0x63, 0x52,
0x7C, 0x4D, 0x1E, 0x2F, 0xB8, 0x89, 0xDA, 0xEB,
0x3D, 0x0C, 0x5F, 0x6E, 0xF9, 0xC8, 0x9B, 0xAA,
0x84, 0xB5, 0xE6, 0xD7, 0x40, 0x71, 0x22, 0x13,
0x7E, 0x4F, 0x1C, 0x2D, 0xBA, 0x8B, 0xD8, 0xE9,
0xC7, 0xF6, 0xA5, 0x94, 0x03, 0x32, 0x61, 0x50,
0xBB, 0x8A, 0xD9, 0xE8, 0x7F, 0x4E, 0x1D, 0x2C,
0x02, 0x33, 0x60, 0x51, 0xC6, 0xF7, 0xA4, 0x95,
0xF8, 0xC9, 0x9A, 0xAB, 0x3C, 0x0D, 0x5E, 0x6F,
0x41, 0x70, 0x23, 0x12, 0x85, 0xB4, 0xE7, 0xD6,
0x7A, 0x4B, 0x18, 0x29, 0xBE, 0x8F, 0xDC, 0xED,
0xC3, 0xF2, 0xA1, 0x90, 0x07, 0x36, 0x65, 0x54,
0x39, 0x08, 0x5B, 0x6A, 0xFD, 0xCC, 0x9F, 0xAE,
0x80, 0xB1, 0xE2, 0xD3, 0x44, 0x75, 0x26, 0x17,
0xFC, 0xCD, 0x9E, 0xAF, 0x38, 0x09, 0x5A, 0x6B,
0x45, 0x74, 0x27, 0x16, 0x81, 0xB0, 0xE3, 0xD2,
0xBF, 0x8E, 0xDD, 0xEC, 0x7B, 0x4A, 0x19, 0x28,
0x06, 0x37, 0x64, 0x55, 0xC2, 0xF3, 0xA0, 0x91,
0x47, 0x76, 0x25, 0x14, 0x83, 0xB2, 0xE1, 0xD0,
0xFE, 0xCF, 0x9C, 0xAD, 0x3A, 0x0B, 0x58, 0x69,
0x04, 0x35, 0x66, 0x57, 0xC0, 0xF1, 0xA2, 0x93,
0xBD, 0x8C, 0xDF, 0xEE, 0x79, 0x48, 0x1B, 0x2A,
0xC1, 0xF0, 0xA3, 0x92, 0x05, 0x34, 0x67, 0x56,
0x78, 0x49, 0x1A, 0x2B, 0xBC, 0x8D, 0xDE, 0xEF,
0x82, 0xB3, 0xE0, 0xD1, 0x46, 0x77, 0x24, 0x15,
0x3B, 0x0A, 0x59, 0x68, 0xFF, 0xCE, 0x9D, 0xAC
};
#define CRC8_MAXIM_ITER(crc, chr) \
do { \
(crc) = crc8_maxim_table[(crc) ^ (chr)]; \
} while (0)

55
lab6/create-csum.c Normal file
View File

@ -0,0 +1,55 @@
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "crc8.h"
#define USAGE_STRING "Usage: %s <input_file> <output_file>\n"
#define MAX(a,b) ((a)>(b)?(a):(b))
#define STR_EQ(a, b) (!strcmp((a),(b)))
#define FATAL(fmt, ...) do {\
fprintf(stderr,fmt,##__VA_ARGS__);\
exit(1);\
} while (0)
#define WARNING(fmt, ...) fprintf(stderr,fmt,##__VA_ARGS__)
int main(int argc, char *argv[]) {
if (argc != 3) {
FATAL(USAGE_STRING, argv[0]);
}
FILE *f = fopen(argv[1], "rb");
if (f == NULL) {
perror("fopen()");
FATAL("Could not open %s\n", argv[1]);
}
FILE *fw = fopen(argv[2], "wb");
if (fw == NULL) {
perror("fopen()");
FATAL("Could not open %s\n", argv[2]);
}
int rsize = 0;
uint8_t buf[4096] = {0};
uint8_t s;
while ((rsize = fread(buf, 1, 4096, f)) == 4096) {
s = 0;
for (int i = 0; i < 4096; ++i) {
CRC8_MAXIM_ITER(s, buf[i]);
}
fwrite(&s, 1, 1, fw);
}
if (rsize > 0) {
s = 0;
for (int i = 0; i < rsize; ++i) {
CRC8_MAXIM_ITER(s, buf[i]);
}
fwrite(&s, 1, 1, fw);
}
fclose(f);
fclose(fw);
}

7
lab9/.execme Executable file
View File

@ -0,0 +1,7 @@
#!/usr/bin/env bash
set -euo pipefail
IFS=$'\n\t'
echo "TODO!"
exit 1

57
lab9/CMakeLists.txt Normal file
View File

@ -0,0 +1,57 @@
cmake_minimum_required(VERSION 3.16)
set(CMAKE_C_STANDARD 11)
# Lab name
set(LAB_NAME "lab9")
# Lab tasks
#list(APPEND SOURCE_FILES
# task1.c
# task2.c
# mytail.c
# mystat.c
# myls.c
# task6.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 ()
find_package (Threads)
add_executable(${LAB_NAME}_main-race_run main-race.c mythread.h)
add_executable(${LAB_NAME}_main-deadlock_run main-deadlock.c mythread.h)
add_executable(${LAB_NAME}_main-deadlock-global_run main-deadlock-global.c mythread.h)
add_executable(${LAB_NAME}_main-signal_run main-signal.c mythread.h)
add_executable(${LAB_NAME}_main-signal-cv_run main-signal-cv.c mythread.h)
target_link_libraries (${LAB_NAME}_main-race_run ${CMAKE_THREAD_LIBS_INIT})
target_link_libraries (${LAB_NAME}_main-deadlock_run ${CMAKE_THREAD_LIBS_INIT})
target_link_libraries (${LAB_NAME}_main-deadlock-global_run ${CMAKE_THREAD_LIBS_INIT})
target_link_libraries (${LAB_NAME}_main-signal_run ${CMAKE_THREAD_LIBS_INIT})
target_link_libraries (${LAB_NAME}_main-signal-cv_run ${CMAKE_THREAD_LIBS_INIT})
add_dependencies("${LAB_NAME}" ${LAB_NAME}_main-race_run)
add_dependencies("${LAB_NAME}" ${LAB_NAME}_main-deadlock_run)
add_dependencies("${LAB_NAME}" ${LAB_NAME}_main-deadlock-global_run)
add_dependencies("${LAB_NAME}" ${LAB_NAME}_main-signal_run)
add_dependencies("${LAB_NAME}" ${LAB_NAME}_main-signal-cv_run)
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 ()

231
lab9/README.md Normal file
View File

@ -0,0 +1,231 @@
# Лабораторная работа №9
## Задание 2
Команда запуска:
`valgrind --tool=helgrind ./lab9_main-race_run`
Результат:
```text
...
==4862== Possible data race during read of size 4 at 0x30A014 by thread #1
==4862== Locks held: none
==4862== at 0x108D27: main (main-race.c:15)
==4862==
==4862== This conflicts with a previous write of size 4 by thread #2
==4862== Locks held: none
==4862== at 0x108CDF: worker (main-race.c:8)
==4862== by 0x4C36C26: ??? (in /usr/lib/valgrind/vgpreload_helgrind-amd64-linux.so)
==4862== by 0x4E496DA: start_thread (pthread_create.c:463)
==4862== by 0x518288E: clone (clone.S:95)
==4862== Address 0x30a014 is 0 bytes inside data symbol "balance"
...
```
helgrind сообщает, что возможно использование переменной balance в двух потоках параллельно.
### Решение проблемы:
Блокировать незащищенный процесс.
Заменить:
```c
balance++;
```
на:
```c
Pthread_mutex_lock(&m); // блокировка мьютекса
balance++;
Pthread_mutex_unlock(&m); // разблокировка
```
## Задание 3
Команда запуска:
`valgrind --tool=helgrind ./lab9_main-deadlock_run`
Результат:
```text
...
==6272== Thread #3: lock order "0x30A040 before 0x30A080" violated
==6272==
==6272== Observed (incorrect) order is: acquisition of lock at 0x30A080
==6272== at 0x4C3403C: ??? (in /usr/lib/valgrind/vgpreload_helgrind-amd64-linux.so)
==6272== by 0x108AD7: Pthread_mutex_lock (mythreads.h:23)
==6272== by 0x108D06: worker (main-deadlock.c:13)
==6272== by 0x4C36C26: ??? (in /usr/lib/valgrind/vgpreload_helgrind-amd64-linux.so)
==6272== by 0x4E496DA: start_thread (pthread_create.c:463)
==6272== by 0x518288E: clone (clone.S:95)
==6272==
==6272== followed by a later acquisition of lock at 0x30A040
==6272== at 0x4C3403C: ??? (in /usr/lib/valgrind/vgpreload_helgrind-amd64-linux.so)
==6272== by 0x108AD7: Pthread_mutex_lock (mythreads.h:23)
==6272== by 0x108D12: worker (main-deadlock.c:14)
==6272== by 0x4C36C26: ??? (in /usr/lib/valgrind/vgpreload_helgrind-amd64-linux.so)
==6272== by 0x4E496DA: start_thread (pthread_create.c:463)
==6272== by 0x518288E: clone (clone.S:95)
==6272==
==6272== Required order was established by acquisition of lock at 0x30A040
==6272== at 0x4C3403C: ??? (in /usr/lib/valgrind/vgpreload_helgrind-amd64-linux.so)
==6272== by 0x108AD7: Pthread_mutex_lock (mythreads.h:23)
==6272== by 0x108CEC: worker (main-deadlock.c:10)
==6272== by 0x4C36C26: ??? (in /usr/lib/valgrind/vgpreload_helgrind-amd64-linux.so)
==6272== by 0x4E496DA: start_thread (pthread_create.c:463)
==6272== by 0x518288E: clone (clone.S:95)
==6272==
==6272== followed by a later acquisition of lock at 0x30A080
==6272== at 0x4C3403C: ??? (in /usr/lib/valgrind/vgpreload_helgrind-amd64-linux.so)
==6272== by 0x108AD7: Pthread_mutex_lock (mythreads.h:23)
==6272== by 0x108CF8: worker (main-deadlock.c:11)
==6272== by 0x4C36C26: ??? (in /usr/lib/valgrind/vgpreload_helgrind-amd64-linux.so)
==6272== by 0x4E496DA: start_thread (pthread_create.c:463)
==6272== by 0x518288E: clone (clone.S:95)
==6272==
==6272== Lock at 0x30A040 was first observed
==6272== at 0x4C3403C: ??? (in /usr/lib/valgrind/vgpreload_helgrind-amd64-linux.so)
==6272== by 0x108AD7: Pthread_mutex_lock (mythreads.h:23)
==6272== by 0x108CEC: worker (main-deadlock.c:10)
==6272== by 0x4C36C26: ??? (in /usr/lib/valgrind/vgpreload_helgrind-amd64-linux.so)
==6272== by 0x4E496DA: start_thread (pthread_create.c:463)
==6272== by 0x518288E: clone (clone.S:95)
==6272== Address 0x30a040 is 0 bytes inside data symbol "m1"
==6272==
==6272== Lock at 0x30A080 was first observed
==6272== at 0x4C3403C: ??? (in /usr/lib/valgrind/vgpreload_helgrind-amd64-linux.so)
==6272== by 0x108AD7: Pthread_mutex_lock (mythreads.h:23)
==6272== by 0x108CF8: worker (main-deadlock.c:11)
==6272== by 0x4C36C26: ??? (in /usr/lib/valgrind/vgpreload_helgrind-amd64-linux.so)
==6272== by 0x4E496DA: start_thread (pthread_create.c:463)
==6272== by 0x518288E: clone (clone.S:95)
==6272== Address 0x30a080 is 0 bytes inside data symbol "m2"
...
```
helgrind сообщает, что блокировка мьютексов происходит в неправильном порядке.
## Задание 4
Команда запуска:
`valgrind --tool=helgrind ./lab9_main-deadlock-global_run`
Результат:
```text
...
==7133== Thread #3: lock order "0x30A080 before 0x30A0C0" violated
==7133==
==7133== Observed (incorrect) order is: acquisition of lock at 0x30A0C0
==7133== at 0x4C3403C: ??? (in /usr/lib/valgrind/vgpreload_helgrind-amd64-linux.so)
==7133== by 0x108AD7: Pthread_mutex_lock (mythreads.h:23)
==7133== by 0x108D12: worker (main-deadlock-global.c:15)
==7133== by 0x4C36C26: ??? (in /usr/lib/valgrind/vgpreload_helgrind-amd64-linux.so)
==7133== by 0x4E496DA: start_thread (pthread_create.c:463)
==7133== by 0x518288E: clone (clone.S:95)
==7133==
==7133== followed by a later acquisition of lock at 0x30A080
==7133== at 0x4C3403C: ??? (in /usr/lib/valgrind/vgpreload_helgrind-amd64-linux.so)
==7133== by 0x108AD7: Pthread_mutex_lock (mythreads.h:23)
==7133== by 0x108D1E: worker (main-deadlock-global.c:16)
==7133== by 0x4C36C26: ??? (in /usr/lib/valgrind/vgpreload_helgrind-amd64-linux.so)
==7133== by 0x4E496DA: start_thread (pthread_create.c:463)
==7133== by 0x518288E: clone (clone.S:95)
==7133==
==7133== Required order was established by acquisition of lock at 0x30A080
==7133== at 0x4C3403C: ??? (in /usr/lib/valgrind/vgpreload_helgrind-amd64-linux.so)
==7133== by 0x108AD7: Pthread_mutex_lock (mythreads.h:23)
==7133== by 0x108CF8: worker (main-deadlock-global.c:12)
==7133== by 0x4C36C26: ??? (in /usr/lib/valgrind/vgpreload_helgrind-amd64-linux.so)
==7133== by 0x4E496DA: start_thread (pthread_create.c:463)
==7133== by 0x518288E: clone (clone.S:95)
==7133==
==7133== followed by a later acquisition of lock at 0x30A0C0
==7133== at 0x4C3403C: ??? (in /usr/lib/valgrind/vgpreload_helgrind-amd64-linux.so)
==7133== by 0x108AD7: Pthread_mutex_lock (mythreads.h:23)
==7133== by 0x108D04: worker (main-deadlock-global.c:13)
==7133== by 0x4C36C26: ??? (in /usr/lib/valgrind/vgpreload_helgrind-amd64-linux.so)
==7133== by 0x4E496DA: start_thread (pthread_create.c:463)
==7133== by 0x518288E: clone (clone.S:95)
==7133==
==7133== Lock at 0x30A080 was first observed
==7133== at 0x4C3403C: ??? (in /usr/lib/valgrind/vgpreload_helgrind-amd64-linux.so)
==7133== by 0x108AD7: Pthread_mutex_lock (mythreads.h:23)
==7133== by 0x108CF8: worker (main-deadlock-global.c:12)
==7133== by 0x4C36C26: ??? (in /usr/lib/valgrind/vgpreload_helgrind-amd64-linux.so)
==7133== by 0x4E496DA: start_thread (pthread_create.c:463)
==7133== by 0x518288E: clone (clone.S:95)
==7133== Address 0x30a080 is 0 bytes inside data symbol "m1"
==7133==
==7133== Lock at 0x30A0C0 was first observed
==7133== at 0x4C3403C: ??? (in /usr/lib/valgrind/vgpreload_helgrind-amd64-linux.so)
==7133== by 0x108AD7: Pthread_mutex_lock (mythreads.h:23)
==7133== by 0x108D04: worker (main-deadlock-global.c:13)
==7133== by 0x4C36C26: ??? (in /usr/lib/valgrind/vgpreload_helgrind-amd64-linux.so)
==7133== by 0x4E496DA: start_thread (pthread_create.c:463)
==7133== by 0x518288E: clone (clone.S:95)
==7133== Address 0x30a0c0 is 0 bytes inside data symbol "m2"
...
```
helgrind сообщает об аналогичной ошибке 3 заданию, тогда как она отсутствует, так как дополнительный мьютекс не допускает такую ситуацию.
## Задание 5
Команда запуска:
`valgrind --tool=helgrind ./lab9_main-signal_run`
Результат:
```text
...
==7499== Possible data race during write of size 4 at 0x30A014 by thread #2
==7499== Locks held: none
==7499== at 0x108D36: worker (main-signal.c:9)
==7499== by 0x4C36C26: ??? (in /usr/lib/valgrind/vgpreload_helgrind-amd64-linux.so)
==7499== by 0x4E496DA: start_thread (pthread_create.c:463)
==7499== by 0x518288E: clone (clone.S:95)
==7499==
==7499== This conflicts with a previous read of size 4 by thread #1
==7499== Locks held: none
==7499== at 0x108D83: main (main-signal.c:16)
==7499== Address 0x30a014 is 0 bytes inside data symbol "done"
...
```
helgrind сообщает о возможном одновременном использовании переменной done.
Такой метод ожидания потока не является эффективным, так как:
- может наступить чтение в момент записи
- требует дополнительных затрат ожидающего потока.
Наиболее эффективно было бы использовать мьютексы.
## Задание 6
Команда запуска:
`valgrind --tool=helgrind ./lab9_main-signal-cv_run`
Результат:
```text
...
==9132== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 7 from 7)
...
```
helgrind не удалось обнаружить ошибок.
По сравнению с прошлой программой данная более эффективна, так как она не производит постоянный опрос,
а ожидает возвращение cond, и более безопасна для данных.

View File

@ -0,0 +1,31 @@
#include <stdio.h>
#include "mythread.h"
pthread_mutex_t g = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t m1 = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t m2 = PTHREAD_MUTEX_INITIALIZER;
void* worker(void* arg) {
Pthread_mutex_lock(&g);
if ((long long) arg == 0) {
Pthread_mutex_lock(&m1);
Pthread_mutex_lock(&m2);
} else {
Pthread_mutex_lock(&m2);
Pthread_mutex_lock(&m1);
}
Pthread_mutex_unlock(&m1);
Pthread_mutex_unlock(&m2);
Pthread_mutex_unlock(&g);
return NULL;
}
int main(int argc, char *argv[]) {
pthread_t p1, p2;
Pthread_create(&p1, NULL, worker, (void *) (long long) 0);
Pthread_create(&p2, NULL, worker, (void *) (long long) 1);
Pthread_join(p1, NULL);
Pthread_join(p2, NULL);
return 0;
}

28
lab9/main-deadlock.c Normal file
View File

@ -0,0 +1,28 @@
#include <stdio.h>
#include "mythread.h"
pthread_mutex_t m1 = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t m2 = PTHREAD_MUTEX_INITIALIZER;
void* worker(void* arg) {
if ((long long) arg == 0) {
Pthread_mutex_lock(&m1);
Pthread_mutex_lock(&m2);
} else {
Pthread_mutex_lock(&m2);
Pthread_mutex_lock(&m1);
}
Pthread_mutex_unlock(&m1);
Pthread_mutex_unlock(&m2);
return NULL;
}
int main(int argc, char *argv[]) {
pthread_t p1, p2;
Pthread_create(&p1, NULL, worker, (void *) (long long) 0);
Pthread_create(&p2, NULL, worker, (void *) (long long) 1);
Pthread_join(p1, NULL);
Pthread_join(p2, NULL);
return 0;
}

23
lab9/main-race.c Normal file
View File

@ -0,0 +1,23 @@
#include <stdio.h>
#include "mythread.h"
pthread_mutex_t m = PTHREAD_MUTEX_INITIALIZER;
int balance = 0;
void *worker(void *arg) {
Pthread_mutex_lock(&m);
balance++;
Pthread_mutex_unlock(&m);
return NULL;
}
int main(int argc, char *argv[]) {
pthread_t p;
Pthread_create(&p, NULL, worker, NULL);
Pthread_mutex_lock(&m); // блокировка мютекса
balance++;
Pthread_mutex_unlock(&m); // разблокировка
Pthread_join(p, NULL);
return 0;
}

56
lab9/main-signal-cv.c Normal file
View File

@ -0,0 +1,56 @@
#include <stdio.h>
#include "mythread.h"
//
// simple synchronizer: allows one thread to wait for another
// structure "synchronizer_t" has all the needed data
// methods are:
// init (called by one thread)
// wait (to wait for a thread)
// done (to indicate thread is done)
//
typedef struct __synchronizer_t {
pthread_mutex_t lock;
pthread_cond_t cond;
int done;
} synchronizer_t;
synchronizer_t s;
void signal_init(synchronizer_t *s) {
Pthread_mutex_init(&s->lock, NULL);
Pthread_cond_init(&s->cond, NULL);
s->done = 0;
}
void signal_done(synchronizer_t *s) {
Pthread_mutex_lock(&s->lock);
s->done = 1;
Pthread_cond_signal(&s->cond);
Pthread_mutex_unlock(&s->lock);
}
void signal_wait(synchronizer_t *s) {
Pthread_mutex_lock(&s->lock);
while (s->done == 0)
Pthread_cond_wait(&s->cond, &s->lock);
Pthread_mutex_unlock(&s->lock);
}
void* worker(void* arg) {
printf("this should print first\n");
signal_done(&s);
return NULL;
}
int main(int argc, char *argv[]) {
pthread_t p;
signal_init(&s);
Pthread_create(&p, NULL, worker, NULL);
signal_wait(&s);
printf("this should print last\n");
return 0;
}

20
lab9/main-signal.c Normal file
View File

@ -0,0 +1,20 @@
#include <stdio.h>
#include "mythread.h"
int done = 0;
void* worker(void* arg) {
printf("this should print first\n");
done = 1;
return NULL;
}
int main(int argc, char *argv[]) {
pthread_t p;
Pthread_create(&p, NULL, worker, NULL);
while (done == 0)
;
printf("this should print last\n");
return 0;
}

55
lab9/mythread.h Normal file
View File

@ -0,0 +1,55 @@
#include <pthread.h>
#include <assert.h>
#include <stdlib.h>
#include <sys/time.h>
double Time_GetSeconds() {
struct timeval t;
int rc = gettimeofday(&t, NULL);
assert(rc == 0);
return (double) ((double)t.tv_sec + (double)t.tv_usec / 1e6);
}
void Pthread_mutex_init(pthread_mutex_t *mutex,
const pthread_mutexattr_t *attr) {
int rc = pthread_mutex_init(mutex, attr);
assert(rc == 0);
}
void Pthread_mutex_lock(pthread_mutex_t *m) {
int rc = pthread_mutex_lock(m);
assert(rc == 0);
}
void Pthread_mutex_unlock(pthread_mutex_t *m) {
int rc = pthread_mutex_unlock(m);
assert(rc == 0);
}
void Pthread_cond_init(pthread_cond_t *cond,
const pthread_condattr_t *attr) {
int rc = pthread_cond_init(cond, attr);
assert(rc == 0);
}
void Pthread_cond_wait(pthread_cond_t *cond,
pthread_mutex_t *mutex) {
int rc = pthread_cond_wait(cond, mutex);
assert(rc == 0);
}
void Pthread_cond_signal(pthread_cond_t *cond) {
int rc = pthread_cond_signal(cond);
assert(rc == 0);
}
void Pthread_create(pthread_t *thread, const pthread_attr_t *attr,
void *(*start_routine)(void*), void *arg) {
int rc = pthread_create(thread, attr, start_routine, arg);
assert(rc == 0);
}
void Pthread_join(pthread_t thread, void **value_ptr) {
int rc = pthread_join(thread, value_ptr);
assert(rc == 0);
}