Skip to main content
LibreTexts - Ukrayinska

5.8: Сигнали

  • Page ID
    34487
    \( \newcommand{\vecs}[1]{\overset { \scriptstyle \rightharpoonup} {\mathbf{#1}} } \) \( \newcommand{\vecd}[1]{\overset{-\!-\!\rightharpoonup}{\vphantom{a}\smash {#1}}} \)\(\newcommand{\id}{\mathrm{id}}\) \( \newcommand{\Span}{\mathrm{span}}\) \( \newcommand{\kernel}{\mathrm{null}\,}\) \( \newcommand{\range}{\mathrm{range}\,}\) \( \newcommand{\RealPart}{\mathrm{Re}}\) \( \newcommand{\ImaginaryPart}{\mathrm{Im}}\) \( \newcommand{\Argument}{\mathrm{Arg}}\) \( \newcommand{\norm}[1]{\| #1 \|}\) \( \newcommand{\inner}[2]{\langle #1, #2 \rangle}\) \( \newcommand{\Span}{\mathrm{span}}\) \(\newcommand{\id}{\mathrm{id}}\) \( \newcommand{\Span}{\mathrm{span}}\) \( \newcommand{\kernel}{\mathrm{null}\,}\) \( \newcommand{\range}{\mathrm{range}\,}\) \( \newcommand{\RealPart}{\mathrm{Re}}\) \( \newcommand{\ImaginaryPart}{\mathrm{Im}}\) \( \newcommand{\Argument}{\mathrm{Arg}}\) \( \newcommand{\norm}[1]{\| #1 \|}\) \( \newcommand{\inner}[2]{\langle #1, #2 \rangle}\) \( \newcommand{\Span}{\mathrm{span}}\)

    Процеси, що працюють в системі, вимагають способу розповісти про події, які на них впливають. У UNIX існує інфраструктура між ядром і процесами, які називаються сигналами, що дозволяє процесу отримувати повідомлення про важливі для нього події.

    Коли сигнал надсилається процесу, ядро викликає обробник, який процес повинен зареєструвати в ядрі для роботи з цим сигналом. Обробник - це просто розроблена функція в коді, яка була написана спеціально для боротьби з перериванням. Часто сигнал буде надсилатися зсередини самого ядра, однак також часто один процес надсилає сигнал іншому процесу (одна форма міжпроцесного зв'язку). Обробник сигналу викликається асинхронно; тобто поточна запущена програма переривається від того, що вона робить для обробки події сигналу.

    Наприклад, одним з типів сигналу є переривання (визначене в системних заголовках як SIGINT), яке доставляється в процес при натисканні комбінації ctrl-c.

    Оскільки процес використовує системний виклик читання для читання введення з клавіатури, ядро буде спостерігати за потоком введення, шукаючи спеціальні символи. Якщо він побачить ctrl-c, він перейде в режим обробки сигналів. Ядро буде шукати, чи зареєстрував процес обробник для цього переривання. Якщо він має, то виконання буде передано цій функції, де функція буде обробляти її. Якщо процес не зареєстрував обробник для цього конкретного сигналу, ядро зробить деяку дію за замовчуванням. За допомогою ctrl-c типовою дією є припинення процесу.

    Процес може ігнорувати деякі сигнали, але інші сигнали не дозволяється ігнорувати. Наприклад, SIGKILL - це сигнал, який надсилається, коли процес повинен бути припинений. Ядро побачить, що процесу був посланий цей сигнал і припинить процес від запуску, ніяких питань. Процес не може попросити ядро ігнорувати цей сигнал, і ядро дуже обережно ставиться до того, який процес дозволено надсилати цей сигнал іншому процесу; ви можете надсилати його лише процесам, що належать вам, якщо ви не є користувачем root. Можливо, ви бачили команду kill -9; це походить від сигналу SIGKILL реалізації. Загальновідомо, що SIGKILL насправді визначається як 0x9, і тому, коли вказано як аргумент для програми вбивства означає, що процес, вказаний, буде негайно зупинений. Оскільки процес не може вибрати ігнорувати або обробляти цей сигнал, він розглядається як проспект останньої надії, оскільки програма не матиме шансів очистити або вийти чисто. Вважається, що краще спочатку відправити SIGTERM (для припинення) до процесу, і якщо він зазнав аварії або іншим чином не вийде, то вдатися до SIGKILL. Як правило, більшість програм встановлять обробник для SIGHUP (зависання - залишилося від днів послідовних терміналів і модемів), який перезавантажить програму, можливо, щоб забрати зміни в конфігураційному файлі або подібному.

    Якщо ви запрограмували в системі Unix, ви були б знайомі з несправностями сегментації при спробі читання або запису в пам'ять, яка не була виділена вам. Коли ядро помітить, що ви торкаєтеся пам'яті поза вашим розподілом, воно надішле вам сигнал несправності сегментації. Зазвичай процес не має обробника, встановленого для цього, і тому відбувається дія за замовчуванням для завершення програми (отже, ваша програма «аварійно завершує роботу»). У деяких випадках програма може встановити обробник несправностей сегментації, хоча причини для цього обмежені.

    При цьому виникає питання про те, що відбувається після прийому сигналу. Після того, як обробник сигналу закінчив роботу, управління повертається до процесу, який триває з того місця, де він зупинився.

    Наступна проста програма вводить багато сигналів для запуску!

    Приклад 5.4. Приклад сигналів
      1 $ cat signal.c
        #include <stdio.h>
        #include <unistd.h>
        #include <signal.h>
      5 
        void sigint_handler(int signum)
        {
                printf("got SIGINT\n");
        }
     10 
        int main(void)
        {
                signal(SIGINT, sigint_handler);
                printf("pid is %d\n", getpid());
     15         while (1)
                        sleep(1);
        }
        $ gcc -Wall -o signal signal.c
        $ ./signal
     20 pid is 2859
        got SIGINT # press ctrl-c 
                   # press ctrl-z
        [1]+  Stopped                 ./signal
        
     25 $ kill -SIGINT 2859
        $ fg
        ./signal
        got SIGINT
        Quit # press ctrl-\
     30 
        $

    У нас є проста програма, яка просто визначає обробник сигналу SIGINT, який надсилається при натисканні користувачем ctrl-c. Всі сигнали для системи визначаються в signal.h, включаючи функцію сигналу, яка дозволяє нам реєструвати функцію обробки.

    Програма просто сидить в щільному циклі, нічого не роблячи, поки не вийде. Коли ми запускаємо програму, ми намагаємося натиснути ctrl-c, щоб вона вийшла. Замість того, щоб робити дію за замовчуванням, або викликається обробник, і ми отримуємо результат, як очікувалося.

    Потім ми натискаємо ctrl-z, який посилає SIGSTOP, який за замовчуванням ставить процес у сплячий режим. Це означає, що він не ставиться в чергу для запуску планувальника і, таким чином, дрімає в системі.

    В якості ілюстрації ми використовуємо програму kill для відправки такого ж сигналу з іншого вікна терміналу. Це насправді реалізовано з викликом системи kill, який приймає сигнал і PID для відправки (ця функція трохи неправильно названа, тому що не всі сигнали насправді вбивають процес, як ми бачимо, але функція сигналу вже була прийнята для реєстрації обробника). Коли процес зупиняється, сигнал потрапляє в чергу для процесу. Це означає, що ядро приймає до відома сигнал і подасть його, коли це доречно.

    У цей момент ми пробуджуємо процес за допомогою команди fg. Це насправді посилає сигнал SIGCONT процесу, який за замовчуванням буде пробуджувати процес назад. Ядро знає, щоб поставити процес в чергу запуску і дати йому процесорний час знову. Ми бачимо, що в цей момент сигнал в черзі доставляється.

    У розпачі, щоб позбутися від програми, ми нарешті спробуємо ctrl-\ який посилає SIGQUIT (перервати) до процесу. Але якщо процес перервався, звідки взялися вихідні дані Quit?

    Ви здогадалися, більше сигналів! Коли батьківська дитина має процес, який помирає, він отримує сигнал SIGCHLD назад. У цьому випадку оболонка була батьківським процесом, і тому вона отримала сигнал. Пам'ятайте, як у нас є процес зомбі, який потрібно пожинати викликом очікування, щоб отримати код повернення з дочірнього процесу? Ну ще одна річ, яку він також дає батькові, - це номер сигналу, від якого дитина, можливо, померла. Таким чином оболонка знає, що дочірній процес помер від SIGABRT і як інформаційна служба друкує стільки ж для користувача (той самий процес відбувається, щоб роздрукувати «Segmentation Fault», коли дочірній процес вмирає від SIGSEGV).

    Ви можете побачити, як навіть у простій програмі було використано близько 5 різних сигналів для спілкування між процесами та ядром та підтримки роботи. Є багато інших сигналів, але вони, безумовно, є одними з найпоширеніших. Більшість з них мають системні функції, визначені ядром, але є кілька сигналів, зарезервованих для користувачів для використання в своїх цілях у своїх програмах (SIGUSR).