Файлы, каталоги и файловые дескрипторы
«Всё есть файл»
В Unix через файловый интерфейс (open, read, write, close) доступно почти всё:
| Тип | ls -l |
Пример |
|---|---|---|
| обычный файл | - |
/etc/passwd |
| каталог | d |
/home |
| символическая ссылка | l |
/bin → usr/bin |
| блочное устройство | b |
/dev/sda — диск |
| символьное устройство | c |
/dev/tty, /dev/null |
| именованный канал | p |
созданный mkfifo |
| сокет | s |
/run/systemd/notify |
Плюс псевдофайлы /proc и /sys.
Дерево каталогов
Все файловые системы собраны в одно дерево с корнем /. Каталог — особый файл, содержащий таблицу «имя → номер индексного дескриптора». Путь /home/anna/notes.txt разрешается по шагам: корень → запись home → запись anna → запись notes.txt.
Три уровня таблиц
Когда процесс открывает файл, ядро связывает три структуры:
| Уровень | Где | Что хранит |
|---|---|---|
| Таблица дескрипторов | у каждого процесса | номер → указатель на открытый файл, флаг FD_CLOEXEC |
Таблица открытых файлов (struct file) |
общая в ядре | текущая позиция, режим открытия, флаги, счётчик ссылок, операции |
| Индексный дескриптор (inode) | общий, по одному на файл | метаданные и расположение данных файла |
Следствия:
forkиdupкопируют дескриптор — позиция общая;- два
openодного файла — две разные записиstruct file, позиции независимы; - запись открытого файла освобождается, когда закрыты все ссылающиеся на неё дескрипторы (счётчик ссылок = 0).
dup и dup2
int copy = dup(fd); /* наименьший свободный номер — копия fd */
dup2(fd, 1); /* сделать 1 копией fd (1 закрывается, если был открыт) */
Так shell выполняет cmd > file:
pid_t pid = fork();
if (pid == 0) {
int fd = open("file", O_WRONLY | O_CREAT | O_TRUNC, 0644);
dup2(fd, STDOUT_FILENO);
close(fd);
execvp("cmd", argv);
}
Права доступа
Каждый файл имеет владельца, группу и 9 бит прав: rwx для владельца, группы и остальных.
$ ls -l script.sh
-rwxr-x--- 1 anna dev 120 окт 25 10:00 script.sh
chmod 750 script.sh # rwx r-x ---
| Бит | Для файла | Для каталога |
|---|---|---|
| r | читать содержимое | читать список имён |
| w | изменять содержимое | создавать, удалять, переименовывать файлы в нём |
| x | выполнять | входить (проходить по пути) |
Удаление файла — право записи в каталог, а не в сам файл.
Метаданные: stat
struct stat st;
stat("data.txt", &st);
printf("inode=%lu links=%lu size=%ld\n",
(unsigned long)st.st_ino, (unsigned long)st.st_nlink, (long)st.st_size);
stat data.txt
Итог
- В Unix «всё есть файл»: обычные файлы, каталоги, устройства, каналы, сокеты.
- Три уровня: дескрипторы процесса → таблица открытых файлов (позиция) → inode (метаданные).
forkиdupразделяют позицию;openещё раз — новая позиция.openвозвращает наименьший свободный номер;dup2— перенаправления.
Практика решено 0 из 5
Что общее, а что своё после fork, если файл был открыт до него?
| Свой у каждого процесса | Общий для родителя и потомка | |
|---|---|---|
| Таблица файловых дескрипторов | ||
| Структура открытого файла (struct file) | ||
| Текущая позиция в файле (offset) | ||
| Флаги открытия (O_APPEND, O_NONBLOCK) | ||
| Флаг FD_CLOEXEC дескриптора |
Что окажется в файле out.txt?
int fd = open("out.txt", O_WRONLY | O_CREAT | O_TRUNC, 0644);
if (fork() == 0) { write(fd, "AB", 2); _exit(0); }
wait(NULL);
write(fd, "CD", 2);
Перенаправьте стандартный вывод в файл: откройте "log.txt" на запись (создать, обрезать, права 0644)
и сделайте дескриптор 1 его копией через dup2, затем закройте лишний дескриптор.
int fd = open("log.txt", O_WRONLY | O_CREAT | O_TRUNC, 0644);
dup2(fd, 1);
close(fd);
Какой наименьший номер дескриптора вернёт open у процесса, у которого открыты только 0, 1 и 2, а потом закрыт 0?
Какой тип файла показывает первый символ в ls -l?
| обычный файл | каталог | символическая ссылка | блочное устройство | символьное устройство | канал FIFO | сокет | |
|---|---|---|---|---|---|---|---|
| - | |||||||
| d | |||||||
| l | |||||||
| b | |||||||
| c | |||||||
| p | |||||||
| s |
Войдите, чтобы отмечать пройденные уроки.