问题
I want to have a shared memory block (an ashmem
region) that's mapped and accessed from native code. I also want this block to be used by several applications. I also want it to work on SDK level 7 (Android 2.1)
There are two routes. I can create an ashmem region in native code; but then the question is - how do I pass an integer file descriptor to another process? You can marshal FileDescriptor
objects via a Parcel
, but there's no way to construct one around a raw FD. There's also ParcelFileDescriptor
which supports constructing around and retrieving integer FD's, but the relevant methods are only supported in SDK level 12 or even higher.
Alternatively, I can create a MemoryFile
. There's a fugly way to pass it around in Parcels. But how do I retrieve a file descriptor from it so that native code has something to mmap()
?
回答1:
On all versions of Android since 1.5 to 4.1, FileDescriptor
has an int
data member called descriptor
. It's package-private on earlier versions of Android, private on recent ones. With a bit of deliberate access control subversion, you can access it - either via reflection, or via JNI. Each can bypass access control - in case of reflection, via Field.setAccessible()
, in case of JNI - by default.
With that in mind, you can construct a FileDescriptor
around a native FD just fine. Construct a blank one, then set descriptor
. That's what bits and pieces of Android code do when constructing those.
Whether this dirty hack will break eventually, who knows. Fortunately, it's not a core piece of functionality in my case - there's some graceful degradation.
One may conditionally employ supported ParcelFileDescriptor
methods, if the platform allows, using the field access hack as a fallback. This way, it'll be relatively future proof.
回答2:
There is a method in helper library libnativehelper.so for this jniCreateFileDescriptor() https://android.googlesource.com/platform/libnativehelper/+/jb-dev/include/nativehelper/JNIHelp.h. It basically does the same thing as stated in previous answer but you might find this approach a bit cleaner.
回答3:
This is how it worked for me while working with a similar problem:
Instead of using shmfd = open(SHM_PATH, O_RDWR) for creating and getting file descriptor I replaced it with
int fd = ashmem_create_region("SharedRegionName", size);
and used the file descriptor to get base address:
int base_address = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
// write data You can pass the base_address to your java code from the native code using a native function that returns the descriptor.
Then I create a Service with aidl interface and used this interface to bind this service from another process. From the Service I have used a ParcelFileDescriptor object to return to another process. You can create ParcelFileDescriptor by:
ParcelFileDescriptor desc = ParcelFileDescriptor.fromFd(fd);
来源:https://stackoverflow.com/questions/12864778/shared-memory-region-in-ndk