rtc wakeup


原文链接: rtc wakeup

Linux RTC驱动模型分析之rtc-sysfs.c - 嵌入式Linux中文站

rtcwake: /dev/rtc0 not enabled for wakeup events - Raspberry Pi Forums

(8条消息)Linux时间系统之RTC时间 - u013686019的专栏 - CSDN博客

使用rtcwake定时唤醒linux

目录

最近下载一批资料,但是由于网络不太好,下载的速度太慢,只有凌晨的时候下载速度会快一点,因此就想写一个定时任务在凌晨三点自动开始下载。
但是另一方面我也不希望电脑一直开着,这样太浪费电了...
经过一番搜索,最终让我发现了这一工具: rtcwake

测试本机是否支持定时唤醒

我们都知道,让linux定时关机或者定时休眠是一件很简单的事情,只需要通过 atcron 设置好定时执行关机或休眠程序即可,基本上任何电脑都能够做到这一点。
但是如果想反过来,想在指定的时间让电脑自动启动就需要硬件和驱动的支持了。

我们可以通过下面命令来测试一下计算机是不是支持定时唤醒:

sudo rtcwake -m standby -s 10

rtcwake -m freeze -s 10

正常情况下该命令会休眠到内存,然后10秒后又被唤醒。

rtcwake使用方法

rtcwake其实包括两个动作,第一个动作是休眠或关机,第二个动作是定时唤醒。

设置关机/休眠模式

我们使用 -m mode 来设置rtcwake的关机/休眠模式,理论上 mode 的可选值包括:

standby
待机状态,节省的电源比较少,但是唤醒速度很快
freeze
所有进程被冻结,所有设备暂停运行,但系统依然在运行。该模式需要linux3.9以上的支持
mem
休眠到内存,除了内存以外的大部分机器部件都进入断电状态。 这种休眠状态恢复速度特别快,但由于内存中的数据并没有被保存下来,因此这个状态的系统并没有进入真正意义上的休眠状态,还在持续耗电。
disk
休眠到磁盘, 这种休眠状态恢复速度特别快,但由于内存中的数据并没有被保存下来,因此这个状态的系统并没有进入真正意义上的休眠状态,还在持续耗电。这种情况下系统可以完全断电,但由于要保存/读取系统状态到/从交换空间,因此速度会比较慢.
off
这就不是休眠了,而是直接关机
no
不进行休眠或关机,只设置唤醒时间
disable
取消上一次设置的定时唤醒
show
显示设置的定时唤醒信息

但事实上,本机可能不会支持所有的mode,我们可以通过 --list-modes 选项来查看本机支持的关机/休眠模式

rtcwake --list-modes


freeze mem disk off no on disable show `

设置定时唤醒时间

有三种格式来设置定时唤醒时间:

  1. 使用 --date 特定时间 来明确指定哪个特定时间唤醒,这个 特定时间 的格式可以是:

    YYYYMMDDhhmmss
    YYYY年MM月DD日hh时mm分ss秒
    YYYY-MM-DD hh:mm:ss
    YYYY年MM月DD日hh时mm分ss秒
    YYYY-MM-DD hh:mm
    YYYY年MM月DD日hh时mm分00秒
    YYYY-MM-DD
    YYYY年MM月DD日00时00分00秒
    hh:mm:ss
    今天的hh时mm分ss秒
    hh:mm
    今天的hh时mm分00秒
    tomorrow
    明天的00时00分00秒
    +5min
    5分钟后
  2. 使用 -s N 指定 N 秒后唤醒

  3. 使用 -t N, 其中 N1970-01-01 00:00 UTC 开始计数的秒钟数

关于硬件时钟的说明

rtcwake命令默认根据 /etc/adjtime(man adjtime_config) 中的第三行信息(UTC或LOCAL)来判断硬件时钟是按照UTC还是本地时间来设置的.
但是你可以通过 -u/--utc 来强制要求rtcwake命令将硬件时钟看成是UTC时间,或者通过 -l/--local 选项来将硬件时钟看成是本地时间。
甚至可以通过 -A/--adjfile file 来指定另一个 adjtime 文件供 rtcwake 命令判断。

rtcwake

1.功能作用
rtcwake 是利用电脑主板上带的rtc来作为定时基准,在给定的时间唤醒处于待机或休眠状态的电脑。

2.位置
/usr/sbin/rtcwake

3.格式用法
rtcwake [options]

4.主要参数

-d, –device select rtc device (rtc0|rtc1|…)
-n, –dry-run does everything, but suspend
-l, –local RTC uses local timezone
-m, –mode 使用的模式
standby 普通待机模式,为默认选项,对应 ACPI state S1
mem 待机到内存,即除内存之外把其他设备都进入低功耗模式,对应 ACPI state S3
disk 待机到硬盘,即休眠,把电脑的当前状态保存到硬盘,几乎不消耗外部电源,对应 ACPI state S4
off 通过条用系统的关机命令来休眠,对应 ACPI state S5
-s, –seconds seconds to sleep
-t, –time 在具体某个时间唤醒
-u, –utc RTC uses UTC
-v, –verbose 显示详细的运行状态信息
-V, –version 显示版本信息

5.应用实例

a、电脑进入休眠模式,并在2个小时(7200秒)后自动唤醒
sudo rtcwake -m disk -s 7200

b、电脑进入休眠,并在09:00这个时刻唤醒
sudo rtcwake -m disk -l -t date -d 09:00 +%s

c、会让系统进入S3,睡20秒再自动唤醒
sudo rtcwake -v -s 20 -m mem


Linux内核电源管理模块提供了4种不同的休眠模式 mem , standby , freeze , 和disk

可以使用下面的命令使内核进入低功耗模式

echo mem > /sys/power/state
echo standby > /sys/power/state
echo freeze > /sys/power/state

echo disk > /sys/power/state

echo +100 > /sys/class/rtc/rtc0/wakealarm

可以使用RTC唤醒.

RTC 子系统
RTC 子系统在目录 /sys/class/rtc/ 下,会根据设备创建对应的目录,需要在内核中开启支持,配置如下:
CONFIG_HPET_EMULATE_RTC=y
CONFIG_PM_TRACE_RTC=y
CONFIG_RTC_LIB=y
CONFIG_RTC_CLASS=y
CONFIG_RTC_HCTOSYS=y
CONFIG_RTC_HCTOSYS_DEVICE="rtc0"

CONFIG_RTC_DEBUG is not set

RTC interfaces

CONFIG_RTC_INTF_SYSFS=y
CONFIG_RTC_INTF_PROC=y
... (略)

debian/ubuntu 的系统可以在 /boot/config-$(uname -r) 查看内核的编译选项,archlinux 可以在 /proc/config.gz 查看。
RTC 目录下的文件含义可以在 sysfs-class-rtc 中查看。
wakealarm
RTC 目录中的 wakealarm 文件内容便是下次触发唤醒时间的时间,默认这个文件是没有值的。文件的内容需要设置是时间的秒数,可以向下面这样设置:

echo $(date +%s --date 'now + 1 minutes') > /sys/class/rtc/rtc0/wakealarm

这就表示在 1 minute 后触发唤醒事件。
所以做休眠唤醒测试的流程如下:

设置下次唤醒的时间到 wakealarm 中
休眠系统
被 wakealarm 唤醒后,开始做一些期望的事情(如检查系统是否正常),然后继续执行步骤 1,就这样一直循环,直至满足条件后终止

FAQ

设置无效?
首先检查内核是否开启了 rtc 支持,如果支持就检查时间标准是否是 UTC,localtime 时间标准时设置 wakealarm 是不生效的,内容一直是空的。通过 timedatectl 命令可以查询和设置时间标准。

作者:jouyouyun
链接:https://www.jianshu.com/p/8b3ff24ca3d4
来源:简书
简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。

// From <linux/rtc.h> struct rtc_time
type rtcTime struct {
	tmSec   uint32
	tmMin   uint32
	tmHour  uint32
	tmMday  uint32
	tmMon   uint32
	tmYear  uint32
	tmWday  uint32
	tmYday  uint32
	tmIsdst uint32
}

const (
	// iocREAD and friends are from <linux/asm-generic/ioctl.h>
	iocREAD      = uintptr(2)
	iocNRBITS    = uintptr(8)
	iocNRSHIFT   = uintptr(0)
	iocTYPEBITS  = uintptr(8)
	iocTYPESHIFT = iocNRSHIFT + iocNRBITS
	iocSIZEBITS  = uintptr(14)
	iocSIZESHIFT = iocTYPESHIFT + iocTYPEBITS
	iocDIRSHIFT  = iocSIZESHIFT + iocSIZEBITS
	// rtcRDTIMENR and friends are from <linux/rtc.h>
	rtcRDTIMENR   = uintptr(0x09)
	rtcRDTIMETYPE = uintptr(112)
)

func rtcReadTime() rtcTime {
	f, err := os.Open("/dev/rtc0")
	if err != nil {
		log.Fatalf("Failed to open /dev/rtc0: %v", err)
	}
	defer f.Close()
	result := rtcTime{}
	arg := uintptr(0)
	arg |= (iocREAD << iocDIRSHIFT)
	arg |= (rtcRDTIMETYPE << iocTYPESHIFT)
	arg |= (rtcRDTIMENR << iocNRSHIFT)
	arg |= (unsafe.Sizeof(result) << iocSIZESHIFT)
	_, _, errno := syscall.Syscall(syscall.SYS_IOCTL, f.Fd(), arg, uintptr(unsafe.Pointer(&result)))
	if errno != 0 {
		log.Fatalf("RTC_RD_TIME failed: %v", errno)
	}
	return result
}

`