API级别低于26的NDK和SDK之间的共享内存 [英] Shared memory between NDK and SDK below API Level 26

查看:258
本文介绍了API级别低于26的NDK和SDK之间的共享内存的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

用c ++编写的库可产生连续的数据流,并且必须将其移植到不同的平台上.现在将lib集成到android应用程序中,我试图在NDK和SDK之间创建共享内存.

Library written in c++ produces continuous stream of data and same has to be ported on different platforms. Now integrating the lib to android application, I am trying to create shared memory between NDK and SDK.

下面是有效的代码段,

本机代码:

#include <jni.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <linux/ashmem.h>
#include <android/log.h>
#include <string>

char  *buffer;
constexpr size_t BufferSize=100;
extern "C" JNIEXPORT jobject JNICALL
Java_test_com_myapplication_MainActivity_getSharedBufferJNI(
        JNIEnv* env,
        jobject /* this */) {

    int fd = open("/dev/ashmem", O_RDWR);

    ioctl(fd, ASHMEM_SET_NAME, "shared_memory");
    ioctl(fd, ASHMEM_SET_SIZE, BufferSize);

    buffer = (char*) mmap(NULL, BufferSize, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);

    return (env->NewDirectByteBuffer(buffer, BufferSize));
}

extern "C" JNIEXPORT void JNICALL
Java_test_com_myapplication_MainActivity_TestBufferCopy(
        JNIEnv* env,
        jobject /* this */) {

   for(size_t i=0;i<BufferSize;i = i+2) {
       __android_log_print(ANDROID_LOG_INFO, "native_log", "Count %d value:%d", i,buffer[i]);
   }

   //pass `buffer` to dynamically loaded library to update share memory
   //

}

SDK代码:

//MainActivity.java
public class MainActivity extends AppCompatActivity {

    // Used to load the 'native-lib' library on application startup.

    static {
        System.loadLibrary("native-lib");
    }

    final int BufferSize = 100;
    @RequiresApi(api = Build.VERSION_CODES.Q)
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        ByteBuffer byteBuffer = getSharedBufferJNI();

        //update the command to shared memory here
        //byteBuffer updated with commands
        //Call JNI to inform update and get the response
        TestBufferCopy();
    }


    /**
     * A native method that is implemented by the 'native-lib' native library,
     * which is packaged with this application.
     */
    public native ByteBuffer getSharedBufferJNI();
    public native int TestBufferCopy();
}

问题:

  1. 访问原始数组.其他方法是真的吗?
  2. Android平台是否保证总是参考从NDK共享到SDK,而没有冗余副本?
  3. 这是共享内存的正确方法吗?
  1. Accessing primitive arrays from Java to native is reference only if garbage collector supports pinning. Is it true for other way around ?
  2. Is it guaranteed by android platform that ALWAYS reference is shared from NDK to SDK without redundant copy?
  3. Is it the right way to share memory?

推荐答案

您只需要/dev/ashmem即可在进程之间共享内存. NDK和SDK(Java/Kotlin)在相同的Linux进程中工作,并且可以完全访问相同的内存空间.

You only need /dev/ashmem to share memory between processes. NDK and SDK (Java/Kotlin) work in same Linux process and have full access to same memory space.

定义可在C ++和Java中使用的内存的常用方法是创建直接字节缓冲区.您不需要JNI,Java API具有 ByteBuffer.allocateDirect(int容量).如果逻辑流更自然地在C ++端分配缓冲区,则JNI具有

The usual way to define memory that can be used both from C++ and Java is by creating a Direct ByteBuffer. You don't need JNI for that, Java API has ByteBuffer.allocateDirect(int capacity). If it's more natural for your logical flow to allocate the buffer on the C++ side, JNI has the NewDirectByteBuffer(JNIEnv* env, void* address, jlong capacity) function that you used in your question.

在C ++方面使用Direct ByteBuffer非常容易,但在JVM方面则效率不高.原因是此缓冲区不由数组支持,并且您拥有的唯一API position ,但是以这种方式工作需要一定的纪律:每个 get()操作都会更新当前位置.而且,对该缓冲区的随机访问相当慢,因为它涉及到调用定位API和获取API.因此,在某些非平凡数据结构的情况下,使用C ++编写自定义访问代码并通过JNI调用智能" getter可能会更容易.

Working with Direct ByteBuffer is very easy on the C++ side, but not so efficient on the JVM side. The reason is that this buffer is not backed by array, and the only API you have involves ByteBuffer.get() with typed variations (getting byte array, char, int, …). You have control of current position in the buffer, but working this way requires certain discipline: every get() operation updates the current position. Also, random access to this buffer is rather slow, because it involves calling both positioning and get APIs. Therefore, in some cases of non-trivial data structures, it may be easier to write your custom access code in C++ and have 'intelligent' getters called through JNI.

重要的是不要忘记设置 ByteBuffer.order(ByteOrder.nativeOrder()).与之相反,新创建的字节缓冲区的顺序为BIG_ENDIAN.这适用于从Java和C ++创建的缓冲区.

It's important not to forget to set ByteBuffer.order(ByteOrder.nativeOrder()). The order of a newly-created byte buffer is counterintuitively BIG_ENDIAN. This applies both to buffer created from Java and from C++.

如果您可以在C ++需要访问此类共享内存的情况下隔离实例,而实际上并不需要一直固定它,则值得考虑使用字节数组.在Java中,您可以进行更有效的随机访问.在NDK方面,您将调用 GetByteArrayElements() GetPrimitiveArrayCritical().后者效率更高,但是它的使用对在数组释放之前可以调用的Java函数施加了限制.在Android上,这两种方法都不涉及内存分配和复制(尽管没有官方保证).即使C ++端使用与Java相同的内存,您的JNI代码也必须调用适当的Release…()函数,最好尽早这样做.通过RAII处理此Get/Release是一个好习惯.

If you can isolate the instances when C++ needs access to such shared memory, and don't really need it to be pinned all the time, it's worth to consider working with byte array. In Java, you have more efficient random access. On the NDK side, you will call GetByteArrayElements() or GetPrimitiveArrayCritical(). The latter is more efficient, but its use imposes restrictions on what Java functions you can call until the array is released. On Android, both methods don't involve memory allocation and copy (with no official guarantee, though). Even though C++ side uses the same memory as Java, your JNI code must call the appropriate Release…() function, and better do that as early as possible. It's a good practice to handle this Get/Release via RAII.

这篇关于API级别低于26的NDK和SDK之间的共享内存的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆