欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

Android native进程间通信实例-binder结合共享内存

程序员文章站 2022-03-21 21:36:02
在android源码的驱动目录下,一般会有共享内存的相关实现源码,目录是:kernel\drivers\staging\android\ashmem.c。但是本篇文章不是讲解android共享内存的功能实现原理,而是讲怎么运用它。 1. 在linux中,不同进程间拥有自己独立的内存空间,32位操作系 ......

  在android源码的驱动目录下,一般会有共享内存的相关实现源码,目录是:kernel\drivers\staging\android\ashmem.c。但是本篇文章不是讲解android共享内存的功能实现原理,而是讲怎么运用它。

  1. 

  在linux中,不同进程间拥有自己独立的内存空间,32位操作系统中好像一个进程能用的内存大小是4g吧。而且一般不同进程间不能够互相使用各自内存的数据。

  当然不同进程间共享数据方法很多,比如之前说的进程间通信binder,socket等等,不过android出了一个共享内存的概念,为的是不同进程间能够共同操作同一块内存数据,比如进程1001往一块共享内存addr里面写数据“hello world”,进程1009往这块共享内存addr读取出“hello world”,也能够往这块共享内存addr写数据“hello china”。这就是共同享用了一块内存的基本概念了(说白了就是同耕一块田)。讲的够仔细了吧,如果不清楚评论区见。

  注意:好像binder传输的数据实现也是类似于共享内存,读者可以自行去了解。

  

  2.

  先说一下等会写程序的思路:

  首先想想代码编译出两个可执行文件后如何操作,打开两个终端,都进入设备adb shell,第一个终端执行进程a,第二个终端执行进程b。在进程a输入一串数据后,在进程b中可以读出这段数据(也能够改写这段数据,读者可以自行添加这部分功能)。

  然后再想想实现的方式,

  进程a:1. 创建共享内存,设置共享内存大小,这时会得到一个fd。2. 获取共享内存地址。3. 先读取地址数据,然后往地址写入数据。4. 把fd通过binder发送给需要使用的进程。

  进程b:1. 通过binder读取到fd。2. 用fd获取共享内存地址。3. 读取共享内存数据,然后往地址写入数据。

  注意:linux一切皆文件,所以文件描述符fd很重要。  

  

  3. 

  3.1

  捋清思路后,就可以开始写代码了(android.mk的编写可以参考之前的文章),进程a,命名为mysharememory_a代码如下:

  

#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

   mysharememory_b代码如下:

  

#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

  回收关闭部分代码可选择添加在mysharememory_b中,如下:

  

    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 

  演示截图:

  Android native进程间通信实例-binder结合共享内存

 

 

   4. 为了骗取评论,我不再解释代码,心累。不过您可以把代码直接拷贝去编译执行,再通过调试去理解代码的精髓,也是没问题的。