Android系统的Ashmem匿名共享内存子系统分析(2)- 运行时库cutils的Ashmem访问接口

声明

1 Ashmem的架构

  Android 系统实现的 Ashmem 匿名共享内存子系统,用来在应用程序之间共享数据。Ashmem 与传统的Linux系统实现的共享内存一样,都是基于内核提供的临时文件系统tmpfs实现的,但是 Ashmem 对内存块进行了更为精细化的管理。应用程序可以动态地将一块匿名共享内存划分为若干个小块,当这些小块内存不再需要使用时,它们就可以被内存管理系统回收。通过这种动态的、分而治之的内存管理方式,Android系统就能够有效地使用系统内存,适应内存较小的移动设备环境。

  匿名共享内存系统是以Ashmem驱动程序为基础的,系统中所有的匿名共享内存都由Ashmem驱动程序负责分配和管理。Android系统在 Native 层提供了 C/C++ 调用接口和 Framework 层提供了 Java 调用接口。

  • 在Framework 层中,提供了两个C++类 MemoryBase 和 MemoryHeapBase,以及一个 Java 类 MemoryFile 来使用匿名共享内存。
  • 在运行时库 cutils 中,主要提供了三个C函数 ashmem_create_region、ashmem_pin_region 和 ashmem_unpin_region 来访问 Ashmem 驱动程序。

  Ashmem驱动程序在启动时,会创建一个 /dev/ashmem 设备文件,这样,运行时库 cutils 中的匿名共享内存接口就可以通过文件操作函数 open 和 ioctl 等来访问 Ashmem 驱动程序。
在这里插入图片描述

  传统的 Linux 系统使用一个整数来标志一块共享内存,而 Android 系统则使用一个文件描述符来标志一块匿名共享内存。使用文件描述符来描述一块匿名共享内存有两个好处:

  1. 可以方便地将它映射到进程的地址空间,从而可以直接访问它的内容;
  2. 可以使用 Binder 进程间通信机制来传输这个文件描述符,从而实现在不同的应用程序之间共享一块匿名内存。

  Binder 进程间通信机制使用一个类型为 BINDER_TYPE_FD 的 Binder 对象来描述一个文件描述符,当 Binder 驱动程序发现进程间通信数据中包含有这种 Binder 对象时,就会将对应的文件描述符复制到目标进程中,从而实现在两个进程中共享同一个文件。

2 运行时库cutils的Ashmem访问接口

运行时库 cutils 的匿名共享内存访问接口实现在源码 system/core/libcutils/ashmem-dev.c 文件中。这个文件提供了五个 C 接口来访问 Ashmem 驱动程序,它们分别是:

  • ashmem_create_region
  • ashmempin_region
  • ashmem_unpin_region
  • ashmem_set_prot_region
  • ashmem_get_size_region

2.1 ashmem_create_region

/*
 * ashmem_create_region - creates a new ashmem region and returns the file
 * descriptor, or <0 on error
 *
 * `name' is an optional label to give the region (visible in /proc/pid/maps)
 * `size' is the size of the region, in page-aligned bytes
 */
int ashmem_create_region(const char *name, size_t size)
{
    int ret, save_errno;

    int fd = __ashmem_open();
    if (fd < 0) {
        return fd;
    }

    if (name) {
        char buf[ASHMEM_NAME_LEN] = {0};

        strlcpy(buf, name, sizeof(buf));
        ret = TEMP_FAILURE_RETRY(ioctl(fd, ASHMEM_SET_NAME, buf));
        if (ret < 0) {
            goto error;
        }
    }

    ret = TEMP_FAILURE_RETRY(ioctl(fd, ASHMEM_SET_SIZE, size));
    if (ret < 0) {
        goto error;
    }

    return fd;

error:
    save_errno = errno;
    close(fd);
    errno = save_errno;
    return ret;
}

  函数 ashmem_create_region 用来请求 Ashmem 驱动程序为应用程序创建一块匿名共享内存,其中参数 name 和 size 分别表示请求创建的匿名共享内存的名称和大小。请求Ashmem驱动程序创建一块匿名共享内存分三步来完成。

  1. 第一步是调用函数 open 打开设备文件 ASHMEM_DEVICE,即设备文件 /dev/ashmem,它的定义如下所示:

    #define ASHMEM_DEVICE "/dev/ashmem"
    

    调用函数 open 打开设备文件 /dev/ashmem 时,Ashmem 驱动程序的函数 ashmem_open 就会被调用主要是为应用程序创建一个 ashmem_area 结构体,用来描述一块匿名共享内存。打开了设备文件 /dev/ashmem 之后,就会得到一个文件描述符,接下来就可以通过这个文件描述符来访问前面请求 Ashmem 驱动程序创建的匿名共享内存

  2. 第二步是使用 IO 控制命令 ASHMEM_SET_NAME 来请求 Ashmem 驱动程序将前面所创建的匿名共享内存的名称修改为name

  3. 第三步是使用 IO 控制命令 ASHMEM_SET_SIZE 来请求 Ashmem 驱动程序将前面所创建的匿名共享内存的大小修改为 size

2.2 ashmem_pin_region

/*
	 fd:		前面打开设备文件 /dev/ashmem 所得到的一个文件描述符;
	 offset:	用来指定要锁定的内存块在其宿主匿名共享内存中的偏移地址
	 len:		用来指定要锁定的内存块在其宿主匿名共享内存中的长度
*/
int ashmem_pin_region(int fd, size_t offset, size_t len)
{
    struct ashmem_pin pin = { offset, len };

    int ret = __ashmem_is_ashmem(fd);
    if (ret < 0) {
        return ret;
    }

    return TEMP_FAILURE_RETRY(ioctl(fd, ASHMEM_PIN, &pin));
}

  函数 ashmem_pin_region 使用 IO 控制命令 ASHMEM_PIN 来请求 Ashmem 驱动程序锁定一小块匿名共享内存

2.3 ashmem_unpin_region

/*
	 fd:		前面打开设备文件 /dev/ashmem 所得到的一个文件描述符;
	 offset:	用来指定要解锁的内存块在其宿主匿名共享内存中的偏移地址
	 len:		用来指定要解锁的内存块在其宿主匿名共享内存中的长度
*/
int ashmem_unpin_region(int fd, size_t offset, size_t len)
{
    struct ashmem_pin pin = { offset, len };

    int ret = __ashmem_is_ashmem(fd);
    if (ret < 0) {
        return ret;
    }

    return TEMP_FAILURE_RETRY(ioctl(fd, ASHMEM_UNPIN, &pin));
}

  函数 ashmem_unpin_region 使用 IO 控制命令 ASHMEM_UNPIN 来请求 Ashmem 驱动程序解锁一小块匿名共享内存

2.4 ashmem_set_prot_region

/*
	 fd:		前面打开设备文件 /dev/ashmem 所得到的一个文件描述符;
	 prot:		指定要修改的访问保护位,它的取值为PROT_EXEC、PROTREAD、PROT_WRITE或其组合值;
*/
int ashmem_set_prot_region(int fd, int prot)
{
    int ret = __ashmem_is_ashmem(fd);
    if (ret < 0) {
        return ret;
    }

    return TEMP_FAILURE_RETRY(ioctl(fd, ASHMEM_SET_PROT_MASK, prot));
}

  函数 ashmem_set_prot_region 使用 IO 控制命令 ASHMEM_SET_PROT_MASK 来请求 Ashmem 驱动程序修改一块匿名共享内存的访问保护位。Ashmem 驱动程序中的函数 set_prot_mask 负责处理 IO 控制命令 ASHMEM_SET_PROT_MASK,它的实现如下所示:

static int set_prot_mask(struct ashmem_area *asma, unsigned long prot)
{
    int ret = 0;

    mutex_lock(&ashmem_mutex);

    /* the user can only remove, not add, protection bits */
    if (unlikely((asma->prot_mask & prot) != prot)) {
        ret = -EINVAL;
        goto out;
    }

    /* does the application expect PROT_READ to imply PROT_EXEC? */
    if ((prot & PROT_READ) && (current->personality & READ_IMPLIES_EXEC))
        prot |= PROT_EXEC;

    asma->prot_mask = prot;

out:
    mutex_unlock(&ashmem_mutex);
    return ret;
}

  Ashmem 驱动程序在创建一块匿名共享内存时,将它的访问保护位设置为 PROT_MASK,表示这块匿名共享内存具有可执行、读和写权限。此后,应用程序只能删除它的访问保护位,而不能增加它的访问保护位。因此,第8行的i语句首先检查要修改的访问保护位 prot 是否超出了目标匿名共享内存 asma 所允许的范围。

  有一种特殊情况,即当当前进程 current 的 personality 属性的 READIMPLIES_EXEC 位被设置为1时,第14行就会检查参数 prot 的 PROT_READ 位是否被设置为1。如果是,那么第15行就将它的 PROT_EXEC 位也设置为1,因为当一个进程的 personality 属性的 READ_IMPLIES_EXEC 位被设置为1时,就表示当它有权限读一块内存时,也隐含着它对该内存有执行权限。最后,第17行将目标匿名共享内存 asma 的访问保护位 prot_mask 设置为参数 prot 的值。

2.5 ashmem_get_size_region

/*
	 fd:		前面打开设备文件 /dev/ashmem 所得到的一个文件描述符;
*/
int ashmem_get_size_region(int fd)
{
    int ret = __ashmem_is_ashmem(fd);
    if (ret < 0) {
        return ret;
    }

    return TEMP_FAILURE_RETRY(ioctl(fd, ASHMEM_GET_SIZE, NULL));
}

  函数 ashmem_get_size_region 使用 IO 控制命令 ASHMEM_GET_SIZE 来请求 Ashmem 驱动程序返回块匿名共享内存的大小。Ashmem 驱动程序中的函数 ashmem_ioctl 负责处理 IO 控制命 ASHMEM_GET_SIZE,它的实现如下所示:

static long ashmem_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
    struct ashmem_area *asma = file->private_data;		//先找到要获得其大小的匿名共享内存 asma
    long ret = -ENOTTY;

    switch (cmd) {
    case ASHMEM_SET_NAME:
        ret = set_name(asma, (void __user *) arg);
        break;
    case ASHMEM_GET_NAME:
        ret = get_name(asma, (void __user *) arg);
        break;
    case ASHMEM_SET_SIZE:
        ret = -EINVAL;
        if (!asma->file) {
            ret = 0;
            asma->size = (size_t) arg;
        }
        break;
    case ASHMEM_GET_SIZE:
        ret = asma->size;								//再将它的大小size返回给调用者
        break;
    case ASHMEM_SET_PROT_MASK:
        ret = set_prot_mask(asma, arg);
        break;
    case ASHMEM_GET_PROT_MASK:
        ret = asma->prot_mask;
        break;
    case ASHMEM_PIN:
    case ASHMEM_UNPIN:
    case ASHMEM_GET_PIN_STATUS:
        ret = ashmem_pin_unpin(asma, cmd, (void __user *) arg);
        break;
    case ASHMEM_PURGE_ALL_CACHES:
        ret = -EPERM;
        if (capable(CAP_SYS_ADMIN)) {
            struct shrink_control sc = {
                .gfp_mask = GFP_KERNEL,
                .nr_to_scan = 0,
            };
            ret = ashmem_shrink(&ashmem_shrinker, &sc);
            sc.nr_to_scan = ret;
            ashmem_shrink(&ashmem_shrinker, &sc);
        }
        break;
    }

    return ret;
}

至此,分析完成运行时库 cutils 中的匿名共享内存的C访问接口了。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
在组织上,本书将上述内容划分为初识Android系统Android专用驱动系统Android应用程序框架三大篇。初识Android系统篇介绍了参考书籍、基础知识以及实验环境搭建;Android专用驱动系统篇介绍了Logger日志驱动程序、Binder进程间通信驱动程序以及Ashmem匿名共享内存驱动程序;Android应用程序框架篇从组件、进程、消息以及安装四个维度对Android应用程序的框架进行了深入的剖析。 通过上述内容及其组织,本书能使读者既能从整体上把握Android系统的层次结构,又能从细节上掌握每一个层次的要点。 在内容上,本书结合使用情景,全面、深入、细致地分析Android系统的源代码,涉及到Linux内核层、硬件抽象层(HAL)、运行时层(Runtime)、应用程序框架层(Application Framework)以及应用程序层(Application)。, 在组织上,本书将上述内容划分为初识Android系统Android专用驱动系统Android应用程序框架三大篇章。初识Android系统篇介绍了参考书籍、基础知识以及实验环境搭建;Android专用驱动系统篇介绍了Logger日志驱动程序、Bind er进程间通信驱动程序以及Ashmem匿名共享内存驱动程序;Android应用程序框架篇从组件、进程、消息以及安装四个维度来对Android应用程序的框架进行了深入的剖析。, 通过上述内容及其组织,本书能使读者既能从整体上把握Android系统的层次结构,又能从细节上去掌握每一个层次的要点。
在组织上,本书将上述内容划分为初识Android系统Android专用驱动系统Android应用程序框架三大篇。初识Android系统篇介绍了参考书籍、基础知识以及实验环境搭建;Android专用驱动系统篇介绍了Logger日志驱动程序、Binder进程间通信驱动程序以及Ashmem匿名共享内存驱动程序;Android应用程序框架篇从组件、进程、消息以及安装四个维度对Android应用程序的框架进行了深入的剖析。 通过上述内容及其组织,本书能使读者既能从整体上把握Android系统的层次结构,又能从细节上掌握每一个层次的要点。 在内容上,本书结合使用情景,全面、深入、细致地分析Android系统的源代码,涉及到Linux内核层、硬件抽象层(HAL)、运行时层(Runtime)、应用程序框架层(Application Framework)以及应用程序层(Application)。, 在组织上,本书将上述内容划分为初识Android系统Android专用驱动系统Android应用程序框架三大篇章。初识Android系统篇介绍了参考书籍、基础知识以及实验环境搭建;Android专用驱动系统篇介绍了Logger日志驱动程序、Bind er进程间通信驱动程序以及Ashmem匿名共享内存驱动程序;Android应用程序框架篇从组件、进程、消息以及安装四个维度来对Android应用程序的框架进行了深入的剖析。, 通过上述内容及其组织,本书能使读者既能从整体上把握Android系统的层次结构,又能从细节上去掌握每一个层次的要点
### 回答1: Android C的共享内存是一种高效的IPC机制,它允许不同的进程共享内存区域,从而实现数据共享和数据传输。在Android系统中,使用共享内存有两种基本方法:POSIX共享内存Ashmem。 POSIX共享内存(shm_open系统调用)是基于文件的IPC机制,它可以在不同的进程间共享文件系统中的内存块。在使用该方法时,首先创建并打开一个共享内存对象以便其他进程能够在其中写入或读取数据。与普通文件不同的是,该对象可以被多个进程同时访问,从而实现内存共享和数据传输。 AshmemAndroid专有的共享内存机制,它通过匿名内存映射(mmap系统调用)来创建共享内存,使多个进程可以共享相同的内存区域。在使用Ashmem时,首先在一个进程中分配一块内存区域,并将其标记为共享内存。其他进程可以通过Binder接口来获取该内存区域所对应的Ashmem文件描述符,并进一步映射内存区域,以便共享数据。 正如所见,Android C的共享内存机制提供了一种高效的IPC方法,可以在不同的进程之间实现数据共享和数据传输。但是由于共享内存存在并发访问、内存泄露等问题,因此在应用中使用时需要格外小心。 ### 回答2: Android C共享内存是一种在Android系统中用于不同进程间共享数据的机制。在多进程应用程序中,进程之间共享数据允许各个进程共同访问数据,从而提高系统的整体性能。C共享内存实现了这种数据共享的方式,允许多个进程可以同步地访问相同的内存区域,从而实现数据共享。 C共享内存操作需要用到管道和信号量等Linux中的IPC技术。进程可以通过信号量来控制对共享内存区域的访问,从而实现数据同步。同时,通过管道机制,同步地向共享内存区域写入和读出数据。在Android开发中,通常会使用NDK和底层C语言来实现共享内存操作,可以对共享内存区域进行读写操作和管理。 通常情况下,在Android的多进程应用程序中,可以使用C共享内存来实现不同进程之间的数据共享,从而提高应用程序的整体性能和响应速度。C共享内存也可以被用于进程间的通信,例如在游戏和音视频应用程序中,可以使用共享内存来实现不同进程的交互与协作。总的来说,Android C共享内存提供了一种能够优化应用程序性能和提高用户体验的底层机制。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小馬佩德罗

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值