一般Openwrt系统较多用于网络方面的产品,比如路由器,但路由器几乎都没有硬件RTC,因此系统初始时间不准.
在Openwrt系统中,初始时间可以认为有2个, 固件编译时间和文件系统加载后的初始时间
关于固件编译时间,即为Linux kernel 编译时间, 即是/proc/version内容,比如:
root@UVCOSS:~# cat /proc/version
Linux version 4.14.221 (xxxx) (gcc version 7.5.0 (OpenWrt GCC 7.5.0 r11063-85e04e9f46)) #0 Tue Aug 31 06:45:02 2021
kernel 编译时间通过宏KBUILD_BUILD_TIMESTAMP来传入
宏初始化, 从SOURCE_DATE_EPOCH获取时间
SOURCE_DATE_EPOCH 由脚本生成
脚本get_source_date_epoch.sh如下:
分别从version.date文件,git,hg来进行初始化
version.date文件存储的即为编译时的UTC时间
# cat version.date 1630392302
#!/usr/bin/env bash
export LANG=C
export LC_ALL=C
[ -n "$TOPDIR" ] && cd $TOPDIR
try_version() {
[ -f version.date ] || return 1
SOURCE_DATE_EPOCH="$(cat version.date)"
[ -n "$SOURCE_DATE_EPOCH" ]
}
try_git() {
[ -e .git ] || return 1
SOURCE_DATE_EPOCH="$(git log -1 --format=format:%ct)"
[ -n "$SOURCE_DATE_EPOCH" ]
}
try_hg() {
[ -d .hg ] || return 1
SOURCE_DATE_EPOCH="$(hg log --template '{date}' -l 1 | cut -d. -f1)"
[ -n "$SOURCE_DATE_EPOCH" ]
}
try_mtime() {
perl -e 'print((stat $ARGV[0])[9])' "$0"
[ -n "$SOURCE_DATE_EPOCH" ]
}
try_version || try_git || try_hg || try_mtime || SOURCE_DATE_EPOCH=""
echo "$SOURCE_DATE_EPOCH"
宏传入:
宏使用 scripts/mkcompile_h 脚本:
#!/bin/sh
# SPDX-License-Identifier: GPL-2.0
TARGET=$1
ARCH=$2
SMP=$3
PREEMPT=$4
CC=$5
vecho() { [ "${quiet}" = "silent_" ] || echo "$@" ; }
# If compile.h exists already and we don't own autoconf.h
# (i.e. we're not the same user who did make *config), don't
# modify compile.h
# So "sudo make install" won't change the "compiled by <user>"
# do "compiled by root"
if [ -r $TARGET -a ! -O include/generated/autoconf.h ]; then
vecho " SKIPPED $TARGET"
exit 0
fi
# Do not expand names
set -f
# Fix the language to get consistent output
LC_ALL=C
export LC_ALL
if [ -z "$KBUILD_BUILD_VERSION" ]; then
if [ -r .version ]; then
VERSION=`cat .version`
else
VERSION=0
echo 0 > .version
fi
else
VERSION=$KBUILD_BUILD_VERSION
fi
#### 获取外部定义,没有则自动生成,以当前时间为主
if [ -z "$KBUILD_BUILD_TIMESTAMP" ]; then
TIMESTAMP=`date`
else
TIMESTAMP=$KBUILD_BUILD_TIMESTAMP
fi
if test -z "$KBUILD_BUILD_USER"; then
LINUX_COMPILE_BY=$(whoami | sed 's/\\/\\\\/')
else
LINUX_COMPILE_BY=$KBUILD_BUILD_USER
fi
if test -z "$KBUILD_BUILD_HOST"; then
LINUX_COMPILE_HOST=`hostname`
else
LINUX_COMPILE_HOST=$KBUILD_BUILD_HOST
fi
UTS_VERSION="#$VERSION"
CONFIG_FLAGS=""
if [ -n "$SMP" ] ; then CONFIG_FLAGS="SMP"; fi
if [ -n "$PREEMPT" ] ; then CONFIG_FLAGS="$CONFIG_FLAGS PREEMPT"; fi
UTS_VERSION="$UTS_VERSION $CONFIG_FLAGS $TIMESTAMP"
# Truncate to maximum length
UTS_LEN=64
UTS_TRUNCATE="cut -b -$UTS_LEN"
# Generate a temporary compile.h
( echo /\* This file is auto generated, version $VERSION \*/
if [ -n "$CONFIG_FLAGS" ] ; then echo "/* $CONFIG_FLAGS */"; fi
echo \#define UTS_MACHINE \"$ARCH\"
echo \#define UTS_VERSION \"`echo $UTS_VERSION | $UTS_TRUNCATE`\"
echo \#define LINUX_COMPILE_BY \"`echo $LINUX_COMPILE_BY | $UTS_TRUNCATE`\"
echo \#define LINUX_COMPILE_HOST \"`echo $LINUX_COMPILE_HOST | $UTS_TRUNCATE`\"
echo \#define LINUX_COMPILER \"`$CC -v 2>&1 | grep ' version ' | sed 's/[[:space:]]*$//'`\"
) > .tmpcompile
# Only replace the real compile.h if the new one is different,
# in order to preserve the timestamp and avoid unnecessary
# recompilations.
# We don't consider the file changed if only the date/time changed.
# A kernel config change will increase the generation number, thus
# causing compile.h to be updated (including date/time) due to the
# changed comment in the
# first line.
if [ -r $TARGET ] && \
grep -v 'UTS_VERSION' $TARGET > .tmpver.1 && \
grep -v 'UTS_VERSION' .tmpcompile > .tmpver.2 && \
cmp -s .tmpver.1 .tmpver.2; then
rm -f .tmpcompile
else
vecho " UPD $TARGET"
mv -f .tmpcompile $TARGET
fi
rm -f .tmpver.1 .tmpver.2
最终生成在文件include/generated/compile.h
/* This file is auto generated, version 0 */
#define UTS_MACHINE "mips"
#define UTS_VERSION "#0 Tue Aug 31 06:45:02 2021"
#define LINUX_COMPILE_BY "gwind"
#define LINUX_COMPILE_HOST "gwind-XX"
#define LINUX_COMPILER "gcc version 7.5.0 (OpenWrt GCC 7.5.0 r11063-85e04e9f46)"
关于 文件系统加载后的初始时间, 可以理解为用户应用起来后的系统时间
Openwrt系统启动过程中会进行系统文件更新时间判断,以最新的文件更新时间设置为当前系统时间
实现在脚本package/base-files/files/etc/init.d/sysfixtime
#!/bin/sh /etc/rc.common
# Copyright (C) 2013-2014 OpenWrt.org
### 00,最先运行
START=00
STOP=90
RTC_DEV=/dev/rtc0
HWCLOCK=/sbin/hwclock
boot() {
start && exit 0
local maxtime="$(maxtime)"
local curtime="$(date +%s)"
[ $curtime -lt $maxtime ] && date -s @$maxtime
}
start() {
[ -e "$RTC_DEV" ] && [ -e "$HWCLOCK" ] && $HWCLOCK -s -u -f $RTC_DEV
}
stop() {
[ -e "$RTC_DEV" ] && [ -e "$HWCLOCK" ] && $HWCLOCK -w -u -f $RTC_DEV && \
logger -t sysfixtime "saved '$(date)' to $RTC_DEV"
}
maxtime() {
local file newest
#### 搜索/etc下最新的文件
for file in $( find /etc -type f ) ; do
[ -z "$newest" -o "$newest" -ot "$file" ] && newest=$file
done
### 以最新的文件时间设置为系统时间
[ "$newest" ] && date -r "$newest" +%s
}
最终系统运行日志:
root@UVCOSS:~# logread |head
Tue Aug 31 14:45:10 2021 kern.notice kernel: [ 0.000000] Linux version 4.14.221 (gcc version 7.5.0 (OpenWrt GCC 7.5.0 r11063-85e04e9f46)) #0 Tue Aug 31 06:45:02 2021====Kernel时间为6:45编译时间, 标准UTC时间
=== 系统时间为14:45, 因为当前时区是CST-8
Tue Aug 31 14:45:10 2021 kern.info kernel: [ 0.000000] Board has DDR2
Tue Aug 31 14:45:10 2021 kern.info kernel: [ 0.000000] Analog PMU set to hw control
Tue Aug 31 14:45:10 2021 kern.info kernel: [ 0.000000] Digital PMU set to hw control
Tue Aug 31 14:45:10 2021 kern.info kernel: [ 0.000000] SoC Type: MediaTek MT7688 ver:1 eco:2
Tue Aug 31 14:45:10 2021 kern.info kernel: [ 0.000000] bootconsole [early0] enabled
Tue Aug 31 14:45:10 2021 kern.info kernel: [ 0.000000] CPU0 revision is: 00019655 (MIPS 24KEc)