Android匿名共享(ashmem)内存使用分析

         最近遇到个问题,需要使用共享内存。之前看audioTrack与audioFlinger部份时就看到共存内存的分配与同步,但是没有细研究这部份。这几天细看了下流程,发现使用起来挺简单的。

         ashmem是android封装提供的一种共享内存实现方式。驱动会在/dev/下面注册ashmem字符设备,供上层做文件操作(ashmem驱动后面分析)。

                                       

         按照一般理解(如camera videobuffer操作), 既然内核给分配了内存,那上层用户空间要使用的话,要么通过read/write文件操作接口,在用户层与内核层作拷贝;要么就是mmap映射,然后直接使用。ashmem就是直接mmap到用户空间的。大概使用原理就是server、client一端打开/dev/ashmem得到fd句柄,并mmap得到共享内存地址;再把fd与size传到对应的client、server,另一端再mmap下也得到了此地址,这样二边就可以直接操作各自mmap得到的地址就行了。当然从server、client传fd到client、server这里面也包含了binder通信。

                            

 

         android对ashemem的封装很简单(/system/core/libcutils/Ashmem-dev.c),提供了以下几个接口。一般我们只取ashmem_create_region,得到fd(既然是fd,那必然有close,但android并没有提供,因为只需要close(fd)就行了。那还有一个问题,既然server与client端都是fd,按理解那这2个fd的值应该是不同的,肯定会dup)。

                            ashmem_create_region

                            ashmem_set_prot_region

                            ashmem_pin_region

                            ashmem_unpin_region

                            ashmem_get_size_region

         完全可以在这几个封装接口的基础上直接使用。除此之外android framework还封了一个IMemory类给上层操作。

 

 

1、直接操作Ashmem-dev.c接口demo

         demo目录如下所示:

                           

                           

                           

 

       demo由server与client及接口组成,共享内存可由server产生也可由client去申请。为了验证共享内存的使用及正确性,server与client都可以读写此共享内存。这样就产生了同步问题。为了简化起见,弄了个ICallback类,在server、client操作完后,callback对方,这时对方才操作shareMemory,这样就不需要花太多精力与时间去弄同步部份。(典型同步见audioTrack与audioFlinger之间的cblk同步)

 

具体代码如下:

ITestBinder.h

#ifndef _ITESTBINDER_H_

#define _ITESTBINDER_H_

 

#include <binder/IInterface.h>  

#include <utils/String8.h>

#include "ICallback.h"

 

namespace android {

              enum {

                            msg_mem = 0x0000f000,

                            msg_mem_client_not_prepare = msg_mem + 0x01,

                            msg_mem_client_prepared = msg_mem + 0x02,

 

                            msg_mem_client_req_server_alloc = msg_mem + 0x10,

 

                            msg_mem_server_not_prepare = msg_mem + 0x020,

                            msg_mem_server_prepared = msg_mem + 0x021,

                           

                            msg_data = 0x0000f100,

                            msg_data_client_write = msg_data +  0x01,

                            msg_data_server_read = msg_data +  0x02,

                             

                            msg_diconnect = 0x0000ff00,

              };

 

              struct testShareMem

              {

                            int fd;

                            int size;

              };

 

              class ITestBinder : public IInterface

              {

              public:

                            DECLARE_META_INTERFACE(TestBinder);

                            virtual int setCallback(const sp<ICallback>& callback) = 0;

                            virtual int notify(int msg, struct testShareMem *mem) = 0;

              };

 

              class BnTestBinder : public BnInterface<ITestBinder>

              {

              public:

                            virtual status_t onTransact( uint32_t code,

                                                                                                                              const Parcel& data,

                                                                                                                              Parcel* reply,

                                                                                                                              uint32_t flags = 0);

              };

}

 

#endif  //_ITESTBINDER_H_

 

ITestBinder.cpp

#define LOG_NDEBUG 0

#define LOG_TAG "ITestBinder"

#include <utils/Log.h>

 

#include <binder/Parcel.h>

#include <binder/IInterface.h>

#include "ITestBinder.h"

 

namespace android {

              enum {

                            CONNECT = 0,

                            NOTIFY,

                            SET_CALLBACK

              };

 

              class BpTestBinder : public BpInterface<ITestBinder>

              {

              public:

                            BpTestBinder(const sp<IBinder>& impl) : BpInterface<ITestBinder>(impl)

                            {

                            }

 

                            virtual int notify(int msg, struct testShareMem *mem)

                            {

                                          //ALOGD("%s", __func__);

                                          Parcel data,reply;

                                          data.writeInt32(msg);

 

                                          if (msg == msg_mem_client_prepared)

                                          {

                                                        if (mem != NULL)

                                                        {

                                                                      data.writeFileDescriptor(mem->fd);

                                                                      data.writeInt32(mem->size);

                                                        }

                                          }

                                          else if (msg == msg_mem_client_req_server_alloc)

                                          {

                                                        if (mem != NULL)

                                                        {

                                                                      data.writeInt32(mem->size);

                                                        }

                                          }

 

                                          remote()->transact(NOTIFY, data, &reply);

                                          if (msg == msg_mem_client_req_server_alloc)

                                          {

                                                        int ret = reply.readInt32();

                                                        if (!ret)

                                                        {

                                                                      int fd = reply.readFileDescriptor();

                                                                      int size = reply.readInt32();

                                                                      ALOGD("bpTestBinder get: fd=%d, size=%d", fd, size);

                                                                      mem->fd = fd;

                                                                      mem->fd = dup(mem->fd); //why must dup???

                                                                      mem->size = size;

                                                        }

                                                        return ret;

                                          }

                                          else

                                          {

                                                        return reply.readInt32();

                                          }

                            }

 

                            virtual int setCallback(const sp<ICallback>& callback)

                            {

                                          //ALOGD("%s", __func__);

                                          Parcel data, reply;

                                          //data.writeStrongBinder(callback->asBinder());

                                          data.writeStrongBinder(IInterface::asBinder(callback));

                                          remote()->transact(SET_CALLBACK, data, &reply);

                                          return reply.readInt32();

                            }

              };

 

              IMPLEMENT_META_INTERFACE(TestBinder, "android.test.ITestBinder");

 

 

///////////////////////////////

              status_t BnTestBinder::onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)

              {

                            //ALOGD("%s, code=%d\n", __func__, code);

                            switch (code)

                            {

                                          case SET_CALLBACK:

                                          {

                                                        //ALOGD("%s, SET_CALLBACK", __func__);

                                                        sp<ICallback> callback = interface_cast<ICallback>(data.readStrongBinder());

                                                        reply->writeInt32(setCallback(callback));

                                                        return NO_ERROR;

                                          }

 

                                          case NOTIFY:

                                          {

                                                        //CHECK_INTERFACE(ITestBinder, data, reply); 

                                                        //ALOGD("%s, NOTIFY", __func__);

                                                        int msg = data.readInt32();

 

                                                        struct testShareMem *mem = new testShareMem;

 

                                                        if (msg == msg_mem_client_prepared)

                                                        {

                                                                      if (data.dataAvail() > 0)

                                                                      {

                                                                                    mem->fd = data.readFileDescriptor();

                                                                                    mem->size = data.readInt32();

                                                                      }

                                                                      reply->writeInt32(notify(msg, mem));

                                                        }

                                                        else if (msg == msg_mem_client_req_server_alloc)

                                                        {

                                                                      mem->size = data.readInt32();

                                                                      int ret = notify(msg, mem);

                                                                      if (!ret)

                                                                      {

                                                                                    reply->writeInt32(0); //success

                                                                                    reply->writeFileDescriptor(mem->fd);

                                                                                    reply->writeInt32(mem->size);

                                                                      }

                                                        }

                                                        else

                                                        {

                                                                      reply->writeInt32(notify(msg, mem));

                                                        }

                                                        free(mem);

                                                        return NO_ERROR;

                                          }

 

                                          default:

                                          {

                                                        //ALOGD("%s, default onTransact handle\n", __func__);

                                                        return BBinder::onTransact(code, data, reply, flags);

                                          }

                            }

              }

 

 

ICallback.h

#ifndef _ICALLBACK_H_   

#define _ICALLBACK_H_

 

#include <binder/IInterface.h>

 

namespace android {

              class ICallback : public IInterface

              {

              public: 

                            DECLARE_META_INTERFACE(Callback);

                            virtual int notifyCallback(int msg) = 0;

              };

 

              class BnCallback : public BnInterface<ICallback>

              {

              public:

                            virtual status_t onTransact( uint32_t code,

                                                                                                                const Parcel& data,

                                                                                                                Parcel* reply,

                                                                                                                uint32_t flags = 0);

              };

}

 

#endif //_ICALLBACK_H_

 

ICallback.cpp

#define LOG_NDEBUG 0

#define LOG_TAG "ICallback"

#include <utils/Log.h>

 

#include <binder/Parcel.h>

#include <binder/IInterface.h>

 

#include "ICallback.h"

 

namespace android{

 

              enum { 

                            NOTIFY_CALLBACK = 0,

              };

 

              class BpCallback : public BpInterface<ICallback>

              {

              public:

                            BpCallback(const sp<IBinder>& impl) : BpInterface<ICallback>(impl)

                            {

                            }

 

                            virtual int notifyCallback(int msg)

                            {

                                          //ALOGD("%s", __func__);

                                          Parcel data,reply;

                                          data.writeInt32(msg);

                                          remote()->transact(NOTIFY_CALLBACK, data, &reply);

                                          return reply.readInt32();

                            }

              };

 

              IMPLEMENT_META_INTERFACE(Callback, "android.test.ICallback");

 

 

////////////////////////////////////////////////

              status_t BnCallback::onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)

              {

                            //ALOGD("%s, code=%d\n", __func__, code);

                            switch (code)

                            {

                                          case NOTIFY_CALLBACK:

                                          {

                                                        //CHECK_INTERFACE(ICallback, data, reply);                                     

                                                        reply->writeInt32(notifyCallback((int) data.readInt32()));

                                                        return NO_ERROR;

                                          }

 

                                          default:

                                          {

                                                        return BBinder::onTransact(code, data, reply, flags);

                                          }

                            }

              }

}

 

Android.mk

include $(call all-subdir-makefiles)

 

TestService.cpp

#define LOG_NDEBUG 0

#define LOG_TAG "TestService"

#include <utils/Log.h>

 

#include <sys/mman.h>

#include <cutils/ashmem.h>

 

#include <binder/IPCThreadState.h>

#include <binder/IServiceManager.h>

#include <binder/Parcel.h>

#include <utils/String8.h>

#include "TestService.h"

 

 

namespace android {

              TestService::TestService()

              {

                            mShareClient.mShareMem.fd = -1;

                            mShareClient.mShareMem.size = 0;

                            mShareClient.mMapAddr = MAP_FAILED;

 

                            mShareMemHost.mShareMem.fd = -1;

                            mShareMemHost.mShareMem.size = 0;

                            mShareMemHost.mMapAddr = MAP_FAILED;

              }

 

              TestService::~TestService()

              {

                            if (mShareClient.mShareMem.fd > 0)

                            {

                                          close(mShareClient.mShareMem.fd);

                            }

                            if (mShareClient.mMapAddr != MAP_FAILED)

                            {

                                          munmap(mShareClient.mMapAddr, mShareClient.mShareMem.size);

                            }

 

              //free host

                            if (mShareMemHost.mShareMem.fd > 0)

                            {

                                          close(mShareMemHost.mShareMem.fd);

                            }

                            if (mShareMemHost.mMapAddr != MAP_FAILED)

                            {

                                          munmap(mShareMemHost.mMapAddr, mShareMemHost.mShareMem.size);

                            }

              }

             

              int TestService::setCallback(const sp<ICallback>& cb)

              {

                            //ALOGD("%s", __func__);

                            mCallback = cb;

                            return 0;

              }

             

              int TestService::notify(int msg, /*const*/ struct testShareMem *mem)

              {

                            //ALOGD("%s", __func__);

 

                            switch (msg)

                            {

                            case msg_mem_client_prepared:

                                          {

                                                        ALOGD("get client share mem: fd=%d, size=%d", mem->fd, mem->size);

                                                        //int fd = dup(mem->fd);

                                                        int fd = mem->fd;

                                                        mShareClient.mShareMem.size = mem->size;

                                                        mShareClient.mMapAddr = mmap(0, mShareClient.mShareMem.size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);

                                                        if (mShareClient.mMapAddr == MAP_FAILED)

                                                        {

                                                                      ALOGE("mmap fail!!");

                                                                      close(fd);

                                                                      //return -1;

                                                        }

                                                        else

                                                        {

                                                                      mShareClient.mShareMem.fd = fd;

                                                        }

                                                        ALOGD("mmaped mem: %p", mShareClient.mMapAddr);

                                          }

                                          break;

                           

                            case msg_data_client_write:

                                          {

                                                        ALOGD("get client data msg:");

                                                        if (mShareClient.mMapAddr != MAP_FAILED)

                                                        {

                                                                      ALOGD("                        %s", (char *)mShareClient.mMapAddr);

                                                                      sprintf((char *)mShareClient.mMapAddr, "%s", "service write: 1234567890");

                                                        }

                                                        else if (mShareMemHost.mMapAddr != MAP_FAILED)

                                                        {

                                                                      ALOGD("                        %s", (char *)mShareMemHost.mMapAddr);

                                                                      sprintf((char *)mShareMemHost.mMapAddr, "%s", "2018 123456789");

                                                        }

 

                                                        sp<ICallback> cb = mCallback;

                                                        if (cb != 0)

                                                        {

                                                                      cb->notifyCallback(msg_data_server_read);

                                                        }

                                          }

                                          break;

 

                            case msg_mem_client_req_server_alloc:

                                          {

                                                        if (mem == NULL || mem->size <=0)

                                                        {

                                                                      ALOGW("client must send right param go get share mem fd!!");

                                                                      return -1;

                                                        }

                                                        ALOGD("client request server to alloc share mem, size:%d!!", mem->size);

 

                                                        int size = mem->size;

                                                        int fd = ashmem_create_region("test_ash_mem_share_host", size);

                                                        mShareMemHost.mShareMem.fd = fd;

                                                        mShareMemHost.mMapAddr = mmap(0, size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);

                                                        if (mShareMemHost.mMapAddr == MAP_FAILED)

                                                        {

                                                                      ALOGE("mmap(fd=%d, size=%u) failed (%s)", fd, uint32_t(size), strerror(errno));

                                                                      close(fd);

                                                                      mShareMemHost.mShareMem.fd = -1;

                                                                      return -errno;

                                                        }

                                                        mShareMemHost.mShareMem.size = size;

                                                        ALOGD("malloc share_mem, fd=%d, size=%d", fd, mShareMemHost.mShareMem.size);

                                                        ALOGD("mmap addr: %p", mShareMemHost.mMapAddr);

                                                       

                                                        /*sp<ICallback> cb = mCallback;

                                                        if (cb != 0)

                                                        {

                                                                      cb->notifyCallback(msg_mem_server_prepared);

                                                        }*/

 

                                                        mem->fd = mShareMemHost.mShareMem.fd;

                                                        mem->size = mShareMemHost.mShareMem.size;

                                          }

                                          break;

                                         

                            case msg_diconnect:

                                          {

                                                        ALOGD("client disconnect!!");

                                          }

                                          break;

 

                            default:

                                          break;

                            }

                           

                            return 0;

              }

}

 

TestService.h

#ifndef _TEST_SERVICE_H_

#define _TEST_SERVICE_H_

 

#include <binder/BinderService.h>

#include "../ITestBinder.h"

#include "../ICallback.h"

 

namespace android {

              typedef struct shareMemory

              {

                            void *mMapAddr;

                            struct testShareMem mShareMem;

              }severShareMemory_t;

 

              class TestService : public BinderService<TestService>,  public BnTestBinder

              {

                            friend class BinderService<TestService>;

              public:

                            TestService();

                            virtual ~TestService();

 

                            static char const* getServiceName()

                            {

                                          return "test.ITestService";

                            }

 

                            virtual int notify(int msg, /*const*/ struct testShareMem *mem);

                            virtual int setCallback(const sp<ICallback>& callback);

 

              private:

                            severShareMemory_t mShareClient;

                            sp<ICallback> mCallback;

                            severShareMemory_t mShareMemHost;

              };

}

 

#endif  //_TEST_SERVICE_H_

 

main.cpp

#define LOG_NDEBUG 0

#define LOG_TAG "server_main"

#include <utils/Log.h>

 

#include <binder/IPCThreadState.h>

#include <binder/ProcessState.h>

#include <binder/IServiceManager.h>

 

#include "TestService.h"

 

using namespace android;

 

 

int main(int argc, char** argv)

{

              sp<ProcessState> proc(ProcessState::self());

              sp<IServiceManager> sm = defaultServiceManager();

 

              ALOGI("ServiceManager: %p", sm.get());

              TestService::instantiate();

 

              ProcessState::self()->startThreadPool();

              IPCThreadState::self()->joinThreadPool();

              return 0;

}

 

Android.mk

LOCAL_PATH:= $(call my-dir)

 

include $(CLEAR_VARS) 

 

LOCAL_SRC_FILES:= \

    main.cpp \

    TestService.cpp \

    ../ITestBinder.cpp \

    ../ICallback.cpp

 

LOCAL_SHARED_LIBRARIES := \

    libcutils \

    libutils \

    liblog \

    libbinder

 

LOCAL_MODULE:= test_mem_server_4

LOCAL_MODULE_TAGS := eng

include $(BUILD_EXECUTABLE)

 

TestClient.cpp

#define LOG_NDEBUG 0

#define LOG_TAG "TestClient"

#include <sys/mman.h>

#include <utils/Log.h>

#include <binder/IServiceManager.h>

#include <cutils/ashmem.h>

#include "TestClient.h"

 

namespace android {

              int TestClient::Callback::notifyCallback(int msg)

              {

                            switch (msg)

                            {

                            case msg_data_server_read:

                                          ALOGD("get service read echo back:");

                                          ALOGD("                        %s", (char *)(tc->getCurShareMemory()->mMapAddr));

                                          break;

 

                            default:

                                          break;

                            }

                            return 0;

              }

 

              sp<ITestBinder> TestClient::gTestService = NULL;

 

              const sp<ITestBinder>& TestClient::get_test_service()

              {

                            if (gTestService == NULL)

                            {

                                          sp<IServiceManager> sm = defaultServiceManager();

                                          sp<IBinder> binder;

                                          do {

                                                        binder = sm->getService(String16("test.ITestService"));

                                                        if (binder != 0)

                                                                      break;

                                                        ALOGD("testbinder not published, waiting...");

                                                        usleep(500000); // 0.5 s

                                          } while (1);

                                          gTestService = interface_cast<ITestBinder>(binder);

                            }

 

                            if(gTestService == NULL)

                            {

                                          ALOGE("connetct no testbinder!");

                            }

                            return gTestService;

              }

 

              TestClient::TestClient()

                            : mCb(NULL)

                            , curShareMemType(SHAREMEM_CREATE_BY_CLIENT)

              {

                            mShareMemLocal.mShareMem.fd = -1;

                            mShareMemLocal.mShareMem.size = 0;

                            mShareMemLocal.mMapAddr = MAP_FAILED;

 

                            mShareMemServer.mShareMem.fd = -1;

                            mShareMemServer.mShareMem.size = 0;

                            mShareMemServer.mMapAddr = MAP_FAILED;

              }

 

              TestClient::~TestClient()

              {

                            if (mCb.get() != NULL)

                            {

                                          mCb.clear();

                            }

                            if (mShareMemLocal.mMapAddr != MAP_FAILED)

                                          munmap(mShareMemLocal.mMapAddr, mShareMemLocal.mShareMem.size);

                            if (mShareMemLocal.mShareMem.fd > 0)

                                          close(mShareMemLocal.mShareMem.fd);

 

                            if (mShareMemServer.mMapAddr != MAP_FAILED)

                                          munmap(mShareMemServer.mMapAddr, mShareMemServer.mShareMem.size);

                            if (mShareMemServer.mShareMem.fd > 0)

                                          close(mShareMemServer.mShareMem.fd);

              }

 

              int TestClient::init(int shareMemType)

              {

                            sp<ITestBinder> ts = get_test_service();

                            if (ts == NULL)

                                          return -1;

 

                            mCb = new Callback(this);

                            ts->setCallback(mCb);

 

                            if (shareMemType == SHAREMEM_CREATE_BY_CLIENT)

                                          curShareMemType = SHAREMEM_CREATE_BY_CLIENT;

                            else

                                          curShareMemType = SHAREMEM_CREATE_BY_SERVER;

 

                            return allocShareMemory(curShareMemType);

              }

 

              shareMemory_t* TestClient::getCurShareMemory()

              {

                            if (curShareMemType == SHAREMEM_CREATE_BY_SERVER)

                            {

                                          return &mShareMemServer;

                            }

                            else

                            {

                                          return &mShareMemLocal;

                            }

              }

 

              int TestClient::localAlloc(int size)

              {

                            ALOGD("%s, local alloc share memory", __func__);

 

                            if (size <= 0)

                                          return -1;

                           

                            int fd = ashmem_create_region("test_ash_mem_share", size);

                            mShareMemLocal.mShareMem.fd = fd;

                            mShareMemLocal.mMapAddr = mmap(0, size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);

                            if (mShareMemLocal.mMapAddr == MAP_FAILED)

                            {

                                          ALOGE("mmap(fd=%d, size=%u) failed (%s)", fd, size, strerror(errno));

                                          close(fd);

                                          mShareMemLocal.mShareMem.fd = -1;

                                          return -errno;

                            }

                            mShareMemLocal.mShareMem.size = size;

 

                            ALOGD("fd: %d, size: %d", mShareMemLocal.mShareMem.fd, mShareMemLocal.mShareMem.size);

                            return 0;

              }

 

              int TestClient::serverAlloc(int size)

              {

                            sp<ITestBinder> ts = get_test_service();

                            ALOGD("%s, server alloc share memory", __func__);

 

                            if (ts == 0)

                                          return 0;

 

                            struct testShareMem mem;

                            mem.size = size;

                            int ret = ts->notify(msg_mem_client_req_server_alloc, &mem);

                            if (!ret)

                            {

                                          mShareMemServer.mShareMem = mem;

                                          mShareMemServer.mMapAddr = mmap(0, mem.size, PROT_READ|PROT_WRITE, MAP_SHARED, mem.fd, 0);

                                          if (mShareMemServer.mMapAddr == MAP_FAILED)

                                          {

                                                        ALOGE("mmap(fd=%d, size=%u) failed (%s)", mem.fd, size, strerror(errno));

                                                        close(mem.fd);

                                                        mShareMemServer.mShareMem.fd = -1;

                                                        return -errno;

                                          }

                                          mShareMemServer.mShareMem.size = size;

                            }

 

                            ALOGD("fd: %d, size: %d", mShareMemServer.mShareMem.fd, mShareMemServer.mShareMem.size);

                            return ret;

              }

 

              int TestClient::allocShareMemory(int type)

              {

                            int ret;

 

                            switch(type)

                            {

                            case SHAREMEM_CREATE_BY_BOTH:

                                          {

                                                        ALOGD("not realize yet!!!");

                                          }

                                          break;

 

                            case SHAREMEM_CREATE_BY_SERVER:

                                          {

                                                        ret = serverAlloc(2018);

                                          }

                                          break;

 

                            case SHAREMEM_CREATE_BY_CLIENT:

                                          {

                                                        ret = localAlloc(2018);

 

                                                        sp<ITestBinder> ts = get_test_service();

                                                        ts->notify(msg_mem_client_prepared, &mShareMemLocal.mShareMem);

                                          }

                                          break;

 

                            default:

                                          ALOGD("error type!!!");

                                          break;

                            }

 

                            return ret;

              }

 

              int TestClient::write(String8 &str)

              {

                            //ALOGD("%s", __func__);

                            sp<ITestBinder> ts = get_test_service();

                            if (ts == NULL)

                                          return -1;

 

                            char *mem;

                            size_t size;

                            if (curShareMemType == SHAREMEM_CREATE_BY_CLIENT)

                            {

                                          mem = (char *)mShareMemLocal.mMapAddr;

                                          size = mShareMemLocal.mShareMem.size;

                            }

                            else

                            {

                                          mem = (char *)mShareMemServer.mMapAddr;

                                          size = mShareMemServer.mShareMem.size;

                            }

                            memset((void *)mem, 0, (unsigned int)size);

                            memcpy((void *)mem, str.string(), str.length());

 

                            ts->notify(msg_data_client_write, NULL);

                            return 0;

              }

 

              int TestClient::exit()

              {

                            ALOGD("%s", __func__);

                            sp<ITestBinder> ts = get_test_service();

                            if (ts == NULL)

                                          return -1;

                            ts->notify(msg_diconnect, 0);

 

                            return 0;

              }

 

TestClient.h

#ifndef _TEST_CLIENT_H_

#define _TEST_CLIENT_H_

 

#include "../ITestBinder.h"

#include "../ICallback.h"

 

namespace android {

              enum{

                            SHAREMEM_CREATE_BY_CLIENT = 0,

                            SHAREMEM_CREATE_BY_SERVER = 1,

                            SHAREMEM_CREATE_BY_BOTH = 2, //reserve, two share memory for test

              };

 

              typedef struct shareMemory

              {

                            void *mMapAddr;

                            struct testShareMem mShareMem;

              }shareMemory_t;

 

              class TestClient

              {

              public:

                            static const sp<ITestBinder>& get_test_service();

                            static sp<ITestBinder> gTestService;

 

              public:

                            TestClient();

                            ~TestClient();

 

                            int init(int);

                            int write(String8 &str);

                            int exit();

                           

              protected:

                            class Callback : public BnCallback

                            {

                                          friend class TestClient;

                            public:

                                          Callback(TestClient *tclient)

                                                        : tc(tclient)

                                          {}

                                         

                                          virtual ~Callback()

                                          {}

                                         

                                          virtual int notifyCallback(int msg);

 

                            private:

                                          TestClient *tc;

                            };

 

              public:

                            shareMemory_t* getCurShareMemory();

 

              private:

                            int localAlloc(int size);

                            int serverAlloc(int size);

                            int allocShareMemory(int type);

 

              private:

                            shareMemory_t mShareMemLocal;

                            sp<Callback> mCb;

                            shareMemory_t mShareMemServer;

                            int curShareMemType; //client or server alloc sharemem

              };

}

 

#endif //_TEST_CLIENT_H_

 

main.cpp

#define LOG_NDEBUG 0

#define LOG_TAG "client_main"

#include <utils/Log.h>

 

#include "TestClient.h"

 

using namespace android;

 

int main(int argc, char* argv[])

{

              int cnt = 0;

 

              TestClient* myclient = new TestClient();

              if(myclient == NULL)

              {

                            return 0;

              }

 

              myclient->init(/*SHAREMEM_CREATE_BY_CLIENT*/SHAREMEM_CREATE_BY_SERVER);

 

              while(cnt++ < 5)

              {

                            String8 str("binder mem test");

                            myclient->write(str);

              }

             

              myclient->exit();

              delete myclient;

              return 0;

}

 

 

Android.mk

LOCAL_PATH:= $(call my-dir)

 

include $(CLEAR_VARS) 

 

LOCAL_SRC_FILES:= \

    TestClient.cpp \

    main.cpp \

              ../ITestBinder.cpp \

              ../ICallback.cpp

 

LOCAL_SHARED_LIBRARIES := \

    libcutils \

    libutils \

    liblog \

    libbinder

 

LOCAL_MODULE:= test_mem_client_4

LOCAL_MODULE_TAGS := eng

include $(BUILD_EXECUTABLE)

 

 

1.1、由client申请共享内存

                              

         fd与size是怎样传到server端的。BpTestBinder直接写fd与size到BnTestBinder。

                             

         bn端的处理:

                                     

                            

         可以说是直接传了fd与size,server端直接拿来就mmap了。

         关闭时也是各自关闭各自的fd。

                         

                                 

         操作起来相当简单。我们看打印2个fd的值。fd=5、fd=7果然是不一致。是否是ashmem驱动帮我们dup了??

                          

                  

 

1.2、由server申请共享内存

                       

                       

         跟前面client端申请内存一样的,注意下面的,传fd、size值。在bp端读取bn端的fd时,必须得dup下,这点有些不懂,其实不dup的话,打印fd值,驱动是有帮dup的,这里为什么要再dup下,实在是想不通。如果这里不dup,那就会出现mmap fail。

                       

 

                       

                       

 

         在bp那里没有加dup(),mmap fail:

                        

 

2、IMemory分析

         通常我们调用IMemory去分配共享内存时,一般都如下面操作:

                       sp<MemoryHeapBase> mMemHeap = new MemoryHeapBase(sndDataSize, 0, "AudioTrack Heap Base");

                            sp<MemoryBase> mMemBase = new MemoryBase(mMemHeap, 0, sndDataSize);

                            memcpy(mMemHeap->getBase(), sndDataBuffer, sndDataSize);

              由BnMemoryHeapBase转换到BnMemory,再通过IMemory类去实现进程间的共享内存通信。

         IMemory是framework层在ashmem-dev.c接口上封的一个共享内存类。Bn端是memoryHeapBase与memoryBase。与直接调用ashmem-dev.c接口基本相同,memoryHeapBase也是对open与mmap封装了下。

                       

                      

        BpMemopry端,我们看常见的getBase()接口。

                           

         fd、size、offset对应Bn端mmap()操作的参数。同样可以看到BpMemory端的dup()操作,这也验证了上面demo只有加dup()才会正常。

                          

 

         所以可以看到,IMemory类的操作与直接调用ashmem-dev.c接口也是大同小异。

         用一个简单的audioTrack demo来说明IMemory接口的使用。

test.cpp

#include <stdio.h>

#include <stdlib.h>

#include <string.h>

#include <binder/Parcel.h>

#include <binder/IPCThreadState.h>

#include <binder/IServiceManager.h>

#include <binder/MemoryHeapBase.h>

#include <binder/MemoryBase.h>

#include <media/AudioTrack.h>

 

 

#define uint32_t unsigned int

#define uint16_t unsigned short

 

#define ID_RIFF 0x46464952

#define ID_WAVE 0x45564157

#define ID_FMT  0x20746d66

#define ID_DATA 0x61746164

 

struct riff_wave_header {

    uint32_t riff_id;

    uint32_t riff_sz;

    uint32_t wave_id;

};

 

struct chunk_header {

    uint32_t id;

    uint32_t sz;

};

 

struct chunk_fmt {

    uint16_t audio_format;

    uint16_t num_channels;

    uint32_t sample_rate;

    uint32_t byte_rate;

    uint16_t block_align;

    uint16_t bits_per_sample;

};

 

static char *sndDataBuffer;

static uint32_t sndReadSize;

static uint32_t sampleRate;

static uint32_t audioFormat;

 

using namespace android;

 

#define TEST_FILE_PATH  "/data/audio/test_ringtone.wav"

 

int loadSoundFile(const char *path)

{

              struct riff_wave_header riff_wave_header;

    struct chunk_header chunk_header;

    struct chunk_fmt chunk_fmt;

    int more_chunks = 1;

 

              int file_pos_cur,file_pos_end;

              int buffer_size;

 

              FILE *sndFile;

 

    sndFile = fopen(path, "rb");

    if (!sndFile) {

        ALOGE("Unable to open file '%s'\n", path);

        return -1;

    }

             

              fread(&riff_wave_header, sizeof(riff_wave_header), 1, sndFile);

    if ((riff_wave_header.riff_id != ID_RIFF) ||

        (riff_wave_header.wave_id != ID_WAVE)) {

        ALOGE("Error: '%s' is not a riff/wave file\n", path);

        fclose(sndFile);     

        return -1;

    }

 

    do {

        fread(&chunk_header, sizeof(chunk_header), 1, sndFile);

 

        switch (chunk_header.id) {

        case ID_FMT:

            fread(&chunk_fmt, sizeof(chunk_fmt), 1, sndFile);

            /* If the format header is larger, skip the rest */

            if (chunk_header.sz > sizeof(chunk_fmt))

                fseek(sndFile, chunk_header.sz - sizeof(chunk_fmt), SEEK_CUR);

            break;

        case ID_DATA:

            /* Stop looking for chunks */

            more_chunks = 0;

            break;

        default:

            /* Unknown chunk, skip bytes */

            fseek(sndFile, chunk_header.sz, SEEK_CUR);

        }

    } while (more_chunks);

 

              if (chunk_fmt.bits_per_sample == 32)

              {

                            audioFormat = AUDIO_FORMAT_PCM_32_BIT;

              }

    else if (chunk_fmt.bits_per_sample == 16)

    {

                            audioFormat = AUDIO_FORMAT_PCM_16_BIT;

    }

              sampleRate = chunk_fmt.sample_rate;

              file_pos_cur = ftell(sndFile);

              fseek(sndFile, 0, SEEK_END);

              file_pos_end = ftell(sndFile);

 

              buffer_size = file_pos_end - file_pos_cur;

              if (sndDataBuffer) {

                            free(sndDataBuffer);

                            sndDataBuffer = NULL;

              }

    sndDataBuffer = (char*)malloc(buffer_size);

    if (!sndDataBuffer) {

        ALOGE("Unable to allocate %d bytes\n", buffer_size);

                            fclose(sndFile);

        return -1;

    }

              fseek(sndFile, file_pos_cur, SEEK_SET);

              sndReadSize = fread(sndDataBuffer, 1, buffer_size, sndFile);

              if(sndReadSize <= 0) {

        ALOGE("Unable to allocate %d bytes\n", buffer_size);

                            fclose(sndFile);

        return -1;

    }  

              fclose(sndFile);

              return 0;

}

 

int playSound(char *sndDataBuffer, unsigned int sndDataSize, uint32_t sampleRate, uint32_t format)

{

              int res;

              sp<AudioTrack> audioTrack = NULL;

              if ((sndDataBuffer == NULL) || !sndDataSize)

                            return -1;

 

              ALOGD("samplerate: %d, format: %d\n", sampleRate, format);

              sp<MemoryHeapBase> mMemHeap = new MemoryHeapBase(sndDataSize, 0, "AudioTrack Heap Base");

              sp<MemoryBase> mMemBase = new MemoryBase(mMemHeap, 0, sndDataSize);

              memcpy(mMemHeap->getBase(), sndDataBuffer, sndDataSize);

                                                                                     

              audioTrack = new AudioTrack();

              res = audioTrack->set(AUDIO_STREAM_MUSIC,

                                                                      sampleRate,

                                                                      (audio_format_t)format,

                                                                      AUDIO_CHANNEL_OUT_STEREO,

                                                                      0,

                                                                      AUDIO_OUTPUT_FLAG_PRIMARY,

                                                                      NULL,

                                                                      NULL,

                                                                      0,

                                                                      mMemBase   //sp<IMemory>& sharedBuffer

                                                                      );

 

    ALOGD("audioTrack->set() res=%d\n", res);

 

              audioTrack->setVolume(1.0, 1.0);

              audioTrack->start();

             

#if 0     

              while(!audioTrack->stopped()) //怎样获取停止状态的???没用.....

              {

                            sleep(1);

              }

#endif

 

              sleep(5);

              audioTrack.clear();

              return 0;

}

 

int main(int argc, char **argv)

{

              char file_path[128] = {0};

              sndDataBuffer = NULL;

 

              strcpy(file_path, TEST_FILE_PATH);

              if (argc > 1)

                            strcpy(file_path, (char *)(argv[1]));

              ALOGD("file: %s", file_path);

              if (!loadSoundFile(file_path))

              {

                            playSound(sndDataBuffer, sndReadSize, sampleRate, audioFormat);

              }

             

              if (sndDataBuffer != NULL)

              {

                            free(sndDataBuffer);

                            sndDataBuffer = NULL;

              }

              return 0;

}

 

Android.mk

LOCAL_PATH:= $(call my-dir)

 

include $(CLEAR_VARS) 

LOCAL_C_INCLUDES := \

    frameworks/av/include/

 

LOCAL_SRC_FILES:= test.cpp

 

LOCAL_SHARED_LIBRARIES := \

    libcutils \

    libutils \

              libbinder \

    libmedia \

              liblog

 

 

LOCAL_MODULE_TAGS := eng

LOCAL_MODULE:= audiotrack_sharebuffer_test

include $(BUILD_EXECUTABLE)

 

         可以看到,真正的使用接口就是下面的:

                         

         从字面上理解是先分配堆内存,再在堆内存上分配实现内存,但是实际代码的操作明显就是一样的操作。为什么这样写接口,而不是一步封出来??而要有个BnMemoryHeap ->BnMemory的转换??

猜你喜欢

转载自blog.csdn.net/wenjin359/article/details/82151616