mirror of https://github.com/t1meshift/os_labs.git
Compare commits
8 Commits
fca8d3a057
...
ae84f722a8
Author | SHA1 | Date |
---|---|---|
Yury Kurlykov | ae84f722a8 | |
Yury Kurlykov | f6cf3ad3eb | |
Yury Kurlykov | 9be2f2302e | |
Yury Kurlykov | 4ad7a6d3ab | |
Yury Kurlykov | 9e82285e43 | |
Yury Kurlykov | 037a74ed69 | |
Yury Kurlykov | e69a88c336 | |
Yury Kurlykov | ea3de372a4 |
|
@ -16,4 +16,8 @@ endfunction()
|
|||
define_lab(lab2)
|
||||
define_lab(lab3)
|
||||
define_lab(lab4)
|
||||
define_lab(lab5)
|
||||
define_lab(lab6)
|
||||
define_lab(lab7)
|
||||
define_lab(lab9)
|
||||
define_lab(lab10)
|
|
@ -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>
|
||||
```
|
|
@ -0,0 +1,7 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
set -euo pipefail
|
||||
IFS=$'\n\t'
|
||||
|
||||
echo "TODO!"
|
||||
exit 1
|
|
@ -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 ()
|
|
@ -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. зависит от выбранного кванта.
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
|
||||
int main() {
|
||||
if (unlink("test.txt") != 0) {
|
||||
printf("Unlink error.");
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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
|
||||
|
|
@ -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 работает медленнее других в любом случае.
|
|
@ -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.
|
||||
|
||||
|
|
@ -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;
|
||||
}
|
|
@ -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__
|
|
@ -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__
|
|
@ -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"
|
||||
|
|
@ -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"
|
|
@ -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"
|
||||
|
|
@ -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__
|
||||
|
|
@ -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"
|
||||
|
|
@ -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"
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
set -euo pipefail
|
||||
IFS=$'\n\t'
|
||||
|
||||
echo "TODO!"
|
||||
exit 1
|
|
@ -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 ()
|
|
@ -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
|
||||
```
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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
|
|
@ -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 ()
|
|
@ -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Кб-блоков в файле, а также их сверки.
|
||||
|
||||
В тестах проверяется адекватность программ (создание контрольной суммы с последующей сверкой),
|
||||
а так же поведение при повреждении файла.
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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)
|
|
@ -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);
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
set -euo pipefail
|
||||
IFS=$'\n\t'
|
||||
|
||||
echo "TODO!"
|
||||
exit 1
|
|
@ -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 ()
|
|
@ -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, и более безопасна для данных.
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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);
|
||||
}
|
Loading…
Reference in New Issue