💀⚰️🔥 Файловые дескрипторы: часть 1 — что это за херня и нахуя она нужна

Ебать, поздравляю! Случился грандиозный образовательный пиздец. 90% "разработчиков" не знают, что такое файловые дескрипторы, а остальные 10% думают, что это "что-то про файлы, наверное". Блядь, это как ебашить код и не знать, что такое память.

Истина жизни: даже бездомная псина понимает базовые принципы — взял кость, сгрыз, выбросил объедки. А программисты хватают системные ресурсы и держат их до скончания веков, как последние долбоёбы.

---

## 🔥 Что такое файловые дескрипторы: ликбез для тех, кто проебал основы

Файловый дескриптор (FD) — это охренительно важная штука в Unix/Linux, блядь. Это просто число (обычно от 0 до `65535`), которое ядро выдаёт процессу как "пропуск" для работы с ресурсами.

### Аналогия на пальцах (для особо тупых):
Представь бордель с номерками:
- Пришёл, заплатил за девку
- Получил номер комнаты (это и есть твой FD)
- Работаешь с девкой через этот номер
- Кончил — освобождаешь комнату

В системе аналогично:
- Процесс просит у ядра ресурс ("дай файлик")
- Ядро выдаёт номер-пропуск (FD)
- Процесс долбит ресурс через этот номер
- Закончил — отдаёт номер обратно

Только если в борделе забыл освободить комнату — заплатишь за всю ночь. А здесь забыл закрыть FD — система сдохнет от нехватки ресурсов, сука.

---

## 🤖 Основные типы FD (чтобы не путались, блядь)

### Что можно открыть и как это выглядит:

#### 1. Обычные файлы (для чайников)

# Открыли файл — получили номерок 3
exec 3< /etc/passwd # FD 3 = файл /etc/passwd
read line <&3 # Читаем через FD 3
exec 3<&- # Закрыли FD 3 (ВАЖНО!)


#### 2. Сокеты (сетевая хрень)

# TCP — когда нужно поговорить с сервером
nc google.com 80 # FD для TCP соединения с гуглом

# UDP — когда похуй на доставку
nc -u 8.8.8.8 53 # FD для UDP сокета (DNS)

# Unix socket — локальная переписка
nc -U /tmp/socket # FD для локального сокета


#### 3. Pipes (когда процессы болтают друг с другом)

# Анонимный pipe (классика)
echo "пошли в пивбар" | grep "пиво" # 2 FD: писать|читать

# Именованный pipe (для упоротых)
mkfifo /tmp/chat
echo "привет, долбоёб" > /tmp/chat &
cat < /tmp/chat # Получит сообщение


#### 4. Устройства (системная магия)

echo "hello" > /dev/tty # FD к терминалу
echo "в жопу" > /dev/null # FD к чёрной дыре системы
head -c 10 /dev/urandom # FD к генератору случайных байт


---

## 📊 Стандартные дескрипторы: основы для тех, кто в танке

Каждый процесс автоматически получает 3 FD (как в комплекте "стартовый набор долбоёба"):


# FD 0 — stdin (куда ты вводишь херню)
read input # Читает с клавиатуры (FD 0)

# FD 1 — stdout (куда выводится результат твоей работы)
echo "всё ок" # Выводит в терминал (FD 1)

# FD 2 — stderr (куда летят твои ошибки)
echo "всё хуёво" >&2 # Выводит ошибку (FD 2)


### Перенаправление дескрипторов (чтобы не орать в терминал):

# Результат в файл (чтобы сохранить)
echo "результат работы" > output.txt # FD 1 → файл

# Ошибки в файл (чтобы потом посмотреть, что накосячил)
./my_script.sh 2> errors.log # FD 2 → файл ошибок

# Ошибки туда же, где и результат (всё в одну кучу)
./my_script.sh 2>&1 # FD 2 → FD 1

# Заткнуть ошибки (когда похуй на них)
./my_script.sh 2>/dev/null # FD 2 → в жопу

# Открыть свой FD (для продвинутых извращений)
exec 3> myfile.txt # FD 3 → файл
echo "секретные данные" >&3
exec 3>&- # Закрыли FD 3


---

## 🔍 Как глянуть, что творится с дескрипторами (чтобы понять масштаб бедствия) ### Команды для тех, кто хочет узнать правду:


# Твои лимиты (сколько FD можешь открыть)
ulimit -n # Мягкий лимит (обычно 1024, блядь)
ulimit -Hn # Жёсткий лимит (до скольки можно поднять)
# Глобальная картина системы
cat /proc/sys/fs/file-max # Максимум FD в системе
cat /proc/sys/fs/file-nr # Сколько сейчас открыто
# Формат: открыто свободно максимум
# Пример: 5000 500 2097152 = 5К открыто, лимит 2М

# Что творится с конкретным процессом
ls -la /proc/$PID/fd/ # Список всех FD процесса
lsof -p $PID # Подробности: что открыто
lsof -p $PID | wc -l # Просто количество

# Разбивка по типам (чтобы понять, что именно течёт)
lsof -p $PID | awk '{print $5}' | sort | uniq -c
# Типичный вывод:
# 150 IPv4 # TCP/UDP сокеты (сеть)
# 50 REG # Обычные файлы
# 10 FIFO # Pipes
# 3 CHR # Устройства
```shell

### Мониторинг в реальном времени (чтобы смотреть, как всё катится в пизду):

```bash
# Следим за ростом FD (как растёт катастрофа)
watch -n 1 "lsof -p $PID | wc -l"

# Топ процессов-пожирателей FD
lsof | awk '{print $2}' | sort | uniq -c | sort -nr | head -10

# Системная статистика каждую секунду
watch -n 1 "cat /proc/sys/fs/file-nr"


---

## ⚙️ Лимиты: почему они есть и как не обосраться об потолок

### Зачем ядро ставит лимиты (чтобы ты не угробил систему):

1. Защита от жадных процессов
- Каждый FD жрёт память ядра (~1KB на штук)
- Без лимитов твоя программа сожрёт всю память и система встанет колом

2. Защита от долбоёбов
- Злобный (или тупой) процесс может открыть дохрена файлов
- Система станет недоступной для остальных, блядь

3. Производительность не резиновая
- Поиск свободного FD — медленная операция O(n)
- Много FD = тормозная система

### Типы лимитов (мягкий и жёсткий):


# Soft limit — сколько сейчас можешь открыть
ulimit -n 1024

# Hard limit — до скольки можешь поднять soft
ulimit -Hn 4096

# Поднимаем soft (но не выше hard, сука!)
ulimit -n 2048


### Где настраивать лимиты (чтобы не сдохнуть в проде):


# Глобальные настройки системы
echo "2097152" > /proc/sys/fs/file-max # Общий лимит FD

# Лимиты для пользователей
# /etc/security/limits.conf
myapp soft nofile 4096 # Мягкий лимит для пользователя myapp
myapp hard nofile 8192 # Жёсткий лимит

# Лимиты для systemd сервисов
# /etc/systemd/system/myapp.service
[Service]
LimitNOFILE=4096 # Лимит FD для сервиса


---

## 🧠 Как это работает внутри ядра (для тех, кому интересно заглянуть под капот)

### Что происходит внутри системы:

При открытии файла (твой open() или `fopen()`):
1. Ядро ищет свободный номер в таблице FD процесса
2. Создаёт структуру для работы с файлом
3. Связывает номер (FD) со структурой
4. Возвращает тебе этот номер

При закрытии (твой close() или `fclose()`):
1. Ядро освобождает номер в таблице
2. Удаляет структуру файла
3. Освобождает память
4. Номер можно использовать снова

Если забыл закрыть (классика говнокода):
1. Номер остаётся занятым навечно
2. Структура файла висит в памяти
3. Свободных номеров становится меньше
4. В итоге получаешь EMFILE (**Too many open files**), сука!

---

## 🎯 Практические примеры (чтобы понять на реальном коде)

### Пример 1: Файлы в C (классика жанра)


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

int main() {
// Открываем файл (ядро даёт нам FD)
int fd = open("/etc/passwd", O_RDONLY);
if (fd == -1) {
perror("Блядь, файл не открылся");
return 1;
}

printf("Получили FD: %d\n", fd);

// Читаем через наш FD
char buffer[100];
ssize_t bytes = read(fd, buffer, sizeof(buffer) - 1);
if (bytes > 0) {
buffer[bytes] = '\0';
printf("Прочитали: %s\n", buffer);
}

// ОБЯЗАТЕЛЬНО закрываем FD (иначе утечка!)
close(fd);

return 0;
}


### Пример 2: Python и TCP (чтобы понять сокеты)

```python
import socket

# Создаём TCP сокет (получаем FD для сети)
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
print(f"Сокет получил FD: {sock.fileno()}") try:
# Подключаемся к серверу
sock.connect(('google.com', 80))

# Шлём HTTP запрос
sock.send(b'GET / HTTP/1.0\r\n\r\n')

# Получаем ответ
response = sock.recv(1024)
print(f"Ответ сервера: {response[:50]}")

finally:
# КРИТИЧЕСКИ ВАЖНО: закрываем сокет
sock.close() # Иначе FD утечёт навсегда!


### Пример 3: Bash и pipes (межпроцессная магия)

```bash
#!/bin/bash

# Создаём именованный pipe (получаем FD)
mkfifo /tmp/chat

# Запускаем писателя в фоне
{
echo "Сообщение 1"
sleep 1
echo "Сообщение 2"
echo "Конец связи"
} > /tmp/chat &

# Читаем из pipe
while IFS= read -r line; do
echo "Получили: $line"
if [[ "$line" == "Конец связи" ]]; then
break
fi
done < /tmp/chat

# Убираем pipe (освобождаем ресурс)
rm /tmp/chat


---

## 🚨 Основные правила (чтобы не обосраться)

### 1. Открыл — закрой, блядь!

# НЕПРАВИЛЬНО (путь к утечкам)
exec 3< file.txt
# ... работа с файлом
# exec 3<&- # ЗАБЫЛИ ЗАКРЫТЬ — ПИЗДЕЦ!

# ПРАВИЛЬНО (как нормальный человек)
exec 3< file.txt
# ... работа с файлом
exec 3<&- # ЗАКРЫЛИ, СУКА!


### 2. Проверяй, что система тебе вернула

// НЕПРАВИЛЬНО (для долбоёбов)
int fd = open("file.txt", O_RDONLY);
read(fd, buffer, size); // А если open() сказал "иди нахуй" (-1)?

// ПРАВИЛЬНО (для людей с мозгами)
int fd = open("file.txt", O_RDONLY);
if (fd == -1) {
perror("Система послала нас");
return -1;
}


### 3. Используй автоматическое закрытие (для ленивых)

# НЕПРАВИЛЬНО (классика говнокода)
f = open('file.txt', 'r')
data = f.read()
# f.close() # ЗАБЫЛИ ЗАКРЫТЬ — ДОЛБОЁБЫ!

# ПРАВИЛЬНО (для умных)
with open('file.txt', 'r') as f:
data = f.read()
# Файл закроется сам, даже если ты обосрался с ошибкой


---

## 💡 Заключение: теперь ты знаешь основы

Файловые дескрипторы — это ебучая основа всего ввода-вывода в Unix-системах. Понимание того, как они работают, — это разница между:

- Кодером, который копипастит со Stack Overflow
- Разработчиком, который хотя бы понимает, что происходит под капотом

### Главные истины, которые нужно врезать в память:

1. Каждый открытый ресурс ДОЛЖЕН быть закрыт. Это не "хорошая практика" — это основа выживания системы.

2. FD — это не бесконечный ресурс. Система имеет лимиты, и если их превысить — получишь EMFILE и система встанет колом.

3. Утечки FD убивают систему медленно, но верно. Как рак — сначала незаметно, потом внезапно всё сдохнет.

4. Мониторинг FD критически важен. Если не следишь за дескрипторами — рано или поздно обосрёшься в проде.

### Что дальше?

Во второй части разберём, как именно всё идёт по пизде, когда эти простые правила игнорируют. Посмотрим на реальные примеры говнокода, который убивает системы, и научимся чинить этот пиздец.

Потому что знать теорию — это хорошо, а не допускать, чтобы твоя система сдохла от банальных утечек — это уже _профессионализм_.

---

#FileDescriptors #UnixBasics #НеБудьДолбоёбом #SystemProgramming