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

Android设备inotify监听特定文件事件失效问题求助

解决Galaxy S7待机后CPU频率节点inotify监听失效问题

我来帮你解决这个Galaxy S7上inotify监听失效的问题——这个现象其实和Android低功耗状态下的CPU管理机制直接相关,咱们一步步拆解:

问题回顾

你用inotify同时监听两个系统文件:

  • /sys/class/graphics/fb0/show_blank_event:屏幕待机前后都能正常触发IN_MODIFY事件
  • /sys/devices/system/cpu/cpu2/cpufreq/interactive:屏幕关闭待机数分钟后,IN_MODIFY事件完全停止触发,且已确认没有触发IN_IGNORE事件

相关代码

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/inotify.h>
#include <pthread.h>
#include <linux/inotify.h>
#include <fcntl.h>
#include <string.h>
#include <sys/stat.h>

#define BUF_LEN 1024

// 假设线程函数已实现
// void* handleScreenEvent(void* arg);
// void* handleGovEvent(void* arg);

int main(int argc, char* argv[]) { 
    char* p; 
    struct inotify_event *event; 
    int sd, gd, fd; 
    char buffer[BUF_LEN]; 

    fd = inotify_init(); 
    if( fd < 0) { 
        perror("inotify_init failed");
        return 1; 
    } 

    gd = inotify_add_watch(fd, "/sys/devices/system/cpu/cpu2/cpufreq/interactive", IN_MODIFY); 
    sd = inotify_add_watch(fd, "/sys/class/graphics/fb0/show_blank_event", IN_MODIFY); 

    for(;;) { 
        ssize_t bytes_read = read(fd, buffer, BUF_LEN);
        if (bytes_read < 0) {
            perror("read failed");
            sleep(1);
            continue;
        }

        event = (struct inotify_event *) buffer; 
        // 遍历所有事件(read可能返回多个事件)
        while ((char*)event < buffer + bytes_read) {
            if(event->mask & IN_MODIFY) { 
                if(event->wd == sd) { 
                    printf("Handle screen event\n"); 
                    //pthread_create(&handle_screen_event, NULL, handleScreenEvent, (void*)NULL); 
                } 
                if(event->wd == gd && gd != -1) { 
                    printf("Handle gov event\n"); 
                    //pthread_create(&handle_gov_event, NULL, handleGovEvent, (void*)NULL); 
                } 
            }

            // 检查watch是否被忽略
            if(event->mask & IN_IGNORE) {
                printf("Watch ignored for wd: %d\n", event->wd);
                if(event->wd == gd) {
                    gd = -1;
                }
            }

            // 移动到下一个事件
            event = (struct inotify_event*)((char*)event + sizeof(struct inotify_event) + event->len);
        }
    } 

    return 0; 
}

核心原因分析

两个节点表现差异的本质是Android待机时的硬件资源管理策略:

  • 显示节点稳定性:/sys/class/graphics/fb0/show_blank_event属于显示子系统的核心控制节点,即使屏幕关闭,显示控制器仍会保持活跃(用于响应唤醒指令),所以这个节点的生命周期不会被中断,inotify监听始终有效。
  • CPU节点动态性:Galaxy S7进入深度待机(比如Doze模式)后,内核会自动将闲置的CPU核心离线(热插拔)来降低功耗。此时/sys/devices/system/cpu/cpu2/整个目录会被内核移除,对应的cpufreq节点自然消失。而部分内核实现中,sysfs目录的移除并不会直接触发IN_IGNORE事件,导致你的代码误以为watch仍有效,但实际上已经无法捕获后续事件。即使CPU重新上线,节点被重建,你的代码也没有逻辑去重新添加watch。

可行解决方案

方案1:监听父目录,自动恢复watch

通过监听CPU2 cpufreq目录的创建/删除事件,在interactive节点重建时自动恢复watch:

// 在main函数中添加父目录监听
int cpufreq_dir_wd = inotify_add_watch(fd, "/sys/devices/system/cpu/cpu2/cpufreq", IN_CREATE | IN_DELETE);

// 在事件处理循环中增加逻辑
if(event->mask & IN_CREATE) {
    if(strcmp(event->name, "interactive") == 0) {
        if(gd == -1) {
            gd = inotify_add_watch(fd, "/sys/devices/system/cpu/cpu2/cpufreq/interactive", IN_MODIFY);
            printf("Restored watch for interactive governor\n");
        }
    }
}
if(event->mask & IN_DELETE) {
    if(strcmp(event->name, "interactive") == 0) {
        if(gd != -1) {
            inotify_rm_watch(fd, gd);
            gd = -1;
            printf("Interactive governor node deleted\n");
        }
    }
}

方案2:定期检查节点状态

如果目录监听不可靠,可以添加一个定时线程,周期性检查节点是否存在并恢复watch:

typedef struct {
    int inotify_fd;
    int* gov_wd;
} recovery_args_t;

void* watch_recovery_thread(void* arg) {
    recovery_args_t* args = (recovery_args_t*)arg;
    while(1) {
        sleep(60); // 每分钟检查一次
        if(access("/sys/devices/system/cpu/cpu2/cpufreq/interactive", F_OK) == 0) {
            if(*(args->gov_wd) == -1) {
                *(args->gov_wd) = inotify_add_watch(args->inotify_fd, "/sys/devices/system/cpu/cpu2/cpufreq/interactive", IN_MODIFY);
                printf("Restored watch via periodic check\n");
            }
        } else {
            if(*(args->gov_wd) != -1) {
                inotify_rm_watch(args->inotify_fd, *(args->gov_wd));
                *(args->gov_wd) = -1;
                printf("Removed invalid watch\n");
            }
        }
    }
    return NULL;
}

// 在main函数中启动线程
recovery_args_t args = {fd, &gd};
pthread_t recovery_thread;
pthread_create(&recovery_thread, NULL, watch_recovery_thread, (void*)&args);
pthread_detach(recovery_thread); // 分离线程,避免资源泄漏

方案3:监听CPU在线状态文件

最可靠的方式是监听/sys/devices/system/cpu/online文件,它记录了当前在线的CPU核心列表,当CPU2重新上线时再恢复watch:

// 添加online文件监听
int online_wd = inotify_add_watch(fd, "/sys/devices/system/cpu/online", IN_MODIFY);

// 事件处理逻辑
if(event->wd == online_wd) {
    char online_cpus[32] = {0};
    FILE* f = fopen("/sys/devices/system/cpu/online", "r");
    if(f) {
        fread(online_cpus, sizeof(online_cpus)-1, 1, f);
        fclose(f);
        // 检查CPU2是否在线(格式可能是"0-3"或"0,2")
        if(strstr(online_cpus, "2") != NULL) {
            if(gd == -1) {
                gd = inotify_add_watch(fd, "/sys/devices/system/cpu/cpu2/cpufreq/interactive", IN_MODIFY);
                printf("CPU2 online, restored watch\n");
            }
        } else {
            if(gd != -1) {
                inotify_rm_watch(fd, gd);
                gd = -1;
                printf("CPU2 offline, removed watch\n");
            }
        }
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.27 09:56:51