00 Watchdog nedir — hardware vs software WDT
Watchdog timer, yazılımın kendisini periyodik olarak "ben hâlâ çalışıyorum" diye onaylamasını gerektirir. Onay gelmezse donanım sistemi sıfırlar.
Neden gerekli?
Gömülü sistemlerde yazılım birçok nedenden dolayı kilitlenebilir: sonsuz döngü, deadlock, bellek bozulması, kernel panik. İnsan müdahalesi mümkün olmayan uzak veya güvenlik-kritik sistemlerde watchdog, son savunma hattıdır.
Temel çalışma prensibi
Uygulama başlar
│
├─ /dev/watchdog açılır → WDT başlar (örn. 30sn)
│
├─ Döngü: iş yap → watchdog besle (her ~10sn)
│ │
│ └─ WDT sayacı sıfırlanır
│
├─ Kilitlenme!
│ │
│ └─ watchdog beslenmez
│ │
│ timeout (30sn) dolunca
│ │
└──────────────────── HARDWARE RESET
/dev/watchdog'u açtıktan sonra beslemezseniz sistem resetlenir. Test ortamında her zaman "magic close" mekanizmasını doğru kullanın: dosyayı kapatmadan önce 'V' karakteri yazın.
01 /dev/watchdog API — open, write, ioctl
Linux watchdog UAPI basittir: aç, besle, kapat. Tüm kontrol /dev/watchdog üzerinden ioctl ile yapılır.
Temel C API
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <linux/watchdog.h>
int main(void) {
int fd, timeout = 30;
/* WDT aç — bu noktadan itibaren beslenmezse reset gelir */
fd = open("/dev/watchdog", O_RDWR);
if (fd < 0) { perror("open /dev/watchdog"); return 1; }
/* Zaman aşımını ayarla (saniye) */
if (ioctl(fd, WDIOC_SETTIMEOUT, &timeout) < 0) {
perror("WDIOC_SETTIMEOUT");
}
/* Gerçek timeout'u oku (donanım en yakın desteklenen değere yuvarlar) */
ioctl(fd, WDIOC_GETTIMEOUT, &timeout);
printf("WDT timeout: %d saniye\n", timeout);
/* Keepalive döngüsü */
for (int i = 0; i < 10; i++) {
sleep(10);
/* Yöntem 1: write() ile besle */
write(fd, "1", 1);
/* Yöntem 2: ioctl ile besle */
/* ioctl(fd, WDIOC_KEEPALIVE, 0); */
printf("WDT beslendi (%d/10)\n", i + 1);
}
/* Magic close: 'V' yazarak güvenli kapat */
write(fd, "V", 1);
close(fd);
printf("WDT durduruldu (magic close)\n");
return 0;
}
IOCTL komutları
#include <linux/watchdog.h>
struct watchdog_info info;
ioctl(fd, WDIOC_GETSUPPORT, &info);
printf("Identity: %s\n", info.identity);
printf("Firmware: %u\n", info.firmware_version);
printf("Options: 0x%08x\n", info.options);
/* WDIOF flag'leri kontrol et */
if (info.options & WDIOF_SETTIMEOUT)
printf(" - Timeout ayarlanabilir\n");
if (info.options & WDIOF_KEEPALIVEPING)
printf(" - Keepalive destekleniyor\n");
if (info.options & WDIOF_MAGICCLOSE)
printf(" - Magic close destekleniyor\n");
if (info.options & WDIOF_PRETIMEOUT)
printf(" - Pre-timeout destekleniyor\n");
/* Son reset WDT'den mi? */
int boot_status;
ioctl(fd, WDIOC_GETBOOTSTATUS, &boot_status);
if (boot_status & WDIOF_CARDRESET)
printf("Son reset: WDT reset!\n");
02 Kernel watchdog driver — struct watchdog_device
Linux watchdog core, platform-specific WDT donanımını soyutlayan birleşik bir altyapı sağlar. Yeni bir WDT driver yazmak için watchdog_device struct'ını ve ops'larını doldurmak yeterlidir.
#include <linux/watchdog.h>
#include <linux/platform_device.h>
struct mywdt_priv {
void __iomem *base;
struct watchdog_device wdd;
};
static int mywdt_start(struct watchdog_device *wdd)
{
struct mywdt_priv *priv = watchdog_get_drvdata(wdd);
/* Donanımı başlat */
writel(WDT_ENABLE | WDT_RESET_ON_TIMEOUT,
priv->base + WDT_CTRL_REG);
return 0;
}
static int mywdt_stop(struct watchdog_device *wdd)
{
struct mywdt_priv *priv = watchdog_get_drvdata(wdd);
writel(0, priv->base + WDT_CTRL_REG);
return 0;
}
static int mywdt_ping(struct watchdog_device *wdd)
{
struct mywdt_priv *priv = watchdog_get_drvdata(wdd);
/* Sayacı sıfırla */
writel(WDT_KICK_VALUE, priv->base + WDT_KICK_REG);
return 0;
}
static int mywdt_set_timeout(struct watchdog_device *wdd,
unsigned int timeout)
{
struct mywdt_priv *priv = watchdog_get_drvdata(wdd);
u32 count = timeout * WDT_CLK_FREQ;
writel(count, priv->base + WDT_LOAD_REG);
wdd->timeout = timeout;
return 0;
}
static const struct watchdog_ops mywdt_ops = {
.owner = THIS_MODULE,
.start = mywdt_start,
.stop = mywdt_stop,
.ping = mywdt_ping,
.set_timeout = mywdt_set_timeout,
};
static const struct watchdog_info mywdt_info = {
.options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,
.identity = "MyPlatform WDT",
};
static int mywdt_probe(struct platform_device *pdev)
{
struct mywdt_priv *priv;
struct resource *res;
priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
if (!priv) return -ENOMEM;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
priv->base = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(priv->base)) return PTR_ERR(priv->base);
priv->wdd.info = &mywdt_info;
priv->wdd.ops = &mywdt_ops;
priv->wdd.timeout = 30; /* default 30s */
priv->wdd.min_timeout = 1;
priv->wdd.max_timeout = 128;
priv->wdd.parent = &pdev->dev;
watchdog_set_drvdata(&priv->wdd, priv);
watchdog_set_nowayout(&priv->wdd, nowayout);
return devm_watchdog_register_device(&pdev->dev, &priv->wdd);
}
nowayout=1 modülü yüklendiğinde magic close çalışmaz — WDT bir kez açıldıktan sonra kapatılamaz. Üretim sistemleri için önerilir; test sırasında false resetleri önlemek için 0 kullanın.
03 systemd watchdog entegrasyonu
systemd, watchdog'u kendi servis izleme sistemiyle entegre eder. Servis belirli aralıklarla "ben sağlıklıyım" bildirimi göndermezse systemd servisi yeniden başlatır; daha uzun sürede yanıt gelmezse sistem resetlenir.
systemd watchdog yapılandırması
[Manager]
RuntimeWatchdogSec=30s # sistemin WDT timeout'u
RebootWatchdogSec=10min # reboot sırasında WDT timeout
KExecWatchdogSec=10min # kexec sırasında WDT timeout
[Unit]
Description=Kritik Sensör Daemon
[Service]
Type=notify
ExecStart=/usr/local/bin/sensor-daemon
# Servis 20 saniyede bir sd_notify(WATCHDOG=1) çağırmalı
WatchdogSec=20s
# WDT timeout'u aşılırsa ne yap?
Restart=on-watchdog # servisi yeniden başlat
RestartSec=5s
# veya sistem reset için:
# WatchdogSignal=SIGABRT # önce ABRT, sonra reset
[Install]
WantedBy=multi-user.target
sd_notify API ile servis içi watchdog
#include <systemd/sd-daemon.h>
#include <stdint.h>
#include <time.h>
int main(void) {
uint64_t watchdog_usec = 0;
struct timespec ts;
/* systemd watchdog interval'ını öğren */
sd_watchdog_enabled(0, &watchdog_usec);
uint64_t interval_usec = watchdog_usec / 2; /* interval/2 sıklıkla besle */
/* Hazır olduğunu bildir */
sd_notify(0, "READY=1\nSTATUS=Sensör daemon başladı");
while (1) {
/* ... iş yap ... */
do_sensor_reading();
/* WDT besle */
sd_notify(0, "WATCHDOG=1");
/* İsteğe bağlı: durum güncelle */
sd_notifyf(0, "WATCHDOG=1\nSTATUS=Ölçüm #%d tamamlandı",
reading_count++);
/* Hesaplanan interval kadar bekle */
usleep(interval_usec);
}
return 0;
}
/* Derleme: gcc ... -lsystemd */
#!/usr/bin/env python3
import os, time, socket
def sd_notify(msg):
"""systemd sd_notify() Python uygulaması"""
addr = os.environ.get("NOTIFY_SOCKET")
if not addr: return
if addr.startswith('@'):
addr = '\0' + addr[1:]
sock = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM)
sock.sendto(msg.encode(), addr)
sock.close()
# Başlangıç
sd_notify("READY=1\nSTATUS=başlatıldı")
count = 0
while True:
time.sleep(5)
count += 1
# Watchdog besle
sd_notify(f"WATCHDOG=1\nSTATUS=döngü {count}")
print(f"WDT beslendi (döngü {count})")
# Sistemin WDT kullandığını doğrula
systemctl show | grep -i watchdog
# RuntimeWatchdogUSec=30s
# RuntimeWatchdogPreUSec=0
# servis watchdog durumu
systemctl status sensor-daemon.service | grep -i watchdog
04 Pretimeout — önceden uyarı ve panic
Pretimeout, gerçek WDT resetinden önce belirli bir süre içinde kernel'a veya kullanıcı alanına uyarı verir. Bu sürede teşhis bilgisi toplanabilir veya kontrollü kapatma yapılabilir.
#include <linux/watchdog.h>
int fd = open("/dev/watchdog", O_RDWR);
int timeout = 30;
int pretimeout = 5; /* resetden 5 saniye önce uyar */
ioctl(fd, WDIOC_SETTIMEOUT, &timeout);
ioctl(fd, WDIOC_SETPRETIMEOUT, &pretimeout);
int actual_pre;
ioctl(fd, WDIOC_GETPRETIMEOUT, &actual_pre);
printf("Pretimeout: %d saniye resetden önce\n", actual_pre);
watchdog_pretimeout_governor
# mevcut pretimeout governor'ları
ls /sys/bus/platform/drivers/ | grep pretimeout
cat /sys/kernel/debug/watchdog0/pretimeout_governor
# noop
# panic governor'a geç (kernel panic + kdump)
echo panic > /sys/kernel/debug/watchdog0/pretimeout_governor
# systemd ile pretimeout
# /etc/systemd/system.conf içine:
# RuntimeWatchdogPreUSec=5s
Pretimeout + panic + kdump kombinasyonu, production sistemlerde "neden kilitlendi?" sorusunu yanıtlamak için en güçlü araçtır. Pretimeout'ta kernel panic tetiklenince kdump crash dump kaydeder, sonra asıl WDT reset atar ve sistem yeniden başlar.
05 watchdog-d daemon — /etc/watchdog.conf
watchdog-d (veya watchdog paketi), /dev/watchdog'u yöneten, çeşitli sistem sağlık testlerini yapan hazır bir daemon'dur.
# Debian/Ubuntu
apt install watchdog
# servis başlat
systemctl enable --now watchdog
/etc/watchdog.conf açıklamalı
# WDT cihaz dosyası
watchdog-device = /dev/watchdog
# WDT timeout (saniye)
watchdog-timeout = 30
# Kaç saniyede bir besle
interval = 10
# Sıcaklık eşiği (°C) — /sys/class/thermal/ veya hwmon
#temperature-device = /dev/cpu_temp0
#max-temperature = 90
# CPU yük ortalaması testi
max-load-1 = 24 # 1dk load average > 24 ise hata
max-load-5 = 18 # 5dk load average
max-load-15 = 12 # 15dk load average
# Bellek testi
min-memory = 1 # MB cinsinden minimum free bellek
#allocatable-memory = 1
# Dosya değişiklik testi (dosya güncellenmezse hata)
#file = /var/run/heartbeat.pid
#change = 60 # 60 saniyede değişmeli
# Dosya erişilebilirlik testi
#file = /var/run/myapp.pid
# PID dosyası — process çalışıyor mu?
#pidfile = /var/run/sshd.pid
# Özel test scripti
#test-binary = /usr/local/bin/wdt-health-check.sh
#test-timeout = 5 # script 5 saniyede bitirmeli
# Hata durumunda ne yap
repair-binary = /usr/local/bin/wdt-repair.sh
# Log
log-dir = /var/log/watchdog
verbose = 0
#!/bin/bash
# /usr/local/bin/wdt-health-check.sh
# watchdog test-binary olarak çağrılır
# Başarı: exit 0, Hata: exit 1
# I2C sensör erişilebilir mi?
if ! i2cdetect -y 1 2>/dev/null | grep -q "^20:"; then
logger -p daemon.error "WDT: I2C sensor kayboldu!"
exit 1
fi
# Kritik servis çalışıyor mu?
if ! systemctl is-active --quiet mqtt-bridge.service; then
logger -p daemon.error "WDT: mqtt-bridge durdu!"
exit 1
fi
# Disk yazılabilir mi?
if ! touch /tmp/wdt_test 2>/dev/null; then
logger -p daemon.error "WDT: disk yazılamıyor!"
exit 1
fi
rm -f /tmp/wdt_test
exit 0
06 Platform WDT: BCM2835, i.MX WDOG, STM32 IWDG
Her SoC'un kendi WDT donanımı ve driver'ı vardır. Üç yaygın platform için notlar.
Raspberry Pi — BCM2835/BCM2711 WDT
# BCM2835 WDT device tree node (Pi 4)
# bcm2835-wdt otomatik yüklenir — kernel config: CONFIG_BCM2835_WDT=y
# Driver yüklü mü?
dmesg | grep bcm2835_wdt
# bcm2835-wdt: Broadcom BCM2835 watchdog timer
cat /sys/devices/platform/soc/1c000000.watchdog/identity 2>/dev/null ||
cat /sys/class/watchdog/watchdog0/identity
# Broadcom BCM2835 Watchdog timer
# Maksimum timeout: ~15 saniye (BCM2835 sınırlaması)
cat /sys/class/watchdog/watchdog0/max_timeout
# 15
# BCM2711 (Pi 4): daha uzun timeout destekler
# PM kütüphanesi üzerinden de kontrol edilebilir
i.MX WDOG (NXP i.MX6/7/8)
wdog1: watchdog@20bc000 {
compatible = "fsl,imx6q-wdt", "fsl,imx21-wdt";
reg = <0x020bc000 0x4000>;
interrupts = <GIC_SPI 80 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&clks IMX6QDL_CLK_IPG>;
};
# i.MX WDOG kernel config: CONFIG_IMX2_WDT=y
dmesg | grep imx2_wdt
# imx2-wdt 20bc000.watchdog: Initial timeout: 60s
# i.MX WDOG maksimum timeout: ~128 saniye
cat /sys/class/watchdog/watchdog0/max_timeout
# 128
# i.MX WDOG reset kaynağını kontrol et
# SRC (System Reset Controller) registerı
devmem2 0x020D8008 w # SRC_SRSR: reset status register
# bit[4]: WDOG reset bit
STM32 IWDG ve WWDG
/* STM32MP157 IWDG (Independent Watchdog) */
iwdg2: watchdog@5a002000 {
compatible = "st,stm32mp1-iwdg";
reg = <0x5a002000 0x400>;
clocks = <&rcc IWDG2>, <&rcc CK_LSI>;
clock-names = "pclk", "lsi";
timeout-sec = <32>;
status = "okay";
};
/* WWDG (Window Watchdog) — pencereli WDT */
wwdg: watchdog@40002c00 {
compatible = "st,stm32-wwdg";
reg = <0x40002c00 0x400>;
clocks = <&rcc WWDG1K>;
/* min ve max arasında beslenmeli! */
};
07 Yocto/Buildroot — WDT enablement
Gömülü Linux dağıtımlarında WDT genellikle varsayılan olarak devre dışıdır. Yocto ve Buildroot'ta etkinleştirme adımları.
Yocto
# local.conf veya distro.conf
DISTRO_FEATURES:append = " watchdog"
# kernel konfigürasyonu (meta-layer/recipes-kernel/linux/linux-yocto.bbappend)
KERNEL_EXTRA_FEATURES:append = " watchdog.scc"
# watchdog paketi ekle
IMAGE_INSTALL:append = " watchdog"
# systemd entegrasyonu için (meta-systemd)
IMAGE_INSTALL:append = " systemd-conf"
# meta-myboard/recipes-support/watchdog/watchdog_%.bbappend
FILESEXTRAPATHS:prepend := "${THISDIR}/files:"
SRC_URI:append = " file://watchdog.conf"
do_install:append() {
install -d ${D}${sysconfdir}
install -m 0644 ${WORKDIR}/watchdog.conf ${D}${sysconfdir}/watchdog.conf
}
Buildroot
# make menuconfig içinde:
# Target packages → System tools → watchdog utilities
BR2_PACKAGE_WATCHDOG=y
# Kernel config fragment (board/myboard/linux.config)
CONFIG_WATCHDOG=y
CONFIG_WATCHDOG_CORE=y
CONFIG_BCM2835_WDT=y # ya da platform-specific
CONFIG_SOFT_WATCHDOG=y # yedek olarak softdog
Systemd servis — önyükleme sırasında WDT başlatma
[Unit]
Description=Start Hardware Watchdog
DefaultDependencies=no
Before=sysinit.target
After=local-fs.target
[Service]
Type=oneshot
RemainAfterExit=yes
# WDT modülünü yükle (gerekirse)
ExecStartPre=-/sbin/modprobe bcm2835_wdt
# watchdog daemon başlat
ExecStart=/sbin/watchdogd -F -c /etc/watchdog.conf
[Install]
WantedBy=sysinit.target
08 Pratik: Python watchdog daemon ve kill testi
Python ile tam watchdog keepalive daemon, ardından kill testi ile hardware reset'i gözlemleme.
Python watchdog daemon
#!/usr/bin/env python3
"""
Watchdog keepalive daemon.
- /dev/watchdog'u açar, timeout ayarlar
- Her FEED_INTERVAL saniyede bir besler
- Sağlık kontrolü başarısız olursa beslemeyi durdurur → reset
"""
import os, fcntl, struct, time, signal, logging, subprocess
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s %(levelname)s %(message)s',
datefmt='%H:%M:%S'
)
log = logging.getLogger("wdt")
# /usr/include/linux/watchdog.h değerleri
WDIOC_SETTIMEOUT = 0xC0045706
WDIOC_GETTIMEOUT = 0x80045707
WDIOC_KEEPALIVE = 0x80002705
WDIOC_GETBOOTSTATUS = 0x80045702
WDIOF_CARDRESET = 0x0020
WDT_DEVICE = "/dev/watchdog"
WDT_TIMEOUT = 30 # saniye
FEED_INTERVAL = 10 # saniye — timeout/3
MAGIC_CLOSE = b'V'
class WatchdogDaemon:
def __init__(self):
self.fd = None
self.running = True
signal.signal(signal.SIGTERM, self._shutdown)
signal.signal(signal.SIGINT, self._shutdown)
def _shutdown(self, *args):
log.info("Kapatma sinyali alındı")
self.running = False
def open(self):
self.fd = os.open(WDT_DEVICE, os.O_RDWR)
# Boot status kontrol
buf = struct.pack('I', 0)
res = fcntl.ioctl(self.fd, WDIOC_GETBOOTSTATUS, buf)
boot_status = struct.unpack('I', res)[0]
if boot_status & WDIOF_CARDRESET:
log.warning("Son reboot WDT resetinden! Sistem kilitlenmesi oldu.")
# Timeout ayarla
buf = struct.pack('I', WDT_TIMEOUT)
res = fcntl.ioctl(self.fd, WDIOC_SETTIMEOUT, buf)
actual = struct.unpack('I', res)[0]
log.info(f"WDT açıldı, timeout={actual}s, besle={FEED_INTERVAL}s")
def feed(self):
"""WDT besle"""
os.write(self.fd, b'1')
def close_safe(self):
"""Magic close — WDT'yi güvenli durdur"""
if self.fd is not None:
os.write(self.fd, MAGIC_CLOSE)
os.close(self.fd)
self.fd = None
log.info("WDT magic close ile durduruldu")
def health_check(self):
"""Sistem sağlık kontrolü — False döndürürse besleme durur"""
# Kritik servis çalışıyor mu?
ret = subprocess.run(
["systemctl", "is-active", "--quiet", "sshd"],
capture_output=True
)
if ret.returncode != 0:
log.error("sshd çalışmıyor!")
return False
# Disk alanı kontrolü (minimum 10MB)
stat = os.statvfs("/")
free_mb = (stat.f_bavail * stat.f_frsize) / (1024 * 1024)
if free_mb < 10:
log.error(f"Disk doldu: {free_mb:.1f}MB kaldı!")
return False
return True
def run(self):
self.open()
try:
while self.running:
if self.health_check():
self.feed()
log.debug("WDT beslendi, sistem sağlıklı")
else:
log.critical("SAĞLIK KONTROLÜ BAŞARISIZ — WDT beslenmeyecek!")
# Beslemeyi durdur, reset bekle
while True:
time.sleep(1)
time.sleep(FEED_INTERVAL)
finally:
self.close_safe()
if __name__ == "__main__":
daemon = WatchdogDaemon()
daemon.run()
Kill testi — hardware reset gözlemleme
# 1. Daemon'ı başlat
python3 watchdog_daemon.py &
WDT_PID=$!
echo "WDT daemon PID: $WDT_PID"
# 2. Normal çalıştığını doğrula (30 saniye izle)
sleep 30
echo "Sistem hâlâ çalışıyor — WDT düzgün besleniyor"
# 3. Daemon'ı kill et (SIGKILL — magic close yapamaz)
echo "WDT daemon'ını öldürüyoruz..."
kill -9 $WDT_PID
# 4. WDT_TIMEOUT saniye bekle → sistem resetlenecek
# Bu noktadan sonra terminal oturumu kapanır!
echo "WDT beslenmeyecek, $WDT_TIMEOUT saniye içinde reset bekleniyor..."
# 5. Reboot sonrası — WDT resetinden mi önyüklendi?
dmesg | grep -i "watchdog\|wdt\|reset"
# bcm2835-wdt: Watchdog reset triggered
# 6. Boot status kontrolü
python3 -c "
import os, fcntl, struct
WDIOC_GETBOOTSTATUS = 0x80045702
WDIOF_CARDRESET = 0x0020
fd = os.open('/dev/watchdog', os.O_RDWR)
buf = struct.pack('I', 0)
res = fcntl.ioctl(fd, WDIOC_GETBOOTSTATUS, buf)
status = struct.unpack('I', res)[0]
print('WDT reset:', bool(status & WDIOF_CARDRESET))
os.write(fd, b'V')
os.close(fd)
"
kill -9 ile daemon öldürüldükten WDT_TIMEOUT (30) saniye sonra sistem resetlenir. Reboot sonrası WDIOC_GETBOOTSTATUS WDIOF_CARDRESET bitini set gösterir. dmesg'de "Watchdog reset triggered" veya benzeri mesaj görülür. Bu, hardware WDT'nin yazılımdan bağımsız olarak düzgün çalıştığını doğrular.