Golang实现Watchdog功能的方法与实践

Golang实现Watchdog功能的方法与实践 我正在尝试使用 syscall.SYS_IOCTL 实现以下看门狗功能:

  1. 启用看门狗
  2. 禁用看门狗
  3. 设置超时和预超时
  4. 获取看门狗状态

我在设置预超时时遇到了问题,并且也面临其他一些困难。是否有其他方法可以实现这些看门狗功能?

1 回复

更多关于Golang实现Watchdog功能的方法与实践的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


在Go中实现看门狗功能,syscall.SYS_IOCTL 是直接与Linux内核交互的底层方式。以下是完整的实现示例,包括预超时设置:

package main

import (
    "fmt"
    "os"
    "syscall"
    "unsafe"
)

const (
    WATCHDOG_IOCTL_BASE = 'W'
    
    WDIOC_GETSUPPORT    = 0x80285700
    WDIOC_SETOPTIONS    = 0x80045701
    WDIOC_GETSTATUS     = 0x80045702
    WDIOC_SETTIMEOUT    = 0xc0045706
    WDIOC_SETPRETIMEOUT = 0xc0045709
    WDIOC_GETTIMEOUT    = 0x80045707
    WDIOC_GETPRETIMEOUT = 0x8004570a
    
    WDIOF_KEEPALIVEPING = 0x8000
    WDIOS_DISABLECARD   = 0x0001
    WDIOS_ENABLECARD    = 0x0002
)

type watchdogInfo struct {
    options      uint32
    firmwareVer  uint32
    identity     [32]byte
}

func openWatchdog() (int, error) {
    fd, err := syscall.Open("/dev/watchdog", syscall.O_RDWR, 0)
    if err != nil {
        return -1, fmt.Errorf("failed to open watchdog: %v", err)
    }
    return fd, nil
}

func enableWatchdog(fd int) error {
    options := uint32(WDIOS_ENABLECARD)
    _, _, errno := syscall.Syscall(
        syscall.SYS_IOCTL,
        uintptr(fd),
        uintptr(WDIOC_SETOPTIONS),
        uintptr(unsafe.Pointer(&options)),
    )
    if errno != 0 {
        return fmt.Errorf("failed to enable watchdog: %v", errno)
    }
    return nil
}

func disableWatchdog(fd int) error {
    options := uint32(WDIOS_DISABLECARD)
    _, _, errno := syscall.Syscall(
        syscall.SYS_IOCTL,
        uintptr(fd),
        uintptr(WDIOC_SETOPTIONS),
        uintptr(unsafe.Pointer(&options)),
    )
    if errno != 0 {
        return fmt.Errorf("failed to disable watchdog: %v", errno)
    }
    return nil
}

func setTimeout(fd int, timeoutSec int) error {
    timeout := uint32(timeoutSec)
    _, _, errno := syscall.Syscall(
        syscall.SYS_IOCTL,
        uintptr(fd),
        uintptr(WDIOC_SETTIMEOUT),
        uintptr(unsafe.Pointer(&timeout)),
    )
    if errno != 0 {
        return fmt.Errorf("failed to set timeout: %v", errno)
    }
    return nil
}

func setPretimeout(fd int, pretimeoutSec int) error {
    pretimeout := uint32(pretimeoutSec)
    _, _, errno := syscall.Syscall(
        syscall.SYS_IOCTL,
        uintptr(fd),
        uintptr(WDIOC_SETPRETIMEOUT),
        uintptr(unsafe.Pointer(&pretimeout)),
    )
    if errno != 0 {
        return fmt.Errorf("failed to set pretimeout: %v", errno)
    }
    return nil
}

func getStatus(fd int) (uint32, error) {
    var status uint32
    _, _, errno := syscall.Syscall(
        syscall.SYS_IOCTL,
        uintptr(fd),
        uintptr(WDIOC_GETSTATUS),
        uintptr(unsafe.Pointer(&status)),
    )
    if errno != 0 {
        return 0, fmt.Errorf("failed to get status: %v", errno)
    }
    return status, nil
}

func keepAlive(fd int) error {
    _, err := syscall.Write(fd, []byte{1})
    if err != nil {
        return fmt.Errorf("failed to keep alive: %v", err)
    }
    return nil
}

func main() {
    fd, err := openWatchdog()
    if err != nil {
        fmt.Printf("Error: %v\n", err)
        os.Exit(1)
    }
    defer syscall.Close(fd)
    
    err = enableWatchdog(fd)
    if err != nil {
        fmt.Printf("Enable error: %v\n", err)
        return
    }
    
    err = setTimeout(fd, 60)
    if err != nil {
        fmt.Printf("Set timeout error: %v\n", err)
    }
    
    err = setPretimeout(fd, 30)
    if err != nil {
        fmt.Printf("Set pretimeout error: %v\n", err)
    }
    
    status, err := getStatus(fd)
    if err != nil {
        fmt.Printf("Get status error: %v\n", err)
    } else {
        fmt.Printf("Watchdog status: 0x%x\n", status)
    }
    
    err = keepAlive(fd)
    if err != nil {
        fmt.Printf("Keep alive error: %v\n", err)
    }
}

对于预超时设置失败的问题,需要检查内核是否支持该功能:

func checkPretimeoutSupport(fd int) (bool, error) {
    var info watchdogInfo
    _, _, errno := syscall.Syscall(
        syscall.SYS_IOCTL,
        uintptr(fd),
        uintptr(WDIOC_GETSUPPORT),
        uintptr(unsafe.Pointer(&info)),
    )
    if errno != 0 {
        return false, fmt.Errorf("failed to get watchdog info: %v", errno)
    }
    
    // 检查是否支持预超时功能
    // 具体标志位需要根据内核版本和硬件确定
    return (info.options & 0x00080000) != 0, nil
}

替代方案可以使用 golang.org/x/sys/unix 包,它提供了更类型安全的接口:

import "golang.org/x/sys/unix"

func setPretimeoutUnix(fd int, pretimeoutSec int) error {
    pretimeout := uint32(pretimeoutSec)
    return unix.IoctlSetInt(fd, WDIOC_SETPRETIMEOUT, int(pretimeout))
}

func getTimeoutUnix(fd int) (int, error) {
    return unix.IoctlGetInt(fd, WDIOC_GETTIMEOUT)
}

预超时设置失败通常是因为硬件不支持或内核配置未启用 CONFIG_WATCHDOG_PRETIMEOUT。使用 WDIOC_GETSUPPORT 可以检测硬件能力。

回到顶部