Skip to content

Latest commit

 

History

History
361 lines (283 loc) · 15.8 KB

File metadata and controls

361 lines (283 loc) · 15.8 KB
  • Работа с файлами
  • Файловая система — способ организации и хранения файлов (ext4, btrfs, XFS)
  • Inode (index node — индексный дескриптор) — это структура данных в файловой системе Unix/Linux, которая хранит метаданные о файле (всё, кроме его имени и содержимого).
  • Виртуальная файловая система — это абстракция, которая представляет недисковые сущности (процессы, устройства, системную информацию) в виде обычных файлов и каталогов, предоставляя унифицированный файловый интерфейс для взаимодействия с ними.

1. Типы файлов в Linux

ВАЖНО: в Linux ВСЁ — это файлы.

Есть 7 типов файлов:

  1. Обычные (регулярные) файлы (- в ls -l)

    • Последовательность байтов (текст, бинарники, исполняемые файлы)
  2. Директории (d)

    • Файл, содержащий список записей вида имя файла → inode
    • Как напоминание, inode — структура, хранящая полную информацию о файле в ФС
  3. Символические ссылки (l)

    • Ярлыки, содержащие путь к целевому файлу
  4. Символьные устройства (c - character device)

    • Это уже не фиксированный кусок информации - это поток байтов (терминалы, /dev/random)
    • Не поддерживают lseek и прочее из разряда Random Access (доступ последовательный)
  5. Блочные устройства (b - block)

    • Работают с блоками данных (диски: /dev/sda, /dev/nvme0n1)
    • Поддерживают lseek (произвольный доступ)
    • Нельзя скопировать обычным cp
  6. Именованные пайпы (p - pipe/FIFO)

    • Межпроцессное взаимодействие через FIFO-буфер
  7. Сокеты (s - socket)

    • Файлы для сетевого/межпроцессного взаимодействия

Примеры для каждого типа:

  • - (обычный): /etc/passwd, /bin/bash
  • d (директория): /home, /tmp
  • l (символическая ссылка): /usr/bin/python → python3.10
  • c (символьное устройство): /dev/tty, /dev/null
  • b (блочное устройство): /dev/sda1, /dev/nvme0n1p2
  • p (пайп): создаётся через mkfifo
  • s (сокет): /run/systemd/journal/socket

1.1 swapfile — файл подкачки

Это файл, используемый ядром Linux как виртуальная память (swap). Когда физической памяти (RAM) не хватает, часть данных перемещается в swap, предотвращая падение системы.

# создаёт файл /swapfile размером 1 ГБ (1024 × 1 МБ).
dd if=/dev/zero of=/swapfile bs=1M count=1024

# форматирует файл как раздел подкачки
mkswap /swapfile

# активирует его
swapon /swapfile

2. /dev — устройства и виртуальные файлы

cd /dev
ls -l

Увидим файлы устройств в флагами b и c. Файл - это не обязательно выделенный отрезок на диске

  • Это нечто более абстрактное, с чем можно проделывать операции типа "открыть", "закрыть", "читать", "писать", в зависимости от типа этого самого "файла"

Типы устройств:

  • c (символьные): /dev/tty, /dev/random, /dev/null
  • b (блочные): /dev/sda, /dev/nvme0n1

Виртуальные устройства:

  • /dev/null — игнорирует запись, возвращает EOF при чтении
    • Абстракция уровня ОС
  • /dev/zero — возвращает нулевые байты
  • /dev/random и /dev/urandom — генераторы случайных чисел

Пишем буквально в никуда:

echo "Hello" > /dev/null
  • Например, хочу подавить вывод stderr

Выводит ТОЛЬКО НУЛЕВЫЕ байты:

/dev/zero

У этого файла нет физической оболочки - это "фейковая" сущность.

Вообще, папка dev содержит файлы девайсов и драйверов. Например, /dev/snd содержит файлы звуковой карты; nvme0... - твердотельные накопители (уже тип b) (суффиксы вида pX (partition X)).

2.1 Утилита dd

dd - утилита, которая открывает поток и пишет данные с другого потока

Просто обнуляем весь диск:

dd if=/dev/zerо оf=/dev/nvme0n1р1
  • Хуже, чем sudo rm / -f

Пример чтения из /dev/random:

dd if=/dev/random of=random_data.bin bs=1 count=16  # 16 байт случайных данных

2.2 Пишем на символьные устройства и читаем с них

Чтение с символьного устройства:

int fd = open("/dev/random", O_RDONLY);
unsigned char buffer[32];
read(fd, buffer, sizeof(buffer));

Запись на устройство (терминал):

int fd = open("/dev/tty", O_WRONLY);
write(fd, "Hello\n", 6);

3. Пайпы (FIFO) и сокеты

Pipes — это механизм межпроцессного взаимодействия (IPC), который работает по принципу FIFO (First In, First Out). Однако pipes делятся на два основных типа, которые сильно отличаются по способу использования и "жизни".

  1. Неименованные (анонимные) пайпы (Unnamed pipes, или просто "pipes")
  2. Именованные пайпы (Named pipes, FIFO)

3.1 Именованные пайпы (FIFO)

FIFO — named pipe, существует как файл в файловой системе.

mknod - утилита, позволяющая создавать файлы разных типов:

man mknod
  • Ему соответствует одноименный syscall mknod.

Файл типа pipe (труба: можно писать в один конец и получать через другой конец - FIFO!):

mknod myfifo p
  • Можно только писать потоком и читать потоком
    • Причем данные пишутся в ОЗУ, а на диск не пишется

Есть также syscall mkfifo:

#include <sys/types.h>
#include <sys/stat.h>

int mkfifo(const char *pathname, mode_t mode);

Пишем в пайп и читаем с него:

// write_to_fifo.cpp

#include <sys/types.h>
#include <sys/stat.h>

#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <stdio.h>

int main() {
	mkfifo("./test_fifo", 0666);
	int fd = open("./test_fifo", O_WRONLY);
	
	char* str = "Hello!";
	write(fd, str, strlen(str));
	
	close(fd);
	return 0;
}
// read_from_fifo.cpp

#include <sys/types.h>
#include <sys/stat.h>

#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>

int main() {
	int fd = open("./test_fifo", O_RDONLY);
	
	char str[10];
	read(fd, str, 5);
	
	printf("%s", str);
	
	close(fd);
	return 0;
}
  • Запускаем первую программу (./write_to_fifo) - она зависает на моменте открытия (open): ждет появления читателя (этакое рандеву)
  • Запускаем вторую (./read_from_fifo) - выводит Hello, а первая программа заканчивает выполнение

Забудем про первую программу и сделаем через echo:

echo "Hello" > test_fifo
  • Тоже зависло
./read_from_fifo
  • echo завершилась.

  • Неблокирующий режим: open("myfifo", O_RDONLY | O_NONBLOCK) — возвращает управление сразу.

    • read и write тоже не задерживают работу.
int fd = open("myfifo", O_RDONLY | O_NONBLOCK);
if (fd == -1) {
    perror("open");
    return 1;
}
// Дальше можно читать без блокировки
  • Данные хранятся в ядре (буфер до 64 КБ по умолчанию в Linux). Если буфер переполняется:
    • Для записи: процесс блокируется до освобождения места.
    • В неблокирующем режиме: вызов write вернёт ошибку EAGAIN.

3.2 Pipes (анонимные каналы)

Pipes — однонаправленные каналы между процессами. Создаются с помощью pipe().

#include <unistd.h>
#include <stdio.h>
#include <sys/wait.h>

int main() {
    int fd[2];
    pipe(fd); // fd[0] - чтение, fd[1] - запись
    
    if (fork() == 0) {
        // child: пишет в pipe
        close(fd[0]);
        write(fd[1], "Hello", 6);
        close(fd[1]);
        return 0;
    } else {
        // parent: читает из pipe
        close(fd[1]);
        char buf[100];
        read(fd[0], buf, sizeof(buf));
        printf("Parent got: %s\n", buf);
        close(fd[0]);
        wait(NULL);
    }
    return 0;
}

Broken pipe (SIGPIPE) возникает при записи в pipe, у которого нет читателей.

3.3 Сравнение типов пайпов (pipes)

Характеристика Неименованные пайпы (Unnamed pipes) Именованные пайпы (Named pipes, FIFO)
Создание pipe() (в коде) или | (в shell) mkfifo() (в коде) или mkfifo (в shell)
Идентификация Нет имени в файловой системе. Дескрипторы файлов в ядре ОС Есть имя в файловой системе (например, /tmp/myfifo)
Внешний вид Невидимы в файловой системе Видны в ls -l с символом p (тип файла "pipe")
Связь процессов Только родственные процессы (обычно родитель-потомок) Любые, не родственные процессы
Механизм связи Наследование дескрипторов при вызове fork() Открытие по имени файла
Время жизни Пока жив хотя бы один из связанных процессов Как у обычного файла (до явного удаления)
Типичное использование Команды в shell: cmd1 | cmd2 Долговременное взаимодействие независимых программ
Принцип работы FIFO (для обоих типов) FIFO (для обоих типов)

3.4 Сокеты

Как FIFO, только общение распространяется по сети.

  • То есть это файлы, которые работают по принципу FIFO, но обращение к данным осуществляется посредством сетевой карты.

4. /proc и /sys — виртуальные файловые системы

4.1 /proc— информация о процессах и ядре в реальном времени

Рассмотрим другую "директорию".

cd /proc/

Это псевдофайловая система (pseudo-filesystem) (то есть не хранится фактически на диске, но предоставляет интерфейс в виде файлов и каталогов к данным о состоянии ядра, процессах, устройствах и других системных ресурсах).

Мануал к ней:

man proc

Так, можно узнать почти все про текущие процессы. Например, можно узнать PID последнего выполняемого процесса

cat loadavg  # нам нужно последнее число

Рассмотрим все содержимое:

cd /proc/
ls -l

Здесь также много директорий, отвечающих за процессы (названия директорий - это сам PID процесса). Можно зайти в какой-нибудь процесс и узнать, например:

  • установленные на процесс лимиты cat ./limits
  • cat maps — об этом будет позже — диапазоны адресов, выделенные под процесс (иначе говоря, карта памяти процесса)
  • cd fd — симлинки на открытые файлы процесса

Как оказывается, программа top просто читает /proc/{PIDs}/statm (или stat):

  • См. man proc_pid_stat
strace top

Список открытых процессом файлов (list open files):

lsof -p {PID}

Упрощенная реализация lsof:

#include <dirent.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>

int main() {
    DIR *dir = opendir("/proc/self/fd");
    struct dirent *entry;
    
    while ((entry = readdir(dir)) != NULL) {
        if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)
            continue;
        
        char path[256];
        snprintf(path, sizeof(path), "/proc/self/fd/%s", entry->d_name);
        
        char target[256];
        int len = readlink(path, target, sizeof(target)-1);
        target[len] = '\0';
        
        printf("%s -> %s\n", entry->d_name, target);
    }
    
    closedir(dir);
    return 0;
}

4.2 /sys — информация об устройствах и ядре в структурированном виде

Информация об устройствах, но в более читаемом виде. Тоже является фейковой ФС.

Все устройства, которые реально существуют:

cd /sys/dev/

Информация о ядре:

cd /sys/kernel