Ashmem anonymous shared memory subsystem analysis of Android system (2) - Ashmem access interface of runtime library cutils

statement

1 Architecture of Ashmem

  The Ashmem anonymous shared memory subsystem implemented by the Android system is used to share data between applications. Ashmem, like the shared memory implemented by the traditional Linux system, is implemented based on the temporary file system tmpfs provided by the kernel, but Ashmem manages the memory blocks more finely. The application program can dynamically divide an anonymous shared memory into several small blocks. When these small blocks of memory are no longer needed, they can be reclaimed by the memory management system. Through this dynamic, divide-and-conquer memory management method, the Android system can effectively use the system memory and adapt to the mobile device environment with small memory.

  The anonymous shared memory system is based on the Ashmem driver. All anonymous shared memory in the system is allocated and managed by the Ashmem driver. The Android system provides a C/C++ call interface at the Native layer and a Java call interface at the Framework layer.

  • In the Framework layer, two C++ classes MemoryBase and MemoryHeapBase, and a Java class MemoryFile are provided to use anonymous shared memory.
  • In the runtime library cutils, three C functions ashmem_create_region, ashmem_pin_region and ashmem_unpin_region are mainly provided to access the Ashmem driver.

  When the Ashmem driver starts, it will create a /dev/ashmem device file, so that the anonymous shared memory interface in the runtime library cutils can access the Ashmem driver through the file operation functions open and ioctl.
insert image description here

  The traditional Linux system uses an integer to mark a piece of shared memory, while the Android system uses a file descriptor to mark a piece of anonymous shared memory. Using a file descriptor to describe an anonymous shared memory has two advantages:

  1. It can be conveniently mapped into the address space of the process, allowing direct access to its contents;
  2. You can use the Binder inter-process communication mechanism to transfer this file descriptor, so as to share an anonymous memory between different applications.

  The Binder inter-process communication mechanism uses a Binder object of type BINDER_TYPE_FD to describe a file descriptor. When the Binder driver finds that the inter-process communication data contains such a Binder object, it will copy the corresponding file descriptor to the target process. , so as to share the same file in two processes.

2 Ashmem access interface of runtime library cutils

The anonymous shared memory access interface of the runtime library cutils is implemented in the source code system/core/libcutils/ashmem-dev.c file. This file provides five C interfaces to access the Ashmem driver, they are:

  • 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;
}

  The function ashmem_create_region is used to request the Ashmem driver to create an anonymous shared memory for the application, where the parameters name and size indicate the name and size of the anonymous shared memory requested to be created respectively. Requesting the Ashmem driver to create an anonymous shared memory is done in three steps.

  1. The first step is to call the function open to open the device file ASHMEM_DEVICE, that is, the device file /dev/ashmem, and its definition is as follows:

    #define ASHMEM_DEVICE "/dev/ashmem"
    

    When the function open is called to open the device file /dev/ashmem, the function ashmem_open of the Ashmem driver will be called mainly to create an ashmem_area structure for the application to describe an anonymous shared memory. After opening the device file /dev/ashmem, you will get a file descriptor, and then you can use this file descriptor to access the anonymous shared memory created by the previously requested Ashmem driver .

  2. The second step is to use the IO control command ASHMEM_SET_NAME to request the Ashmem driver to change the name of the previously created anonymous shared memory to name .

  3. The third step is to use the IO control command ASHMEM_SET_SIZE to request the Ashmem driver to modify the size of the previously created anonymous shared memory to 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));
}

  The function ashmem_pin_region uses the IO control command ASHMEM_PIN to request the Ashmem driver to lock a small anonymous shared memory .

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));
}

  The function ashmem_unpin_region uses the IO control command ASHMEM_UNPIN to request the Ashmem driver to unlock a small anonymous shared memory .

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));
}

  The function ashmem_set_prot_region uses the IO control command ASHMEM_SET_PROT_MASK to request the Ashmem driver to modify the access protection bit of an anonymous shared memory . The function set_prot_mask in the Ashmem driver is responsible for processing the IO control command ASHMEM_SET_PROT_MASK, and its implementation is as follows:

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;
}

  When the Ashmem driver creates an anonymous shared memory, it sets its access protection bit to PROT_MASK, indicating that this anonymous shared memory has executable, read and write permissions. Thereafter, an application can only remove its access protection bit, not increase it. Therefore, the i statement on line 8 first checks whether the access protection bit prot to be modified exceeds the range allowed by the target anonymous shared memory asma.

  There is a special case, that is, when the READIMPLIES_EXEC bit of the personality attribute of the current process is set to 1, line 14 checks whether the PROT_READ bit of the parameter prot is set to 1. If it is, then line 15 sets its PROT_EXEC bit to 1, because when the READ_IMPLIES_EXEC bit of a process's personality attribute is set to 1, it means that when it has permission to read a block of memory, it also implies it Execute permission on this memory. Finally, line 17 sets the access protection bit prot_mask of the target anonymous shared memory asma to the value of the parameter 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));
}

  The function ashmem_get_size_region uses the IO control command ASHMEM_GET_SIZE to request the Ashmem driver to return the size of the block anonymous shared memory . The function ashmem_ioctl in the Ashmem driver is responsible for processing the IO control command ASHMEM_GET_SIZE, and its implementation is as follows:

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;
}

So far, the analysis has completed the C access interface of the anonymous shared memory in the runtime library cutils.

Guess you like

Origin blog.csdn.net/Xiaoma_Pedro/article/details/131078263
Recommended