Як сигнали працюють всередині?


31

Загалом, щоб убити процеси ми генеруємо сигнали , такі як SIGKILL, і SIGTSTPт.д.

Але як відомо, хто замовив той чи інший сигнал, хто надіслав його до певного процесу, і взагалі як сигнали виконують свої операції? Як сигнали працюють внутрішньо?


Питання трохи важко зрозуміти. Прошу вибачення і маю на увазі відсутність неповаги. Ви хочете знати, хто, можливо, запустив команду, яка вбила процес, або ви хочете дізнатися більше про SIGKILL і SIGSTP?
pullsumo

@mistermister Я хочу знати, хто міг би запустити команду, яка вбила процес і як?
Варун Чхангані

Відповіді:


35

Погляд на 50 000 футів:

  1. Сигнал генерується або ядром внутрішньо (наприклад, SIGSEGVколи доступ до недійсної адреси, або SIGQUITпри натисканні Ctrl+ \), або програмою, що використовує killsyscall (або декілька пов'язаних з ними).

  2. Якщо це за допомогою одного з системних викликів, то ядро ​​підтверджує, що процес виклику має достатньо привілеїв для передачі сигналу. Якщо ні, помилка повертається (і сигнал не відбувається).

  3. Якщо це один із двох спеціальних сигналів, ядро ​​беззастережно діє на нього, без будь-якого введення цільового процесу. Два спеціальних сигналу - SIGKILL і SIGSTOP. Усі наведені нижче матеріали про дії за замовчуванням, блокування сигналів тощо не мають значення для цих двох.

  4. Далі ядро ​​з'ясовує, що робити із сигналом:

    1. Для кожного процесу існує дія, пов'язана з кожним сигналом. Існує купа за замовчуванням, і програми можуть встановлювати різні, використовуючи sigaction, signalі т. Д. До них відносяться такі речі, як "ігнорувати його повністю", "вбивати процес", "вбивати процес за допомогою дамп-ядра", "зупиняти процес", тощо.

    2. Програми також можуть відключити доставку сигналів ("заблокованих") на основі сигналу за сигналом. Потім сигнал залишається в очікуванні до розблокування.

    3. Програми можуть вимагати, щоб замість того, щоб ядро ​​здійснювало певні дії, воно надсилало сигнал процесу або синхронно (з sigwait, і т. Ін., Або signalfd), або асинхронно (перериваючи все, що робить процес, і викликаючи вказану функцію).

Існує другий набір сигналів під назвою "сигнали в режимі реального часу", які не мають конкретного значення, а також дозволяють встановити в чергу кілька сигналів (звичайні сигнали в черзі лише один з кожного, коли сигнал заблокований). Вони використовуються в багатопотокових програмах для передачі потоків між собою. Наприклад, декілька використовуються в реалізації потоків POSIX потоків glibc. Вони також можуть використовуватися для зв'язку між різними процесами (наприклад, ви можете використовувати декілька сигналів у реальному часі, щоб програма fooctl надсилала повідомлення демону foo).

Щоб переглянути ногу не на 50 000, спробуйте man 7 signalтакож документацію (або джерело) для внутрішнього ядра.


"Два спеціальних сигналу - SIGKILL і SIGSTOP", то, що може бути SIGCONT ...
Хоуке Лагінг

@HaukeLaging SIGCONT - це сигнал, який скасовує SIGSTOP. У документації не вказано його як спеціальне ... Тож я не впевнений, якщо технічно процес може встановити його на ігнорування, тоді ви не зможете відновити його (лише SIGKILL).
дероберт

22

Реалізація сигналу дуже складна і специфічна для ядра. Іншими словами, різні ядра будуть по-різному реалізовувати сигнали. Спрощене пояснення полягає в наступному:

ЦП, заснований на спеціальному значенні реєстру, має адресу в пам'яті, де він очікує знайти "таблицю дескрипторів переривання", яка є фактично векторною таблицею. Існує один вектор для кожного можливого винятку, як поділ на нуль або пастка, як INT 3 (налагодження). Коли CPU стикається з винятком, він зберігає прапорці та поточний вказівник інструкції на стеку, а потім переходить на адресу, вказану відповідним вектором. У Linux цей вектор завжди вказує на ядро, де є обробник винятків. Зараз процесор готовий, а ядро ​​Linux бере на себе.

Зауважте, що ви також можете викликати виняток із програмного забезпечення. Наприклад, користувач натискає CTRL- C, тоді цей виклик переходить до ядра, яке викликає власний обробник винятків. Як правило, існують різні способи дістатися до обробника, але незалежно від того самого основного, що відбувається: контекст зберігається в стеці, і до нього обробляється обробник виключення ядра.

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

Для передачі сигналу, що робить ядро, спочатку встановлюється значення, що вказує тип сигналу, SIGHUPабо будь-який інший. Це просто ціле число. Кожен процес має область пам'яті "очікуючий сигнал", де зберігається це значення. Потім ядро ​​створює структуру даних з інформацією про сигнал. Ця структура включає сигнал "диспозиція", який може бути за замовчуванням, ігнорувати або обробляти. Потім ядро ​​викликає власну функцію do_signal(). Починається наступна фаза.

do_signal()спочатку вирішує, чи буде він обробляти сигнал. Наприклад, якщо це вбивство , то do_signal()просто вбиває процес, кінець історії. Інакше це дивиться на диспозицію. Якщо диспозиція за замовчуванням, то do_signal()обробляє сигнал відповідно до політики за замовчуванням, яка залежить від сигналу. Якщо диспозиція є ручкою, то це означає, що в програмі користувача є функція, призначена для обробки відповідного сигналу, і покажчик на цю функцію буде знаходитись у вищезгаданій структурі даних. У цьому випадку do_signal () викликає іншу функцію ядра,handle_signal(), який потім проходить процес переходу в режим користувача та виклику цієї функції. Деталі цієї передачі надзвичайно складні. Цей код у вашій програмі зазвичай автоматично підключається до вашої програми під час використання функцій в signal.h.

Вивчивши відповідне значення сигналу, що очікує, ядро ​​може визначити, чи обробляє процес всі сигнали, і вживе відповідних дій, якщо його немає, що може перевести процес сну або вбити його чи іншу дію, залежно від сигналу.


15

Хоча на це питання відповіли, дозвольте мені опублікувати детальний потік подій у ядрі Linux.
Це повністю скопійовано з постів Linux: Сигнали Linux - Внутрішні записи в блозі "Пост Linux" на сайті sklinuxblog.blogspot.in.

Програма "Простір користувача C"

Почнемо з написання простої програми програмного забезпечення для простору користувача C:

#include<signal.h>
#include<stdio.h>

/* Handler function */
void handler(int sig) {
    printf("Receive signal: %u\n", sig);
};

int main(void) {
    struct sigaction sig_a;

    /* Initialize the signal handler structure */
    sig_a.sa_handler = handler;
    sigemptyset(&sig_a.sa_mask);
    sig_a.sa_flags = 0;

    /* Assign a new handler function to the SIGINT signal */
    sigaction(SIGINT, &sig_a, NULL);

    /* Block and wait until a signal arrives */
    while (1) {
            sigsuspend(&sig_a.sa_mask);
            printf("loop\n");
    }
    return 0;
};

Цей код призначає новий обробник сигналу SIGINT. SIGINT може бути відправлений до запущеного процесу, використовуючи комбінацію клавіш Ctrl+ C. Коли Ctrl+ Cнатискається, асинхронний сигнал SIGINT надсилається завдання. Це також еквівалентно відправленню kill -INT <pid>команди в інший термінал.

Якщо ви робите kill -l(це малий регістр L, який означає "список"), ви дізнаєтесь про різні сигнали, які можна надіслати до запущеного процесу.

[root@linux ~]# kill -l
 1) SIGHUP        2) SIGINT        3) SIGQUIT       4) SIGILL        5) SIGTRAP
 6) SIGABRT       7) SIGBUS        8) SIGFPE        9) SIGKILL      10) SIGUSR1
11) SIGSEGV      12) SIGUSR2      13) SIGPIPE      14) SIGALRM      15) SIGTERM
16) SIGSTKFLT    17) SIGCHLD      18) SIGCONT      19) SIGSTOP      20) SIGTSTP
21) SIGTTIN      22) SIGTTOU      23) SIGURG       24) SIGXCPU      25) SIGXFSZ
26) SIGVTALRM    27) SIGPROF      28) SIGWINCH     29) SIGIO        30) SIGPWR
31) SIGSYS       34) SIGRTMIN     35) SIGRTMIN+1   36) SIGRTMIN+2   37) SIGRTMIN+3
38) SIGRTMIN+4   39) SIGRTMIN+5   40) SIGRTMIN+6   41) SIGRTMIN+7   42) SIGRTMIN+8
43) SIGRTMIN+9   44) SIGRTMIN+10  45) SIGRTMIN+11  46) SIGRTMIN+12  47) SIGRTMIN+13
48) SIGRTMIN+14  49) SIGRTMIN+15  50) SIGRTMAX-14  51) SIGRTMAX-13  52) SIGRTMAX-12
53) SIGRTMAX-11  54) SIGRTMAX-10  55) SIGRTMAX-9   56) SIGRTMAX-8   57) SIGRTMAX-7
58) SIGRTMAX-6   59) SIGRTMAX-5   60) SIGRTMAX-4   61) SIGRTMAX-3   62) SIGRTMAX-2
63) SIGRTMAX-1   64) SIGRTMAX

Також для передачі певних сигналів може використовуватися наступна комбінація клавіш:

  • Ctrl+ C- надсилає SIGINT, яка за замовчуванням повинна припинити програму.
  • Ctrl+ \  - надсилає SIGQUIT, дія за замовчуванням якої полягає у припиненні демпінгу ядра програми.
  • Ctrl+ Z- посилає SIGSTOP, який призупиняє програму.

Якщо ви компілюєте та запускаєте вищевказану програму C, ви отримаєте такий вихід:

[root@linux signal]# ./a.out
Receive signal: 2
loop
Receive signal: 2
loop
^CReceive signal: 2
loop

Навіть з Ctrl+ Cабо kill -2 <pid>процес не припиниться. Замість цього він виконає обробник сигналу та повернеться.

Як сигнал передається в процес

Якщо ми побачимо внутрішню передачу сигналу в процес і поставимо Jprobe з dump_stack у __send_signalфункцію, ми побачимо наступний слід виклику:

May  5 16:18:37 linux kernel: dump_stack+0x19/0x1b
May  5 16:18:37 linux kernel: my_handler+0x29/0x30 (probe)
May  5 16:18:37 linux kernel: complete_signal+0x205/0x250
May  5 16:18:37 linux kernel: __send_signal+0x194/0x4b0
May  5 16:18:37 linux kernel: send_signal+0x3e/0x80
May  5 16:18:37 linux kernel: do_send_sig_info+0x52/0xa0
May  5 16:18:37 linux kernel: group_send_sig_info+0x46/0x50
May  5 16:18:37 linux kernel: __kill_pgrp_info+0x4d/0x80
May  5 16:18:37 linux kernel: kill_pgrp+0x35/0x50
May  5 16:18:37 linux kernel: n_tty_receive_char+0x42b/0xe30
May  5 16:18:37 linux kernel:  ? ftrace_ops_list_func+0x106/0x120
May  5 16:18:37 linux kernel: n_tty_receive_buf+0x1ac/0x470
May  5 16:18:37 linux kernel: flush_to_ldisc+0x109/0x160
May  5 16:18:37 linux kernel: process_one_work+0x17b/0x460
May  5 16:18:37 linux kernel: worker_thread+0x11b/0x400
May  5 16:18:37 linux kernel: rescuer_thread+0x400/0x400
May  5 16:18:37 linux kernel:  kthread+0xcf/0xe0
May  5 16:18:37 linux kernel:  kthread_create_on_node+0x140/0x140
May  5 16:18:37 linux kernel:  ret_from_fork+0x7c/0xb0
May  5 16:18:37 linux kernel: ? kthread_create_on_node+0x140/0x140

Отже, основні виклики функції передачі сигналу виглядають так:

First shell send the Ctrl+C signal using n_tty_receive_char
n_tty_receive_char()
isig()
kill_pgrp()
__kill_pgrp_info()
group_send_sig_info() -- for each PID in group call this function
do_send_sig_info()
send_signal()
__send_signal() -- allocates a signal structure and add to task pending signals
complete_signal()
signal_wake_up()
signal_wake_up_state()  -- sets TIF_SIGPENDING in the task_struct flags. Then it wake up the thread to which signal was delivered.

Тепер все налаштовано і вноситься необхідні зміни до task_structпроцесу.

Поводження з сигналом

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

Функція int_signal викликається функцією, з entry_64.Sякої викликає функцію do_notify_resume().

Перевіримо функцію do_notify_resume(). Ця функція перевіряє, чи TIF_SIGPENDINGвстановлений у нас прапор у task_struct:

 /* deal with pending signal delivery */
 if (thread_info_flags & _TIF_SIGPENDING)
  do_signal(regs);
do_signal calls handle_signal to call the signal specific handler
Signals are actually run in user mode in function:
__setup_rt_frame -- this sets up the instruction pointer to handler: regs->ip = (unsigned long) ksig->ka.sa.sa_handler;

СИСТЕМНІ дзвінки та сигнали

"Повільні" системні виклики, наприклад, блокування читання / запису, переведення процесів у стан очікування: TASK_INTERRUPTIBLEабо TASK_UNINTERRUPTIBLE.

Завдання в стані TASK_INTERRUPTIBLEбуде змінено на TASK_RUNNINGстан сигналом. TASK_RUNNINGозначає, що процес може бути запланований.

Якщо він виконаний, його обробник сигналу буде запущений до завершення "повільного" системного виклику. Значення syscallза замовчуванням не завершується.

Якщо SA_RESTARTпрапор встановлено, syscallпісля закінчення обробника сигналу перезапускається.

Список літератури


Дякуємо, що доклали зусиль, щоб зробити свій внесок на сайт, але (1) якщо ви збираєтесь копіювати матеріали з іншого сайту (слово в слово, лист за буквою, включаючи граматичні та пунктуаційні помилки), слід сказати, що ви робите так, набагато чіткіше. Перерахування джерела як «довідника», хоча це є необхідним, недостатньо. Якщо ви не автор блогу (K_K = sk?), В такому випадку вам не потрібно посилатися на нього - але, якщо ви це зробите, ви повинні розкрити (тобто сказати), що це ваше. … (Продовжував)
G-Man каже: «Відновіть Моніку»

(Продовження)… (2) Ваше джерело (блог, з якого ви скопіювали), не дуже добре. Минуло чотири роки, як було задано питання; не вдалося знайти кращу посилання для копіювання? (Якщо ви є оригінальним автором, вибачте.) Окрім згаданих вище граматичних та пунктуаційних помилок (і взагалі неохайного формулювання та поганого форматування), це неправильно. (2a) Ctrl + Z посилає SIGTSTP, а не SIGSTOP. (SIGTSTP, як SIGTERM, може бути спійманий; SIGSTOP, як SIGKILL, не може.)… (Продовжував)
G-Man каже: «Відновіть Моніку»

(Продовження)… (2b) Оболонка не надсилає сигнал Ctrl + C. Оболонка не має ніякої ролі в передачі сигналів (за винятком випадків, коли користувач використовує killкоманду, яка є вбудованою оболонкою). (2c) Хоча крапки з комою після закриття }функції не є, суворо кажучи, помилками, вони є непотрібними і вкрай неортодоксальними. (3) Навіть якби все було правильно, це було б не дуже вдалою відповіддю на питання. (3a) Це питання, хоча дещо незрозуміле, фокусується на тому, як суб'єкти (користувачі та процес) ініціюють (тобто надсилають ) сигнали. … (Продовжував)
G-Man каже: «Відновіть Моніку»

(Продовження)… Здається, відповідь орієнтована на сигнали, генеровані ядром (зокрема сигнали, створені клавіатурою) та те, як процес одержувача реагує на сигнали. (3b) Питання здається на рівні «Хтось убив мій процес - хто це зробив, і як?» У відповіді обговорюється API обробки сигналів, підпрограми ядра, налагодження ядра (Jprobe?), Сліди стека ядра та структури даних ядра. ІМО, це недостатньо низький рівень - тим більше, що він не дає жодних посилань, де читач міг би більше дізнатися про ці внутрішні розробки.
G-Man каже: "Відновіть Моніку"

1
Це мій власний блог .. мої власні сліди .. ось що я хочу .. кожен повинен знати такий детальний потік .. говорити в повітрі не має сенсу .. навіть якщо після того, як це порушує ці вказівки щодо спільнот, будь ласка, видаліть мою відповідь через належне канал .. це внутрішня відповідь ядра, а не внутрішня граматика.
K_K
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.