Android native Interprocess Communication Instance-binder Combined with Shared Memory

Keywords: Android Linux socket shell

Under the driver directory of Android source code, there will be related implementation source codes of shared memory. The directory is: kernel drivers staging android ashmem.c. But this article is not about the principle of Android shared memory, but about how to use it.

  1. 

In linux, different processes have their own independent memory space. In 32-bit operating system, it seems that the size of memory that a process can use is 4G. In general, different processes can not use their own memory data.

Of course, there are many ways to share data between different processes, such as the communication binder between processes, socket and so on, but android has come up with a concept of shared memory, in order that different processes can operate on the same piece of memory data together, such as process 1001 writing data "hello world" into a shared memory addr. Process 1009 reads out "hello world" from the shared memory addr, and can also write data "hello china" to the shared memory addr. This is the basic concept of sharing a piece of memory (in other words, plowing a field together). Be careful enough, if you don't know the comment area clearly.

Note: It seems that the data implementation of binder transmission is similar to that of shared memory, and readers can understand it on their own.

  

  2.

First, let's talk about the idea of writing the program later.

First, think about how to operate after the code compiles two executable files. Open two terminals and enter the device adb shell. The first terminal executes process a and the second terminal executes process b. After a series of data is input into process a, the data can be read out in process b (and the data can be rewritten, and the reader can add this part of the function on his own).

Then think about the way to achieve it.

Process a: 1. Create shared memory, set the size of shared memory, and then get an fd. 2. Get the shared memory address. 3. Read the address data first, and then write the data to the address. 4. Send FD through binder to processes that need to be used.

Process b: 1. Read FD through binder. 2. Get the shared memory address with fd. 3. Read the shared memory data and write the data to the address.

Note: linux is all files, so the file descriptor fd is very important.   

  

  3. 

  3.1

After clearing your mind, you can start to write code (android.mk can refer to the previous article), process a, named mysharememory_a code as follows:

  

#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <linux/ashmem.h>
#include <sys/mman.h>
#include <sys/ioctl.h>
#include <stddef.h>
#include <linux/ipc.h>
#include <linux/shm.h>

#include <binder/IServiceManager.h>
#include <binder/IPCThreadState.h>
#include <binder/Parcel.h>
#include <binder/IInterface.h>

#define DEVASHMEM "/dev/ashmem"
#define SHNAME "hellomemory"
#define MAXBUFSIZE 1024
#define TRANSFDCODE 1000
#define WRITEDATACODE 1001

using namespace android;

int  main(int argc, char *argv[])
{
    int fd = open(DEVASHMEM, O_RDWR);
    if(fd < 0)
    {
        return -1;
    }

    int ret = ioctl(fd, ASHMEM_SET_NAME, SHNAME);
    if(ret < 0){
        close(fd);
        return -1;
    }

    char *get_sh_addr_write = NULL;    
    ret = ioctl(fd, ASHMEM_SET_SIZE, MAXBUFSIZE);
    if(ret < 0){
        close(fd);
        return -1;
    }

    get_sh_addr_write = (char*)mmap(NULL, MAXBUFSIZE , PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
    if(NULL == get_sh_addr_write)
    {
        return -1;
    }
    sp<IServiceManager> sm = defaultServiceManager();
    sp<IBinder> binder = sm->checkService(String16("mybindertag"));
    Parcel data, reply;

    data.writeDupFileDescriptor(fd);
    binder->transact(TRANSFDCODE, data, &reply);
    
    char input_data[MAXBUFSIZE] = {0};
    while(1)
    {
        printf("read share memory buf is %s\n", get_sh_addr_write);
        printf("please input data to buf :");
        scanf("%s", input_data);
        getchar();

        strcpy(get_sh_addr_write,input_data);
        binder->transact(WRITEDATACODE, data, &reply);
    }
    return ret;
}

  3.2

The mysharememory_b code is as follows:

  

#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <linux/ashmem.h>
#include <sys/mman.h>
#include <sys/ioctl.h>
#include <stddef.h>
#include <linux/ipc.h>
#include <linux/shm.h>

#include <binder/IServiceManager.h>
#include <binder/IPCThreadState.h>
#include <binder/Parcel.h>
#include <binder/IInterface.h>

#define DEVASHMEM "/dev/ashmem"
#define SHNAME "hellomemory"
#define MAXBUFSIZE 1024
#define TRANSFDCODE 1000
#define WRITEDATACODE 1001

using namespace android;

int g_sh_fd = 0;
class MyBinderService : public BBinder{
            status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
            {
                int ret;
                char *get_sh_addr_read = NULL;
                int get_sh_size;
            
                printf("songsong!! **** onTransact ***** code = %d \n",code);
                switch(code)
                {
                    case TRANSFDCODE:
                        g_sh_fd = data.readFileDescriptor();
                        break;

                    case WRITEDATACODE:
                        get_sh_size = ioctl(g_sh_fd, ASHMEM_GET_SIZE,NULL);
                        if(get_sh_size > 0)
                        { 
                            get_sh_addr_read = (char*)mmap(NULL, get_sh_size, PROT_READ | PROT_WRITE, MAP_SHARED, g_sh_fd, 0);
                        }
                        else
                        {
                            printf("mmap failed %d\n", get_sh_size);
                            return -1;     
                        }
                        printf("what is in the share memory: %s\n", get_sh_addr_read);
                        break;
                    default:

                        break;
                }
                return NO_ERROR;
            }
};

int  main(int argc, char *argv[])
{
    defaultServiceManager()->addService(String16("mybindertag"), new MyBinderService());

    sp<ProcessState> proc(ProcessState::self());
    ProcessState::self()->startThreadPool();
    IPCThreadState::self()->joinThreadPool();
    return 0;
}

 

  3.3

The recycle closure code can be optionally added to mysharememory_b as follows:

  

    int ret;
    ret = munmap((void*)get_sh_addr_read, get_sh_size);
    if(ret == -1)
    {
        return -1;
    }
    
    ret = close(g_sh_fd);
    if(ret == -1)
    {
        return -1;
    }

 

  

 

  3.4 

Demo screenshots:

  

 

 

4. In order to fool comments, I stopped explaining the code and felt tired. However, you can copy the code directly to compile and execute, and then through debugging to understand the essence of the code, it is no problem.

Posted by ziegel on Fri, 09 Aug 2019 04:13:40 -0700