User Space — Приложения (процессы) выполняют системный вызов (syscall) для взаимодействия с ядром и получают результат / errno.
Kernel Space — Ядро Linux:
Scheduler (планировщик)
Memory Manager (VM)
VFS (файловая система)
Network Stack
Device Drivers
IPC
Процесс: экземпляр запущенной программы. Имеет собственное адресное пространство, файловые дескрипторы, PID. Изолирован от других процессов.
Поток (Thread): единица выполнения внутри процесса. Потоки одного процесса разделяют адресное пространство, файловые дескрипторы, но имеют собственный стек и регистры.
Системный вызов (syscall): интерфейс между user space и kernel. open(), read(), write(), fork(), mmap() и другие вызовы являются syscall-ами. Переход из user mode в kernel mode через прерывание.
Файловый дескриптор (fd): целое число, ссылка на открытый ресурс ядра (файл, сокет, pipe, устройство). Процесс работает с ресурсами только через fd.
Виртуальная память: каждый процесс видит своё изолированное адресное пространство. MMU + page table транслируют виртуальные адреса в физические. Процессы не видят память друг друга.
errno: глобальная переменная (thread-local), содержащая код последней ошибки syscall-а. Проверяй после каждого вызова, если он вернул ошибку.
Процессы
Адресное пространство процесса
Адресное пространство процесса
Высокие адреса (0xFFFF…) – Низкие адреса (0x0000…):
// fork + exec — основной паттерн запуска программ
pid_t pid =fork();
if (pid ==0) {
// Дочерний процесс
execvp("ls", (char*[]){"ls", "-la", NULL});
perror("exec failed"); // сюда попадём только при ошибке exec
_exit(1);
} elseif (pid >0) {
// Родительский процесс
int status;
waitpid(pid, &status, 0);
if (WIFEXITED(status)) {
printf("Child exited with code %d\n", WEXITSTATUS(status));
}
} else {
perror("fork failed");
}
fork() создаёт почти полную копию процесса. Возвращает 0 в ребёнке, PID ребёнка в родителе. Использует Copy-on-Write (COW), при котором физические страницы памяти копируются только при записи.
Состояния процесса
Состояния процесса
fork() создаёт процесс, далее:
RUNNING – CPU выполняет
READY – в очереди планировщика
SLEEPING – ожидание I/O, таймера, сигнала
STOPPED – SIGSTOP / SIGTSTP (Ctrl+Z)
ZOMBIE – завершён, но родитель не вызвал wait()
REMOVED – родитель вызвал wait(), PID освобождён
Коды состояний процесса
D – Uninterruptible sleep (ожидание I/O, нельзя прервать сигналом)
R – Running / Runnable
S – Interruptible sleep (ожидание события)
T – Stopped (SIGSTOP)
Z – Zombie (завершён, ждёт wait() от родителя)
Инструменты
# Информация о процессахps aux # все процессыps -ef # все процессы (другой формат)ps -eLf # с потоками (LWP)ps -o pid,ppid,state,rss,vsz,comm # кастомный формат# Дерево процессовpstree -p # с PID-амиpstree -p <pid> # поддерево конкретного процесса# Интерактивный мониторингtop # классическийhtop # удобнее (F6 сортировка, F5 дерево)btop # современный# Детали процессаcat /proc/<pid>/status # статус, память, потокиcat /proc/<pid>/maps # карта памятиcat /proc/<pid>/fd/ # открытые файловые дескрипторыls -la /proc/<pid>/fd # то же через lscat /proc/<pid>/cmdline | tr '\0'' '# командная строкаcat /proc/<pid>/environ | tr '\0''\n'# переменные окруженияcat /proc/<pid>/limits # лимиты (ulimit)cat /proc/<pid>/io # статистика I/O# Файловые дескрипторыlsof -p <pid> # открытые файлы процессаlsof -i :8080 # кто слушает порт 8080lsof +D /path/to/dir # кто использует директорию# Системные вызовыstrace -p <pid> # трассировка syscall-ов работающего процессаstrace -f -e trace=open,read,write cmd # трассировка конкретных syscall-овstrace -c cmd # статистика syscall-ов (сколько, время)strace -e trace=network cmd # только сетевые вызовы# Библиотечные вызовыltrace cmd # трассировка вызовов libc# Лимитыulimit -a # текущие лимитыulimit -n # макс. файловых дескрипторовulimit -n 65536# установить (для текущего shell)cat /proc/sys/fs/file-max # системный лимит# /etc/security/limits.conf (постоянные лимиты)# * soft nofile 65536# * hard nofile 65536
Zombie и Orphan
Zombie: процесс завершился, но родитель не вызвал wait(). Занимает PID и запись в таблице процессов. Решение: исправить родителя или убить родителя (init/systemd подберёт zombie).
Orphan: родитель завершился раньше ребёнка. Ребёнок «усыновляется» init (PID 1) или subreaper. Это нормально.
В обработчике сигнала нельзя: malloc, printf, mutex lock, вызывать любые async-signal-unsafe функции. Только атомарные записи в volatile sig_atomic_t или write() в fd.
Потоки (Threads)
POSIX Threads (pthreads)
#include<pthread.h>void*worker(void*arg) {
int id =*(int*)arg;
printf("Thread %d started\n", id);
// работа...
return NULL;
}
intmain() {
pthread_t threads[4];
int ids[4];
for (int i =0; i <4; i++) {
ids[i] = i;
pthread_create(&threads[i], NULL, worker, &ids[i]);
}
for (int i =0; i <4; i++) {
pthread_join(threads[i], NULL); // ждать завершения
}
}
// Компиляция: gcc -pthread program.c
Модели многопоточности
Модели многопоточности
1:1 – один user thread = один kernel thread (Linux pthreads, Go runtime). Полный параллелизм, планировщик ОС.
M:N – M user threads на N kernel threads (Go goroutines, Erlang processes). User-space планировщик + kernel threads. Go: goroutine = лёгкий «поток» (~2KB стека), M:N на GOMAXPROCS kernel threads.
Green threads – все в user space, нет параллелизма (Python GIL, Ruby GIL). Конкурентность есть, параллелизм – нет.
Конкурентность vs Параллелизм
Конкурентность vs Параллелизм
Конкурентность (Concurrency): Управление несколькими задачами одновременно. Может быть на 1 ядре (переключение контекста). Структура программы.
Параллелизм (Parallelism): Выполнение нескольких задач одновременно. Требует несколько ядер/процессоров. Способ выполнения.
Конкурентность без параллелизма: event loop (Node.js), async/await (Python asyncio)
Параллелизм без конкурентности: SIMD, GPU-вычисления
Оба: Go goroutines, Rust tokio, Java virtual threads
Примитивы синхронизации
Примитивы синхронизации
Mutex (Mutual Exclusion):lock() – захватить (если занят – ждать), unlock() – освободить. Защищает критическую секцию. Один поток одновременно.
RWLock (Read-Write Lock):read_lock() – несколько читателей одновременно, write_lock() – эксклюзивный доступ (ждёт пока все читатели отпустят). Оптимизация для read-heavy нагрузки.
Semaphore:wait() / acquire() – уменьшить счётчик (если 0 – ждать), post() / release() – увеличить счётчик. Ограничивает одновременный доступ N потокам.
Condition Variable:wait(mutex) – отпустить mutex + заснуть, при пробуждении – захватить mutex. signal() / notify() – разбудить один ожидающий поток. broadcast() – разбудить все ожидающие потоки.
Spinlock: Как mutex, но вместо сна – busy-wait (крутится в цикле). Для очень коротких критических секций. Не спит – тратит CPU.
Atomic operations: compare_and_swap (CAS), fetch_and_add, load, store. Без блокировок (lock-free). Для счётчиков, флагов.
Data Race: Два потока одновременно обращаются к одной памяти, хотя бы один пишет. Результат недетерминирован. Решение: mutex, atomic, не делить mutable state.
Deadlock: Поток A держит lock1 и ждёт lock2. Поток B держит lock2 и ждёт lock1. Оба ждут вечно. Решение: всегда захватывать locks в одном порядке, использовать trylock с таймаутом.
Livelock: Потоки реагируют друг на друга, но не продвигаются. Как два человека, уступающих друг другу в коридоре.
False Sharing: Потоки пишут в разные переменные, но они попали в одну cache line (64 байта). Каждая запись инвалидирует cache line на всех ядрах. Решение: padding (выравнивание по cache line).
Huge Page – 2MB или 1GB (для больших объёмов данных)
TLB (Translation Lookaside Buffer) – кеш page table в CPU. TLB miss – обращение к page table в RAM (медленно).
Page Fault
Page Fault
Minor page fault: Страница есть в RAM, но нет в page table процесса. Быстро – обновить page table. Пример: COW после fork(), доступ к mmap-файлу уже в page cache.
Major page fault: Страницы нет в RAM – нужно читать с диска. Медленно (~10ms для HDD, ~0.1ms для SSD). Пример: swap in, первое обращение к mmap-файлу.
malloc и аллокаторы
malloc и аллокаторы
malloc(size): Маленькие аллокации (<128KB) – brk()/sbrk() – расширение heap. Большие аллокации (>=128KB) – mmap() – отдельный регион.
free(ptr): Память возвращается аллокатору, но не всегда ОС. Аллокатор переиспользует освобождённые блоки.
jemalloc – Facebook, меньше фрагментация (Redis, Rust)
tcmalloc – Google, per-thread cache (Go runtime основан на идее)
mimalloc – Microsoft, компактный, быстрый
mmap
// Маппинг файла в память
int fd =open("data.bin", O_RDONLY);
void*ptr =mmap(NULL, file_size, PROT_READ, MAP_PRIVATE, fd, 0);
// ptr указывает на содержимое файла — доступ как к массиву
// Данные подгружаются лениво (page fault при первом обращении)
munmap(ptr, file_size);
// Анонимный маппинг (не привязан к файлу)
void*mem =mmap(NULL, size, PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
// Shared маппинг (IPC — несколько процессов видят одну память)
void*shared =mmap(NULL, size, PROT_READ | PROT_WRITE,
MAP_SHARED | MAP_ANONYMOUS, -1, 0);
Флаги mmap
MAP_PRIVATE – изменения видны только этому процессу (COW)
MAP_SHARED – изменения видны всем, записываются в файл
MAP_ANONYMOUS – без файла (аналог malloc для больших блоков)
OOM Killer
Когда физическая память исчерпана, ядро убивает процесс с наибольшим oom_score:
# OOM score процесса (чем выше — тем вероятнее будет убит)cat /proc/<pid>/oom_score
# Защитить процесс от OOM killerecho -1000 > /proc/<pid>/oom_score_adj # никогда не убиватьecho 1000 > /proc/<pid>/oom_score_adj # убить первым# Отключить overcommit (строгий режим)# /etc/sysctl.conf:# vm.overcommit_memory = 2# vm.overcommit_ratio = 80
Overcommit: Linux по умолчанию разрешает malloc() больше памяти, чем физически доступно. Реальная память выделяется при первой записи. Если её не хватает, срабатывает OOM killer.
Инструменты
# Память системыfree -h # общее состояниеvmstat 1# виртуальная память (каждую секунду)cat /proc/meminfo # детальная информация# Память процессаcat /proc/<pid>/status | grep -E 'VmRSS|VmSize|VmPeak'# VmSize — виртуальная (выделено)# VmRSS — резидентная (реально в RAM)# VmPeak — максимум за время жизниpmap -x <pid> # карта памяти процессаsmem -p # USS/PSS/RSS для всех процессов# USS — уникальная память (не shared)# PSS — пропорциональная (shared / кол-во процессов)# RSS — резидентная (включая shared)# Утечки памятиvalgrind --leak-check=full ./program # C/C++# Go: pprof (net/http/pprof)# Python: tracemalloc, objgraph# Java: jmap, jcmd, VisualVM# Perf (профилирование)perf stat ./program # счётчики (cycles, instructions, cache misses)perf record -g ./program # запись профиляperf report # анализ
Иерархия памяти
Уровень
Размер
Задержка
Пропускная способность
L1 cache (ядро)
32-64 KB
~1 ns
~1 TB/s
L2 cache (ядро)
256 KB-1 MB
~3-5 ns
~500 GB/s
L3 cache (shared)
8-64 MB
~10-20 ns
~200 GB/s
RAM
16-512 GB
~50-100 ns
~50 GB/s
SSD (NVMe)
TB
~10-100 us
~5 GB/s
HDD
TB
~5-10 ms
~200 MB/s
Сеть (LAN)
–
~0.5 ms
~10 Gbps
Сеть (Internet)
–
~10-100 ms
varies
Числа, которые нужно знать
L1 cache reference: 1 ns
L2 cache reference: 4 ns
RAM reference: 100 ns
SSD random read: 16,000 ns = 16 us
HDD seek: 10,000,000 ns = 10 ms
Отправка пакета в LAN: 500,000 ns = 0.5 ms
Round-trip в датацентре: 1,000,000 ns = 1 ms
Cache line = 64 байта. Последовательный доступ к данным (arrays) гораздо быстрее случайного (linked lists, hash maps) из-за CPU prefetch и cache locality.
Файловая система и I/O
Файловые дескрипторы
Файловые дескрипторы
Каждый процесс имеет таблицу fd:
0 – stdin
1 – stdout
2 – stderr
3, 4, 5, … – открытые файлы, сокеты, pipes
fd – File Description (в ядре) – inode – данные на диске
Путь данных: Приложение – libc buffer (fwrite) – kernel page cache – диск
User Space:fwrite() записывает в stdio buffer. fflush() сбрасывает буфер в ядро.
Kernel Space: данные попадают в page cache (RAM). fsync()/fdatasync() сбрасывают на диск.
fflush() – сбросить libc буфер в kernel page cache
fsync(fd) – сбросить page cache на физический диск (данные + метаданные)
fdatasync(fd) – сбросить только данные (без метаданных, быстрее)
sync() – сбросить все буферы всех файлов
I/O модели
Модели I/O
1. Блокирующий I/O (по умолчанию): read(fd) – процесс спит, пока данные не будут готовы. Прост, но один поток обслуживает один fd.
2. Неблокирующий I/O:O_NONBLOCK: read(fd) – если данных нет, сразу EAGAIN. Нужно poll-ить в цикле (busy wait) или с мультиплексированием.
3. I/O мультиплексирование (event-driven): epoll / select / poll: ожидать события на многих fd одновременно. Один поток обслуживает тысячи соединений.
4. Асинхронный I/O: io_uring (Linux 5.1+): отправить запрос – ядро выполнит – уведомит. Нет блокировки, нет syscall на каждую операцию.
epoll: основа всех event loop-ов
// epoll — эффективное мультиплексирование I/O (Linux)
// Используется в: nginx, Node.js (libuv), Go runtime, Redis, ...
int epfd =epoll_create1(0);
struct epoll_event ev = {
.events = EPOLLIN | EPOLLET, // чтение, edge-triggered
.data.fd = server_fd,
};
epoll_ctl(epfd, EPOLL_CTL_ADD, server_fd, &ev);
struct epoll_event events[MAX_EVENTS];
while (1) {
int n =epoll_wait(epfd, events, MAX_EVENTS, -1); // -1 = ждать бесконечно
for (int i =0; i < n; i++) {
if (events[i].data.fd == server_fd) {
// новое соединение → accept()
} else {
// данные готовы → read()
}
}
}
Сравнение механизмов мультиплексирования
select() – старый, лимит 1024 fd, O(n) на каждый вызов
poll() – без лимита на количество fd, но всё ещё O(n)
epoll() – O(1) для ожидания, O(n) только для готовых fd. Linux-specific.
kqueue() – аналог epoll для BSD/macOS
Level-triggered (LT): уведомлять пока данные доступны (по умолчанию, проще)
Edge-triggered (ET): уведомлять один раз при появлении данных (быстрее, сложнее)
io_uring
io_uring
Современный асинхронный I/O (Linux 5.1+). Submission Queue + Completion Queue. Минимум syscall-ов: один вызов может отправить множество запросов.
Используется: Rust (tokio, glommio), Java (Netty), базы данных.
Submission Queue (SQ) [read, write, …] – ядро обрабатывает – Completion Queue (CQ) [результаты]. Shared memory между user space и kernel – zero-copy для метаданных.
Полезные утилиты для I/O
# Мониторинг I/Oiostat -x 1# статистика дисков (каждую секунду)iotop # процессы с наибольшим I/Opidstat -d 1# I/O по процессам# Файловая системаdf -h # использование дисковdu -sh /path # размер директорииfindmnt # точки монтированияstat /path/to/file # метаданные файла (inode, size, timestamps)filefrag -v /path/to/file # фрагментация файла# Производительностьdd if=/dev/zero of=test bs=1M count=1024 oflag=direct # тест записи (~sequential)fio --name=randread --ioengine=libaio --direct=1\
--bs=4k --size=1G --numjobs=4 --rw=randread # тест случайного чтения
Контейнеры не являются виртуальными машинами. Это процессы с изоляцией через механизмы ядра:
Namespaces (изоляция)
Linux Namespaces
PID namespace – свои PID-ы (PID 1 внутри контейнера)
Network namespace – своя сеть (интерфейсы, IP, порты, iptables)
Mount namespace – своя файловая система (chroot на стероидах)
UTS namespace – свой hostname
User namespace – свои UID/GID (root в контейнере != root на хосте)
IPC namespace – своя shared memory, semaphores
Cgroup namespace – свой view на cgroups
# Создать процесс в новых namespacesunshare --pid --fork --mount-proc bash # новый PID namespace# ps aux покажет только процессы внутри namespace# Войти в namespace другого процессаnsenter -t <pid> -p -n -m bash # PID + Net + Mount namespaces контейнераnsenter -t $(docker inspect -f '{{.State.Pid}}' mycontainer) -p -n bash
# Просмотр namespacesls -la /proc/<pid>/ns/ # namespaces процессаlsns # все namespaces в системе
Cgroups (ограничение ресурсов)
# cgroups v2 (unified hierarchy)# /sys/fs/cgroup/# CPUecho 100000 > /sys/fs/cgroup/mygroup/cpu.max # 100ms из 100ms (100% одного ядра)echo "50000 100000" > /sys/fs/cgroup/mygroup/cpu.max # 50% одного ядра# Памятьecho 256M > /sys/fs/cgroup/mygroup/memory.max # лимит 256MBecho 128M > /sys/fs/cgroup/mygroup/memory.high # throttling с 128MB# Процессыecho <pid> > /sys/fs/cgroup/mygroup/cgroup.procs # добавить процесс# Docker использует cgroups автоматически:docker run --memory=256m --cpus=0.5 myapp
Capabilities (гранулярные права root)
Linux Capabilities
Вместо «root или не root» – набор отдельных привилегий:
CAP_SYS_ADMIN – mount, sethostname, … (почти как root)
CAP_DAC_OVERRIDE – обходить проверки прав файлов
CAP_CHOWN – менять владельца файлов
# Посмотреть capabilities процессаgetpcaps <pid>
cat /proc/<pid>/status | grep Cap
# Дать бинарнику capabilitysudo setcap cap_net_bind_service=+ep /path/to/binary
# Теперь binary может слушать порт 80 без root# Docker: урезать capabilitiesdocker run --cap-drop=ALL --cap-add=NET_BIND_SERVICE myapp
seccomp (фильтрация syscall-ов)
# Docker по умолчанию блокирует ~44 syscall-а# Например: mount, reboot, kexec_load, ...# Свой профильdocker run --security-opt seccomp=profile.json myapp
# Отключить (не для prod)docker run --security-opt seccomp=unconfined myapp
Systemd
# Управление сервисамиsystemctl start nginx
systemctl stop nginx
systemctl restart nginx
systemctl reload nginx # перечитать конфиг без рестартаsystemctl status nginx # статус + последние логиsystemctl enable nginx # автозапуск при загрузкеsystemctl disable nginx
systemctl is-active nginx
systemctl is-enabled nginx
# Логиjournalctl -u nginx # логи сервисаjournalctl -u nginx -f # followjournalctl -u nginx --since "1 hour ago"journalctl -u nginx -p err # только ошибкиjournalctl -k # логи ядра (kernel)journalctl --disk-usage # сколько места занимают логи
# После изменения unit-файлаsystemctl daemon-reload
systemctl restart myapp
# Проверить конфигурациюsystemd-analyze verify /etc/systemd/system/myapp.service
systemd-analyze security myapp # аудит безопасности
Производительность: инструменты
Общая картина
# Быстрая диагностика (60-секундный чеклист)uptime # load averagedmesg -T | tail # ошибки ядраvmstat 15# CPU, memory, I/Ompstat -P ALL 1# CPU по ядрамpidstat 1# CPU по процессамiostat -xz 1# дисковый I/Ofree -h # памятьsar -n DEV 1# сетевой трафикss -tlnp # слушающие порты
CPU
# Load averageuptime
# 3 числа: за 1, 5, 15 минут# load = количество процессов в Running + Waiting for I/O# load = число ядер → CPU загружен на 100%# load > число ядер → очередь, процессы ждутnproc # количество ядерcat /proc/cpuinfo # информация о CPU# По процессамtop -o %CPU # сортировка по CPUpidstat 1# CPU по процессам/потокам# Профилированиеperf top # real-time профиль (какие функции жгут CPU)perf record -g -p <pid> -- sleep 10# запись 10 секундperf report # анализ# Flame graphperf record -g -p <pid> -- sleep 30perf script | stackcollapse-perf.pl | flamegraph.pl > flame.svg
Диск
iostat -x 1# %util — загруженность диска (>80% = bottleneck)# await — среднее время ожидания I/O (ms)# r/s, w/s — операции чтения/записи в секунду# По процессамiotop -oPa # только активные, накопительноpidstat -d 1# I/O по процессам
Сеть
# Пропускная способностьsar -n DEV 1# трафик по интерфейсамnload # визуальный мониторингiftop # трафик по соединениям# Соединенияss -s # статистика (total, established, time-wait, ...)ss -tan state established | wc -l # количество establishedconntrack -L | wc -l # количество отслеживаемых соединений (NAT)# Пакетыcat /proc/net/netstat | awk '{print $1}'# статистика TCPnetstat -s # то же, человеко-читаемоnstat -sz # дельта-статистика# Потери и ретрансмитыss -ti # TCP info (retransmits, cwnd, rtt)netstat -s | grep -i retrans
/proc и /sys (виртуальные файловые системы)
# /proc — информация о процессах и ядре/proc/<pid>/ — всё о конкретном процессе
/proc/cpuinfo — информация о CPU
/proc/meminfo — информация о памяти
/proc/loadavg — load average
/proc/net/ — сетевая статистика
/proc/sys/ — настройки ядра (sysctl)# /sys — информация об устройствах и драйверах/sys/class/net/ — сетевые интерфейсы
/sys/block/ — блочные устройства
/sys/fs/cgroup/ — cgroups
# Изменение параметров ядраsysctl -a # все параметрыsysctl vm.swappiness # конкретныйsysctl -w vm.swappiness=10# изменить (до перезагрузки)# /etc/sysctl.conf — постоянные изменения
Тюнинг для высоконагруженного сервера
# /etc/sysctl.conf# Памятьvm.swappiness =10# использовать swap неохотноvm.overcommit_memory =0# эвристический overcommit# Сетьnet.core.somaxconn =65535# backlog для listen()net.core.netdev_max_backlog =65535# очередь входящих пакетовnet.ipv4.tcp_max_syn_backlog =65535# SYN backlognet.ipv4.ip_local_port_range =102465535# ephemeral-портыnet.ipv4.tcp_tw_reuse =1# переиспользование TIME_WAITnet.ipv4.tcp_fin_timeout =15# таймаут FIN_WAIT_2net.ipv4.tcp_keepalive_time =300# keepalive через 5 минутnet.ipv4.tcp_keepalive_intvl =30# интервал keepalivenet.ipv4.tcp_keepalive_probes =5# попыток keepalivenet.ipv4.tcp_slow_start_after_idle =0# не сбрасывать cwnd при idle# Файлыfs.file-max =2097152# максимум fd в системеfs.inotify.max_user_watches =524288# для file watchers (IDE, hot reload)# /etc/security/limits.conf# * soft nofile 65536# * hard nofile 65536
Частые проблемы
Процесс не отвечает (D-state):
ps aux | awk '$8 ~ /D/'# найти процессы в D-statecat /proc/<pid>/wchan # на чём заблокированcat /proc/<pid>/stack # стек ядра (kernel stack)dmesg -T | tail # ошибки ядра
D-state = uninterruptible sleep, обычно ожидание I/O. Даже kill -9 не поможет. Причины: зависший NFS, проблемы с диском, баг в драйвере.
Too many open files:
# Проверить лимитulimit -n # текущий лимитcat /proc/<pid>/limits | grep "open files"# Сколько открытоls /proc/<pid>/fd | wc -l
lsof -p <pid> | wc -l
# Увеличитьulimit -n 65536# для текущего shell# Или через systemd: LimitNOFILE=65536# Или /etc/security/limits.conf
Высокий load average при низком CPU:
# Load average включает процессы, ожидающие I/O (D-state)vmstat 1# wa = I/O waitiostat -x 1# %util диска
Причина: I/O bottleneck (медленный диск, NFS, swap). Решение: оптимизировать I/O, добавить RAM, перейти на SSD.
OOM killer убивает процессы:
dmesg -T | grep -i "oom\|killed"# кто был убитjournalctl -k | grep -i oom
# Защитить важный процессecho -1000 > /proc/<pid>/oom_score_adj
# Найти, кто жрёт памятьps aux --sort=-%mem | head -20
smem -rs pss | tail -20
Port already in use:
ss -tlnp | grep :8080 # кто слушает портfuser -k 8080/tcp # убить процесс на порту (осторожно)# Причина: TIME_WAIT от предыдущего процесса# Решение: SO_REUSEADDR в коде или:sysctl net.ipv4.tcp_tw_reuse=1
Утечка файловых дескрипторов:
# Мониторинг количества открытых fdwatch -n1 'ls /proc/<pid>/fd | wc -l'# Что именно открытоls -la /proc/<pid>/fd # типы: socket, pipe, файлыlsof -p <pid>
Причина: не закрытые файлы, сокеты, pipes в коде. В C используйте close(fd) в finally / defer / destructor. В Go используйте defer f.Close(). Утечка fd приводит к “too many open files”.
Swap thrashing (система еле шевелится):
free -h # Swap used?vmstat 1# si/so (swap in/out) > 0?swapon --show # swap-устройства# Уменьшить использование swapsysctl vm.swappiness=10# Или добавить RAM / убить жрущие процессы