Пишем в ашмем / почему андроид бесплатно ашмем?

Я хочу делиться данными между двумя (ndk-) процессами. Для этого я использую Ashmem, используя этот источник.
Один процесс постоянно читает (read_mem) и один процесс пишет один раз (write_mem).

Проблема в том, что процесс чтения не получает значения автора.

А ТАКЖЕ

Просматривая карты ридера, я обнаружил, что Android удаляет файл общей памяти сразу после ashmem_create_region,


read_mem.c

// read_mem.c
#include <stdio.h>
#include <errno.h>
#include <sys/mman.h>
#include "ashmem.h"

#define SHM_NAME "test_mem"
int main(int argc, char **argv) {
    int shID = ashmem_create_region(SHM_NAME, 2);
    if (shID < 0)
    {
        perror("ashmem_create_region failed\n");
        return 1;
    }
    // right here /dev/ashmem/test_mem is deleted
    printf("ashmem_create_region: %d\n", shID);
    char *sh_buffer = (char*)mmap(NULL, 2, PROT_READ | PROT_WRITE, MAP_SHARED, shID, 0);
    if (sh_buffer == (char*)-1)
    {
        perror("mmap failed");
        return 1;
    }
    printf("PID=%d", getpid());
    do
    {
        printf("VALUE = 0x%x\n", sh_buffer[0]);
    }
    while (getchar());
    return 0;
}

write_mem.c

// write_mem.c
#include <stdio.h>
#include <errno.h>
#include <sys/mman.h>
#include "ashmem.h"

#define SHM_NAME "test_mem"
int main(int argc, char **argv) {
    int shID = ashmem_create_region(SHM_NAME, 2);
    if (shID < 0)
    {
        perror("ashmem_create_region failed\n");
        return 1;
    }
    printf("ashmem_create_region: %d\n", shID);
    char *sh_buffer = (char*)mmap(NULL, 2, PROT_READ | PROT_WRITE, MAP_SHARED, shID, 0);
    if (sh_buffer == (char*)-1)
    {
        perror("mmap failed");
        return 1;
    }
    printf("PID=%d\n", getpid());
    int ch = getchar();
    sh_buffer[0] = ch;
    printf("Written 0x%x\n", ch);
    munmap(sh_buffer, 2);
    close(shID);
    return 0;
}

Это вывод:
чтение

130|shell@mako:/data/local/tmp $ ./read_mem
ashmem_create_region: 3
PID=29655
VALUE = 0x0

Пишу

shell@mako:/data/local/tmp $ ./write_mem
ashmem_create_region: 3
PID=29691
A
Written 0x41

Чтение снова VALUE = 0x0 (нажав возврат)

Смотря карты читателя:

shell@mako:/ $ cat /proc/29655/maps | grep test_mem
b6ef5000-b6ef6000 rw-s 00000000 00:04 116213     /dev/ashmem/test_mem (deleted)

как вы видете test_mem удаляется пока read_mem все еще жив.


Дополнительная информация

Оба файла скомпилированы как исполняемые с помощью Android ndk-build команда
Устройство: LG Nexus 4 (AOSP Lollypop)
Я проверил /dev/ashmem это существует.
ашмем взят отсюда

1 ответ

Решение

Ashmem не работает как обычная общая память в Linux, и этому есть веская причина.

Во-первых, давайте попробуем объяснить "(удаленную)" часть, это деталь реализации того, как ashmem реализован в ядре. На самом деле это означает, что запись файла была создана в каталоге / dev / ashmem /, а затем удалена, но соответствующий i-узел все еще существует, поскольку для него существует хотя бы один открытый дескриптор файла.

На самом деле вы можете создать несколько областей Ashmem с одним и тем же именем, и все они будут отображаться как "/ dev / ashmem / (удалено)", но каждый из них будет соответствовать отдельному i-узлу и, следовательно, другому область памяти. И если вы посмотрите в / dev / ashmem /, то увидите, что каталог все еще пуст.

Вот почему название области ашмем действительно используется только для отладки. Невозможно "открыть" существующий регион по имени.

I-узел ashmem и соответствующая память автоматически освобождаются, когда закрывается последний дескриптор файла. Это полезно, потому что это означает, что если ваш процесс умирает из-за сбоя, ядро ​​автоматически восстанавливает память. Это не относится к обычной разделяемой памяти SysV (сбойный процесс просто приводит к утечке памяти! Что-то недопустимое во встроенной системе, такой как Android).

Ваши тестовые программы создают две разные области ашмэма с одинаковыми именами, поэтому они не работают так, как вы думаете. Вместо этого вам нужно:

1) Создайте одну область ашмэма в одном процессе.

2) Передать новый файловый дескриптор в область от первого процесса ко второму.

Один из способов сделать это - форкать первый процесс для создания второго (это автоматически продублирует дескрипторы файлов), но это обычно не очень хорошая идея для Android.

Лучшая альтернатива - использовать sendmsg() и recvmsg() для отправки дескриптора файла через сокет Unix-домена между двумя процессами. Это, как правило, сложно, но в качестве примера рассмотрим функции SendFd() и ReceiveFd() в следующем исходном файле, написанном для NDK:

https://android.googlesource.com/platform/ndk/+/android-5.0.0_r7/sources/android/crazy_linker/tests/test_util.h

Вуаля, надеюсь, это поможет

Другие вопросы по тегам