00 Signal nedir
Sinyal, kernel'ın veya başka bir process'in, çalışan bir process'e gönderdiği asenkron bildirimdir — process'in o sırada ne yaptığına bakılmaksızın iletilir.
Bir process herhangi bir system call beklerken, CPU hesabı yaparken veya belleği okurken; tamamen farklı bir noktadan gelen bir sinyal onu keser. Bu mekanizma, Unix'in ilk günlerinden beri var olan temel bir IPC aracıdır. Modern Linux çekirdeği de aynı modeli sürdürür: sinyal gönderildiğinde process'in normal akışı durdurulur, handler (varsa) çalıştırılır ve ardından kaldığı yerden devam eder.
Signal delivery akışı
kill(pid, SIGTERM)
│
▼
kernel: hedef process'in signal queue'suna SIGTERM ekle
│
▼
process scheduled (CPU'ya alınır) → signal mask kontrolü
│ │
│ masked? → pending set'te bekle
│
▼
handler veya default action çalışır
kill -l çıktısı
Linux'ta kill -l komutu tüm sinyalleri listeler. 1–31 arası standart (POSIX) sinyaller, 32–64 arası ise POSIX real-time sinyallerdir:
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 ... 64) SIGRTMAX
Default action tablosu
| Default action | Açıklama | Örnekler |
|---|---|---|
Term | Process'i sonlandır | SIGTERM, SIGINT, SIGHUP, SIGPIPE |
Core | Core dump oluştur, sonlandır | SIGQUIT, SIGSEGV, SIGFPE, SIGABRT, SIGBUS |
Ign | Sinyali yoksay | SIGCHLD, SIGURG, SIGWINCH |
Stop | Process'i durdur (suspend) | SIGSTOP, SIGTSTP, SIGTTIN, SIGTTOU |
Cont | Durdurulmuş process'i devam ettir | SIGCONT |
Sinyal göndermenin tek yolu kill() değildir. Klavyeden Ctrl+C → SIGINT, Ctrl+\ → SIGQUIT, Ctrl+Z → SIGTSTP gönderir. Kernel de donanım hatalarında SIGSEGV, SIGBUS, SIGFPE üretir. alarm() system call'u ise belirtilen süre sonra SIGALRM gönderir.
01 Önemli sinyaller
Her sinyalin bir numarası, bir default action'ı ve tipik bir kullanım senaryosu vardır — bunları ezberlemek değil, nereye bakacağınızı bilmek önemlidir.
| No | İsim | Default | Açıklama / Kullanım |
|---|---|---|---|
| 1 | SIGHUP | Term | Terminal kapandı; daemon'larda config reload için kullanılır |
| 2 | SIGINT | Term | Ctrl+C — kullanıcı interrupt |
| 3 | SIGQUIT | Core | Ctrl+\ — core dump ile çık |
| 4 | SIGILL | Core | Geçersiz CPU instruction |
| 5 | SIGTRAP | Core | Breakpoint / trace — debugger kullanır |
| 6 | SIGABRT | Core | abort() çağrısı — assertion failure |
| 7 | SIGBUS | Core | Bus error — hizalanmamış bellek erişimi |
| 8 | SIGFPE | Core | Floating point / tamsayı sıfıra bölme |
| 9 | SIGKILL | Term | Yakalanamaz, maskelenemez — zorla sonlandır |
| 10 | SIGUSR1 | Term | Kullanıcı tanımlı; log rotate, durum dökümü |
| 11 | SIGSEGV | Core | Geçersiz bellek erişimi (segmentation fault) |
| 12 | SIGUSR2 | Term | Kullanıcı tanımlı; config reload, mod değişimi |
| 13 | SIGPIPE | Term | Yazılan pipe/socket kapandı — genellikle ignore edilir |
| 14 | SIGALRM | Term | alarm() veya setitimer() süresi doldu |
| 15 | SIGTERM | Term | Nazik sonlandırma isteği — graceful shutdown için |
| 17 | SIGCHLD | Ign | Child process bitti veya durdu |
| 18 | SIGCONT | Cont | Durdurulmuş process'i devam ettir |
| 19 | SIGSTOP | Stop | Yakalanamaz, maskelenemez — process'i durdur |
| 20 | SIGTSTP | Stop | Ctrl+Z — terminal stop, yakalanabilir |
SIGKILL ve SIGSTOP neden yakalanamaz?
SIGKILL ve SIGSTOP, kernel tarafından doğrudan işlenir — process'in kullanıcı alanındaki signal handler mekanizması hiç devreye girmez. Bu iki sinyal için sigaction() çağrısı EINVAL döndürür. Tasarım gereğidir: işletim sistemi her zaman bir process'i durdurabilmeli veya öldürebilmelidir; aksi hâlde kilitlenmiş bir process sistemi bloke edebilir.
SIGUSR1 / SIGUSR2 kullanım senaryoları
Bu iki sinyal uygulama tarafından serbestçe kullanılabilir. Yaygın örüntüler:
# nginx log rotation: önce log dosyalarını rotate et, sonra sinyal gönder
mv /var/log/nginx/access.log /var/log/nginx/access.log.1
kill -SIGUSR1 $(cat /var/run/nginx.pid)
# nginx yeni access.log açar, eski dosya kapatılır
# pidof ile: çalışan prosese sinyal gönder
kill -SIGUSR1 $(pidof nginx)
# SIGUSR2 ile config reload (uygulama bunu destekliyorsa)
kill -SIGUSR2 $(pidof myapp)
SIGPIPE genellikle daemon'larda SIG_IGN ile ignore edilir. Bir TCP bağlantısı kapandıktan sonra write() çağrısı SIGPIPE üretir; ignore edilmezse daemon beklenmedik biçimde sonlanabilir. Bunun yerine send(..., MSG_NOSIGNAL) veya SO_NOSIGPIPE socket seçeneği de kullanılabilir.
02 signal() vs sigaction()
signal() POSIX tarafından eskimiş (obsolescent) kabul edilir; sigaction() güvenilir, taşınabilir ve özellik bakımından üstündür.
signal() API'sinin sorunları
signal(SIGTERM, handler) çağrısı basit görünse de ciddi sorunlar içerir. POSIX, handler çalıştırıldıktan sonra sinyalin default action'a sıfırlanıp sıfırlanmayacağını platforma bırakmıştır. Linux'ta sıfırlanmaz, ancak BSD türevlerinde sıfırlanabilir. Bunun ötesinde SA_RESTART bayrağı yok: sistem çağrıları EINTR ile kesilebilir ve errno == EINTR kontrolü yapılmazsa hatalar oluşur.
sigaction() yapısı
#include <signal.h>
/* struct sigaction alanları */
struct sigaction {
void (*sa_handler)(int); /* basit handler */
void (*sa_sigaction)(int, siginfo_t *, void *); /* SA_SIGINFO ile */
sigset_t sa_mask; /* handler çalışırken blokla */
int sa_flags; /* SA_RESTART vb. */
void (*sa_restorer)(void); /* dahili, kullanma */
};
/* imza */
int sigaction(int signum,
const struct sigaction *act,
struct sigaction *oldact); /* eski handler'ı kaydet */
sa_flags tablosu
| Flag | Açıklama |
|---|---|
SA_RESTART | Signal tarafından kesilen sistem çağrılarını otomatik yeniden başlat; EINTR döndürme |
SA_SIGINFO | sa_handler yerine sa_sigaction kullan; siginfo_t ve ucontext_t bilgisi ile |
SA_NOCLDWAIT | SIGCHLD için: child zombie oluşturma, kernel otomatik temizler |
SA_NODEFER | Handler çalışırken aynı sinyalin tekrar teslimini engelleme (re-entrant handler) |
SA_RESETHAND | Handler çağrıldıktan sonra default action'a dön (signal() davranışını taklit eder) |
SA_ONSTACK | Alternatif sinyal stack'te çalıştır (sigaltstack ile) |
Doğru kullanım örneği
#include <signal.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
static void term_handler(int sig) {
/* Bu bölümde ne yazılmalı — s3'te detaylandırılıyor */
(void)sig;
}
int setup_signals(void) {
struct sigaction sa;
memset(&sa, 0, sizeof(sa));
sa.sa_handler = term_handler;
sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_RESTART; /* sistem çağrılarını yeniden başlat */
if (sigaction(SIGTERM, &sa, NULL) != 0) {
perror("sigaction SIGTERM");
return -1;
}
if (sigaction(SIGINT, &sa, NULL) != 0) {
perror("sigaction SIGINT");
return -1;
}
/* SIGPIPE: yazılan pipe kapanınca crash yerine hata kodu dönsün */
sa.sa_handler = SIG_IGN;
sigaction(SIGPIPE, &sa, NULL);
return 0;
}
Eski kod tabanlarında signal() ile karşılaşabilirsiniz. Yeni kod yazarken her zaman sigaction() kullanın. Her iki API de aynı signal numaralarını kabul eder; geçiş sadece yapısal olarak birkaç satır eklemek demektir.
03 Signal handler yazımı
Signal handler, interrupt context'te çalışır — ana program yarım kalmış olabilir; güvensiz fonksiyon çağırmak tanımsız davranışa yol açar.
Async-signal-safe nedir?
Bir fonksiyon, signal handler içinde çağrılabilmesi için "async-signal-safe" olmalıdır. POSIX bu listeyi açıkça tanımlar. Bu fonksiyonlar re-entrant'tır: yani hem normal akışta hem de sinyal kesintisi sırasında eş zamanlı çağrılabilirler. POSIX listesinden önemli olanlar:
write(2), read(2), _exit(2), open(2), close(2), sem_post(3), kill(2), signal(2), sigaction(2), getpid(2), getppid(2)malloc(), free(), printf(), fprintf(), syslog(), pthread_mutex_lock(), exit() (flushing yapar), fopen(), fclose(), strdup()Neden malloc yasak?
malloc, heap'i yönetmek için dahili bir mutex kullanır. Handler, malloc çağrısı yapan normal kodu tam ortasında keserse bu mutex zaten kilitli olabilir. Handler tekrar malloc çağırırsa kilitlenme (deadlock) oluşur. printf da benzer nedenle yasaktır: stdio tamponlarını manipüle eder ve dahili kilit kullanır.
Canonical pattern: flag set
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
/* volatile: derleyici optimize etmesin; sig_atomic_t: atomik okuma/yazma */
static volatile sig_atomic_t g_shutdown = 0;
static volatile sig_atomic_t g_reload = 0;
static void handle_term(int sig) {
(void)sig;
g_shutdown = 1; /* tek ve güvenli işlem */
}
static void handle_usr1(int sig) {
(void)sig;
g_reload = 1;
}
static int setup_signals(void) {
struct sigaction sa;
memset(&sa, 0, sizeof(sa));
sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_RESTART;
sa.sa_handler = handle_term;
sigaction(SIGTERM, &sa, NULL);
sigaction(SIGINT, &sa, NULL);
sa.sa_handler = handle_usr1;
sigaction(SIGUSR1, &sa, NULL);
return 0;
}
static void do_reload(void) {
/* burası normal akışta çalışır — malloc, printf hepsi serbest */
printf("Config yeniden yükleniyor...\n");
}
int main(void) {
setup_signals();
printf("PID: %d — SIGTERM veya SIGINT ile durdurun\n", getpid());
while (!g_shutdown) {
if (g_reload) {
g_reload = 0;
do_reload(); /* normal akışta güvenli */
}
/* asıl iş burada */
sleep(1);
}
printf("Kapatılıyor...\n");
return 0;
}
SA_SIGINFO ile genişletilmiş bilgi
SA_SIGINFO bayrağı ile handler üç parametre alır: signal numarası, siginfo_t yapısı (sinyali kimin ve neden gönderdiği) ve ucontext_t (CPU durumu). SIGSEGV gibi hatalarda si_addr alanı hangi adreste hata oluştuğunu gösterir.
Handler içinde write() kullanmak güvenlidir ama printf() kesinlikle değildir. Debug amacıyla bile olsa handler'dan printf çağırmayın; beklenmedik deadlock veya heap bozulmasına neden olabilir.
Bu bölümde öğrendikleriniz
- Async-signal-safe fonksiyonlar nelerdir ve neden önemlidir
- Signal handler'da yalnızca volatile sig_atomic_t flag set edilmeli
- Asıl iş, main döngüsünde flag kontrol edilerek yapılmalı
- SA_SIGINFO ile sinyali gönderenin kimliği ve hata adresi öğrenilebilir
04 Signal masking
Signal mask, bir process'in hangi sinyalleri şu an teslim almayacağını belirler — kritik bölümleri korumak ve sinyal teslimini kontrol etmek için kullanılır.
sigprocmask
#include <signal.h>
/* sinyal seti oluşturma */
sigset_t mask, oldmask;
sigemptyset(&mask); /* boş set */
sigfillset(&mask); /* tüm sinyaller */
sigaddset(&mask, SIGTERM); /* belirli sinyal ekle */
sigdelset(&mask, SIGTERM); /* belirli sinyal çıkar */
sigismember(&mask, SIGTERM); /* üye mi? → 1/0 */
/* mask uygula */
sigprocmask(SIG_BLOCK, &mask, &oldmask); /* ekle, eskiyi kaydet */
sigprocmask(SIG_UNBLOCK, &mask, NULL); /* kaldır */
sigprocmask(SIG_SETMASK, &mask, NULL); /* tam değiştir */
/* kritik bölüm: SIGTERM gelirse pending'de beklesin */
sigprocmask(SIG_BLOCK, &mask, &oldmask);
/* ... kritik işlem ... */
sigprocmask(SIG_SETMASK, &oldmask, NULL); /* eski mask'e dön */
sigpending — bekleyen sinyaller
#include <signal.h>
#include <stdio.h>
sigset_t pending;
sigpending(&pending); /* mevcut pending sinyalleri al */
if (sigismember(&pending, SIGTERM)) {
printf("SIGTERM pending — mask kaldırıldığında teslim edilecek\n");
}
sigsuspend — atomik bekle
sigsuspend(), mask'i değiştirip signal beklemeyi atomik olarak yapar. Mask değiştirip pause() çağırmak arasında sinyal gelirse kaçırılır; sigsuspend() bu race condition'ı önler.
#include <signal.h>
sigset_t wait_mask;
sigemptyset(&wait_mask);
/* SIGTERM dışındaki her şeyi blokla ve sinyal bekle */
sigfillset(&wait_mask);
sigdelset(&wait_mask, SIGTERM);
/* atomik: mask'i uygula + uyku, sinyal gelince dön */
sigsuspend(&wait_mask); /* her zaman -1 döndürür, errno=EINTR */
Multi-thread: pthread_sigmask
Çok thread'li programlarda her thread'in ayrı bir signal mask'i vardır. sigprocmask() çağrısı POSIX'te multi-threaded programlarda tanımsız davranışa yol açabilir; bunun yerine pthread_sigmask() kullanılmalıdır. Aynı parametre yapısına sahiptir.
#include <signal.h>
#include <pthread.h>
void *worker_thread(void *arg) {
sigset_t mask;
sigfillset(&mask); /* bu thread tüm sinyalleri bloklar */
pthread_sigmask(SIG_BLOCK, &mask, NULL);
/* sinyaller yalnızca main thread'e teslim edilir */
return NULL;
}
int main(void) {
/* main thread: sinyalleri dinler */
pthread_t t;
pthread_create(&t, NULL, worker_thread, NULL);
/* ... signal handling burada ... */
return 0;
}
Multi-threaded programlarda sigprocmask() yerine mutlaka pthread_sigmask() kullanın. sigprocmask() POSIX'te single-threaded programlar için tanımlanmıştır; multi-thread bağlamda hangi thread'in mask'ini değiştirdiği belirsizdir ve davranış platform bağımlıdır.
05 Graceful shutdown
Graceful shutdown, bir process'in SIGTERM aldığında kaynakları temizleyerek düzgünce kapanmasıdır — zorla öldürme yerine her zaman tercih edilmeli.
Neden graceful shutdown?
Bir daemon aniden öldürülürse şu sorunlar oluşabilir: açık dosyalar flush edilmez ve veri kaybolur; veritabanı bağlantıları kapanmaz, bağlantı havuzu tükenir; lock dosyaları kalır, bir sonraki başlatmada process başlayamaz; PID dosyaları temizlenmez; yarım işlemler geri alınmaz. SIGTERM bunların hepsini önler — process'e "kapatılmak istiyorsun" der.
systemctl stop myapp
│
▼
SIGTERM → g_shutdown = 1
│
▼
main loop: g_shutdown kontrol → cleanup() → exit(0)
│
▼
zaman aşımı (5s): SIGKILL → zorla sonlandır
Shell'den graceful shutdown
# nazik sonlandırma — process'e SIGTERM gönder
kill <PID> # varsayılan: SIGTERM
kill -SIGTERM <PID> # açık
kill -15 <PID> # numarası ile
# 5 saniye bekle, hâlâ çalışıyorsa SIGKILL
kill -SIGTERM <PID>
sleep 5
if kill -0 <PID> 2>/dev/null; then
kill -SIGKILL <PID> # son çare
fi
atexit() ve on_exit()
#include <stdio.h>
#include <stdlib.h>
static void cleanup_db(void) {
printf("DB bağlantısı kapatılıyor\n");
}
static void remove_pidfile(void) {
unlink("/var/run/myapp.pid");
}
int main(void) {
/* atexit: exit() veya return çağrısında çalışır */
/* Kayıt sırası LIFO: en son kayıt ilk çalışır */
atexit(remove_pidfile); /* 2. çalışır */
atexit(cleanup_db); /* 1. çalışır */
/* NOT: atexit _exit() veya SIGKILL'de çalışmaz */
return 0;
}
atexit() yalnızca exit() veya main'den return ile çalışır. _exit(), _Exit() veya SIGKILL ile öldürüldüğünde çalışmaz. Bu nedenle SIGTERM handler'ı, cleanup'ı doğrudan tetiklemeli ya da exit() çağırmalıdır — _exit() değil.
06 Zombie process ve SIGCHLD
Zombie, işini bitirmiş ama parent'ı henüz exit status'unu okumamış process'tir — kernel process tablosunda bir satır kaplar ama CPU kullanmaz.
Zombie nasıl oluşur?
Bir child process bittiğinde kernel onu hemen process tablosundan silmez. Parent'ın wait() veya waitpid() çağırarak exit status'u okumasını bekler. Parent bunu yapmadan önce child "zombie" (Z) durumundadır. Parent bu çağrıyı hiç yapmazsa veya kendisi de ölürse (init/systemd sahip olur ve temizler), zombie'ler birikerek process tablosunu doldurabilir.
# zombie'leri görmek
ps aux | grep " Z "
pstree -p # ağaç yapısında — <defunct> = zombie
waitpid ile çocuk topla
#include <sys/wait.h>
#include <unistd.h>
#include <stdio.h>
/* belirli bir child bekle — bloklayan */
pid_t pid = fork();
if (pid == 0) { _exit(42); } /* child */
int status;
waitpid(pid, &status, 0); /* blokla: child bitene kadar bekle */
if (WIFEXITED(status))
printf("exit: %d\n", WEXITSTATUS(status)); /* 42 */
/* tüm child'ları non-blocking topla (SIGCHLD handler içinde) */
while (waitpid(-1, NULL, WNOHANG) > 0)
; /* daha fazla zombie kalmayınca -1 döner */
SIGCHLD handler ile async reaping
#include <signal.h>
#include <sys/wait.h>
static void sigchld_handler(int sig) {
(void)sig;
int saved_errno = errno; /* handler errno'yu bozabilir */
while (waitpid(-1, NULL, WNOHANG) > 0)
;
errno = saved_errno;
}
/* setup */
struct sigaction sa;
memset(&sa, 0, sizeof(sa));
sa.sa_handler = sigchld_handler;
sa.sa_flags = SA_RESTART | SA_NOCLDSTOP; /* yalnızca exit, stop değil */
sigaction(SIGCHLD, &sa, NULL);
SA_NOCLDWAIT ile otomatik temizlik
/* SA_NOCLDWAIT: child'lar zombie olmadan doğrudan silinir
SIGCHLD hâlâ üretilir ama waitpid() gerek kalmaz */
struct sigaction sa;
memset(&sa, 0, sizeof(sa));
sa.sa_handler = SIG_DFL;
sa.sa_flags = SA_NOCLDWAIT;
sigaction(SIGCHLD, &sa, NULL);
/* Linux'ta signal(SIGCHLD, SIG_IGN) de zombie önler ama POSIX garantisi yok */
signal(SIGCHLD, SIG_IGN); /* çalışır ama taşınabilir değil */
SIGCHLD handler içinde errno'yu kaydedin ve geri yükleyin. waitpid() başarısız olduğunda errno'yu değiştirir; normal akışın errno'sunu bozarsanız tespit edilmesi güç hatalar oluşur.
07 Pratik: daemon için signal-safe shutdown
Tüm öğrendiklerinizi birleştiren tam bir daemon: PID dosyası, graceful shutdown, config reload ve child reaping.
/* gcc -o daemon daemon.c
Derle: gcc -Wall -Wextra -o daemon daemon.c */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <signal.h>
#include <fcntl.h>
#include <errno.h>
#include <poll.h>
#include <sys/wait.h>
#include <sys/stat.h>
#define PID_FILE "/var/run/mydaemon.pid"
static volatile sig_atomic_t g_shutdown = 0;
static volatile sig_atomic_t g_reload = 0;
/* ─── signal handlers ─────────────────────────────── */
static void handle_term(int sig) { (void)sig; g_shutdown = 1; }
static void handle_usr1(int sig) { (void)sig; g_reload = 1; }
static void handle_chld(int sig) {
(void)sig;
int saved = errno;
while (waitpid(-1, NULL, WNOHANG) > 0) ;
errno = saved;
}
/* ─── PID file ────────────────────────────────────── */
static int write_pidfile(void) {
int fd = open(PID_FILE,
O_WRONLY | O_CREAT | O_TRUNC, 0644);
if (fd < 0) { perror("open pid"); return -1; }
char buf[32];
int n = snprintf(buf, sizeof(buf), "%d\n", getpid());
write(fd, buf, n);
close(fd);
return 0;
}
static void remove_pidfile(void) {
unlink(PID_FILE);
}
/* ─── cleanup ─────────────────────────────────────── */
static void cleanup(void) {
remove_pidfile();
/* flush buffers, close DB, release locks... */
printf("[daemon] temizlendi, çıkılıyor.\n");
}
/* ─── signal setup ────────────────────────────────── */
static void setup_signals(void) {
struct sigaction sa;
memset(&sa, 0, sizeof(sa));
sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_RESTART;
sa.sa_handler = handle_term;
sigaction(SIGTERM, &sa, NULL);
sigaction(SIGINT, &sa, NULL);
sa.sa_handler = handle_usr1;
sigaction(SIGUSR1, &sa, NULL);
sa.sa_handler = handle_chld;
sa.sa_flags |= SA_NOCLDSTOP;
sigaction(SIGCHLD, &sa, NULL);
sa.sa_handler = SIG_IGN;
sa.sa_flags = 0;
sigaction(SIGPIPE, &sa, NULL);
}
/* ─── main ────────────────────────────────────────── */
int main(void) {
if (write_pidfile() != 0) return 1;
atexit(remove_pidfile);
setup_signals();
printf("[daemon] başladı, PID=%d\n", getpid());
/* systemd Type=notify ile entegrasyon */
/* sd_notify(0, "READY=1"); */
struct pollfd pfd = { .fd = -1, .events = 0 };
while (!g_shutdown) {
int ret = poll(&pfd, 1, 1000); /* 1s timeout */
if (ret < 0 && errno != EINTR) {
perror("poll");
break;
}
if (g_reload) {
g_reload = 0;
printf("[daemon] SIGUSR1 — config reload\n");
/* load_config() burada */
}
/* periyodik iş */
printf("[daemon] çalışıyor...\n");
}
cleanup();
return 0;
}
systemd entegrasyonu
[Unit]
Description=Signal-safe örnek daemon
After=network.target
[Service]
Type=simple
ExecStart=/usr/local/bin/daemon
ExecReload=/bin/kill -SIGUSR1 $MAINPID
Restart=on-failure
RestartSec=5s
TimeoutStopSec=10s
[Install]
WantedBy=multi-user.target
POSIX real-time sinyalleri (SIGRTMIN..SIGRTMAX): Linux'ta SIGRTMIN (34) ile SIGRTMAX (64) arasında 30 adet real-time sinyal vardır. Standart sinyallerden farkları: kuyruğa alınırlar (aynı sinyal birden fazla kez gönderilebilir, hepsi teslim edilir), öncelik sırasına göre işlenirler ve sigqueue() ile birlikte bir değer (integer veya pointer) taşıyabilirler. Standart sinyaller birden fazla pending kalırsa tek birleştirilir.
Bu bölümde öğrendikleriniz
- PID dosyası yazma ve atexit() ile temizleme
- SIGTERM, SIGUSR1, SIGCHLD handler'larını tek setup fonksiyonunda toplamak
- poll() ile event loop: SA_RESTART sayesinde EINTR'yi yönetmek
- systemd ExecReload ile SIGUSR1 tabanlı reload entegrasyonu
- Real-time sinyallerin standart sinyallerden temel farkları