1. BUG描述
使用网关来管理若干个子设备,子设备每隔15秒来给网关发送心跳包。当网关超过45秒没有收到子设备的心跳包时,则认为子设备异常,需要重启子设备。
观察日志,发现子设备频繁重启,不符合设计要求.
2. BUG分析
观察系统的日志,发现系统上电时比较容易出现重启子设备的现象。因为系统是使用ntp来同步时间的,遂怀疑是时间不对引起的。
ntp服务采用的是ntpdate作为客户端。每分钟由cron同步一次。为了降低bug复现的难度,将同步间隔改为5分钟一次,观察bug是否必现。
抓取日志如下图
由上图可知,在NTP同步的前后,确实发生了子设备重启的问题。
再结合代码分析,终于发现了问题,原来代码中使用了gettimeofday!
gettimeofday是获取本地时间的,其值会随着系统时间的改变而改变。应该采用clock_gettime!
3. gettimeofday介绍
gettimeofday原型如下:
#include <time.h> int gettimeofday(struct timeval *tv, struct timezone *tz); |
说明: tz可以为NULL |
struct timeval { time_t tv_sec; /* seconds */ suseconds_t tv_usec; /* microseconds */ }; |
gettimeofday的功能是将自1970-01-01 00:00:00 +0000 (UTC)以来的秒数和微秒数放入tv 中,当返回值为0时表示成功,否则失败。
注意:获取到的时间会随着系统时间的改变而改变。
4. clock_gettime介绍
#include <time.h> int clock_gettime( clockid_t clock_id,struct timespec * tp ); |
说明: clock_id: 表示获取时间的类型 |
struct timespec { time_t tv_sec; // 秒 long tv_nsec; // 纳秒 }; |
clock_gettime的功能是返回某种类型的时间--包括秒和纳秒,其类型与参数clock_id有关。
当返回值为0时表示成功,否则失败。
clock_id的取值以及意义:
CLOCK_REALTIME: 是指系统时间,随着系统时间的改变而改变。系统时钟会被用户而改变。
CLOCK_MONOTONIC: 指从系统启动时开始计时。不受系统影响,也不会被用户改变。
CLOCK_PROCESS_CPUTIME_ID: 指这个进程运行到当前代码时,系统花费的时间。
CLOCK_THREAD_CPUTIME_ID: 指这个线程运行到当前代码时,系统花费的时间。