Область общей памяти в NDK
Я хочу иметь общий блок памяти (ashmem
регион), который сопоставлен и доступен из нативного кода. Я также хочу, чтобы этот блок использовался несколькими приложениями. Я тоже хочу, чтобы он работал на SDK level 7 (Android 2.1)
Есть два маршрута. Я могу создать область Ashmem в родном коде; но тогда возникает вопрос - как передать целочисленный файловый дескриптор другому процессу? Вы можете маршал FileDescriptor
объекты через Parcel
, но нет способа построить один вокруг необработанного FD. Есть также ParcelFileDescriptor
который поддерживает построение и извлечение целочисленных FD, но соответствующие методы поддерживаются только на уровне SDK 12 или даже выше.
В качестве альтернативы я могу создать MemoryFile
, В " Посылках " есть дурацкий способ раздать это. Но как мне извлечь из него дескриптор файла, чтобы нативный код mmap()
?
3 ответа
На всех версиях Android начиная с 1.5 до 4.1, FileDescriptor
имеет int
элемент данных называется descriptor
, Это частный пакет на более ранних версиях Android, частный на последних. С помощью преднамеренного подрывного контроля доступа вы можете получить к нему доступ - либо через отражение, либо через JNI. Каждый может обойти контроль доступа - в случае отражения, через Field.setAccessible()
, в случае JNI - по умолчанию.
Имея это в виду, вы можете построить FileDescriptor
вокруг родной фд просто отлично. Построить пустой, затем установить descriptor
, Вот что делают фрагменты кода Android при их создании.
Будет ли этот грязный хак сломаться в конце концов, кто знает. К счастью, в моем случае это не ключевой элемент функциональности - есть некоторая изящная деградация.
Можно условно использовать поддерживаемых ParcelFileDescriptor
методы, если платформа позволяет, используя взлом доступа к полю как запасной вариант. Таким образом, это будет относительно будущее.
Для этого jniCreateFileDescriptor() есть метод в библиотеке помощников libnativehelper.so https://android.googlesource.com/platform/libnativehelper/+/jb-dev/include/nativehelper/JNIHelp.h. Он в основном делает то же самое, что и в предыдущем ответе, но вы можете найти этот подход немного чище.
Вот как это работает для меня при работе с похожей проблемой:
Вместо того чтобы использовать shmfd = open(SHM_PATH, O_RDWR) для создания и получения файлового дескриптора, я заменил его на
int fd = ashmem_create_region("SharedRegionName", size);
и использовал дескриптор файла, чтобы получить базовый адрес:
int base_address = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
// запись данных Вы можете передать base_address в свой код Java из собственного кода, используя встроенную функцию, которая возвращает дескриптор.
Затем я создаю Сервис с интерфейсом aidl и использую этот интерфейс для связывания этого сервиса с другим процессом. Из Службы я использовал объект ParcelFileDescriptor для возврата к другому процессу. Вы можете создать ParcelFileDescriptor следующим образом:
ParcelFileDescriptor desc = ParcelFileDescriptor.fromFd(fd);