Linux应用层例程5 看门狗应用编程

        在产品化的嵌入式系统中,为了使系统在异常情况下能自动复位,一般都需要引入看门狗。看门狗其实就是一个可以在一定时间内被复位的计数器。当看门狗启动后,计数器开始自动计数,经过一定时间,如果没有被复位,计数器溢出就会对 CPU 产生一个复位信号使系统重启(俗称“被狗咬”)。系统正常运行时,需要在看门狗允许的时间间隔内对看门狗计数器清零(俗称“喂狗”),不让复位信号产生。如果系统不出问题,程序保证按时“喂狗”,一旦程序跑飞,没有“喂狗”,系统“被咬”复位。
看门狗应用编程介绍
        前面已经说到,看门狗其实就是一个可以在一定时间内被复位/ 重置的计数器,一般叫做看门狗计时器 (或看门狗定时器);如果在规定时间内没有复位看门狗计时器,计数器溢出则会对 CPU 产生一个复位信号使系统重启,当然有些看门狗也可以只产生中断信号而不会使系统复位。
        I.MX6UL/I.MX6ULL SoC 集成了两个看门狗定时器( WDOG ): WDOG1 WDOG2 WDOG2 用于安全目的,而 WDOG1 则是一个普通的看门狗,支持产生中断信号以及复位 CPU
        Linux 系统中所注册的看门狗外设,都会在 /dev/ 目录下生成对应的设备节点(设备文件),设备节点名称通常为 watchdogX X 表示一个数字编号 0 1 2 3 等),譬 /dev/watchdog0/dev/watchdog1 ,通过这些设备节点可以控制看门狗外设。
 

 

应用层控制看门狗其实非常简单,通过 ioctl() 函数即可做到!接下来笔者向大家进行介绍。
        首先在我们的应用程序中,需要包含头文件<linux/watchdog.h> 头文件,该头文件中定义了一些 ioctl 指令宏,每一个不同的指令宏表示向设备请求不同的操作,如下所示:
#define WDIOC_GETSUPPORT _IOR(WATCHDOG_IOCTL_BASE, 0, struct watchdog_info)
#define WDIOC_GETSTATUS _IOR(WATCHDOG_IOCTL_BASE, 1, int)
#define WDIOC_GETBOOTSTATUS _IOR(WATCHDOG_IOCTL_BASE, 2, int)
#define WDIOC_GETTEMP _IOR(WATCHDOG_IOCTL_BASE, 3, int)
#define WDIOC_SETOPTIONS _IOR(WATCHDOG_IOCTL_BASE, 4, int)
#define WDIOC_KEEPALIVE _IOR(WATCHDOG_IOCTL_BASE, 5, int)
#define WDIOC_SETTIMEOUT _IOWR(WATCHDOG_IOCTL_BASE, 6, int)
#define WDIOC_GETTIMEOUT _IOR(WATCHDOG_IOCTL_BASE, 7, int)
#define WDIOC_SETPRETIMEOUT _IOWR(WATCHDOG_IOCTL_BASE, 8, int)
#define WDIOC_GETPRETIMEOUT _IOR(WATCHDOG_IOCTL_BASE, 9, int)
#define WDIOC_GETTIMELEFT _IOR(WATCHDOG_IOCTL_BASE, 10, int)
比 较 常 用 指 令 包 括 :
WDIOC_GETSUPPORT
WDIOC_SETOPTIONS
WDIOC_KEEPALIVE
WDIOC_SETTIMEOUT
WDIOC_GETTIMEOUT ,说明如下:

 

具体步骤

操作一 打开设备
首先在调用 ioctl() 函数之前,需要先打开看门狗设备得到文件描述符,如下所示:
int fd;

fd = open("/dev/watchdog", "O_RDWR");
if (0 > fd)
    fprintf(stderr, "open error: %s: %s\n", "/dev/watchdog", strerror(errno));

操作二 获取设备支持哪些功能:WDIOC_GETSUPPORT

使用 WDIOC_GETSUPPORT 指令获取看门狗设备支持哪些功能,使用方式如下:
ioctl(int fd, WDIOC_GETSUPPORT, struct watchdog_info *info);
        使用 WDIOC_GETSUPPORT 指令可以获取设备的信息,调用 ioctl() 需要传入一个 struct watchdog_info *指针, ioctl() 会将获取到的数据写入到 info 指针所指向的对象中。 struct watchdog_info 结构体描述了看门狗设备的信息,我们来看看 struct watchdog_info 结构体的定义:
示例代码 27.1.1 struct watchdog_info 结构体
struct watchdog_info {
 __u32 options; /* Options the card/driver supports */
 __u32 firmware_version; /* Firmware version of the card */
 __u8 identity[32]; /* Identity of the board */
};

options 字段记录了设备支持哪些功能或选项;
firmware_version 字段记录了设备的固件版本号;
identity 字段则是一个描述性的字符串。
options 字段,该字段描述了设备支持哪些功能、选项,该字段的值如下(可以是以下任意一个值或多个值的位或关系)
#define WDIOF_OVERHEAT 0x0001 /* Reset due to CPU overheat */
#define WDIOF_FANFAULT 0x0002 /* Fan failed */
#define WDIOF_EXTERN1 0x0004 /* External relay 1 */
#define WDIOF_EXTERN2 0x0008 /* External relay 2 */
#define WDIOF_POWERUNDER 0x0010 /* Power bad/power fault */
#define WDIOF_CARDRESET 0x0020 /* Card previously reset the CPU */
#define WDIOF_POWEROVER 0x0040 /* Power over voltage */
#define WDIOF_SETTIMEOUT 0x0080 /* Set timeout (in seconds) */
#define WDIOF_MAGICCLOSE 0x0100 /* Supports magic close char */
#define WDIOF_PRETIMEOUT 0x0200 /* Pretimeout (in seconds), get/set */
#define WDIOF_ALARMONLY 0x0400 /* Watchdog triggers a management or other external alarm 
not a reboot */
#define WDIOF_KEEPALIVEPING 0x8000 /* Keep alive ping reply */
一般常见的值包括: WDIOF_SETTIMEOUT WDIOF_KEEPALIVEPING WDIOF_SETTIMEOUT 表示设备支持设置超时时间;
WDIOF_KEEPALIVEPING 表示设备支持“喂狗”操作,也就是重置看门狗计时器。
struct watchdog_info info;

if (0 > ioctl(fd, WDIOC_GETSUPPORT, &info)) {
    fprintf(stderr, "ioctl error: WDIOC_GETSUPPORT: %s\n", strerror(errno));
    return -1;
}

printf("identity: %s\n", info.identity);
printf("version: %u\n", firmware_version);

if (0 == (WDIOF_KEEPALIVEPING & info.options))
    printf("设备不支持喂狗操作\n");
if (0 == (WDIOF_SETTIMEOUT & info.options))
    printf("设备不支持设置超时时间\n")

操作三 获取/设置超时时间:WDIOC_GETTIMEOUTWDIOC_SETTIMEOUT

使用 WDIOC_GETTIMEOUT 指令可获取设备当前设置的超时时间,使用方式如下:
ioctl(int fd, WDIOC_GETTIMEOUT, int *timeout);
使用 WDIOC_SETTIMEOUT 指令可设置看门狗的超时时间,使用方式如下:
ioctl(int fd, WDIOC_SETTIMEOUT, int *timeout);
超时时间是以秒为单位,设置超时时间时,不可超过其最大值、否则 ioctl() 调用将会失败,使用示例如下所示:
int timeout;

/* 获取超时时间 */
if (0 > ioctl(fd, WDIOC_GETTIMEOUT, &timeout)) {
    fprintf(stderr, "ioctl error: WDIOC_GETTIMEOUT: %s\n", strerror(errno));
    return -1;
}

printf("current timeout: %ds\n", timeout);

/* 设置超时时间 */
timeout = 10; //10 秒钟
if (0 > ioctl(fd, WDIOC_SETTIMEOUT, &timeout)) {
    fprintf(stderr, "ioctl error: WDIOC_SETTIMEOUT: %s\n", strerror(errno));
    return -1;
}

操作四 开启/关闭看门狗:WDIOC_SETOPTIONS

设置好超时时间之后,接着便可以开启看门狗计时了,使用 WDIOC_SETOPTIONS 指令可以开启看门狗计时或停止看门狗计时,使用方式如下:

ioctl(int fd, WDIOC_SETOPTIONS, int *option);

option 指针指向一个 int 类型变量,该变量可取值如下:

#define WDIOS_DISABLECARD 0x0001 /* Turn off the watchdog timer */
#define WDIOS_ENABLECARD  0x0002 /* Turn on the watchdog timer */
WDIOS_DISABLECARD 表示停止看门狗计时,
WDIOS_ENABLECARD 则表示开启看门狗计时。

使用示例如下所示:需要注意的是,当调用 open()打开看门狗设备的时候,即使程序中没有开启看门狗计时器,当 close()关闭设备时,看门狗会自动启动;所以,当打开设备之后,需要使用 WDIOC_SETOPTIONS 指令停止看门狗计时,等所有设置完成之后再开启看门狗计时器

 

int option = WDIOS_ENABLECARD; //开启
//int option = WDIOS_DISABLECARD; //停止

if (0 > ioctl(fd, WDIOC_SETOPTIONS, &option)) {
    fprintf(stderr, "ioctl error: WDIOC_SETOPTIONS: %s\n", strerror(errno));
    return -1;
}

操作五 喂狗WDIOC_KEEPALIVE

看门狗计时器启动之后,我们需要在超时之前,去“喂狗”,否则计时器溢出超时将会导致系统复位或产生一个中断信号,通过 WDIOC_KEEPALIVE 指令喂狗,使用方式如下:

ioctl(int fd, WDIOC_KEEPALIVE, NULL);

使用示例如下:

if (0 > ioctl(fd, WDIOC_KEEPALIVE, NULL)) {
    fprintf(stderr, "ioctl error: WDIOC_KEEPALIVE: %s\n", strerror(errno));
}

看门狗应用编程实战

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>

#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <errno.h>
#include <string.h>
#include <linux/watchdog.h>
#define WDOG_DEV "/dev/watchdog"
int main(int argc, char *argv[])
{
 struct watchdog_info info;
 int timeout;
 int time;
 int fd;
 int op;

 if (2 != argc) {
 fprintf(stderr, "usage: %s <timeout>\n", argv[0]);
 exit(EXIT_FAILURE);
 }

 /* 打开看门狗 */
 fd = open(WDOG_DEV, O_RDWR);
 if (0 > fd) {
     fprintf(stderr, "open error: %s: %s\n", WDOG_DEV, strerror(errno));
     exit(EXIT_FAILURE);
 }

 /* 打开之后看门狗计时器会开启、先停止它 */
 op = WDIOS_DISABLECARD;
 if (0 > ioctl(fd, WDIOC_SETOPTIONS, &op)) {
     fprintf(stderr, "ioctl error: WDIOC_SETOPTIONS: %s\n", strerror(errno));
     close(fd);
     exit(EXIT_FAILURE);
 }
 timeout = atoi(argv[1]);
 if (1 > timeout)
 timeout = 1;
 /* 设置超时时间 */

 printf("timeout: %ds\n", timeout);
 if (0 > ioctl(fd, WDIOC_SETTIMEOUT, &timeout)) {
     fprintf(stderr, "ioctl error: WDIOC_SETTIMEOUT: %s\n", strerror(errno));
     close(fd);
     exit(EXIT_FAILURE);
 }

 /* 开启看门狗计时器 */
 op = WDIOS_ENABLECARD;
 if (0 > ioctl(fd, WDIOC_SETOPTIONS, &op)) {
     fprintf(stderr, "ioctl error: WDIOC_SETOPTIONS: %s\n", strerror(errno));
     close(fd);
     exit(EXIT_FAILURE);
 }

 /* 喂狗 */
 time = (timeout * 1000 - 100) * 1000;//喂狗时间设置 us 微秒、在超时时间到来前 100ms 喂狗
 for ( ; ; ) {

     usleep(time);
     ioctl(fd, WDIOC_KEEPALIVE, NULL);

 }
}

(代码解析)

        示例代码很简单,首先打开看门狗设备,接着使用 WDIOC_SETOPTIONS 指令( op =
WDIOS_DISABLECARD )先停止看门狗计时器;接着通过 atoi 获取到用户传入的超时时间,所以执行该测试程序的时候,需要传入一个参数作为看门狗超时时间。
        接着使用 WDIOC_SETTIMEOUT 指令设置超时时间,再使用 WDIOC_SETOPTIONS 指令( op = WDIOS_ENABLECARD)开启看门狗计时器,看门狗开始工作。接着我们需要在超时时间到来之前,去喂狗,喂狗之后,计时器重置,重新计时;不断地喂狗重置计时器、不让其超时、如果一旦超时系统将会复位重启。

 

  • 2
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值