分享

【Android】线程/进程绑定指定CPU核

 codingSmart 2021-10-22

code小生,一个专注 Android 领域的技术平台

公众号回复 Android 加入我的安卓技术群

作者:尛尛球
链接:https://blog.csdn.net/a569503963/article/details/86149495
声明:本文来自尛尛球投稿,转发等请联系原作者授权

参考文档:

https://blog.csdn.net/lanyzh0909/article/details/50404664
//线程绑定CPU核-sched_setaffinity
https://blog.csdn.net/lyx2007825/article/details/53885205
//android将线程绑定在指定CPU
https://www.jianshu.com/p/568cbc3ef786
//将C文件通过NDK编译生成SO库
https://my.oschina.net/zhiweiofli/blog/138454
//Android的ps命令介绍和技巧

功能:获取CPU当前核数,将线程/进程绑定到指定CPU核上

原理:利用jni将线程绑定到指定的CPU核上

要说的话:

  1. 此文使用的方式是先编译完so文件,然后将so文件放入项目中使用的。不是直接在项目中放入c代码运行。将c代码直接放入项目时我的项目会在c代码的位置报错,所以采用了先编译成so文件的方式,如果你的项目不报错,可以参考上面第三个文档,将代码直接放入项目中。

  2. h文件、c文件和两个mk文件要在同一文件夹下,ndk编译命令需要在AndroidStudio的Terminal中执行,在外部命令行运行需要指定mk文件地址,可以自行查找命令。

1. c代码:

1.1 com_example_myapplication_Affinity.h文件

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_example_myapplication_Affinity */

#ifndef _Included_com_example_myapplication_Affinity
#define _Included_com_example_myapplication_Affinity
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     com_example_myapplication_Affinity
 * Method:    bindToCpu
 * Signature: (I)V
 */

JNIEXPORT void JNICALL Java_com_example_myapplication_Affinity_bindToCpu
  (JNIEnv *, jclass, jint)
;

/*
 * Class:     com_example_myapplication_Affinity
 * Method:    getCores
 * Signature: ()I
 */

JNIEXPORT jint JNICALL Java_com_example_myapplication_Affinity_getCores
  (JNIEnv *, jclass)
;

#ifdef __cplusplus
}
#endif
#endif

1.2  Affinity.c文件

#include <jni.h>
#include <android/log.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <pthread.h>
#include <sys/syscall.h>

#include <com_example_myapplication_Affinity.h>

#include<stdlib.h>
#include<stdio.h>
#include<sys/types.h>
#include<sys/sysinfo.h>
#include<unistd.h>

#define __USE_GNU
#include<sched.h>
#include<ctype.h>
#define THREAD_MAX_NUM 100  //1个CPU内的最多进程数

int num=0;  //cpu中核数

#define TAG "Affinity"
#define DEBUG 1

#ifndef CPU_ZERO
#define CPU_SETSIZE 1024
#define __NCPUBITS  (8 * sizeof (unsigned long))
typedef struct
{

    unsigned long __bits[CPU_SETSIZE / __NCPUBITS];
cpu_set_t;

#define CPU_SET(cpu, cpusetp) \
  ((cpusetp)->__bits[(cpu)/__NCPUBITS] |= (1UL << ((cpu) % __NCPUBITS)))

#define CPU_ZERO(cpusetp) \
  memset((cpusetp), 0, sizeof(cpu_set_t))

#else
#define CPU_SET(cpu,cpustep) ((void)0)
#define CPU_ZERO(cpu,cpustep) ((void)0)
#endif

#ifdef DEBUG
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG,TAG,__VA_ARGS__)
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,TAG,__VA_ARGS__)
#else
#define LOGD(...) ((void)0)
#define LOGE(...) ((void)0)
#endif

static int getCores() {
    return sysconf(_SC_NPROCESSORS_CONF);
}

JNIEXPORT int JNICALL Java_com_example_myapplication_Affinity_getCores (JNIEnv *env, jclass type) {
    return getCores();
}

JNIEXPORT void JNICALL Java_com_example_myapplication_Affinity_bindToCpu (JNIEnv *env, jclass type, jint cpu) {
    int cores = getCores();
    LOGD("get cpu number = %d\n",cores);
    if (cpu >= cores) {
        LOGE("your set cpu is beyond the cores,exit...");
        return;
    }

    cpu_set_t mask;
    CPU_ZERO(&mask);
    CPU_SET(cpu,&mask);
    if (sched_setaffinity(0sizeof(mask), &mask) == -1)//设置线程CPU亲和力
    {
        LOGD("warning: could not set CPU affinity, continuing...\n");
    }else
    {
        LOGD("set affinity to %d success",cpu);
    }
}

1.3 要点:

  1. c文件的方法与h文件的方法一致,包括方法名、传入参数、返回参数,全都一致。

  2. h文件可以先写java的native方法,调用命令创建。

1.4 备注

1.4.1  调用命令创建.h文件:

  1. 写java文件,并写上native方法

package com.example.myapplication;

public class Affinity {
    static {
        System.loadLibrary("Affinity");
    }
    public static native void bindToCpu(int cpu);
    public static native int getCores();
}
  1. 在Terminal使用命令

先进入工程main目录下 输入自己的Native.class文件的绝对路径
javah -d jni -classpath class路径 包名+类名

例:
javah -d jni -classpath D:\Demo\JNITest\app\build\intermediates\classes\debug com.example.myapplication.Affinity

注意:debug后面要留空格

2. mk文件

2.1 Application.mk文件

APP_PLATFORM := android-16    //编译环境
APP_ABI := armeabi-v7a    //编译后生成so的文件夹

2.2 Android.mk文件

LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)

LOCAL_MODULE := libAffinity    //生成后的so文件名

LOCAL_SRC_FILES := Affinity.c    //要生成的c文件
LOCAL_LDLIBS := -llog -lz

include $(BUILD_SHARED_LIBRARY)

2.3 AndroidManifest.xml文件

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas./apk/res/android"
    package="com.example.myapplication">

    <uses-sdk android:minSdkVersion="16" android:targetSdkVersion="25"></uses-sdk>
    <application
        ...>

             ......
    </application>

</manifest>

2.4 要点

  1. APP_PLATFORM的值必须和AndroidManifest.xml中android:minSdkVersion的值相同

  2. c文件、h文件、两个mk文件,都放在同一目录下

3. 编译成so文件

  1. 在AndroidStudio中打开Terminal。

  2. 进入到c文件所在文件夹。

  3. 使用ndk-build命令。如果你的ndk目录没有配置到环境变量中,使用路径的方式也可以,如:C:\User\admin\Android\sdk\ndk_bundle\ndk-build.cmd

  4. 如果编译正常会出现下图。

    ndk编译成功

4. 将so文件放入项目中

  1. 复制so文件到app\libs中

  2. 在项目的build.gradle中,设置jni目录

android{
    ...
    sourceSets {
        main {
            jniLibs.srcDirs = ['libs']
        }
    }
}

5. 查询是否绑定CPU成功

  1. 代码运行成功会出现log信息“set affinity to 0 success”,这里0代表cpu编号

  2. 使用linux命令查询

adb shell ps -t -p -c

查询出来的结果(取与本应用相关的信息)如下:

查询线程所在cpu

在我的代码中,在应用启动的进程下开了两个子线程,在新开的 myservice 进程中,开了四个子线程。最后一列的 NAME 默认情况下,是包名。
PID 9105 是应用程序所在进程,PID 9132 是应用程序下的主线程,也就是绘制UI的线程,PID 9134 和 9135 是应用程序下的两个子线程。
PID 9136 是 myservice 所在进程,下面的 Thread-62 到 65 是myservice 下的子线程。

从上面的图片可以看到,如果在进程中绑定了cpu核,子线程也会绑定到那个核上;如果在子线程中绑定了与主线程不同的核,也是可以的。

分享技术我是认真的

    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多