• 10.3、android输入系统_必备Linux编程知识_任意进程双向通信(scoketpair+binder)


    3. 任意进程间通信(socketpair_binder)

    进程每执行一次open打开文件,都会在内核中有一个file结构体表示它;

    对每一个进程在内核中都会有一个task_struct表示进程,这个结构体内部有个files_struct结构体,这个结构体里面有个fdtble结构体,这个结构体里有个struct file **fd,fd就是个数组,fd[open时返回的句柄]就保存的对应文件的file结构体

    因此不同进程的文件句柄只在本进程中有含义,如果想要在进程外面使用这个文件句柄,需要让外面进程的fd[任何句柄都可以]指向需要获得的目的进程的file

    这里使用binder来传输文件句柄:

    (1)APP1  open(file)得到fd1;

    (2)通过binder驱动,根据fd1得到file:files->fdt->fd[fd1]

    (3)从APP2的files->fdt->fd取出空项fd2,让fd[fd2]指向该file

    (4)APP1通过fd1,APP2通过fd2就可以访问同一个file文件了,fd1和fd2不一样


    取出APP_0004_Binder_CPP_App V4来修改:
    1、server

    (1)test_server.cpp :fd = open("1.txt");

    (2)BnHelloService.cpp:添加结果get_fd:把fd传给client

    (3)IHelloService.h:添加get_fd接口

    2、client

    (1)test_client.cpp:fd=service->get_fd();read(fd,buf);printf

    (2)BpHelloService:添加get_fd向server发请求

    IHelloService.h:(参考:IMediaPlayerService.h)

    ifndef ANDROID_IHELLOERVICE_H

    #define ANDROID_IHELLOERVICE_H

    ................头文件..................

    #define HELLO_SVR_CMD_SAYHELLO 1

    #define HELLO_SVR_CMD_SAYHELLO_TO 2

    #define HELLO_SVR_CMD_GET_FD 3

    namespace android{

    class IHelloService:public IInterface

    {

    public:

      DECLARE_META_INTERFACE(HelloService);

      virtual void sayhello(void) = 0;

      virtual int sayhello_to(const char *name) = 0;

      virtual int get_fd(void) = 0;

    };

    class BnHelloService:public BnInterface<IHelloService>

    {

    private:

      int fd;

    public:

      virtual status_t onTransact(uint32_t code,const Parcel& data,Parcel* reply,uint32_t flags = 0);

      virtual void sayhello(void);

      virtual int sayhello_to(const char *name);

      virtual int get_to(void);

      BnHelloService();

      BnHelloService(int fd);

    }

    }

    #endfi

    BnHelloService.cpp(参考:IMediaPlayerService.cpp)

    #include LOG_TAG "HelloService"

    #include "IHelloService.h"

    namespace android{

      BnHelloService::BnHelloService()

      {

      }

      BnHelloService::BnHelloService(int fd)

      {

        this->fd = fd;

      }

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

      {

        //解析数据,调用sayhello/sayhello_to

        switch(code){

          case HELLO_SVR_CMD_SAYHELLO:{

            sayhello();

            return NO_ERROR;

          }break;

          case HELLO_SVR_CMD_SAYHELLO_TO:{

            //从data中取出参数

            int32_t policy = data->readInt32();//把四字节的全零数据读出来

            String16 name16 =data->readString16();

            String8 name8(name16);

            int cnt = sayhello_to(name8.string());

            //把返回值写入reply传回去

            reply->writeInt32(cnt);

            return NO_ERROR;

          }break;

          case HELLO_SVR_CMD_GET_FD :{

            int fd = this->get_fd();

            reply->writeInt32(cnt);

            /*

            参考frameworksasecorejniandroid_view_InputChannel.cpp 里面的android_view_InputChannel_nativeWriteToParcel函数

            */

            reply->writeDupFileDescriptor(fd);//这里使用writeDupFile....的原因是reply在析构的时候会close(fd),所以需要dup复制出一个同样的fd让其去close,而不会影响client使用该文件

            return NO_ERROR;

          }break;

          default:

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

        }

      }

      void BnHelloService::sayhello(void);

      {

          static int cnt = 0;

          ALOGI("say hello : %d ", ++cnt);

      }

      int BnHelloService::sayhello_to(const char *name);

      {

          static int cnt = 0;
          ALOGI("say hello to %s : %d ", name, ++cnt);
          return cnt;

      }

      int BnHelloService::sayhello_to(const char *name);

      {

          static int cnt = 0;
          ALOGI("say hello to %s : %d ", name, ++cnt);
          return cnt;

      }

      int BnHelloService::get_fd(void);

      {

          return fd;

      }

      

    }

    BpHelloService.cpp(参考:IMediaPlayerService.cpp)

    #include "IHelloService.h"

    namespace android{

    class BpHelloService:public BpInterface<IHelloService>

    {

    public:

      BpHelloService(const sp<IBinder>& impl):BpInterface<IHelloService>(impl)

      {

      }

      void sayhello(void)

      {

      //构造/发送数据

      Parcel data,reply;

      data.writeInt32(0);//data数据域可以自己定义,这里是为了统一

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

      }

      void sayhello_to(const char *name)

      {

      //构造/发送数据

      Parcel data,reply;

      int exception;

      data.writeInt32(0);//data数据域可以自己定义,这里是为了统一

      data.writeString16(String16("IHelloService"));

      data.writeString16(String16(name));

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

      exception = reply.readInt32();

      if(exception)

        return -1;

      else

        return reply.readInt32();

      }

      int get_fd(void)

      {

      //构造/发送数据

      Parcel data,reply;

      int exception;

      data.writeInt32(0);//data数据域可以自己定义,这里是为了统一

      data.writeString16(String16("IHelloService"));

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

      exception = reply.readInt32();

      if(exception)

        return -1;

      else{

        int rawFd=reply.readFileDescriptor();

        return dup(rawFd);//dup会复制一个fd,即有两个fd指向同一个文件,当函数退出的时候,reply被析构,会close(rawFd),而我们通过dup继续保持文件的fd,这样就不会影响对文件的访问

      }

      }

    }

    IMPLEMENT_META_INTERFACE(HelloService,"android.meida.In");

     }

    test_server.cpp(参考:Main_mediaserver.cpp)

    #define LOG_TAG “HelloService”

    #include "IHelloService.h"

    .................头文件.......................

    using namespace android;

    /*usage:test_server <file>*/

    int main(void)

    {

      int fd;

      if(argc == 2)

        fd = open(argv[1],O_RDWR);

      //add service   //while(1){read data,解析数据,调用服务函数}

      //打开驱动,mmap

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

      //获得BpServiceManager

      sp<IServiceManager> sm = defaultServiceManager();

      sm->addService(String16("hello"),new BnHelloService(fd));

      //循环体

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

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

      return 0;

    }

    test_client.cpp

    #define LOG_TAG “HelloService”

    #include "IHelloService.h"

    .................头文件.......................

    using namespace android;

    void main(int argc,char **argv)

    {

        int cnt;
      if (argc < 2){
            ALOGI("Usage: ");
            ALOGI("%s hello ", argv[0]);
        ALOGI("%s <readfile> ", argv[0]);
            ALOGI("%s hello <name> ", argv[0]);
            return -1;
        }
      //getService

      //打开驱动,mmap

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

      //获得BpServiceManager

      sp<IServiceManager> sm = defaultServiceManager();

      

      if(strcmp(arfv[1],"hello") == 0)

      {

        sp<IBinder> binder = sm->getService(String16("hello"));

        if(binder  == 0)

        {

          ALOGI("can't get hello service ");

          return -1;

        }

        //service肯定是BpHelloService指针

        sp<IHelloService> service = interface_cast<IHelloService>(binder);

        //调用Service函数

        if(argc < 3){

          service->sayhello();

          ALOGI("client call sayhello");

        }

        else{

          cnt = service->sayhello_to(argv[2]);

          ALOGI("client call sayhello_to,cnt = %d ",cnt);

        }

      }

      else if(strcmp(arfv[1],"readfile") == 0)

      {

        sp<IBinder> binder = sm->getService(String16("hello"));

        if(binder  == 0)

        {

          ALOGI("can't get hello service ");

          return -1;

        }

        //service肯定是BpHelloService指针

        sp<IHelloService> service = interface_cast<IHelloService>(binder);

        //调用Service函数

        int fd = service->get_fd();

        lseek(fd,0,SEEK_SET);//移动文件指针,指向头部,否则不能重复读,因为读一次都文件指针一道最后面了,这个时候读数据为空,只能在写入后读取

        char buf[500];

        int len = read(fd,buf,500);

        buf[len] = '';

        ALOGI("client call get_fd= %d ",fd);

        ALOGI("client read file:%s ",buf);

      }

      return 0;

    }


    参考代码:
    frameworksasecorejniandroid_view_InputChannel.cpp (用binder传文件句柄)
    server端写fd: android_view_InputChannel_nativeWriteToParcel
    parcel->writeDupFileDescriptor
    client端读fd: android_view_InputChannel_nativeReadFromParcel
    int rawFd = parcel->readFileDescriptor();
    int dupFd = dup(rawFd);

    frameworks ativelibsinderParcel.cpp


    支持传输文件句柄的程序 v5:
    第一次:
    git clone https://github.com/weidongshan/APP_0004_Binder_CPP_App.git

    更新:
    git pull origin

    取出指定版本:
    git checkout v5 // v5, use binder to transfer file descriptor


    编译:
    把 APP_0004_Binder_CPP_App 放入 /work/android-5.0.2/frameworks/testing

    cd /work/android-5.0.2/
    . setenv
    lunch //选择单板
    mmm frameworks/testing/APP_0004_Binder_CPP_App
    cp /work/android-5.0.2/out/target/product/tiny4412/system/bin/test_* /work/nfs_root/android_fs/


    测试:
    su
    busybox mount -t nfs -o nolock,vers=2 192.168.1.123:/work/nfs_root /mnt
    logcat HelloService:* GoodbyeService:* TestService:* *:S &
    echo asfsdfasdf > 1.txt
    ./test_server 1.txt &
    ./test_client readfile

    通过ls -l /proc/进程号/fd  可以查看文件fd指向的文件  比如:3 -> /mnt/android_fs/1.txt

  • 相关阅读:
    P1117 [NOI2016] 优秀的拆分 SA+DP
    P3346 [ZJOI2015]诸神眷顾的幻想乡 广义SAM
    P3705 [SDOI2017]新生舞会 分数规划+费用流
    P2336 [SCOI2012]喵星球上的点名 SA+树状数组
    543. Diameter of Binary Tree
    451. Sort Characters By Frequency
    563. Binary Tree Tilt
    703. Kth Largest Element in a Stream
    743. Network Delay Time
    kaggle _Titanic: Machine Learning from Disaster
  • 原文地址:https://www.cnblogs.com/liusiluandzhangkun/p/9161570.html
Copyright © 2020-2023  润新知