Linux系统看门狗应用编程

news2024/12/20 11:39:54

目录

  • 看门狗应用编程介绍
    • 打开设备
    • 获取设备支持哪些功能:WDIOC_GETSUPPORT
    • 获取/设置超时时间:WDIOC_GETTIMEOUT、WDIOC_SETTIMEOUT
    • 开启/关闭看门狗:WDIOC_SETOPTIONS
    • 喂狗:WDIOC_KEEPALIVE
  • 看门狗应用编程实战

在产品化的嵌入式系统中,为了使系统在异常情况下能自动复位,一般都需要引入看门狗。看门狗其实就是一个可以在一定时间内被复位的计数器。当看门狗启动后,计数器开始自动计数,经过一定时间,如果没有被复位,计数器溢出就会对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 等,通过这些设备节点可以控制看门狗外设。
在这里插入图片描述
这个watchdog0 其实就是I.MX6U 的WDOG1 所对应的设备节点,从图中可知,除了/dev/watchdog0 之外,还有一个watchdog 设备节点,这个设备节点的名称没有后面的数字编号,这个又是什么意思呢?因为系统中可能注册了多个看门狗设备,/dev/watchdog 设备节点则代表系统默认的看门狗设备;通常这指的就是watchdog0,所以,上图中,/dev/watchdog 其实就等于/dev/watchdog0,也就意味着它俩代表的是同一个硬件外设。
应用层控制看门狗其实非常简单,通过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 结构体的定义:

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_GETTIMEOUT、WDIOC_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 则表示开启看门狗计时。
使用示例如下所示:

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;
}

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

喂狗: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));
}

看门狗应用编程实战

通过上小节介绍之后,我们已经知道了如何编写应用程序去控制看门狗外设了,本小节我们来编写一个简单地看门狗应用程序,示例代码如下所示:
本例程源码对应的路径为:开发板光盘->11、Linux C 应用编程例程源码->27_watchdog->watchdog_test.c。

/***************************************************************
 Copyright © ALIENTEK Co., Ltd. 1998-2021. All rights reserved.
 文件名 : watchdog_test.c
 作者 : 邓涛
 版本 : V1.0
 描述 : 看门狗应用程序示例代码
 其他 : 无
 论坛 : www.openedv.com
 日志 : 初版 V1.0 2021/7/14 邓涛创建
 ***************************************************************/

#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)开启看门狗计时器,看门狗开始工作。接着我们需要在超时时间到来之前,去喂狗,喂狗之后,计时器重置,重新计时;不断地喂狗重置计时器、不让其超时、如果一旦超时系统将会复位重启。
编译示例代码:
在这里插入图片描述
将编译得到的可执行文件拷贝到开发板Linux 系统/home/root 目录下,执行测试程序,譬如启动看门狗,设置超时时间为2 秒钟,如下:
在这里插入图片描述
执行程序之后,开门狗计时器就已经启动了,程序中会不断的喂狗重置计时器,以保证程序不会重启。
现在我们按Ctrl + C 结束程序,结束程序意味着已经停止喂狗了、然后看门狗计时器并没有停止,这样将会导致计时器溢出、发生复位重启:
在这里插入图片描述
当按Ctrl+C 终止进程后,内核打印出“watchdog watchdog0: watchdog did not stop!”信息,表示看门狗计时器还正在计时、未停止。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/371491.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

[计算机网络(第八版)]第二章 物理层(章节测试/章节作业)

章节作业 带答案版 选择题 (单选题)双绞线是用两根绝缘导线绞合而成的&#xff0c;绞合的目的是&#xff08; &#xff09;。 A. 减少干扰 B. 提高传输速度 C. 增大传输距离 D. 增大抗拉强度(单选题)在电缆中采用屏蔽技术可以带来的好处主要是&#xff08; &#xff09;。 A…

DNS 域名解析

介绍域名 网域名称&#xff08;英语&#xff1a;Domain Name&#xff0c;简称&#xff1a;Domain&#xff09;&#xff0c;简称域名、网域。 域名是互联网上某一台计算机或计算机组的名称。 域名可以说是一个 IP 地址的代称&#xff0c;目的是为了便于记忆。例如&#xff0c…

Latex中的表格(2)

Latex中的表格一、一个加脚注的三线表的例子二、一个表格中加注释的例子三、两个并排的两个表格的例子3.1 使用小页环境并排表格3.2 使用子表格并排表格四、一个格式复杂的表格的例子五、一个长表格的例子这篇文章主要罗列一些特殊的表格例子。内容来自&#xff1a;一篇北师大学…

深度剖析数据在内存的存储

目录1.深度剖析数据在内存的存储(前言)数据类型介绍类型的基本归类整形在内存中的存储原码、反码、补码大小端练习总结1.深度剖析数据在内存的存储(前言) 今天就让我戴佳伟给大家讲一下数据在内存中的存储。之中有好多让我们深思的点&#xff0c;大家都拿起笔记本&#xff0c;…

【机器学习笔记】Python基础笔记

目录基础语法加载数据&#xff1a;pd.read_csv查看数据大小&#xff1a;shape浏览数据行字段&#xff1a;columns浏览少量数据&#xff1a;head()浏览数据概要&#xff1a;describe()基础功能语法缺省值去除缺失值&#xff1a;dropna按行删除&#xff1a;存在空值&#xff0c;即…

【数据库】第二章 关系数据库

第二章 关系数据库 2.1关系数据结构及形式化定义 关系 域&#xff08;domain) :域是一组具有相同数据类型的值的集合&#xff0c;可以取值的个数叫基数 笛卡尔积 &#xff1a;一个记录叫做一个元组&#xff08;tuple),元组中每一个属性值&#xff0c;叫一个分量 基数&…

35测试不如狗?是你自己技术不够的怨怼罢了

一、做软件测试怎么样&#xff1f; 引用著名软件测试专家、清华大学郑人杰教授的说法&#xff1a;软件测试工程师是一个越老越吃香的职业。 其中就表达了软件测试工作相对稳定、对年龄没有限制、而且随着项目经验的不断增长和对行业背景的深入了解&#xff0c;会越老越吃香。…

刚上岸字节测试开发岗,全网最真实的大厂面试真题

首先我来解释一下为什么说是全网最真实的面试题&#xff0c;相信大家也发现软件测试面试题在网上流传也已不少&#xff0c;但是经过仔细查看发现了两个很重要的问题。 第一&#xff0c;网上流传的面试题的答案并不能保证百分百正确。也就是说各位朋友辛辛苦苦花了很多时间准备…

《程序员的自我修养》阅读笔记

文章目录【第2部分】静态链接1 编译过程2 编辑器的工作流程3 链接——模块的拼接4 目标文件目标文件中的段&#xff08;section&#xff09;ELF文件结构5 静态链接1 空间与地址分配2 符号解析与重定位【第3部分】装载与动态链接1 装载的方式2 进程的启动3 为什么需要动态链接&a…

通俗易懂,十分钟读懂DES,详解DES加密算法原理,DES攻击手段以及3DES原理。Python DES实现源码

文章目录1、什么是DES2、DES的基本概念3、DES的加密流程4、DES算法步骤详解4.1 初始置换(Initial Permutation&#xff0c;IP置换)4.2 加密轮次4.3 F轮函数4.3.1 拓展R到48位4.3.2 子密钥K的生成4.3.3 当前轮次的子密钥与拓展的48位R进行异或运算4.3.4 S盒替换&#xff08;Subs…

学习 create-vite 源码后,感觉真不一样

前言 已经学习了一段时间的源码了&#xff0c;在写源码 demo 小例子的时候都是使用 react 项目作为模板&#xff0c;而我的 react 项目正好是由create-vite来创建的&#xff0c;因此&#xff0c;今天来学习一下关于create-vite的源码。话不多说&#xff0c;咱们直接开始学习。…

【深度学习】什么是线性回归逻辑回归单层神经元的缺陷

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录逻辑回归&线性回归单层神经元的缺陷单层神经元的缺陷逻辑回归&线性回归 线性回归预测的是一个连续值&#xff0c; 逻辑回归给出的”是”和“否”的回答. 等…

MySQL事务与索引

MySQL事务与索引 一、事务 1、事务简介 在 MySQL 中只有使用了 Innodb 数据库引擎的数据库或表才支持事务。事务处理可以用来维护数据库的完整性&#xff0c;保证成批的 SQL 语句要么全部执行&#xff0c;要么全部不执行。事务用来管理 insert,update,delete 语句 事务特性…

SQL性能优化的47个小技巧,果断收藏!

1、先了解MySQL的执行过程 了解了MySQL的执行过程&#xff0c;我们才知道如何进行sql优化。 客户端发送一条查询语句到服务器&#xff1b; 服务器先查询缓存&#xff0c;如果命中缓存&#xff0c;则立即返回存储在缓存中的数据&#xff1b; 未命中缓存后&#xff0c;MySQL通…

【编程入门】应用市场(go语言版)

背景 前面已输出多个系列&#xff1a; 《十余种编程语言做个计算器》 《十余种编程语言写2048小游戏》 《17种编程语言10种排序算法》 《十余种编程语言写博客系统》 《十余种编程语言写云笔记》 《N种编程语言做个记事本》 目标 为编程初学者打造入门学习项目&#xff0c;使…

qt源码--信号槽

本篇主要从Qt信号槽的连接、断开、调用、对象释放等方面展开&#xff1b; 1.信号建立连接过程 connect有多个重载函数&#xff0c;主要是为了方便使用者&#xff0c;比较常用的有2种方式&#xff1a; a. QObject::connect(&timer, &QTimer::timeout, &loop, &am…

一步步教你电脑变成服务器,tomcat的花生壳设置(原创)

1&#xff0c;首先你去https://console.oray.com/这网站注册个帐号&#xff0c;如果注册成功它会送你一个免费域名&#xff0c;当然不记得也没关系&#xff0c;你记住你注册的 帐号跟密码&#xff0c;然后下载它的软件&#xff08;花生壳动态域名6.0正式版&#xff09;有xp跟li…

19_FreeRTOS软件定时器

目录 软件定时器介绍 FreeRTOS软件定时器特点 软件定时器的命令队列 软件定时器的相关配置 单次定时器和周期定时器 软件定时器结构体成员 FreeRTOS软件定时器相关API函数 实验源码 软件定时器介绍 定时器描述:从指定的时刻开始,经过一个指定时间,然后触发一个超时事件…

操作系统权限提升(十三)之绕过UAC提权-MSF和CS绕过UAC提权

系列文章 操作系统权限提升(十二)之绕过UAC提权-Windows UAC概述 注&#xff1a;阅读本编文章前&#xff0c;请先阅读系列文章&#xff0c;以免造成看不懂的情况&#xff01;&#xff01; MSF和CS绕过UAC提权 CS绕过UAC提权 拿到一个普通管理员的SHELL,在CS中没有*号代表有…

样式冲突太多,记一次前端CSS升级

目前平台前端使用的是原生CSSBEM命名&#xff0c;在多人协作的模式下&#xff0c;容易出现样式冲突。为了减少这一类的问题&#xff0c;提升研效&#xff0c;我调研了业界上主流的7种CSS解决方案&#xff0c;并将最终升级方案落地到了工程中。 样式冲突的原因 目前遇到的样式…