OS lb-7
This commit is contained in:
7
semester-4/ОС/lb-7/README.md
Normal file
7
semester-4/ОС/lb-7/README.md
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
> [!NOTE]
|
||||||
|
> Викладач: Мельникова Р. В.
|
||||||
|
>
|
||||||
|
> Оцінка: in progress
|
||||||
|
|
||||||
|
> [!TIP]
|
||||||
|
> Виконано для Linux в команді.
|
21
semester-4/ОС/lb-7/doc.yaml
Normal file
21
semester-4/ОС/lb-7/doc.yaml
Normal file
@ -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
|
BIN
semester-4/ОС/lb-7/img/getenv.png
Normal file
BIN
semester-4/ОС/lb-7/img/getenv.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 43 KiB |
BIN
semester-4/ОС/lb-7/img/matrix-result.png
Normal file
BIN
semester-4/ОС/lb-7/img/matrix-result.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 47 KiB |
BIN
semester-4/ОС/lb-7/img/text-files.png
Normal file
BIN
semester-4/ОС/lb-7/img/text-files.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 42 KiB |
BIN
semester-4/ОС/lb-7/img/text-result.png
Normal file
BIN
semester-4/ОС/lb-7/img/text-result.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 150 KiB |
BIN
semester-4/ОС/lb-7/img/threads-nowait.png
Normal file
BIN
semester-4/ОС/lb-7/img/threads-nowait.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 64 KiB |
19
semester-4/ОС/lb-7/src/Justfile
Normal file
19
semester-4/ОС/lb-7/src/Justfile
Normal file
@ -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
|
224
semester-4/ОС/lb-7/src/editor/analizer.c
Normal file
224
semester-4/ОС/lb-7/src/editor/analizer.c
Normal file
@ -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;
|
||||||
|
}
|
31
semester-4/ОС/lb-7/src/editor/editor.c
Normal file
31
semester-4/ОС/lb-7/src/editor/editor.c
Normal file
@ -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;
|
||||||
|
}
|
39
semester-4/ОС/lb-7/src/editor/main.c
Normal file
39
semester-4/ОС/lb-7/src/editor/main.c
Normal file
@ -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;
|
||||||
|
}
|
16
semester-4/ОС/lb-7/src/editor/shared.h
Normal file
16
semester-4/ОС/lb-7/src/editor/shared.h
Normal file
@ -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)
|
150
semester-4/ОС/lb-7/src/matrix-calc/main.c
Normal file
150
semester-4/ОС/lb-7/src/matrix-calc/main.c
Normal file
@ -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;
|
||||||
|
}
|
29
semester-4/ОС/lb-7/src/threads/main.c
Normal file
29
semester-4/ОС/lb-7/src/threads/main.c
Normal file
@ -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;
|
||||||
|
}
|
BIN
semester-4/ОС/lb-7/Лр_7_Ситник_Малишкін_ПЗПІ_23_2.pdf
Normal file
BIN
semester-4/ОС/lb-7/Лр_7_Ситник_Малишкін_ПЗПІ_23_2.pdf
Normal file
Binary file not shown.
425
semester-4/ОС/lb-7/Лр_7_Ситник_Малишкін_ПЗПІ_23_2.typ
Normal file
425
semester-4/ОС/lb-7/Лр_7_Ситник_Малишкін_ПЗПІ_23_2.typ
Normal file
@ -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
|
Reference in New Issue
Block a user