mirror of https://github.com/t1meshift/os_labs.git
Compare commits
8 Commits
fca8d3a057
...
ae84f722a8
Author | SHA1 | Date |
---|---|---|
|
ae84f722a8 | |
|
f6cf3ad3eb | |
|
9be2f2302e | |
|
4ad7a6d3ab | |
|
9e82285e43 | |
|
037a74ed69 | |
|
e69a88c336 | |
|
ea3de372a4 |
|
@ -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)
|
|
@ -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;
|
||||
}
|
||||