美文网首页
AndroidV上读取proc/pressure/cpu失败

AndroidV上读取proc/pressure/cpu失败

作者: 欣兄 | 来源:发表于2024-06-12 16:23 被阅读0次

    一、问题与调试

    在做cpu负载问题的是,需要读取/proc/pressure/cpu/的数据,发现打开文件失败

    #define KERNEL_INFO_CPU "proc/pressure/cpu"
    ......
        int fd = TEMP_FAILURE_RETRY(open(KERNEL_INFO_CPU, O_WRONLY | O_CLOEXEC));
        if (fd < 0) {
            ALOGE("open failed (errno=%d)", errno);
            return -1;
        }  
    ......
    

    \color {red}{报错 open failed (errno =13)}

    #define EACCES 13 /* Permission denied */   意思就是没有权限
    

    查看一下

    进入 proc/pressure/目录下  ls -alh
    -r--r--r-- 1 root root 0 2024-06-11 20:01 cpu
    -rw-rw-r-- 1 system system 0 2024-06-11 19:42 io
    -rw-rw-r-- 1 system system 0 2024-06-11 19:41 memory
    

    可以看到 cpu 访问需要root权限, 那就修改

    system/core/rootdir/init.rc
    在init.rc文件中加入如下
    chown system system /proc/pressure/cpu
    chmod 0664 /proc/pressure/cpu
    

    调试编译也简单,直接在system/core/rootdir/ mm,编译产生的文件在
    target\product\XXXX\system\etc\init\hw\init.rc ,然后 push init.rc system/etc/init/hw 替换其中的init.rc
    当然直接把机器中的init.rc pull 出来,然后修改push进去也可以。

    二、jni的配置例子

    接着整理一下jni的使用,不跨进程,system_server进程中的jni使用。
    主要涉及的文件

    java 文件
    ....../services/core/java/com/android/server/am/NameJavaTemp.java
    cpp 文件
    ....../services/jni/NameCppTemp.cpp
    ....../ services/jni/onload.cpp
    ....../services/jni/Android.bp
    

    1、java 文件
    ....../services/core/java/com/android/server/am/NameJavaTemp.java

    public class NameJavaTemp {
        public  NameJavaTemp(){
            init();
        }
        private void init(){
            new Thread(){
                @Override
                public void run(){
                    javaToNative();
                }
            }.start();
        }
        private native void javaToNative();
        public void nativeTojava(String str){}
    }
    

    2、....../services/jni/Android.bp

    ......
        srcs: [
            "onload.cpp",
            "com_android_server_am_NameCppTemp.cpp",
        ],
    ......
    

    3、....../ services/jni/onload.cpp

    namespace android {
    ......
    int register_android_server_NameCppTemp(JNIEnv* env);
    ......
    };
    
    extern "C" jint JNI_OnLoad(JavaVM *vm, void *reserved) {
        JNIEnv *env = NULL;
    ......
        register_android_server_NameCppTemp(env);
    ......
        return JNI_VERSION_1_4;
    }
    

    4、
    如下代码就包括从Java层通过jni调用到cpp层 javaToNative的具体实现,。

    NameCppTemp.cpp
    ......
    namespace android{
        static int initM(JNIEnv* env, jobject clazz) {
              ......
            jclass cls = env->GetObjectClass(clazz);
            jmethodID methodId = env->GetMethodID(cls, "nativeTojava", "(Ljava/lang/String;)V");
            if (methodId == nullptr) {
                // error
                return -1;
            }
            jstring arg = env->NewStringUTF("1");
            env->CallVoidMethod(clazz, methodId, arg);
        }
        static const JNINativeMethod gMethods[] = {
            { "javaToNative","()V",(void*) initM},   //javaToNative就是 java端声明的native方法,initM就是具体实现的方法名
        };
    
        int register_android_server_NameCppTemp(JNIEnv *env) {
            return jniRegisterNativeMethods(env, "com/android/server/am/NameJavaTemp", gMethods,NELEM(gMethods)); //NameJavaTemp就是java端的类名
        }
    }
    
    

    三、epoll机制例子

    实现了 epoll机制 监听 proc/pressure/cpu的 阈值,

    ....../services/jni/NameCppTemp.cpp
    #define KERNEL_INFO_CPU "proc/pressure/cpu"
    #include <stdio.h>
    #include <sys/epoll.h>
    #include <sys/eventfd.h>
    
    #include <string>
    #include <fcntl.h>
    #include <unistd.h>
    #include <sys/types.h>
    #include <utils/misc.h>
    #include <nativehelper/JNIHelp.h>
    #include <errno.h>
    #include <log/log.h>
    #include "jni.h"
    #include<iostream>
    #include<fstream>
    #include<string>
    
    #define MAX_POLL_EVENT 256
    namespace android{
    static int epollfd = -1;
    static JNIEnv* g_env;
    
    static int initM(JNIEnv* env, jobject clazz) {
        const char trig[] = "some 500000 1000000";
        epollfd = epoll_create(MAX_POLL_EVENT);
        if (epollfd == -1) {
            ALOGE("epoll_create failed: %s", strerror(errno));
            return -1;
        }
        int fd = TEMP_FAILURE_RETRY(open(KERNEL_INFO_CPU, O_WRONLY | O_CLOEXEC));
        if (fd < 0) {
            ALOGE("open failed (errno=%d)", errno);
            return -1;
        }   
     int res;
        struct epoll_event epev;
        epev.events = EPOLLPRI;
    
        if (write(fd, trig, strlen(trig) + 1) < 0) {
            ALOGD("/proc/pressure/cpu write error: %s\n", strerror(errno));
            return -1;
        }
        res = epoll_ctl(epollfd, EPOLL_CTL_ADD, fd, &epev);
        if (res < 0) {
            ALOGE("epoll_ctl for monitor failed; errno=%d", errno);
        }
        while(true) {
            struct epoll_event events[20];
            int eventCount = 0;
            eventCount = epoll_wait(epollfd, events, 20, -1);
            if (eventCount < 0) {
                if (errno == EINTR) {
                    continue;
                 }
                ALOGE("epoll_wait failed (errno=%d)", errno);
                return -1;
            }
            //ALOGD("The eventCount %d", eventCount);
            jclass cls = env->GetObjectClass(clazz);
            jmethodID methodId = env->GetMethodID(cls, "nativeTojava", "(Ljava/lang/String;)V");
            if (methodId == nullptr) {
                // error
                return -1;
            }
            jstring arg = env->NewStringUTF("1");
            env->CallVoidMethod(clazz, methodId, arg);
            //test
            std::ifstream file("proc/pressure/cpu");
            if (!file.is_open()) {
                ALOGD("open file failure");
                return -1;
            }
            std::string buf;
            getline(file, buf);
            ALOGD("ZZZZ %s", buf.c_str());
        }
    }
    static const JNINativeMethod gMethods[] = {
        { "javaToNative","()V",(void*) initM},   //javaToNative就是 java端声明的native方法
    
    };
    int register_android_server_NameCppTemp(JNIEnv *env) {
        return jniRegisterNativeMethods(env, "com/android/server/am/NameJavaTemp", gMethods,NELEM(gMethods)); //NameJavaTemp就是java端的类名
    }
    }
    

    四、所涉及的jni 相关信息
    1、JNI编程中JNIEnv、jobject和jclass这三种基本类型

    • JavaVm是虚拟机在jni层的代表,⼀个进程只有⼀个JavaVm,所有线程共⽤⼀个JavaVM。

    • JNIEnv 是⼀个线程相关的结构体,它代表了java的运⾏环境 。每⼀个线程都会有⼀个,不同的线程中
      不能相互调⽤,每个JNIEnv都是线程专有的。 jni中可以拥有很多个JNIEnv,可以使⽤它来进⾏java层
      和native层的调⽤。

    • JNIEnv 是⼀个指针,指向⼀个线程相关的结构,线程相关结构指向了JNI函数指针数组。这个数组⾥⾯
      定义了⼤量的JNI函数指针。

    • 在同⼀个线程中,多次调⽤JNI层⽅法,传⼊的JNIEnv都是相同的。
      在java层定义的本地⽅法,可以在不同的线程中调⽤,因此是可以接受不同的JNIEnv。

    • jobject:实例引⽤(C++的说法:对象引⽤)(普通函数)

    • jclass: 类引⽤ (静态函数)

    static int initM(JNIEnv* env, jobject clazz) {......}
    

    相关文章

      网友评论

          本文标题:AndroidV上读取proc/pressure/cpu失败

          本文链接:https://www.haomeiwen.com/subject/bgggqjtx.html