架構之Binder 核心原理(二)

架構之Binder 核心原理(一)

  1. 打開binder設備:
    service_manager.c 源碼中:

        if (argc > 1) {
        driver = argv[1];
        } else {
            driver = "/dev/binder"; //打開binder設備文件,返回文件描述符
        }
    
        bs = binder_open(driver, 128*1024); // binder 的 buffer緩衝區創建,用於進程間數據的傳輸 
    
  2. 打開一個binder設備文件,並且開啓一個128k的內存映射。
    binder.c 有內存映射mmap()函數調用

        bs->fd = open(driver, O_RDWR | O_CLOEXEC); // 打開binder設備
        // ...
        bs->mapped = mmap(NULL, mapsize, PROT_READ, MAP_PRIVATE, bs->fd, 0); // 開啓內存映射
    

    mmap 函數是在系統啓動的時候就開啓了binder映射,查看源碼System.map
    在這裏插入圖片描述

  3. ServiceManager啓動
    init.rc中啓動ServiceManager服務
    在這裏插入圖片描述

  4. 打包Parcel中,數據寫入binder設備,copy_from_user

    • IServiceManager.cpp源碼中,搜索addService
          virtual status_t addService(const String16& name, const sp<IBinder>& service,
                                      bool allowIsolated, int dumpsysPriority) {
              Parcel data, reply; //parcel對象,將Service相關的信息打包
              data.writeInterfaceToken(IServiceManager::getInterfaceDescriptor());
              data.writeString16(name);
              data.writeStrongBinder(service);
              data.writeInt32(allowIsolated ? 1 : 0);
              data.writeInt32(dumpsysPriority);
              status_t err = remote()->transact(ADD_SERVICE_TRANSACTION, data, &reply);
              return err == NO_ERROR ? reply.readExceptionCode() : err;
          }
      
      parcel對象,將Service相關的信息打包到parcel對象,並且通過remote()->transact方法傳輸到下一步,
    1. 如何將數據寫入binder設備,查看IIPCThreadState.cpp源碼,IPCThreadState.h
      err = writeTransactionData(BC_TRANSACTION, flags, handle, code, data, NULL);
      
  5. 服務註冊,添加到鏈表svclist中

    int do_add_service(struct binder_state *bs, const uint16_t *s, size_t len, uint32_t handle,
                       uid_t uid, int allow_isolated, uint32_t dumpsys_priority, pid_t spid) {
        struct svcinfo *si;
    
        //ALOGI("add_service('%s',%x,%s) uid=%d\n", str8(s, len), handle,
        //        allow_isolated ? "allow_isolated" : "!allow_isolated", uid);
    
        if (!handle || (len == 0) || (len > 127))
            return -1;
    
        if (!svc_can_register(s, len, spid, uid)) {//檢查服務權限,
            ALOGE("add_service('%s',%x) uid=%d - PERMISSION DENIED\n",
                 str8(s, len), handle, uid);
            return -1;
        }
    
        si = find_svc(s, len);//檢查服務是否已經在svclist鏈表上註冊過
        if (si) {
            if (si->handle) {
                ALOGE("add_service('%s',%x) uid=%d - ALREADY REGISTERED, OVERRIDE\n",
                     str8(s, len), handle, uid);
                svcinfo_death(bs, si);
            }
            si->handle = handle;
        } else {
            si = malloc(sizeof(*si) + (len + 1) * sizeof(uint16_t)); //沒有註冊過,分配服務管理結構svcinfo,並將其添加到鏈表的list當中。
            if (!si) {
                ALOGE("add_service('%s',%x) uid=%d - OUT OF MEMORY\n",
                     str8(s, len), handle, uid);
                return -1;
            }
            si->handle = handle;
            si->len = len;
            memcpy(si->name, s, (len + 1) * sizeof(uint16_t));
            si->name[len] = '\0';
            si->death.func = (void*) svcinfo_death;
            si->death.ptr = si;
            si->allow_isolated = allow_isolated;
            si->dumpsys_priority = dumpsys_priority;
            si->next = svclist; //將代表該服務的結構添加到鏈表中
            svclist = si;
        }
    
        binder_acquire(bs, handle); // 增加binder的應用 計數
        binder_link_to_death(bs, handle, &si->death);//如果服務退出之後,就需要通知ServiceManager
        return 0;
    }
    

    以上整個過程,就是Server如何向ServerManager添加服務的註冊過程。

  6. 定義主線程的線程池,不停地讀寫
    查看IIPCThreadState.cpp源碼

    		void IPCThreadState::joinThreadPool(bool isMain)
    		{
    		    LOG_THREADPOOL("**** THREAD %p (PID %d) IS JOINING THE THREAD POOL\n", (void*)pthread_self(), getpid());
    		
    		    mOut.writeInt32(isMain ? BC_ENTER_LOOPER : BC_REGISTER_LOOPER);
    		
    		    status_t result;
    		    do {
    		        processPendingDerefs();
    		        // now get the next command to be processed, waiting if necessary
    		        result = getAndExecuteCommand();
    		
    		        if (result < NO_ERROR && result != TIMED_OUT && result != -ECONNREFUSED && result != -EBADF) {
    		            ALOGE("getAndExecuteCommand(fd=%d) returned unexpected error %d, aborting",
    		                  mProcess->mDriverFD, result);
    		            abort();
    		        }
    		
    		        // Let this thread exit the thread pool if it is no longer
    		        // needed and it is not the main process thread.
    		        if(result == TIMED_OUT && !isMain) {
    		            break;
    		        }
    		    } while (result != -ECONNREFUSED && result != -EBADF);
    		
    		    LOG_THREADPOOL("**** THREAD %p (PID %d) IS LEAVING THE THREAD POOL err=%d\n",
    		        (void*)pthread_self(), getpid(), result);
    		
    		    mOut.writeInt32(BC_EXIT_LOOPER);
    		    talkWithDriver(false);
    		}
    
  7. 循環從mIn mOut中取出讀寫請求,發送的binder的設備中

    		    pthread_setspecific(gTLS, this); //將對象設置爲當前線程的私有
    		    clearCaller();
    		    mIn.setDataCapacity(256); //輸入分配256字節的空間
    		    mOut.setDataCapacity(256);//輸出分配256字節的空間
    

    輸入/出 預分配256字節的空間,主要工作循環從mIn mOut中進行IO的讀寫工作,然後發送的binder的設備中來。

    繼續往下,是如何檢查是否有數據要讀?

    		    binder_write_read bwr;
    		
    		    // Is the read buffer empty?
    		    const bool needRead = mIn.dataPosition() >= mIn.dataSize(); //檢查是否有讀數據的請求
    		
    		    // We don't want to write anything if we are still reading
    		    // from data left in the input buffer and the caller
    		    // has requested to read the next data.
    		    const size_t outAvail = (!doReceive || needRead) ? mOut.dataSize() : 0; // //檢查是否有寫數據的請求
    		    		// 省略代碼....
    		        if (ioctl(mProcess->mDriverFD, BINDER_WRITE_READ, &bwr) >= 0) //將讀寫的請求 發送到binder中來
    		            err = NO_ERROR;
    		        else
    		            err = -errno;
    

平時項目中,碰到的第三方登錄,分享。實際就是跨進行。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章