← Назад к вопросам

Как послать сигнал процессу?

1.0 Junior🔥 251 комментариев
#Linux и операционные системы

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI28 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Как послать сигнал процессу

Что такое сигналы

Сигналы — это способ асинхронной коммуникации между процессами в Unix/Linux. Это программное прерывание, которое может заставить процесс выполнить определённое действие.

Основные сигналы:

  • SIGTERM (15): мягкое завершение процесса (по умолчанию)
  • SIGKILL (9): принудительное завершение (нельзя перехватить)
  • SIGSTOP (19): остановить процесс (нельзя перехватить)
  • SIGCONT (18): возобновить остановленный процесс
  • SIGHUP (1): перезагрузить конфигурацию
  • SIGUSR1, SIGUSR2: пользовательские сигналы для приложения
  • SIGPIPE: попытка записи в закрытый pipe

1. Команда kill из shell

Простейший способ — использовать kill:

# Отправить SIGTERM (15) процессу с PID 1234
kill 1234

# Отправить конкретный сигнал
kill -SIGTERM 1234
kill -15 1234

# Принудительное завершение (SIGKILL)
kill -9 1234

# Остановить процесс
kill -SIGSTOP 1234

# Возобновить процесс
kill -SIGCONT 1234

# Отправить сигнал ВСЕМ процессам в процесс-группе
kill -15 -1234

2. Функция kill() в C/C++

#include <signal.h>
#include <unistd.h>

int kill(pid_t pid, int sig);
// Возвращает: 0 на успех, -1 на ошибку

Примеры:

#include <signal.h>
#include <unistd.h>
#include <iostream>

int main() {
    pid_t target_pid = 5678;  // PID процесса, которому отправляем сигнал
    
    // Отправить SIGTERM
    if (kill(target_pid, SIGTERM) == -1) {
        perror("kill");  // Ошибка: нет прав, процесс не существует
        return 1;
    }
    
    std::cout << "SIGTERM sent to process " << target_pid << std::endl;
    
    // Отправить SIGUSR1
    kill(target_pid, SIGUSR1);
    
    // Проверить существует ли процесс (отправить сигнал 0)
    if (kill(target_pid, 0) == -1) {
        std::cout << "Process does not exist" << std::endl;
    } else {
        std::cout << "Process exists" << std::endl;
    }
    
    return 0;
}

3. Обработка сигналов в процессе

Процесс может перехватить сигнал и выполнить пользовательский код:

#include <signal.h>
#include <iostream>
#include <unistd.h>

bool shutdown_requested = false;

// Обработчик сигнала
void signal_handler(int sig) {
    if (sig == SIGTERM) {
        std::cout << "Received SIGTERM, shutting down gracefully..." << std::endl;
        shutdown_requested = true;
    } else if (sig == SIGUSR1) {
        std::cout << "Received SIGUSR1, reloading config..." << std::endl;
        // reload_config();
    }
}

int main() {
    // Зарегистрировать обработчик
    signal(SIGTERM, signal_handler);
    signal(SIGUSR1, signal_handler);
    
    // Основной цикл
    while (!shutdown_requested) {
        std::cout << "Working..." << std::endl;
        sleep(1);
    }
    
    std::cout << "Shutdown complete" << std::endl;
    return 0;
}

4. Современный подход: sigaction()

signal() старый, есть проблемы. Используй sigaction():

#include <signal.h>
#include <iostream>

bool should_exit = false;

void handler(int sig) {
    should_exit = true;
}

int main() {
    struct sigaction sa;
    sa.sa_handler = handler;        // Функция-обработчик
    sigemptyset(&sa.sa_mask);       // Нет дополнительных блокируемых сигналов
    sa.sa_flags = 0;                // Стандартное поведение
    
    // Регистрируем обработчик
    sigaction(SIGTERM, &sa, nullptr);
    sigaction(SIGINT, &sa, nullptr);
    
    while (!should_exit) {
        std::cout << "Running..." << std::endl;
        sleep(1);
    }
    
    return 0;
}

5. Блокировка и отмена блокировки сигналов

Можно временно заблокировать определённые сигналы:

#include <signal.h>

int main() {
    sigset_t set;
    
    // Создать набор сигналов
    sigemptyset(&set);  // Пустой набор
    sigaddset(&set, SIGTERM);  // Добавить SIGTERM
    sigaddset(&set, SIGUSR1);  // Добавить SIGUSR1
    
    // Заблокировать эти сигналы
    sigprocmask(SIG_BLOCK, &set, nullptr);
    // Теперь SIGTERM и SIGUSR1 не будут доставлены
    
    // ... критичный код ...
    
    // Разблокировать
    sigprocmask(SIG_UNBLOCK, &set, nullptr);
    
    return 0;
}

6. Отправка сигнала от одного процесса к другому

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

#include <signal.h>
#include <unistd.h>
#include <iostream>
#include <sys/wait.h>

int main() {
    pid_t pid = fork();
    
    if (pid == 0) {
        // Потомок: ловит сигналы
        signal(SIGTERM, [](int) { 
            std::cout << "Child: Received SIGTERM\n"; 
            exit(0);
        });
        
        while (true) {
            std::cout << "Child working..." << std::endl;
            sleep(1);
        }
    } else {
        // Родитель: отправляет сигналы
        sleep(3);  // Дать потомку время на инициализацию
        
        std::cout << "Parent: Sending SIGTERM to child " << pid << std::endl;
        kill(pid, SIGTERM);  // Отправить сигнал
        
        wait(nullptr);  // Дождаться завершения потомка
        std::cout << "Parent: Child terminated" << std::endl;
    }
    
    return 0;
}

7. Сигнал от пользователя (Ctrl+C)

Когда пользователь нажимает Ctrl+C, система отправляет SIGINT:

#include <signal.h>
#include <iostream>

bool interrupted = false;

void handle_interrupt(int sig) {
    std::cout << "\nCtrl+C pressed, shutting down..." << std::endl;
    interrupted = true;
}

int main() {
    signal(SIGINT, handle_interrupt);
    
    while (!interrupted) {
        std::cout << "Processing..." << std::endl;
        sleep(1);
    }
    
    std::cout << "Cleanup completed" << std::endl;
    return 0;
}

8. Практический пример: graceful shutdown

#include <signal.h>
#include <iostream>
#include <thread>
#include <atomic>
#include <vector>

class Server {
private:
    std::atomic<bool> running{true};
    std::vector<std::thread> worker_threads;
    
public:
    static Server* instance;
    
    Server() {
        instance = this;
        // Регистрируем обработчик
        signal(SIGTERM, Server::handle_signal);
        signal(SIGINT, Server::handle_signal);
    }
    
    static void handle_signal(int sig) {
        std::cout << "Received signal " << sig << ", shutting down..." << std::endl;
        if (instance) {
            instance->running = false;
        }
    }
    
    void run() {
        for (int i = 0; i < 4; i++) {
            worker_threads.emplace_back([this]() {
                while (running) {
                    // выполнять работу
                    std::cout << "Working..." << std::endl;
                    sleep(1);
                }
            });
        }
        
        // Дождаться завершения всех потоков
        for (auto& t : worker_threads) {
            t.join();
        }
        
        std::cout << "Server shut down gracefully" << std::endl;
    }
};

Server* Server::instance = nullptr;

int main() {
    Server server;
    server.run();
    return 0;
}

9. Таблица основных сигналов

НомерИмяПо умолчаниюОписание
1SIGHUPTerminateHangup
2SIGINTTerminateInterrupt (Ctrl+C)
3SIGQUITCore dumpQuit
9SIGKILLTerminateKill (нельзя перехватить)
15SIGTERMTerminateTermination signal
18SIGCONTContinueContinue stopped process
19SIGSTOPStopStop (нельзя перехватить)
20SIGTSTPStopStop from tty (Ctrl+Z)
10SIGUSR1TerminateUser-defined
12SIGUSR2TerminateUser-defined

Резюме

Послать сигнал из shell:

kill -SIGTERM <pid>

Послать сигнал из C++:

kill(pid, SIGTERM);

Обработать сигнал:

signal(SIGTERM, handler);
// или лучше:
sigaction(SIGTERM, &sa, nullptr);

Graceful shutdown: обработай SIGTERM, установи флаг, завершися чистенько.