16-bo‘lim
Signallar
Signal turlari, sigaction, async-signal-safe funksiyalar, signal niqobi, SIGCHLD va zombi jarayonlar, xizmatni to'g'ri to'xtatish.
Ushbu bo‘lim mundarijasi
Signal - jarayonga yuboriladigan dasturiy uzilish. U kutilgan joyda emas, istalgan buyruq orasida yetib keladi.
Asosiy signallar #
| Signal | Raqam | Odatiy ta'sir | Sabab |
|---|---|---|---|
SIGHUP | 1 | Tugatish | Terminal yopildi / qayta o'qish |
SIGINT | 2 | Tugatish | Ctrl+C |
SIGQUIT | 3 | Tugatish + core | Ctrl+\ |
SIGKILL | 9 | To'xtatib bo'lmaydi | Majburiy o'ldirish |
SIGSEGV | 11 | Tugatish + core | Xotira buzilishi |
SIGPIPE | 13 | Tugatish | Yopiq quvurga yozish |
SIGALRM | 14 | Tugatish | Taymer ishladi |
SIGTERM | 15 | Tugatish | Muloyim to'xtatish so'rovi |
SIGCHLD | 17 | E'tiborsiz | Bola jarayon holati o'zgardi |
SIGSTOP | 19 | To'xtatib bo'lmaydi | Jarayonni muzlatish |
SIGCONT | 18 | Davom ettirish | Muzlagan jarayonni tiklash |
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 17) SIGCHLD
18) SIGCONT 19) SIGSTOP 20) SIGTSTP 21) SIGTTIN
Signal ishlovchisi #
#include <stdio.h>
#include <signal.h>
#include <string.h>
#include <unistd.h>
static volatile sig_atomic_t keldi = 0;
static void ishlovchi(int raqam) {
keldi = raqam;
}
int main(void) {
struct sigaction sa;
memset(&sa, 0, sizeof sa);
sa.sa_handler = ishlovchi;
sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_RESTART;
if (sigaction(SIGUSR1, &sa, NULL) < 0) {
return 1;
}
printf("SIGUSR1 uchun ishlovchi o'rnatildi\n");
/* O'zimizga signal yuboramiz */
raise(SIGUSR1);
printf("Signal keldimi? : %s\n", keldi != 0 ? "ha" : "yo'q");
printf("Qaysi signal : %d\n", (int) keldi);
printf("SIGUSR1 mi? : %s\n", keldi == SIGUSR1 ? "ha" : "yo'q");
/* kill bilan ham yuborish mumkin */
keldi = 0;
kill(getpid(), SIGUSR1);
printf("kill orqali keldimi?: %s\n", keldi == SIGUSR1 ? "ha" : "yo'q");
return 0;
}
SIGUSR1 uchun ishlovchi o'rnatildi
Signal keldimi? : ha
Qaysi signal : 10
SIGUSR1 mi? : ha
kill orqali keldimi?: ha
signal emas, sigaction ishlatingEski signal() funksiyasi turli tizimlarda turlicha ishlaydi:
| Xususiyat | signal (BSD) | signal (System V) |
|---|---|---|
| Ishlovchi qayta o'rnatiladimi | Yo'q | Ha (har chaqiruvdan keyin) |
| Uzilgan tizim chaqiruvi | Qayta boshlanadi | Xato qaytaradi |
System V xatti-harakatida signal ikki marta tez kelsa, ikkinchisi odatiy ta'sirni bajaradi - dastur o'ladi.
sigaction esa har joyda bir xil ishlaydi va aniq
bayroqlarni beradi:
| Bayroq | Ma'nosi |
|---|---|
SA_RESTART | Uzilgan tizim chaqiruvini qayta boshlash |
SA_NOCLDWAIT | Bola zombi bo'lmasin |
SA_SIGINFO | Ishlovchiga qo'shimcha ma'lumot berish |
Yangi kodda doim sigaction ishlating.
Ishlovchi ichida nima qilish mumkin? #
#include <stdio.h>
#include <signal.h>
#include <string.h>
#include <unistd.h>
static volatile sig_atomic_t bayroq = 0;
/* TO'G'RI: faqat bayroq qo'yadi va write ishlatadi */
static void yaxshi_ishlovchi(int raqam) {
(void) raqam;
bayroq = 1;
const char *m = "[ishlovchi: write xavfsiz]\n";
ssize_t n = write(STDOUT_FILENO, m, strlen(m));
(void) n;
}
int main(void) {
struct sigaction sa;
memset(&sa, 0, sizeof sa);
sa.sa_handler = yaxshi_ishlovchi;
sigemptyset(&sa.sa_mask);
sigaction(SIGUSR1, &sa, NULL);
printf("Signal yuborishdan oldin\n");
fflush(stdout);
raise(SIGUSR1);
printf("Bayroq qiymati: %d\n", (int) bayroq);
printf("Asosiy ish shu yerda bajariladi\n");
return 0;
}
Signal yuborishdan oldin
[ishlovchi: write xavfsiz]
Bayroq qiymati: 1
Asosiy ish shu yerda bajariladi
printf ishlatmangSignal istalgan payt kelishi mumkin - shu jumladan printf
ning o'rtasida.
Agar ishlovchi ham printf chaqirsa, stdio buferi buziladi:
natija yo'qoladi, ikki marta chiqadi yoki dastur qotib qoladi
(malloc mutexi tutilib qolsa).
Ishlovchi ichida faqat async-signal-safe funksiyalar mumkin:
| Ruxsat | Taqiqlangan |
|---|---|
write | printf, fprintf |
_exit | exit |
kill, raise | malloc, free |
signal, sigaction | strerror |
read, close | Ko'pchilik libc funksiyalari |
To'liq ro'yxat:
man 7 signal-safety
Amaliy qoida: ishlovchi faqat bayroq qo'ysin, qolgan ishni asosiy sikl bajarsin:
while (ishlaymiz) {
if (bayroq) {
bayroq = 0;
haqiqiy_ishni_bajarish(); /* bu yerda hammasi mumkin */
}
}
Bayroq turi albatta volatile sig_atomic_t bo'lsin.
Signal niqobi #
#include <stdio.h>
#include <signal.h>
#include <string.h>
#include <unistd.h>
static volatile sig_atomic_t sanoq = 0;
static void ishlovchi(int raqam) {
(void) raqam;
sanoq++;
}
int main(void) {
struct sigaction sa;
memset(&sa, 0, sizeof sa);
sa.sa_handler = ishlovchi;
sigemptyset(&sa.sa_mask);
sigaction(SIGUSR1, &sa, NULL);
/* SIGUSR1 ni bloklaymiz */
sigset_t niqob, eski;
sigemptyset(&niqob);
sigaddset(&niqob, SIGUSR1);
sigprocmask(SIG_BLOCK, &niqob, &eski);
raise(SIGUSR1);
printf("Bloklangan holatda sanoq: %d\n", (int) sanoq);
/* Kutilayotgan signallarni tekshiramiz */
sigset_t kutayotgan;
sigpending(&kutayotgan);
printf("SIGUSR1 kutayaptimi? : %s\n",
sigismember(&kutayotgan, SIGUSR1) ? "ha" : "yo'q");
/* Blokni olamiz - signal darhol yetkaziladi */
sigprocmask(SIG_SETMASK, &eski, NULL);
printf("Blok olingandan keyin : %d\n", (int) sanoq);
return 0;
}
Bloklangan holatda sanoq: 0
SIGUSR1 kutayaptimi? : ha
Blok olingandan keyin : 1
Bloklangan paytda bir xil signal 100 marta kelsa ham, blok olingach faqat bir marta yetkaziladi.
Sabab: kutayotgan signallar bitli niqob (sigset_t) da
saqlanadi - har signal uchun bitta bit.
Bu quyidagi xatoga olib keladi:
/* NOTO'G'RI: har SIGCHLD uchun bitta wait */
static void sigchld_ishlovchi(int r) {
waitpid(-1, NULL, WNOHANG); /* faqat bittasini yig'adi */
}
10 ta bola bir vaqtda tugasa, siz 1-2 ta signal olasiz va qolgan bolalar zombi bo'lib qoladi.
To'g'ri usul - hammasini yig'ish:
static void sigchld_ishlovchi(int r) {
(void) r;
while (waitpid(-1, NULL, WNOHANG) > 0) {
/* barcha tugaganlarni yig'amiz */
}
}
Real-time signallar (SIGRTMIN..SIGRTMAX) esa navbatga
qo'yiladi va yo'qolmaydi.
SIGCHLD va zombi jarayonlar #
#include <stdio.h>
#include <signal.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <sys/wait.h>
static volatile sig_atomic_t yigildi = 0;
static void sigchld_ishlovchi(int raqam) {
(void) raqam;
int saqlangan = errno;
/* while - chunki signallar to'planmaydi */
while (waitpid(-1, NULL, WNOHANG) > 0) {
yigildi++;
}
errno = saqlangan;
}
int main(void) {
struct sigaction sa;
memset(&sa, 0, sizeof sa);
sa.sa_handler = sigchld_ishlovchi;
sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_RESTART | SA_NOCLDSTOP;
sigaction(SIGCHLD, &sa, NULL);
/* Signalni bloklaymiz - poyga holatidan himoya */
sigset_t niqob, bosh;
sigemptyset(&niqob);
sigaddset(&niqob, SIGCHLD);
sigprocmask(SIG_BLOCK, &niqob, &bosh);
fflush(stdout);
/* Beshta bola yaratamiz */
for (int i = 0; i < 5; i++) {
pid_t b = fork();
if (b == 0) {
_exit(0);
}
}
/* sigsuspend - niqobni ochish va kutish atomar bajariladi */
while (yigildi < 5) {
sigsuspend(&bosh);
}
sigprocmask(SIG_SETMASK, &bosh, NULL);
printf("Yaratilgan bolalar : 5\n");
printf("Yig'ilgan bolalar : %d\n", (int) yigildi);
printf("Zombi qoldimi? : %s\n", yigildi == 5 ? "yo'q" : "ha");
return 0;
}
Yaratilgan bolalar : 5
Yig'ilgan bolalar : 5
Zombi qoldimi? : yo'q
errno ni saqlangSignal read ning o'rtasida kelishi mumkin. Agar ishlovchi
waitpid chaqirib errno ni o'zgartirsa, asosiy kod noto'g'ri
xato kodini ko'radi.
Shuning uchun ishlovchi boshida saqlab, oxirida tiklang:
int saqlangan = errno;
/* ... ish ... */
errno = saqlangan;
Bu kichik detal, lekin uni unutish juda chalkash xatolarga olib keladi.
Zombi umuman kerak bo'lmasa, eng oddiy yechim:
signal(SIGCHLD, SIG_IGN);
Bu holda yadro bolalarni avtomatik yig'adi. Lekin siz ularning chiqish kodini bila olmaysiz.
SIGKILL va SIGSTOP ni tutib bo'lmaydi #
#include <stdio.h>
#include <signal.h>
#include <string.h>
#include <errno.h>
static void ishlovchi(int raqam) {
(void) raqam;
}
int main(void) {
struct sigaction sa;
memset(&sa, 0, sizeof sa);
sa.sa_handler = ishlovchi;
sigemptyset(&sa.sa_mask);
int term = sigaction(SIGTERM, &sa, NULL);
printf("SIGTERM ni tutish: %s\n", term == 0 ? "mumkin" : "mumkin emas");
errno = 0;
int kill_natija = sigaction(SIGKILL, &sa, NULL);
printf("SIGKILL ni tutish: %s\n",
kill_natija == 0 ? "mumkin" : "mumkin emas");
printf(" Xato kodi EINVAL mi? %s\n", errno == EINVAL ? "ha" : "yo'q");
errno = 0;
int stop_natija = sigaction(SIGSTOP, &sa, NULL);
printf("SIGSTOP ni tutish: %s\n",
stop_natija == 0 ? "mumkin" : "mumkin emas");
/* Bloklashga ham urinib ko'ramiz */
sigset_t niqob;
sigemptyset(&niqob);
sigaddset(&niqob, SIGKILL);
sigprocmask(SIG_BLOCK, &niqob, NULL);
sigset_t joriy;
sigprocmask(SIG_BLOCK, NULL, &joriy);
printf("\nSIGKILL bloklandimi? %s\n",
sigismember(&joriy, SIGKILL) ? "ha" : "yo'q - yadro e'tiborsiz qoldirdi");
return 0;
}
SIGTERM ni tutish: mumkin
SIGKILL ni tutish: mumkin emas
Xato kodi EINVAL mi? ha
SIGSTOP ni tutish: mumkin emas
SIGKILL bloklandimi? yo'q - yadro e'tiborsiz qoldirdi
SIGKILL tutilmaydiAgar dastur SIGKILL ni tuta olsa, uni hech qachon
to'xtatib bo'lmasdi. Administrator uchun oxirgi chora qolmasdi.
Shuning uchun yadro bu ikki signalni maxsus ko'radi: ular ishlovchiga umuman yetib bormaydi, yadro to'g'ridan-to'g'ri harakat qiladi.
Lekin bitta istisno bor: D holatidagi (uninterruptible sleep)
jarayonni SIGKILL ham o'ldirmaydi.
ps -eo pid,stat,comm | awk '$2 ~ /D/'
4521 D nfsd
Bu jarayon drayver ichida qotib qolgan - odatda buzilgan disk yoki javob bermayotgan NFS server sababli. Yagona yechim - muammoni manbada hal qilish yoki tizimni qayta yuklash.
Muloyim to'xtatish #
#include <stdio.h>
#include <signal.h>
#include <string.h>
#include <unistd.h>
static volatile sig_atomic_t toxtash = 0;
static void toxtatish_signali(int raqam) {
(void) raqam;
toxtash = 1;
}
int main(void) {
struct sigaction sa;
memset(&sa, 0, sizeof sa);
sa.sa_handler = toxtatish_signali;
sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_RESTART;
sigaction(SIGTERM, &sa, NULL);
sigaction(SIGINT, &sa, NULL);
printf("Xizmat ishga tushdi\n");
int qayta_ishlangan = 0;
/* Asosiy sikl */
while (!toxtash) {
qayta_ishlangan++;
/* Ish simulyatsiyasi: 100 ta so'rovdan keyin signal olamiz */
if (qayta_ishlangan == 100) {
raise(SIGTERM);
}
if (qayta_ishlangan > 200) {
break; /* xavfsizlik chegarasi */
}
}
printf("To'xtatish signali keldi\n");
printf("Qayta ishlangan so'rovlar: %d\n", qayta_ishlangan);
printf("Tozalash bajarilmoqda...\n");
printf("Xizmat to'g'ri to'xtadi\n");
return 0;
}
Xizmat ishga tushdi
To'xtatish signali keldi
Qayta ishlangan so'rovlar: 100
Tozalash bajarilmoqda...
Xizmat to'g'ri to'xtadi
Taymer signali #
#include <stdio.h>
#include <signal.h>
#include <string.h>
#include <unistd.h>
static volatile sig_atomic_t taymer = 0;
static void alarm_ishlovchi(int raqam) {
(void) raqam;
taymer = 1;
}
int main(void) {
struct sigaction sa;
memset(&sa, 0, sizeof sa);
sa.sa_handler = alarm_ishlovchi;
sigemptyset(&sa.sa_mask);
sigaction(SIGALRM, &sa, NULL);
alarm(1); /* 1 soniyadan keyin */
printf("Taymer o'rnatildi\n");
/* Signal kelguncha kutamiz */
pause();
printf("Taymer ishladimi? %s\n", taymer ? "ha" : "yo'q");
printf("pause() signal kelganda qaytadi\n");
/* Qolgan vaqtni bekor qilish */
unsigned qolgan = alarm(0);
printf("Bekor qilingan taymerda qolgan: %u soniya\n", qolgan);
return 0;
}
Taymer o'rnatildi
Taymer ishladimi? ha
pause() signal kelganda qaytadi
Bekor qilingan taymerda qolgan: 0 soniya
pause va poyga holatiBu naqsh xavfli:
if (!bayroq) {
pause(); /* signal SHU YERDA kelsa - abadiy kutish */
}
Signal if va pause orasida kelsa, pause uni o'tkazib
yuboradi va dastur abadiy qotadi.
To'g'ri yechim - sigsuspend bilan atomar kutish:
sigset_t niqob, bosh;
sigemptyset(&niqob);
sigaddset(&niqob, SIGUSR1);
sigprocmask(SIG_BLOCK, &niqob, &bosh);
while (!bayroq) {
sigsuspend(&bosh); /* niqobni ochish va kutish - atomar */
}
sigprocmask(SIG_SETMASK, &bosh, NULL);
Zamonaviy alternativa - signalfd: signalni fayl deskriptori
orqali epoll bilan birga kutish mumkin bo'ladi.
Signal yuborish #
kill -TERM 4521
kill -9 4521
killall nginx
pkill -f "python skript.py"
| Buyruq | Ta'siri |
|---|---|
kill <pid> | SIGTERM yuboradi (odatiy) |
kill -9 <pid> | SIGKILL - majburiy |
kill -HUP <pid> | Ko'p xizmatda sozlamani qayta o'qish |
kill -0 <pid> | Signal yubormaydi, faqat mavjudligini tekshiradi |
kill -- -<pgid> | Butun jarayonlar guruhiga |
kill -0 4521 && echo "jarayon ishlayapti"
SIGHUP - sozlamani qayta o'qish an'anasiTarixan SIGHUP terminal uzilganini bildirardi. Fon xizmatlarida
terminal yo'q, shuning uchun bu signal bo'sh qoldi va
"sozlamalarni qayta o'qi" ma'nosini oldi.
sudo kill -HUP $(pidof nginx)
sudo nginx -s reload # ichida aynan shu qiladi
Bu xizmatni to'xtatmasdan sozlamani yangilash imkonini beradi - mavjud ulanishlar uzilmaydi.
Dasturingizda shuni qo'llash oson:
if (sozlama_qayta_oqish_bayrogi) {
sozlama_qayta_oqish_bayrogi = 0;
sozlamani_yuklash();
}
sigactionbilanSIGUSR1uchun ishlovchi o'rnating.signalvasigactionfarqini tushuntiring.- Ishlovchi ichida faqat
writeishlatib xabar chiqaring. sigprocmaskbilan signalni bloklab,sigpendingbilan ko'ring.- Bir xil signal ikki marta kelsa nima bo'lishini sinang.
- Beshta bola yaratib,
SIGCHLDbilan hammasini yig'ing. SIGKILLni tutishga urinib,EINVALni oling.SIGTERMbilan muloyim to'xtatishni amalga oshiring.alarmvapausebilan taymer quring.kill -0bilan jarayon mavjudligini tekshiring.
Xulosa #
- Signal - dasturiy uzilish, istalgan buyruq orasida keladi.
sigactionishlating,signalemas - u portativ emas.- Ishlovchi ichida faqat async-signal-safe funksiyalar.
- Ishlovchi bayroq qo'ysin, ishni asosiy sikl bajarsin.
- Bayroq turi
volatile sig_atomic_tbo'lishi shart. - Ishlovchi ichida
errnoni saqlang va tiklang. - Oddiy signallar to'planmaydi -
SIGCHLDdawhileishlating. SIGKILLvaSIGSTOPni tutib ham, bloklab ham bo'lmaydi.Dholatidagi jarayonniSIGKILLham o'ldirmaydi.- Muloyim to'xtatish: SIGTERM → kutish → SIGKILL.
pauseo'rnigasigsuspendyokisignalfdishlating.SIGHUPan'anaviy ravishda sozlamani qayta o'qishni bildiradi.
Keyingi bo'limda foydalanuvchilar, huquqlar va xavfsizlikni ko'ramiz.
O‘qish tarixini saqlamoqchimisiz?
Tizimga kirsangiz, tugatgan bo‘limlaringiz saqlanadi va qoldirgan joyingizdan davom etasiz.
Xatolik topdingizmi?
Imlo xatosi, ishlamaydigan kod yoki noto‘g‘ri ma‘lumotni ko‘rsangiz - bizga xabar bering. Har bir xabar administrator tomonidan ko‘rib chiqiladi.