Лабораторна робота №2. Системні виклики ядра Linux для роботи з файловими дескрипторами
Мета
Реалізувати й дослідити системні виклики open, read, write, close та lseek у невеликій програмі C, а також навчитися обробляти помилки системних викликів.
Підготовка
Перед виконанням опрацюйте пов'язану лекцію:
Для роботи потрібне Linux-середовище з компілятором GCC. Перевірте:
gcc --version
Робота виконується повністю в межах папки lab-02 у вашому особистому репозиторії operational-systems. Права адміністратора й зміна системних налаштувань не потрібні.
Завдання
Завдання 1. Відтворіть програму з файловими дескрипторами
У папці lab-02 створіть файл fd_demo.c із таким вмістом:
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
int main(void) {
const char *path = "notes.txt";
char buffer[64];
ssize_t n;
int fd;
fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, 0644);
if (fd == -1) {
fprintf(stderr, "open (write): %s\n", strerror(errno));
return 1;
}
const char *line = "Hello from lab-02\n";
if (write(fd, line, strlen(line)) == -1) {
fprintf(stderr, "write: %s\n", strerror(errno));
close(fd);
return 1;
}
close(fd);
fd = open(path, O_RDONLY);
if (fd == -1) {
fprintf(stderr, "open (read): %s\n", strerror(errno));
return 1;
}
n = read(fd, buffer, sizeof(buffer) - 1);
if (n == -1) {
fprintf(stderr, "read: %s\n", strerror(errno));
close(fd);
return 1;
}
buffer[n] = '\0';
printf("Прочитано %zd байтів: %s", n, buffer);
if (lseek(fd, 11, SEEK_SET) == -1) {
fprintf(stderr, "lseek: %s\n", strerror(errno));
close(fd);
return 1;
}
n = read(fd, buffer, 6);
if (n == -1) {
fprintf(stderr, "read: %s\n", strerror(errno));
close(fd);
return 1;
}
buffer[n] = '\0';
printf("Зі зміщення 11 прочитано %zd байтів: %s\n", n, buffer);
close(fd);
return 0;
}
Скомпілюйте з суворими прапорами та запустіть:
gcc -Wall -Wextra -pedantic -o fd_demo fd_demo.c
./fd_demo
Поясніть кожен системний виклик: які аргументи він отримує, що повертає у разі успіху та у разі помилки. Зверніть увагу, що open із прапорцями O_WRONLY | O_CREAT | O_TRUNC створює або очищає файл, а lseek змінює лише позицію у файловому дескрипторі.
Завдання 2. Змініть зміщення та передбачте результат
Перед запуском змініть програму: замініть lseek(fd, 11, SEEK_SET) на lseek(fd, 0, SEEK_SET), а кількість байтів на читання — на 5. Запишіть, який текст, на вашу думку, буде прочитано. Скомпілюйте й запустіть, порівняйте з прогнозом.
Потім поверніть оригінальне значення 11 і виконайте трасування системних викликів, якщо strace доступний:
strace -e trace=openat,read,write,lseek,close ./fd_demo
Знайдіть у трасуванні openat, write, read і lseek, зіставте їх із рядками коду.
Завдання 3. Прикладний: міні-копіювач файлів
Створіть файл copy_file.c, який копіює вміст одного файла в інший, читаючи по 4096 байтів у циклі:
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
int main(int argc, char *argv[]) {
if (argc != 3) {
fprintf(stderr, "Використання: %s <джерело> <призначення>\n", argv[0]);
return 1;
}
int in = open(argv[1], O_RDONLY);
if (in == -1) {
fprintf(stderr, "open %s: %s\n", argv[1], strerror(errno));
return 1;
}
int out = open(argv[2], O_WRONLY | O_CREAT | O_TRUNC, 0644);
if (out == -1) {
fprintf(stderr, "open %s: %s\n", argv[2], strerror(errno));
close(in);
return 1;
}
char buf[4096];
ssize_t n;
while ((n = read(in, buf, sizeof(buf))) > 0) {
if (write(out, buf, n) == -1) {
fprintf(stderr, "write: %s\n", strerror(errno));
close(in);
close(out);
return 1;
}
}
if (n == -1) {
fprintf(stderr, "read: %s\n", strerror(errno));
}
close(in);
close(out);
return n == -1 ? 1 : 0;
}
Скомпілюйте та скопіюйте notes.txt у notes-copy.txt:
gcc -Wall -Wextra -pedantic -o copy_file copy_file.c
./copy_file notes.txt notes-copy.txt
cmp notes.txt notes-copy.txt
cmp без виводу означає, що файли ідентичні. Поясніть, чому copy_file записує рівно стільки байтів, скільки повернув read, а не весь буфер 4096.
Очікуваний результат
Для завдання 1 програма fd_demo має вивести:
Прочитано 18 байтів: Hello from lab-02
Зі зміщення 11 прочитано 6 байтів: lab-02
Для завдання 3 cmp notes.txt notes-copy.txt не має вивести жодного повідомлення, а файли notes.txt і notes-copy.txt мають збігатися за вмістом.
Структура репозиторію
operational-systems/
├── README.md
├── lab-01/
└── lab-02/
├── fd_demo.c
├── copy_file.c
├── notes.txt
└── notes-copy.txt
Що здати
- Посилання на ваш особистий репозиторій
operational-systems, надіслане у формі здачі на поточній сторінці. - Папку
lab-02з файламиfd_demo.cіcopy_file.c(бінарні файли та створеніnotes*.txtзавантажувати не обов'язково). - Оновлений кореневий
README.mdз назвою роботи та командами компіляції.
Питання для захисту
- Що таке файловий дескриптор і що повертає
openу разі помилки? - Для чого потрібні прапорці
O_CREATіO_TRUNC? - Що змінює виклик
lseek? - Чому у
copy_fileзаписують рівноnбайтів, а не4096? - Чи можна виконати ці операції без системних викликів, лише функціями бібліотеки?
