最近写了一个RTC驱动,然后打算在驱动内部定时校准系统时间,搜索了一圈实在是没找到什么有用的信息,就去内核的源码里找了一下还真的有。
这里记录一下这个功能。
一、函数介绍
(1)mktime
- 声明位置:kernel\include\linux\time.h
- 源码位置:kernel\kernel\time.c
这个函数的作用是将输入的年月日时分秒转换成距离 1970-01-01 00:00:00 的秒数。
/* Converts Gregorian date to seconds since 1970-01-01 00:00:00.
* Assumes input in normal date format, i.e. 1980-12-31 23:59:59
* => year=1980, mon=12, day=31, hour=23, min=59, sec=59.
*
* [For the Julian calendar (which was used in Russia before 1917,
* Britain & colonies before 1752, anywhere else before 1582,
* and is still in use by some communities) leave out the
* -year/100+year/400 terms, and add 10.]
*
* This algorithm was first published by Gauss (I think).
*
* WARNING: this function will overflow on 2106-02-07 06:28:16 on
* machines where long is 32-bit! (However, as time_t is signed, we
* will already get problems at other places on 2038-01-19 03:14:08)
*/
unsigned long
mktime(const unsigned int year0, const unsigned int mon0,
const unsigned int day, const unsigned int hour,
const unsigned int min, const unsigned int sec)
{
unsigned int mon = mon0, year = year0;
/* 1..12 -> 11,12,1..10 */
if (0 >= (int) (mon -= 2)) {
mon += 12; /* Puts Feb last since it has leap day */
year -= 1;
}
return ((((unsigned long)
(year/4 - year/100 + year/400 + 367*mon/12 + day) +
year*365 - 719499
)*24 + hour /* now have hours */
)*60 + min /* now have minutes */
)*60 + sec; /* finally seconds */
}
(2)do_settimeofday
- 声明位置:kernel\include\linux\time.h
- 源码位置:kernel\kernel\time\timekeeping.c
这个函数的作用是将要设置的时间的秒数设置到系统时间。
/**
* do_settimeofday - Sets the time of day
* @tv: pointer to the timespec variable containing the new time
*
* Sets the time of day to the new time and update NTP and notify hrtimers
*/
int do_settimeofday(const struct timespec *tv)
{
struct timekeeper *tk = &timekeeper;
struct timespec ts_delta, xt;
unsigned long flags;
if (!timespec_valid_strict(tv))
return -EINVAL;
raw_spin_lock_irqsave(&timekeeper_lock, flags);
write_seqcount_begin(&timekeeper_seq);
timekeeping_forward_now(tk);
xt = tk_xtime(tk);
ts_delta.tv_sec = tv->tv_sec - xt.tv_sec;
ts_delta.tv_nsec = tv->tv_nsec - xt.tv_nsec;
tk_set_wall_to_mono(tk, timespec_sub(tk->wall_to_monotonic, ts_delta));
tk_set_xtime(tk, tv);
timekeeping_update(tk, TK_CLEAR_NTP | TK_MIRROR | TK_CLOCK_WAS_SET);
write_seqcount_end(&timekeeper_seq);
raw_spin_unlock_irqrestore(&timekeeper_lock, flags);
/* signal hrtimers about time change */
clock_was_set();
return 0;
}
二、使用代码
使用起来是挺简单的
struct timespec tv;
unsigned int year;
unsigned int mon;
unsigned int day;
unsigned int hour;
unsigned int min;
unsigned int sec;
tv.tv_sec = mktime(year, mon, day, hour - 8, min, sec); /* 东八区 */
ret = do_settimeofday(&tv);
由于当前是在东八区,所以需要在小时上减 8。
三、测试
测试可以使用 date 命令进行测试。
root@imx6qsabresd:/tmp# date
Tue Mar 3 12:23:33 CST 2020