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