本帖最后由 keer_zu 于 2020-3-25 16:08 编辑
lowpower
@icecut @海中水 @yyy71cj
系统自动进入休眠,并被中断唤醒
后边我们一起分析一下代码的实现。
先看主函数:
/*
* insomniad - user space policy for Linux's autosleep mechanism
*
* Copyright (C) 2014 Tyler Hall <tylerwhall@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
/*
#include <stdlib.h>
#include <limits.h>
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <assert.h>
*/
#include "common.h"
#include "policy.h"
#include "cfm.h"
int main(int argc, char *argv[])
{
struct insomniad_ctx ctx;
get_opts(argc, argv);
insomniad_init(&ctx);
pr_info("Using hysteresis time of %lu ms\n", hysteresis_ms);
while (1) {
int rc;
/* Blocks until no wakeup sources are active */
read_wakeup_count(&ctx);
/* Allow policy to futher delay */
evaluate_policy();
rc = write_wakeup_count(&ctx);
if (rc) {
/* This will fail when an event happens between read() and
* write(). We raced with a wakeup event, so start over. */
pr_info("Event occurred since reading wakeup_count\n");
continue;
}
/* write successful. Going down now. */
pr_info("Going to sleep\n");
rc = go_to_sleep(&ctx);
if (rc) {
/* Non-fatal error. Try again. */
pr_info("Non-fatal error %s writing to suspend state\n", strerror(-rc));
/* Rate-limit suspend attempts to avoid thrashing aborted suspends */
usleep_signal_safe(1000);
continue;
}
pr_info("Exited sleep\n");
/*
* Sleep for at least the requested timeout after wake, emulating a
* wakeup source on resume. Normally this would be taken care of by the
* kernel generating a wakeup event on resume and we would make sure
* not to sleep until it is released + our timeout. This makes sure our
* timeout is applied to spurious wakeups as well.
*/
usleep_signal_safe(hysteresis_ms * 1000);
};
return 0;
}
首先是调用insomniad_init()初始化一个insomniad_ctx,代码如下:
void insomniad_init(struct insomniad_ctx *ctx)
{
/* Necessary to flush logging to systemd journal in a timely manner */
setlinebuf(stdout);
setlinebuf(stderr);
ctx->state_fd = open("/sys/power/state", O_WRONLY, O_CLOEXEC);
if (ctx->state_fd == -1) {
perror("Error opening power state");
exit(1);
}
ctx->wakeup_count_fd = -1;
}
里面主要是打开系统文件/sys/power/state。
然后进入一个无限循环,通过read_wakeup_count读取/sys/power/wakeup_count:
void read_wakeup_count(struct insomniad_ctx *ctx)
{
int rc;
open_wakeup_count(ctx);
memset(&ctx->wakeup_count, 0, sizeof(ctx->wakeup_count));
//rc = TEMP_FAILURE_RETRY(read(ctx->wakeup_count_fd, &ctx->wakeup_count, sizeof(ctx->wakeup_count)));
rc = read(ctx->wakeup_count_fd, &ctx->wakeup_count, sizeof(ctx->wakeup_count));
if (rc == -1) {
perror("Error reading wakeup_count");
exit(1);
}
}
然后是evaluate_policy(),代码如下:
int evaluate_policy(void)
{
struct wakeup_source *wup = get_most_recent_event();
uint64_t current_time = get_time_ms();
uint64_t delta;
if (!wup)
return -ENOMEM;
assert(current_time >= wup->last_change);
delta = current_time - wup->last_change;
pr_info("Last wakeup event at %" PRIu64 "ms\n", wup->last_change);
pr_info("Current time %" PRIu64 "ms\n", current_time);
pr_info("Delta %" PRIu64 "ms\n", delta);
wakeup_source_unref(wup);
if (delta < hysteresis_ms) {
uint64_t delay = hysteresis_ms - delta;
pr_info("Delaying for %" PRIu64 "ms\n", delay);
usleep_signal_safe(delay * 1000);
}
return 1;
}
evaluate_policy()主要是获取有效的wakeup_source。
|