Golang实现Watchdog功能的方法与实践
Golang实现Watchdog功能的方法与实践
我正在尝试使用 syscall.SYS_IOCTL 实现以下看门狗功能:
- 启用看门狗
- 禁用看门狗
- 设置超时和预超时
- 获取看门狗状态
我在设置预超时时遇到了问题,并且也面临其他一些困难。是否有其他方法可以实现这些看门狗功能?
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 可以检测硬件能力。

