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
相关产品推荐
相关产品推荐

