MidПрактика12 min

Linux для инженеров

Ключевые команды, файловые системы, networking stack, процессы и взаимодействие PHP с ОС

Зачем инженеру знать Linux

Подавляющее большинство серверов в production работают на Linux. Умение диагностировать проблемы, настраивать систему и понимать поведение ОС -- обязательный навык для backend-разработчика.

Области применения знаний Linux

Область Примеры задач
Отладка production Найти причину высокого CPU, утечки памяти
Настройка серверов Тюнинг sysctl, лимиты ОС, файловые системы
Контейнеризация Dockerfile, cgroups, namespaces
Мониторинг Метрики ОС, логи, системные вызовы
Сеть Firewall, routing, DNS, проблемы соединений

Управление процессами

Основные команды

Команда Описание Полезные флаги
ps Список процессов ps aux, ps -ef
top / htop Интерактивный монитор htop -p PID
kill Отправить сигнал процессу kill -9 PID (SIGKILL)
pgrep / pkill Поиск/kill по имени pgrep -f php-fpm
strace Трассировка системных вызовов strace -p PID -e trace=network
lsof Открытые файлы/сокеты lsof -p PID, lsof -i :8080

Сигналы

Сигналы -- это механизм межпроцессного взаимодействия в Unix.

Сигнал Номер Действие Использование
SIGHUP 1 Перечитать конфиг Graceful reload (nginx, php-fpm)
SIGINT 2 Прервать Ctrl+C
SIGTERM 15 Завершить (graceful) Стандартное завершение
SIGKILL 9 Убить (нельзя перехватить) Последнее средство
SIGUSR1 10 Пользовательский Reopen logs (nginx)
SIGUSR2 12 Пользовательский Graceful restart (php-fpm)
<?php

declare(strict_types=1);

/**
 * Handling Unix signals in PHP CLI scripts.
 * Essential for long-running workers and daemons.
 */
final class GracefulWorker
{
    private bool $shouldStop = false;

    public function __construct(
        private readonly string $queueName,
    ) {
        // Register signal handlers
        pcntl_signal(SIGTERM, [$this, 'handleSignal']);
        pcntl_signal(SIGINT, [$this, 'handleSignal']);
        pcntl_signal(SIGHUP, [$this, 'handleSignal']);
    }

    public function handleSignal(int $signal): void
    {
        match ($signal) {
            SIGTERM, SIGINT => $this->shouldStop = true,
            SIGHUP => $this->reloadConfig(),
            default => null,
        };
    }

    /**
     * Main loop with graceful shutdown support.
     * Process current job, then check for signals.
     */
    public function run(): void
    {
        while (!$this->shouldStop) {
            // Check for pending signals
            pcntl_signal_dispatch();

            $job = $this->fetchJob();

            if ($job === null) {
                // No jobs available, sleep briefly
                usleep(100_000); // 100ms
                continue;
            }

            // Process the job completely before checking signals
            $this->processJob($job);

            // Check for signals after each job
            pcntl_signal_dispatch();
        }

        // Cleanup before exit
        $this->cleanup();
    }

    private function reloadConfig(): void
    {
        // Re-read configuration without restart
    }

    private function fetchJob(): ?array
    {
        // Fetch from queue
        return null;
    }

    private function processJob(array $job): void
    {
        // Process job
    }

    private function cleanup(): void
    {
        // Release resources, close connections
    }
}
## Файловые системы

Основные файловые системы Linux

FS Назначение Особенности
ext4 Стандартная FS Журналирование, до 1 ЭБ
XFS Большие файлы, БД Отличный параллелизм I/O
Btrfs Copy-on-Write Снапшоты, компрессия, RAID
tmpfs Временные файлы в RAM Быстро, данные теряются при перезагрузке
procfs (/proc) Виртуальная FS ядра Информация о процессах и системе
sysfs (/sys) Виртуальная FS устройств Управление устройствами

Структура каталогов

/
├── bin/          # Основные пользовательские команды
├── etc/          # Конфигурация системы
├── home/         # Домашние каталоги пользователей
├── var/          # Переменные данные (логи, кэш, БД)
│   ├── log/      # Системные логи
│   ├── www/      # Веб-приложения
│   └── lib/      # Данные приложений (PostgreSQL, MySQL)
├── tmp/          # Временные файлы
├── proc/         # Виртуальная FS (информация о процессах)
├── sys/          # Виртуальная FS (устройства)
└── dev/          # Файлы устройств

Работа с файлами

Команда Описание Примеры
df -h Свободное место на дисках Мониторинг заполнения
du -sh * Размер каталогов Поиск больших файлов
find / -size +100M Поиск больших файлов Очистка диска
iostat -x 1 Статистика I/O дисков Диагностика I/O bottleneck
iotop I/O по процессам Кто нагружает диск

Networking Stack

Модель Linux-сети

Приложение (PHP, Nginx)
     |
Socket API (userspace)
     |
TCP/UDP (transport layer)
     |
IP (network layer)
     |
Ethernet / Wi-Fi (link layer)
     |
NIC (физический уровень)

Ключевые сетевые команды

Команда Описание Примеры
ss -tlnp Активные TCP-соединения Кто слушает порт
ip addr IP-адреса интерфейсов Сетевая конфигурация
ip route Таблица маршрутизации Проблемы с роутингом
tcpdump Перехват пакетов tcpdump -i eth0 port 80
curl -v HTTP-запросы с деталями Отладка API
dig / nslookup DNS-запросы Проблемы с DNS
mtr Traceroute + ping Диагностика сети

Тюнинг сетевого стека

Для высоконагруженных серверов нужно тюнить параметры ядра:

Параметр Описание Типичное значение
net.core.somaxconn Макс. очередь соединений 65535
net.ipv4.tcp_max_syn_backlog SYN backlog 65535
net.core.netdev_max_backlog Очередь пакетов на NIC 50000
net.ipv4.tcp_tw_reuse Повторное использование TIME_WAIT 1
net.ipv4.ip_local_port_range Диапазон портов 1024 65535

PHP: взаимодействие с ОС

exec, shell_exec, proc_open

PHP предоставляет несколько способов взаимодействия с ОС. Важно понимать различия и выбирать правильный.

<?php

declare(strict_types=1);

/**
 * Safe OS command execution from PHP.
 * NEVER use user input directly in commands - always escape.
 */
final class SystemCommand
{
    /**
     * Execute a command and capture output safely.
     * Uses proc_open for full control over stdin/stdout/stderr.
     *
     * @return array{exit_code: int, stdout: string, stderr: string}
     */
    public static function execute(
        string $command,
        ?float $timeoutSeconds = 30.0,
        ?string $workingDir = null,
    ): array {
        $descriptors = [
            0 => ['pipe', 'r'], // stdin
            1 => ['pipe', 'w'], // stdout
            2 => ['pipe', 'w'], // stderr
        ];

        $process = proc_open(
            $command,
            $descriptors,
            $pipes,
            $workingDir,
        );

        if (!is_resource($process)) {
            throw new \RuntimeException("Failed to execute: $command");
        }

        // Close stdin - we don't need to write
        fclose($pipes[0]);

        // Set non-blocking mode for timeout support
        stream_set_blocking($pipes[1], false);
        stream_set_blocking($pipes[2], false);

        $stdout = '';
        $stderr = '';
        $startTime = microtime(true);

        while (true) {
            $status = proc_get_status($process);

            if (!$status['running']) {
                // Process finished - read remaining output
                $stdout .= stream_get_contents($pipes[1]);
                $stderr .= stream_get_contents($pipes[2]);
                break;
            }

            // Check timeout
            $elapsed = microtime(true) - $startTime;
            if ($timeoutSeconds !== null && $elapsed > $timeoutSeconds) {
                proc_terminate($process, SIGKILL);
                fclose($pipes[1]);
                fclose($pipes[2]);
                proc_close($process);

                throw new \RuntimeException(
                    "Command timed out after {$timeoutSeconds}s: $command"
                );
            }

            // Read available output
            $stdout .= fread($pipes[1], 8192) ?: '';
            $stderr .= fread($pipes[2], 8192) ?: '';

            usleep(10_000); // 10ms polling interval
        }

        fclose($pipes[1]);
        fclose($pipes[2]);
        $exitCode = proc_close($process);

        return [
            'exit_code' => $exitCode,
            'stdout' => trim($stdout),
            'stderr' => trim($stderr),
        ];
    }

    /**
     * Get system information using OS commands.
     *
     * @return array<string, mixed>
     */
    public static function getSystemInfo(): array
    {
        $cpuInfo = self::execute('nproc');
        $memInfo = self::execute("free -m | awk '/Mem:/ {print $2, $3, $4}'");
        $diskInfo = self::execute("df -h / | tail -1 | awk '{print $2, $3, $5}'");
        $loadAvg = self::execute('cat /proc/loadavg');

        return [
            'cpu_cores' => (int) $cpuInfo['stdout'],
            'memory_raw' => $memInfo['stdout'],
            'disk_raw' => $diskInfo['stdout'],
            'load_average' => $loadAvg['stdout'],
            'php_pid' => getmypid(),
            'php_uid' => posix_getuid(),
            'hostname' => gethostname(),
        ];
    }
}
### Мониторинг PHP-процессов из ОС
Метрика Как получить Что значит
PHP-FPM workers ps aux | grep php-fpm Количество активных воркеров
Memory per worker /proc/{pid}/status -> VmRSS Реальное потребление памяти
Open file descriptors ls /proc/{pid}/fd | wc -l Утечки соединений
CPU time /proc/{pid}/stat Время CPU на процесс
<?php

declare(strict_types=1);

/**
 * Reading process information from /proc filesystem.
 * Works only on Linux.
 */
final class ProcessInfo
{
    /**
     * Get memory usage of current process from /proc.
     * More accurate than memory_get_usage() for total process memory.
     *
     * @return array{vm_size_kb: int, vm_rss_kb: int, vm_peak_kb: int}
     */
    public static function getMemoryFromProc(): array
    {
        $pid = getmypid();
        $statusFile = "/proc/{$pid}/status";

        if (!file_exists($statusFile)) {
            throw new \RuntimeException('Not running on Linux or /proc not available');
        }

        $content = file_get_contents($statusFile);
        $result = [];

        // Parse key metrics from /proc/PID/status
        if (preg_match('/VmSize:\s+(\d+)\s+kB/', $content, $m)) {
            $result['vm_size_kb'] = (int) $m[1];
        }

        if (preg_match('/VmRSS:\s+(\d+)\s+kB/', $content, $m)) {
            $result['vm_rss_kb'] = (int) $m[1]; // Resident Set Size
        }

        if (preg_match('/VmPeak:\s+(\d+)\s+kB/', $content, $m)) {
            $result['vm_peak_kb'] = (int) $m[1];
        }

        return $result;
    }

    /**
     * Count open file descriptors for current process.
     * High count may indicate connection leaks.
     */
    public static function getOpenFileDescriptors(): int
    {
        $pid = getmypid();
        $fdDir = "/proc/{$pid}/fd";

        if (!is_dir($fdDir)) {
            return -1;
        }

        return count(scandir($fdDir)) - 2; // subtract . and ..
    }
}
## Практические сценарии

Диагностика высокого CPU

1. top/htop          -- найти процесс с высоким CPU
2. strace -p PID     -- что делает процесс (syscalls)
3. perf top -p PID   -- где тратится время (функции)
4. PHP: Xdebug/Xhprof profiling

Диагностика нехватки памяти

1. free -h            -- общее состояние RAM
2. ps aux --sort=-rss -- процессы по потреблению RAM
3. cat /proc/meminfo  -- детальная информация
4. dmesg | grep -i oom -- были ли OOM kills

Диагностика проблем с диском

1. df -h              -- свободное место
2. iostat -x 1        -- I/O нагрузка и await
3. iotop              -- процессы, нагружающие диск
4. lsof +D /var/log   -- кто пишет в каталог

Совет: Научитесь читать вывод top и iostat. На production-серверах это ваши главные инструменты диагностики. Значение %wa (I/O wait) в top показывает, сколько времени CPU ждёт диск -- если больше 20%, у вас I/O bottleneck.

Выводы

  • Linux -- основная ОС для серверов, знание его инструментов обязательно
  • Сигналы позволяют graceful shutdown и reload без потери данных
  • /proc и /sys -- источники детальной информации о системе
  • Тюнинг sysctl критичен для высоконагруженных серверов
  • PHP может взаимодействовать с ОС через proc_open, pcntl-функции и чтение /proc
  • Умение диагностировать проблемы CPU, памяти, диска и сети -- ключевой навык