一、问题与调试
在做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;
}
......
#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) {......}