本帖最后由 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。
|