Compare commits
3 Commits
9fb9f66f27
...
bcc528f060
| Author | SHA1 | Date | |
|---|---|---|---|
| bcc528f060 | |||
| 62049428c0 | |||
| 24939470e5 |
@@ -1,7 +1,8 @@
|
||||
> [!NOTE]
|
||||
> Викладач: Мельникова Р. В.
|
||||
>
|
||||
> Оцінка: -
|
||||
> Оцінка: 93 (90 у напарника)
|
||||
|
||||
> [!TIP]
|
||||
> Виконано для Linux в команді.
|
||||
> Їй не сподобалося друге завдання (LRU для регістрів), не знаю чому, мабуть бо вона сама не знає що це таке 😤
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
> [!NOTE]
|
||||
> Викладач: Мельникова Р. В.
|
||||
>
|
||||
> Оцінка: in progress
|
||||
|
||||
> [!TIP]
|
||||
> Виконано для Linux в команді.
|
||||
@@ -0,0 +1,21 @@
|
||||
title: Керування процесами та потоками
|
||||
subject: ОС
|
||||
doctype: ЛБ
|
||||
worknumber: 7
|
||||
mentors:
|
||||
- name: Мельнікова Р. В.,
|
||||
gender: f,
|
||||
degree: доц. каф. ПІ,
|
||||
edu_program: &EDU ПЗПІ
|
||||
university: ХНУРЕ
|
||||
authors:
|
||||
- name: Ситник Є. С.
|
||||
course: 2
|
||||
edu: *EDU
|
||||
gender: m
|
||||
group: 23-2
|
||||
- name: Малишкін. А. С.
|
||||
course: 2
|
||||
edu: *EDU
|
||||
gender: m
|
||||
group: 23-2
|
||||
|
After Width: | Height: | Size: 43 KiB |
|
After Width: | Height: | Size: 47 KiB |
|
After Width: | Height: | Size: 42 KiB |
|
After Width: | Height: | Size: 150 KiB |
|
After Width: | Height: | Size: 64 KiB |
@@ -0,0 +1,19 @@
|
||||
cc := "clang"
|
||||
cc_flags := "-Wall -Wextra -ggdb"
|
||||
|
||||
@clean:
|
||||
rm -rf build
|
||||
|
||||
@mkdir-build: clean
|
||||
mkdir -p build
|
||||
|
||||
@build-editor: mkdir-build
|
||||
{{cc}} {{cc_flags}} -o build/main editor/main.c
|
||||
{{cc}} {{cc_flags}} -o build/editor editor/editor.c
|
||||
{{cc}} {{cc_flags}} -o build/analizer editor/analizer.c
|
||||
|
||||
@build-threads: mkdir-build
|
||||
{{cc}} {{cc_flags}} -o build/main threads/main.c
|
||||
|
||||
@build-matrix-calc: mkdir-build
|
||||
{{cc}} {{cc_flags}} -o build/main matrix-calc/main.c
|
||||
@@ -0,0 +1,224 @@
|
||||
#define _XOPEN_SOURCE
|
||||
#include <time.h>
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <fts.h>
|
||||
#include <iconv.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "shared.h"
|
||||
|
||||
typedef enum { ENC_UTF8, ENC_UTF16LE, ENC_UTF16BE, ENC_CP1251 } Encoding;
|
||||
|
||||
Encoding get_encoding(char *input, size_t length) {
|
||||
unsigned char *data = (unsigned char *)input;
|
||||
|
||||
if (length >= 2) {
|
||||
if (data[0] == 0xFE && data[1] == 0xFF) { // UTF-16 BE BOM
|
||||
return ENC_UTF16BE;
|
||||
} else if (data[0] == 0xFF && data[1] == 0xFE) { // UTF-16 LE BOM
|
||||
return ENC_UTF16LE;
|
||||
}
|
||||
}
|
||||
|
||||
size_t i = 0;
|
||||
while (i < length) {
|
||||
unsigned char c = data[i];
|
||||
|
||||
if (c < 0x80) { // ASCII
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((c & 0xE0) == 0xC0) { // 2-byte UTF8
|
||||
if (i + 1 >= length || (data[i + 1] & 0xC0) != 0x80 || c < 0xC2)
|
||||
return ENC_CP1251; // overflow
|
||||
i += 2;
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((c & 0xF0) == 0xE0) { // 3-byte UTF8
|
||||
if (i + 2 >= length || (data[i + 1] & 0xC0) != 0x80 ||
|
||||
(data[i + 2] & 0xC0) != 0x80 || (c == 0xE0 && data[i + 1] < 0xA0))
|
||||
return ENC_CP1251; // overflow
|
||||
i += 3;
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((c & 0xF8) == 0xF0) { // 4-byte UTF8
|
||||
if (i + 3 >= length || (data[i + 1] & 0xC0) != 0x80 ||
|
||||
(data[i + 2] & 0xC0) != 0x80 || (data[i + 3] & 0xC0) != 0x80 ||
|
||||
(c == 0xF0 && data[i + 1] < 0x90))
|
||||
return ENC_CP1251; // overflow
|
||||
i += 4;
|
||||
continue;
|
||||
}
|
||||
|
||||
return ENC_CP1251; // Invalid utf-8 byte
|
||||
}
|
||||
|
||||
return ENC_UTF8;
|
||||
}
|
||||
|
||||
char *to_utf8(char *input, size_t length, Encoding encoding) {
|
||||
if (!input || length == 0)
|
||||
return NULL;
|
||||
|
||||
char *encstr;
|
||||
int skip_bom = 0;
|
||||
switch (encoding) {
|
||||
case ENC_CP1251:
|
||||
encstr = "CP1251";
|
||||
break;
|
||||
case ENC_UTF8:
|
||||
encstr = "UTF-8";
|
||||
break;
|
||||
case ENC_UTF16LE:
|
||||
encstr = "UTF-16LE";
|
||||
skip_bom = 2;
|
||||
break;
|
||||
case ENC_UTF16BE:
|
||||
encstr = "UTF-16BE";
|
||||
skip_bom = 2;
|
||||
break;
|
||||
}
|
||||
|
||||
iconv_t cd = iconv_open("UTF-8", encstr);
|
||||
if (cd != (iconv_t)-1) {
|
||||
size_t out_size = length * 4 + 1;
|
||||
char *output = malloc(out_size);
|
||||
if (output) {
|
||||
char *in_ptr = (char *)input + skip_bom;
|
||||
size_t in_left = length - skip_bom;
|
||||
char *out_ptr = output;
|
||||
size_t out_left = out_size - 1;
|
||||
|
||||
if (iconv(cd, &in_ptr, &in_left, &out_ptr, &out_left) != (size_t)-1) {
|
||||
*out_ptr = '\0';
|
||||
iconv_close(cd);
|
||||
return output;
|
||||
}
|
||||
free(output);
|
||||
}
|
||||
iconv_close(cd);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
if (argc > 2) {
|
||||
fprintf(stderr, "Error: too many arguments.\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (argc < 2) {
|
||||
fprintf(stderr, "Error: too few arguments.\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
struct tm tm = {0};
|
||||
if (strptime(argv[1], "%s", &tm) == NULL) {
|
||||
fprintf(stderr, "Error: please provide time in UNIX timestamp format.\n");
|
||||
return 1;
|
||||
}
|
||||
time_t norm_time = mktime(&tm);
|
||||
|
||||
char *paths[] = {CWD, NULL};
|
||||
FTS *fts = fts_open(paths, FTS_NOCHDIR, NULL);
|
||||
if (fts == NULL) {
|
||||
err("Can't initialise FTS", __LINE__ - 1);
|
||||
return 2;
|
||||
}
|
||||
|
||||
char *buf = (char *)malloc(1024 * sizeof(char));
|
||||
|
||||
for (FTSENT *ent = fts_read(fts); ent != NULL; ent = fts_read(fts)) {
|
||||
switch (ent->fts_info) {
|
||||
case FTS_F:
|
||||
if (ent->fts_statp->st_mtime > norm_time) {
|
||||
printf(RED);
|
||||
printf("--------------------------------\n");
|
||||
|
||||
printf(GREEN);
|
||||
printf("File: %s\n", ent->fts_name);
|
||||
|
||||
char mtime[1024] = {0};
|
||||
strftime(mtime, 1024, "%T", localtime(&ent->fts_statp->st_mtime));
|
||||
printf("Modified at: %s\n", mtime);
|
||||
|
||||
printf("Size: %ld\n", ent->fts_statp->st_size);
|
||||
|
||||
FILE *file = fopen(ent->fts_path, "r");
|
||||
if (file == NULL) {
|
||||
err("Can't open the file", __LINE__ - 1);
|
||||
break;
|
||||
}
|
||||
|
||||
size_t bytes_read = fread(buf, 1, 1023, file);
|
||||
if (ferror(file) != 0) {
|
||||
err("Can't read the file", __LINE__ - 2);
|
||||
fclose(file);
|
||||
break;
|
||||
}
|
||||
buf[bytes_read] = '\0';
|
||||
|
||||
Encoding enc = get_encoding(buf, bytes_read);
|
||||
|
||||
printf("Lines length:");
|
||||
size_t lines = 0;
|
||||
size_t line_len = 0;
|
||||
for (size_t i = (enc == 2 || enc == 1) ? 2 : 0; i < bytes_read; i++) {
|
||||
line_len++;
|
||||
|
||||
int newline_detected = 0;
|
||||
switch (enc) {
|
||||
case ENC_UTF16BE:
|
||||
if (i + 1 < bytes_read && buf[i] == '\0' && buf[i + 1] == '\n') {
|
||||
newline_detected = 1;
|
||||
line_len++;
|
||||
i++;
|
||||
}
|
||||
break;
|
||||
case ENC_UTF16LE:
|
||||
if (i + 1 < bytes_read && buf[i] == '\n' && buf[i + 1] == '\0') {
|
||||
newline_detected = 1;
|
||||
line_len++;
|
||||
i++;
|
||||
}
|
||||
break;
|
||||
case ENC_CP1251:
|
||||
case ENC_UTF8:
|
||||
default:
|
||||
if (buf[i] == '\n')
|
||||
newline_detected = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
if (newline_detected) {
|
||||
printf(" %zu", line_len);
|
||||
lines++;
|
||||
line_len = 0;
|
||||
}
|
||||
}
|
||||
printf("\nTotal lines: %zu\n", lines);
|
||||
|
||||
if (enc != ENC_UTF8)
|
||||
buf = to_utf8(buf, bytes_read, enc);
|
||||
|
||||
printf(NORMAL);
|
||||
printf("%s\n", buf);
|
||||
|
||||
fclose(file);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
free(buf);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
#include <stdlib.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "shared.h"
|
||||
|
||||
int main() {
|
||||
char *editor = getenv("EDITOR");
|
||||
if (editor == NULL)
|
||||
editor = "vi";
|
||||
|
||||
struct stat st = {0};
|
||||
if (stat(CWD, &st) == -1) {
|
||||
if (mkdir(CWD, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) == -1) {
|
||||
err("Can't create directory for files", __LINE__ - 1);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (chdir(CWD) == -1) {
|
||||
err("Can't change working directory", __LINE__ - 1);
|
||||
return 2;
|
||||
}
|
||||
|
||||
if (execlp(editor, editor, (char *)NULL)) {
|
||||
err("Can't launch editor", __LINE__ - 1);
|
||||
return 3;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
#include <sys/wait.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "shared.h"
|
||||
|
||||
int main(void) {
|
||||
time_t t = time(NULL);
|
||||
char s[256];
|
||||
strftime(s, 256, "%s", localtime(&t));
|
||||
|
||||
__pid_t e_pid = fork();
|
||||
if (e_pid == -1) {
|
||||
err("Can't fork process for editor", __LINE__ - 1);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!e_pid && execl("build/editor", "build/editor", (char *)NULL)) {
|
||||
err("Can't launch build/editor", __LINE__ - 1);
|
||||
return 2;
|
||||
}
|
||||
|
||||
waitpid(e_pid, 0, 0);
|
||||
|
||||
__pid_t a_pid = fork();
|
||||
if (a_pid == -1) {
|
||||
err("Can't fork process for analizer", __LINE__ - 1);
|
||||
return 3;
|
||||
}
|
||||
|
||||
if (!a_pid && execl("build/analizer", "build/analizer", s, (char *)NULL)) {
|
||||
err("Can't launch build/analizer", __LINE__ - 1);
|
||||
return 4;
|
||||
}
|
||||
|
||||
waitpid(a_pid, 0, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#define CWD "/tmp/files-dir"
|
||||
|
||||
#define RED "\x1b[31m"
|
||||
#define GREEN "\x1b[32m"
|
||||
#define NORMAL "\x1b[0m\n"
|
||||
|
||||
#define err(msg, line) \
|
||||
do { \
|
||||
char buf[256]; \
|
||||
snprintf(buf, sizeof(buf), "%s:%d: error: %s", __FILE__, line, msg); \
|
||||
perror(buf); \
|
||||
} while (0)
|
||||
@@ -0,0 +1,150 @@
|
||||
#include <pthread.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <time.h>
|
||||
|
||||
#define MATRIX_SIZE 512
|
||||
|
||||
typedef struct {
|
||||
double **a;
|
||||
double **b;
|
||||
double **r;
|
||||
int start_row;
|
||||
int end_row;
|
||||
int size;
|
||||
} ThreadArgs;
|
||||
|
||||
void mul_seq(double **a, double **b, double **r, int size) {
|
||||
for (int i = 0; i < size; i++) {
|
||||
for (int j = 0; j < size; j++) {
|
||||
r[i][j] = 0;
|
||||
for (int k = 0; k < size; k++)
|
||||
r[i][j] += a[i][k] * b[k][j];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void *mul_par_worker(void *args) {
|
||||
ThreadArgs *t_args = (ThreadArgs *)args;
|
||||
double **a = t_args->a;
|
||||
double **b = t_args->b;
|
||||
double **r = t_args->r;
|
||||
int start_row = t_args->start_row;
|
||||
int end_row = t_args->end_row;
|
||||
int size = t_args->size;
|
||||
|
||||
for (int i = start_row; i < end_row; i++) {
|
||||
for (int j = 0; j < size; j++) {
|
||||
r[i][j] = 0;
|
||||
for (int k = 0; k < size; k++)
|
||||
r[i][j] += a[i][k] * b[k][j];
|
||||
}
|
||||
}
|
||||
|
||||
pthread_exit(NULL);
|
||||
}
|
||||
|
||||
void mul_par(double **a, double **b, double **r, int size, int n) {
|
||||
pthread_t threads[n];
|
||||
ThreadArgs args[n];
|
||||
int rows_per_thread = size / n;
|
||||
int rem_rows = size % n;
|
||||
int curr_row = 0;
|
||||
|
||||
for (int i = 0; i < n; i++) {
|
||||
args[i].a = a;
|
||||
args[i].b = b;
|
||||
args[i].r = r;
|
||||
args[i].start_row = curr_row;
|
||||
args[i].end_row = curr_row + rows_per_thread + (i < rem_rows ? 1 : 0);
|
||||
args[i].size = size;
|
||||
|
||||
if (pthread_create(&threads[i], NULL, mul_par_worker, &args[i]) != 0) {
|
||||
perror("Can't creat thread");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
curr_row = args[i].end_row;
|
||||
}
|
||||
|
||||
for (int i = 0; i < n; i++) {
|
||||
if (pthread_join(threads[i], NULL) != 0) {
|
||||
perror("Can't join thread");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
double **alloc_matrix(int size) {
|
||||
double **matrix = (double **)malloc(size * sizeof(double *));
|
||||
for (int i = 0; i < size; i++)
|
||||
matrix[i] = (double *)malloc(size * sizeof(double));
|
||||
|
||||
return matrix;
|
||||
}
|
||||
|
||||
void free_matrix(double **matrix, int size) {
|
||||
for (int i = 0; i < size; i++)
|
||||
free(matrix[i]);
|
||||
|
||||
free(matrix);
|
||||
}
|
||||
|
||||
void init_matrix(double **matrix, int size) {
|
||||
for (int i = 0; i < size; i++)
|
||||
for (int j = 0; j < size; j++)
|
||||
matrix[i][j] = (double)rand() / RAND_MAX * 10.0;
|
||||
}
|
||||
|
||||
typedef void (*mult_func_seq)(double **, double **, double **, int);
|
||||
typedef void (*mult_func_par)(double **, double **, double **, int, int);
|
||||
|
||||
double measure_time_seq(mult_func_seq func, double **a, double **b, double **r,
|
||||
int size) {
|
||||
struct timespec start, end;
|
||||
clock_gettime(CLOCK_MONOTONIC, &start);
|
||||
|
||||
func(a, b, r, size);
|
||||
|
||||
clock_gettime(CLOCK_MONOTONIC, &end);
|
||||
return (end.tv_sec - start.tv_sec) + (end.tv_nsec - start.tv_nsec) / 1e9;
|
||||
}
|
||||
|
||||
double measure_time_par(mult_func_par func, double **a, double **b, double **r,
|
||||
int size, int n) {
|
||||
struct timespec start, end;
|
||||
clock_gettime(CLOCK_MONOTONIC, &start);
|
||||
|
||||
func(a, b, r, size, n);
|
||||
|
||||
clock_gettime(CLOCK_MONOTONIC, &end);
|
||||
return (end.tv_sec - start.tv_sec) + (end.tv_nsec - start.tv_nsec) / 1e9;
|
||||
}
|
||||
|
||||
int main() {
|
||||
double **a = alloc_matrix(MATRIX_SIZE);
|
||||
double **b = alloc_matrix(MATRIX_SIZE);
|
||||
double **r_seq = alloc_matrix(MATRIX_SIZE);
|
||||
double **r_par = alloc_matrix(MATRIX_SIZE);
|
||||
|
||||
srand(time(NULL));
|
||||
init_matrix(a, MATRIX_SIZE);
|
||||
init_matrix(b, MATRIX_SIZE);
|
||||
|
||||
printf("performing sequentional multiplication...\n");
|
||||
double sequential_time = measure_time_seq(mul_seq, a, b, r_seq, MATRIX_SIZE);
|
||||
printf("time: %f secinds\n", sequential_time);
|
||||
|
||||
int num_threads = 32;
|
||||
printf("\nperforming parrallel multiplication (%d threads)...\n",
|
||||
num_threads);
|
||||
double parallel_time =
|
||||
measure_time_par(mul_par, a, b, r_par, MATRIX_SIZE, num_threads);
|
||||
printf("time: %f seconds\n", parallel_time);
|
||||
|
||||
free_matrix(a, MATRIX_SIZE);
|
||||
free_matrix(b, MATRIX_SIZE);
|
||||
free_matrix(r_seq, MATRIX_SIZE);
|
||||
free_matrix(r_par, MATRIX_SIZE);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
#include <pthread.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#define NUM_THREADS 10
|
||||
|
||||
void *thread_function(void *thread_id) {
|
||||
long tid = (long)thread_id;
|
||||
printf("Begin\t%ld\n", tid);
|
||||
for (int i = 0; i < 100000; ++i)
|
||||
;
|
||||
printf("End\t%ld\n", tid);
|
||||
pthread_exit(NULL);
|
||||
}
|
||||
|
||||
int main() {
|
||||
pthread_t threads[NUM_THREADS];
|
||||
long t;
|
||||
|
||||
for (t = 0; t < NUM_THREADS; t++)
|
||||
pthread_create(&threads[t], NULL, thread_function, (void *)t);
|
||||
|
||||
void *status;
|
||||
pthread_join(threads[0], &status);
|
||||
|
||||
printf("Main thread completed execution\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,425 @@
|
||||
#import "@local/nure:0.1.0": *
|
||||
|
||||
#show: pz-lb.with(..yaml("doc.yaml"))
|
||||
|
||||
#v(-spacing)
|
||||
|
||||
== Мета роботи
|
||||
Метою даної лабораторної роботи є вивчення створення процесів та потоків при виконанні
|
||||
програм.
|
||||
|
||||
== Хід роботи
|
||||
Дану лабораторну роботу нами було виконано для операційної системи Linux із
|
||||
використанням POSIX API.
|
||||
=== Завдання 1 -- програмний комплекс для створення та аналізу текстових файлів.
|
||||
Метою цього завдання є дослідження створення дочірніх процесів із своєї програми.
|
||||
|
||||
Завдання полягає у розробці трьох програм:
|
||||
+ програма 1 запускає текстовий редактор у заданій папці;
|
||||
+ програма 2 аналізує файли у заданій директорії, створені після вказаного часу,
|
||||
визначаючи для них розмір, кількість рядків та довжину кожного рядка, з підтримкою
|
||||
ASCII та UNICODE кодувань;
|
||||
+ програма 3 запускає спочатку програму 1, а потім програму 2, передаючи час запуску
|
||||
першої програми як параметр для другої.
|
||||
|
||||
==== Розробка програми 1
|
||||
Програма 1 повинна запустити системний текстовий редактор за замовчуванням у вказаній
|
||||
директорії.
|
||||
|
||||
Для цього необхідно:
|
||||
+ визначити редактор за замовчуванням;
|
||||
+ створити вказану директорію, якщо вона не існує;
|
||||
+ запустити редактор у вказаній директорії.
|
||||
|
||||
Назва текстового редактору за замовчуванням на POSIX сумісних операційних системах
|
||||
зазвичай зберігається в змінній оточення "EDITOR", отримати її значення можна за
|
||||
допомогою функції "getenv" стандартної бібліотеки. Ця функція повертає вказівник на
|
||||
рядок, що відповідає значенню змінної, або "NULL", якщо ця змінна відсутня.
|
||||
Скористаємося цим, щоб встановити редактор за замовчуванням.
|
||||
|
||||
Щоб визначити чи директорія для файлів існує скористаємося системним викликом
|
||||
"stat" -- якщо він завершиться із помилкою, значить директорії не існує, та її
|
||||
треба створити. Для створення директорії в разі її відсутності використаємо
|
||||
системний виклик "mkdir".
|
||||
|
||||
В POSIX сумісних операційних системах для запуску зовнішніх програм використовується
|
||||
системний виклик "exec". Цей системний виклик замінює образ поточного процесу на новий,
|
||||
а не створює новий процес, тому щоб відкрити редактор у вказаній директорії необхідно
|
||||
зміни робочу директорію поточного процесу. Для цього скористаємося системним викликом
|
||||
"chdir".
|
||||
|
||||
```c
|
||||
#include <stdlib.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#include "shared.h"
|
||||
|
||||
int main() {
|
||||
char *editor = getenv("EDITOR");
|
||||
if (editor == NULL)
|
||||
editor = "vi";
|
||||
|
||||
struct stat st = {0};
|
||||
if (stat(CWD, &st) == -1) {
|
||||
if (mkdir(CWD, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) == -1) {
|
||||
err("Can't create directory for files", __LINE__ - 1);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (chdir(CWD) == -1) {
|
||||
err("Can't change working directory", __LINE__ - 1);
|
||||
return 2;
|
||||
}
|
||||
|
||||
if (execlp(editor, editor, (char *)NULL)) {
|
||||
err("Can't launch editor", __LINE__ - 1);
|
||||
return 3;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
==== Розробка програми 2
|
||||
Друга програма в якості аргументу отримує час, файли відредаговані після якого
|
||||
необхідно обробити. Перевіримо кількість аргументів, що були передані програмі,
|
||||
та перетворимо перший з них в потрібний формат, для цього скористаємося функцією
|
||||
"strptime" стандартної бібліотеки.
|
||||
|
||||
```c
|
||||
if (argc > 2) {
|
||||
fprintf(stderr, "Error: too many arguments.\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (argc < 2) {
|
||||
fprintf(stderr, "Error: too few arguments.\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
struct tm tm = {0};
|
||||
if (strptime(argv[1], "%s", &tm) == NULL) {
|
||||
fprintf(
|
||||
stderr,
|
||||
"Error: please provide time in UNIX timestamp format.\n"
|
||||
);
|
||||
return 1;
|
||||
}
|
||||
time_t norm_time = mktime(&tm);
|
||||
```
|
||||
|
||||
Для зручного перебору всіх файлів в директорії скористаємося засобами "FTS"
|
||||
#footnote([
|
||||
FTS (File Tree Traversal) в POSIX — це набір функцій,
|
||||
що дозволяють послідовно обходити файлову ієрархію (директорії та файли).
|
||||
FTS надає структурований спосіб для навігації по файловій системі,
|
||||
отримуючи інформацію про кожен знайдений елемент.
|
||||
])
|
||||
, що надає стандарт POSIX.
|
||||
|
||||
Створимо об'єкт FTS із коренем в директорії для файлів.
|
||||
```c
|
||||
char *paths[] = {CWD, NULL};
|
||||
FTS *fts = fts_open(paths, FTS_NOCHDIR, NULL);
|
||||
if (fts == NULL) {
|
||||
err("Can't initialise FTS", __LINE__ - 1);
|
||||
return 2;
|
||||
}
|
||||
```
|
||||
|
||||
Виділимо буфер, в який будемо зберігати вміст файлів.
|
||||
```c
|
||||
char *buf = (char *)malloc(1024 * sizeof(char));
|
||||
```
|
||||
|
||||
Та обробимо всі файли, що були змінені після вказаного часу, не заглиблюючись
|
||||
на інші рівні ієрархії файлової системи.
|
||||
```c
|
||||
for (FTSENT *ent = fts_read(fts); ent != NULL; ent = fts_read(fts)) {
|
||||
switch (ent->fts_info) {
|
||||
case FTS_F:
|
||||
if (ent->fts_statp->st_mtime > norm_time) {
|
||||
...
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
free(buf);
|
||||
```
|
||||
|
||||
Відкриємо кожен файл та визначимо його кодування (UTF-8, UTF-16LE, UTF-16BE чи CP1251).
|
||||
```c
|
||||
FILE *file = fopen(ent->fts_path, "r");
|
||||
if (file == NULL) {
|
||||
err("Can't open the file", __LINE__ - 1);
|
||||
break;
|
||||
}
|
||||
|
||||
size_t bytes_read = fread(buf, 1, 1023, file);
|
||||
if (ferror(file) != 0) {
|
||||
err("Can't read the file", __LINE__ - 2);
|
||||
fclose(file);
|
||||
break;
|
||||
}
|
||||
buf[bytes_read] = '\0';
|
||||
|
||||
Encoding enc = get_encoding(buf, bytes_read);
|
||||
```
|
||||
|
||||
В залежності від кодування порахуємо символи нових рядків в файлі. Для UTF-8 та CP1251
|
||||
новий рядок позначається як один байт "\\n", а для UTF-16LE та UTF-16BE як 2 байти
|
||||
"\\n\\0" та "\\0\\n" відповідно.
|
||||
```c
|
||||
printf("Lines length:");
|
||||
size_t lines = 0;
|
||||
size_t line_len = 0;
|
||||
for (size_t i = (enc == 2 || enc == 1) ? 2 : 0; i < bytes_read; i++) {
|
||||
line_len++;
|
||||
|
||||
int newline_detected = 0;
|
||||
switch (enc) {
|
||||
case ENC_UTF16BE:
|
||||
if (i + 1 < bytes_read && buf[i] == '\0' && buf[i + 1] == '\n') {
|
||||
newline_detected = 1;
|
||||
line_len++;
|
||||
i++;
|
||||
}
|
||||
break;
|
||||
case ENC_UTF16LE:
|
||||
if (i + 1 < bytes_read && buf[i] == '\n' && buf[i + 1] == '\0') {
|
||||
newline_detected = 1;
|
||||
line_len++;
|
||||
i++;
|
||||
}
|
||||
break;
|
||||
case ENC_CP1251:
|
||||
case ENC_UTF8:
|
||||
default:
|
||||
if (buf[i] == '\n')
|
||||
newline_detected = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
if (newline_detected) {
|
||||
printf(" %zu", line_len);
|
||||
lines++;
|
||||
line_len = 0;
|
||||
}
|
||||
}
|
||||
printf("\nTotal lines: %zu\n", lines);
|
||||
```
|
||||
|
||||
Надрукуємо вміст файлу в консоль. Для зручнішого друку якщо текст має будь-яке
|
||||
кодування крім UTF-8 перетворимо його в UTF-8 скориставшись засобами "iconv"
|
||||
#footnote([
|
||||
iconv в POSIX -- це бібліотека, яка надає API для зручного перетворення будь-яких
|
||||
кодувань символів.
|
||||
])
|
||||
, що надає стандарт POSIX.
|
||||
```c
|
||||
if (enc != ENC_UTF8)
|
||||
buf = to_utf8(buf, bytes_read, enc);
|
||||
|
||||
printf(NORMAL);
|
||||
printf("%s\n", buf);
|
||||
```
|
||||
==== Розробка програми 3
|
||||
Третя програма повинна по черзі запустити 2 інші, при цьому зберігши час запуску
|
||||
першої, та передавши його другій.
|
||||
```c
|
||||
time_t t = time(NULL);
|
||||
char s[256];
|
||||
strftime(s, 256, "%s", localtime(&t));
|
||||
```
|
||||
|
||||
Як було зазначено раніше, системний виклик "exec" в POSIX сумісних операційних системах
|
||||
заміняє образ процесу в якому він викликається новим, але в даному випадку нам
|
||||
необхідно створити новий процес. Для цього скористаємося системним викликом "fork",
|
||||
який створює точну копію поточного процесу. В скопійованому процесі викличемо "exec",
|
||||
а в оригінальному дочекаємося завершення виконання копії за допомогою "waitpid".
|
||||
```c
|
||||
__pid_t e_pid = fork();
|
||||
if (e_pid == -1) {
|
||||
err("Can't fork process for editor", __LINE__ - 1);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!e_pid && execl("build/editor", "build/editor", (char *)NULL)) {
|
||||
err("Can't launch build/editor", __LINE__ - 1);
|
||||
return 2;
|
||||
}
|
||||
|
||||
waitpid(e_pid, 0, 0);
|
||||
```
|
||||
|
||||
Повторимо те саме для другої програми.
|
||||
```c
|
||||
__pid_t a_pid = fork();
|
||||
if (a_pid == -1) {
|
||||
err("Can't fork process for analizer", __LINE__ - 1);
|
||||
return 3;
|
||||
}
|
||||
|
||||
if (!a_pid && execl("build/analizer", "build/analizer", s, (char *)NULL)) {
|
||||
err("Can't launch build/analizer", __LINE__ - 1);
|
||||
return 4;
|
||||
}
|
||||
|
||||
waitpid(a_pid, 0, 0);
|
||||
```
|
||||
|
||||
==== Тестування
|
||||
Скомпілюємо всі 3 програми та запустимо головну.
|
||||
Перед нами відкриється редактор у вказаній директорії, створимо в ньому 4 файли з
|
||||
різним кодуванням.
|
||||
#figure(image("img/text-files.png"), caption: [Файли з різним кодуванням])
|
||||
|
||||
Після закриття редактору можемо побачити наступний результат аналізу файлів.
|
||||
#figure(image("img/text-result.png"), caption: [Результат аналізу файлів])
|
||||
|
||||
=== Завдання 2 -- аналіз поведінки потоків.
|
||||
Метою цього завдання є дослідження поведінки потоків.
|
||||
|
||||
Завдання полягає у створенні 10 потоків, та спостереженні за ними.
|
||||
|
||||
В POSIX для керування потоками використовується "pthreads".
|
||||
|
||||
Створимо 10 потоків, в кожному з них виведемо в консоль повідомлення про початок
|
||||
виконання, виконаємо затратну по часу операцію, та виведемо повідомлення про
|
||||
завершення виконання.
|
||||
```c
|
||||
#include <pthread.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#define NUM_THREADS 10
|
||||
|
||||
void *thread_function(void *thread_id) {
|
||||
long tid = (long)thread_id;
|
||||
printf("Begin\t%ld\n", tid);
|
||||
for (int i = 0; i < 100000; ++i)
|
||||
;
|
||||
printf("End\t%ld\n", tid);
|
||||
pthread_exit(NULL);
|
||||
}
|
||||
|
||||
int main() {
|
||||
pthread_t threads[NUM_THREADS];
|
||||
long t;
|
||||
|
||||
for (t = 0; t < NUM_THREADS; t++)
|
||||
pthread_create(&threads[t], NULL, thread_function, (void *)t);
|
||||
|
||||
void *status;
|
||||
pthread_join(threads[0], &status);
|
||||
|
||||
printf("Main thread completed execution\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
#figure(image("img/threads-nowait.png"), caption: [Результат виконання])
|
||||
|
||||
Можемо побачити, що потоки починають виконання в хаотичному порядку.
|
||||
Це пояснюється тим, що операційна система передає потоку виконання незважаючи на час,
|
||||
коли потік було створено. Також можемо побачити, що серед виводу нема повідомлень про
|
||||
завершення потоків під номерами 7, 8 та 9. Оскільки головна програма чекає на
|
||||
завершення лише одного потоку, всі потоки, що не встигли завершити своє виконання до
|
||||
виходу першого автоматично завершуються операційною системою. Щоб це виправити
|
||||
необхідно або від'єднувати потоки від головної програми, або чекати на завершення
|
||||
всіх потоків, а не тільки першого.
|
||||
|
||||
=== Завдання 3 -- порівняння швидкодії послідовних та паралельних обчислень.
|
||||
Метою цього завдання є порівняння швидкодії послідовних та паралельних обчислень.
|
||||
|
||||
Завдання полягає у створенні програм для паралельного та послідовного множення матриць.
|
||||
|
||||
Множення матриць це дуже ресурсо-затратна операція, настільки затратна, що для її
|
||||
виконання використовується спеціалізоване обладнання -- графічні прискорювачі, які
|
||||
можуть виконувати тисячі паралельних операцій.
|
||||
|
||||
Створимо функції для послідовного та паралельного множення матриць.
|
||||
```c
|
||||
void mul_seq(double **a, double **b, double **r, int size) {
|
||||
for (int i = 0; i < size; i++) {
|
||||
for (int j = 0; j < size; j++) {
|
||||
r[i][j] = 0;
|
||||
for (int k = 0; k < size; k++)
|
||||
r[i][j] += a[i][k] * b[k][j];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void *mul_par_worker(void *args) {
|
||||
ThreadArgs *t_args = (ThreadArgs *)args;
|
||||
double **a = t_args->a;
|
||||
double **b = t_args->b;
|
||||
double **r = t_args->r;
|
||||
int start_row = t_args->start_row;
|
||||
int end_row = t_args->end_row;
|
||||
int size = t_args->size;
|
||||
|
||||
for (int i = start_row; i < end_row; i++) {
|
||||
for (int j = 0; j < size; j++) {
|
||||
r[i][j] = 0;
|
||||
for (int k = 0; k < size; k++)
|
||||
r[i][j] += a[i][k] * b[k][j];
|
||||
}
|
||||
}
|
||||
|
||||
pthread_exit(NULL);
|
||||
}
|
||||
|
||||
void mul_par(double **a, double **b, double **r, int size, int n) {
|
||||
pthread_t threads[n];
|
||||
ThreadArgs args[n];
|
||||
int rows_per_thread = size / n;
|
||||
int rem_rows = size % n;
|
||||
int curr_row = 0;
|
||||
|
||||
for (int i = 0; i < n; i++) {
|
||||
args[i].a = a;
|
||||
args[i].b = b;
|
||||
args[i].r = r;
|
||||
args[i].start_row = curr_row;
|
||||
args[i].end_row = curr_row + rows_per_thread + (i < rem_rows ? 1 : 0);
|
||||
args[i].size = size;
|
||||
|
||||
if (pthread_create(&threads[i], NULL, mul_par_worker, &args[i]) != 0) {
|
||||
perror("Can't creat thread");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
curr_row = args[i].end_row;
|
||||
}
|
||||
|
||||
for (int i = 0; i < n; i++) {
|
||||
if (pthread_join(threads[i], NULL) != 0) {
|
||||
perror("Can't join thread");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Перевіримо швидкодію обох функцій, проведемо паралельні обчислення із використанням
|
||||
4 потоків.
|
||||
#figure(image("img/matrix-result.png"), caption: [Результати тестів])
|
||||
|
||||
Можемо побачити, що паралельне обчислення із використанням 4 потоків швидше
|
||||
а послідовне приблизно в $4.4$ рази.
|
||||
|
||||
Додаткове збільшення кількості потоків не змінює час виконання в кращу сторону,
|
||||
а завелика кількість потоків навіть може погіршити результати. Використовувати більше
|
||||
потоків ніж одночасно підтримує процесор нема сенсу, адже в такому випадку одному
|
||||
потоку доведеться чекати поки виконується інший, і в такому разі час затрачений
|
||||
на створення потоку та перемикання контексту процесора може навіть перевищити час,
|
||||
що було зекономлено за допомогою паралелізації обчислень.
|
||||
|
||||
== Висновки
|
||||
Під час даної лабораторної роботи ми навчились використовувати процеси та потоки при виконанні програм.
|
||||
|
||||
#show: appendices_style
|
||||
@@ -0,0 +1,6 @@
|
||||
> [!NOTE]
|
||||
> Викладач: Онищенко К. Г.; Афанасьєва І. В.
|
||||
> Оцінка: In Progress
|
||||
|
||||
> [!TIP]
|
||||
> Виконано в команді
|
||||
@@ -0,0 +1,39 @@
|
||||
title: Прототипування із використанням case-засобі
|
||||
subject: ПП
|
||||
doctype: ЛБ
|
||||
worknumber: 2
|
||||
mentors:
|
||||
- name: Онищенко К. Г.,
|
||||
gender: m,
|
||||
degree: ст. викладач кафедри ПІ,
|
||||
- name: Афанасьєва І. В.,
|
||||
gender: f,
|
||||
degree: доц. каф. ПІ,
|
||||
edu_program: &EDU ПЗПІ
|
||||
university: ХНУРЕ
|
||||
authors:
|
||||
- name: Ситник Є. С.
|
||||
course: 2
|
||||
edu: *EDU
|
||||
gender: m
|
||||
group: 23-2
|
||||
- name: Малишкін. А. С.
|
||||
course: 2
|
||||
edu: *EDU
|
||||
gender: m
|
||||
group: 23-2
|
||||
- name: Краснокутська Ю. Є.
|
||||
course: 2
|
||||
edu: *EDU
|
||||
gender: f
|
||||
group: 23-2
|
||||
- name: Семьонов. О. О.
|
||||
course: 2
|
||||
edu: *EDU
|
||||
gender: m
|
||||
group: 23-2
|
||||
- name: Петах С. І.
|
||||
course: 2
|
||||
edu: *EDU
|
||||
gender: m
|
||||
group: 23-2
|
||||
|
After Width: | Height: | Size: 243 KiB |
|
After Width: | Height: | Size: 235 KiB |
|
After Width: | Height: | Size: 163 KiB |
|
After Width: | Height: | Size: 248 KiB |
|
After Width: | Height: | Size: 151 KiB |
|
After Width: | Height: | Size: 136 KiB |
|
After Width: | Height: | Size: 130 KiB |
|
After Width: | Height: | Size: 220 KiB |
|
After Width: | Height: | Size: 1.8 MiB |
|
After Width: | Height: | Size: 218 KiB |
|
After Width: | Height: | Size: 223 KiB |
|
After Width: | Height: | Size: 850 KiB |
|
After Width: | Height: | Size: 1.8 MiB |
|
After Width: | Height: | Size: 1015 KiB |
|
After Width: | Height: | Size: 146 KiB |
|
After Width: | Height: | Size: 59 KiB |
|
After Width: | Height: | Size: 2.8 MiB |
|
After Width: | Height: | Size: 2.7 MiB |
|
After Width: | Height: | Size: 2.9 MiB |
|
After Width: | Height: | Size: 2.9 MiB |
|
After Width: | Height: | Size: 2.7 MiB |
@@ -0,0 +1,80 @@
|
||||
#import "@local/nure:0.1.0": *
|
||||
|
||||
#show: pz-lb.with(..yaml("doc.yaml"))
|
||||
|
||||
#v(-spacing)
|
||||
|
||||
== Мета роботи
|
||||
Оволодіти технікою опису інтерфейсу користувача. Навчитися використовувати програмне забезпечення для створення інтерактивного прототипу проекту.
|
||||
|
||||
== Хід роботи
|
||||
Переглянути прототип проєкту можна за наступним посиланням -- #link("https://www.figma.com/design/gc0RhNRFVDiosJtY8q6eNo/TaskHub?node-id=0-1&t=Ci5rIMELyhWgs83u-1"). Для його створення нами було використано засіб для прототипування Figma.
|
||||
|
||||
=== Сторінка входу
|
||||
Для користуванням сервісом користувач має увійти в свій обліковий запис, або створити новий, на відповідній сторінці.
|
||||
|
||||
#figure(image("img/Login Page.png", width: 90%), caption: [Форма авторизації])
|
||||
#figure(image("img/Login Page-2.png", width: 90%), caption: [Форма реєстрації])
|
||||
#figure(image("img/Login Page-3.png", width: 90%), caption: [Форма відновлення пароля])
|
||||
#figure(image("img/Login Page-1.png", width: 90%), caption: [Форма зміни пароля])
|
||||
#figure(image("img/Login Page-4.png", width: 90%), caption: [Форма підтвердження пошти])
|
||||
|
||||
=== Домашня сторінка
|
||||
Після авторизації користувач потрапляє на домашню сторінку, де він може переглянути останні оновлення доступних йому завдань та рекомендації, а також має швидкий доступ до своїх завдань. Для навігації на кожній сторінці присутня бічна панель.
|
||||
|
||||
#figure(image("img/Frame 160.png", width: 100%), caption: [Домашня сторінка])
|
||||
|
||||
=== Сторінка "Explore"
|
||||
На сторінку "Explore" користувач потрапляє використовуючи панель навігації або пошук. Тут він може легко шукати публічні завдання та групи завдань за ключовими словами.
|
||||
|
||||
#figure(image("img/Frame 165.png", width: 100%), caption: [Сторінка "Explore"])
|
||||
|
||||
=== Сторінка "Tasks"
|
||||
На сторінці "Tasks" користувач має доступ до всіх завдань, які він створив, на які підписався та до яких його додали. Завдання можливо фільтрувати за їх статусом, групами до яких вони належать та категоріями, а також шукати за ключовими словами. На цій же сторінці існує можливість створення нового завдання.
|
||||
|
||||
#figure(image("img/Frame 161.png", width: 90%), caption: [Сторінка "Tasks" без застосованих фільтрів])
|
||||
#figure(image("img/Frame 162.png", width: 90%), caption: [Сторінка "Tasks" із фільтрацією за статусом])
|
||||
#figure(
|
||||
image("img/Frame 163.png", width: 87%),
|
||||
caption: [Сторінка "Tasks" із фільтрацією за статусом та прихованими фільтрами],
|
||||
)
|
||||
#figure(
|
||||
image("img/Frame 164.png", width: 87%),
|
||||
caption: [Сторінка "Tasks" із фільтрацією за статусом та прихованими фільтрами],
|
||||
)
|
||||
|
||||
=== Сторінка "Task groups"
|
||||
На сторінці "Task groups" користувач може побачити всі групи завдань, які він створив, на які підписався та до яких його додали. Групи завдань можна фільтрувати так само як завдання.
|
||||
|
||||
#figure(image("img/Frame 167.png", width: 80%), caption: [Сторінка "Tasks"])
|
||||
#figure(image("img/Frame 168.png", width: 80%), caption: [Сторінка "Tasks" із фільтрами])
|
||||
|
||||
=== Сторінка завдання
|
||||
На сторінці завдання користувач може побачити всю інформацію про завдання, купити його (у випадку із платним завданням), вносити зміни, зберегти до своїх завдань, зробити копію, поставити оцінку та інше.
|
||||
|
||||
#figure(image("img/Frame 170.png", width: 100%), caption: [Сторінка завдання])
|
||||
#figure(image("img/Frame 171.png", width: 100%), caption: [Сторінка завдання із платним доступом])
|
||||
|
||||
=== Сторінка групи завдань
|
||||
Для сторінки групи завдань справедливо все те саме, що і для сторінки завдання.
|
||||
|
||||
#figure(image("img/Frame 172.png", width: 100%), caption: [Сторінка групи завдань])
|
||||
|
||||
=== Сторінка користувача
|
||||
Кожен користувач має особисту сторінку, на якій інші користувачі можуть дізнатись про нього інформацію, як от його завдання, кількість створених ним завдань та груп завдань, а також кількість доданих рішень. Кожен користувач може закріпити деякі завдання та групи завдань на своїй сторінці, щоб відвідувачі в першу чергу бачили їх.
|
||||
|
||||
#figure(image("img/Frame 166.png", width: 100%), caption: [Сторінка користувача])
|
||||
|
||||
=== Панель адміністратора
|
||||
Користувачі із правами адміністратора мають додаткову сторінку, на якій вони можуть керувати користувачами.
|
||||
#figure(image("img/Frame 37963.png", width: 100%), caption: [Панель адміністратора])
|
||||
|
||||
=== Інше
|
||||
На кожній сторінці користувач може за допомогою кнопок, що розташовані на горі, змінити мову та кольорову схему, а також заховати присутні бічні панелі.
|
||||
|
||||
На панелі навігації користувач може побачити інформацію про його поточний план використання із залишками ресурсів, що доступні із цим планом.
|
||||
|
||||
== Висновки
|
||||
Під час даної лабораторної роботи ми оволоділи технікою опису інтерфейсу користувача, та навчилися використовувати програмне забезпечення для створення інтерактивного прототипу проекту.
|
||||
|
||||
#show: appendices_style
|
||||