You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Linux下监听sysfs电池事件问题:libudev与inotify均无法捕获容量变更事件

问题根因

你的两版代码逻辑本身没有致命错误,收不到事件是由Linux内核的底层行为和你对参考代码的理解偏差导致的:

  • libudev收不到容量变更事件:Linux power_supply子系统默认仅在「外接电源插拔、充放电状态切换、电池故障」这类离散突变场景下才会发送uevent通知,电池容量每1%的渐变属于常规状态更新,内核不会主动触发uevent上报,因此udev monitor自然捕获不到对应事件。
  • inotify实现不生效:首先你监听的IN_ACCESS是文件被读取时才会触发的事件,内核更新电池容量参数不会主动读取uevent文件,自然不会触发事件;其次sysfs是虚拟文件系统,仅少数特殊节点支持inotify事件上报,uevent不在支持列表内。你参考的状态栏代码中的inotify仅用于监听电源设备插拔事件,其内部默认内置了30~60秒的定时轮询逻辑来更新电池容量,你只提取了inotify部分的逻辑,遗漏了核心的轮询分支。

适配嵌入式场景的最优实现方案

你需要的低功耗事件驱动方案可以用timerfd + poll/epoll组合实现,完全符合你不使用sleep()的要求,功耗开销极低:

  1. 初始化udev monitor监听power_supply子系统事件,用于处理外接电源插拔这类需要立刻响应的事件
  2. 用timerfd_create创建定时器,设置符合你业务需求的容量检测间隔(太阳能设备如果对容量实时性要求不高,可设置为1~5分钟的间隔,进一步降低功耗)
  3. 将udev的fd和timerfd同时加入poll监听列表
  4. 事件触发时判断事件来源:
    • 来自udev的事件:立刻读取电池全量状态更新
    • 来自timerfd的事件:仅读取容量节点更新数值即可

示例代码

#include <libudev.h>
#include <poll.h>
#include <stdio.h>
#include <sys/timerfd.h>
#include <unistd.h>
#include <stdint.h>

#define BAT_CAP_PATH "/sys/class/power_supply/BAT0/capacity"

static int read_bat_cap(void)
{
    int cap = 0;
    FILE *f = fopen(BAT_CAP_PATH, "r");
    if (!f) return -1;
    fscanf(f, "%d", &cap);
    fclose(f);
    return cap;
}

int main(void)
{
    struct udev *udev = udev_new();
    if (!udev) return 1;

    struct udev_monitor *mon = udev_monitor_new_from_netlink(udev, "udev");
    udev_monitor_filter_add_match_subsystem_devtype(mon, "power_supply", NULL);
    udev_monitor_enable_receiving(mon);
    int udev_fd = udev_monitor_get_fd(mon);

    // 创建定时器,每30秒触发一次
    int timer_fd = timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC);
    struct itimerspec ts = {
        .it_interval = {.tv_sec = 30, .tv_nsec = 0},
        .it_value = {.tv_sec = 1, .tv_nsec = 0} // 首次1秒后触发
    };
    timerfd_settime(timer_fd, 0, &ts, NULL);

    struct pollfd fds[] = {
        {.fd = udev_fd, .events = POLLIN},
        {.fd = timer_fd, .events = POLLIN}
    };

    while (1) {
        if (poll(fds, 2, -1) < 0) break;

        if (fds[0].revents & POLLIN) {
            // 处理电源插拔事件,立刻更新全量状态
            struct udev_device *dev = udev_monitor_receive_device(mon);
            if (dev) {
                printf("电源状态变更,当前容量:%d%%\n", read_bat_cap());
                udev_device_unref(dev);
            }
        }

        if (fds[1].revents & POLLIN) {
            // 定时更新容量
            uint64_t expire_cnt;
            read(timer_fd, &expire_cnt, sizeof(expire_cnt));
            printf("定时刷新,当前容量:%d%%\n", read_bat_cap());
        }
    }

    close(timer_fd);
    udev_unref(udev);
    return 0;
}

这种方案既避免了阻塞的sleep()调用,也没有无效的空轮询,整体开销和纯事件驱动方案一致,完全适合嵌入式太阳能设备的低功耗需求。

内容的提问来源于stack exchange,提问作者pan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.18 03:54:02