美文网首页
Binder和进程的关系

Binder和进程的关系

作者: 我叫王菜鸟 | 来源:发表于2017-11-12 21:04 被阅读0次

    hello,亲,你好,我叫王菜鸟,菜鸟前段时间迷茫了,因为现在是在做app相关,而且陷入了公司的业务逻辑,所以和系统与功能实现还有Android脱轨了,迷茫要不要继续学framework,要不要直接去做功能,因为framework了解的越深,越有助于解决功能性能,优化,还有方法等一系列疑难杂症,所以菜鸟觉得,不能放弃学framework,而且菜鸟现在系统水平和尴尬,你说知道吧,掌握不透,你说不知道吧还知道一点,但是我觉得越来越清晰,我再一次看的时候我觉得我有的就理解了,所以菜鸟觉得应该坚持下去。

    菜鸟这段时间抽风,买了单反,报名吉他,打算充实一些业余生活,菜鸟觉得,应该学会发现美,也许过段时间可能我还会一周来一次直播,直播弹吉他,直播解决我所学的问题。从下周我也开始上传我拍的一些美美照片和大家分享,哈哈,有想法就去实现,就算是现在一无所有也要去实现,人生就那么短,三分之一已经过去了,还有三分之一已经干不动啥了,剩下这三分之一要不去做一些想干的事情,我觉得我会留下遗憾。加油菜鸟,加油大家。

    本文未解决问题:

    • IBinder b = ServiceManager.getService("activity");得到BinderProxy对象
    • 在android_os_BinderProxy_transact中IBinder* target = (IBinder*) env->GetLongField(obj, gBinderProxyOffsets.mObject);保存的是new BpBinder(handle)对象

    gBinderProxyOffsets.mObject中保存的是BpBinder对象, 这是开机时Zygote调用AndroidRuntime::startReg方法来完成jni方法的注册.

    • IPCThreadState::transact中flags |= TF_ACCEPT_FDS是干啥的?
    • BpBinder::transact中IPCThreadState::self()->transact( mHandle, code, data, reply, flags);这个mHandle是从何而来干啥的?
    • IPCThreadState::writeTransactionData() mOut.write(&tr, sizeof(tr)); //写入binder_transaction_data数据

    基础知识

    IActivityManager相关类

    IActivityManager.png
    1. Activity的管理采用binder机制,管理Activity的接口是IActivityManager
    2. ActivityManagerService实现了Activity管理功能,位于system_server进程,ActivityManagerProxy对象是ActivityManagerService在普通应用进程的一个代理对象,应用进程通过ActivityManagerProxy对象调用ActivityManagerService提供的功能
    3. 应用进程并不会直接创建ActivityManagerProxy对象,而是通过调用ActiviyManagerNative类的工具方法getDefault方法得到ActivityManagerProxy对象。所以在应用进程里通常这样启动Activty:
    ActivityManagerNative.getDefault().startActivity()
    

    IApplicationThread相关类

    image.png
    1. 应用进程需要调用ActivityManagerService提供的功能,而ActivityManagerService也需要主动调用应用进程以控制应用进程并完成指定操作。这样ActivityManagerService也需要应用进程的一个Binder代理对象,而这个代理对象就是ApplicationThreadProxy对象。
    2. ActivityManagerService通过IApplicationThread接口管理应用进程,ApplicationThread类实现了IApplicationThread接口,实现了管理应用的操作,ApplicationThread对象运行在应用进程里。
    3. ApplicationThreadProxy对象是ApplicationThread对象在ActivityManagerService线程 (ActivityManagerService线程运行在system_server进程)内的代理对象,ActivityManagerService通过ApplicationThreadProxy对象调用ApplicationThread提供的功能,比如让应用进程启动某个Activity。

    AMS

    image.png
    • mProcessNames:数据类型为ProcessMap,以进程名和userId为key来记录ProcessRecord;
      • 添加进程,addProcessNameLocked()
      • 删除进程,removeProcessNameLocked()
    • mPidsSelfLocked: 数据类型为SparseArray,以进程pid为key来记录ProcessRecord;
      • startProcessLocked(),移除已存在进程,增加新创建进程pid信息;
      • removeProcessLocked,processStartTimedOutLocked,cleanUpApplicationRecordLocked移除进程;
    • mLruProcesses:数据类型为ArrayList,以进程最近使用情况来排序记录ProcessRecord;
      其中第一个元素代表的便是最近最少使用的进程;
    • updateLruProcessLocked()更新进程队列位置;
    • mRemovedProcesses:数据类型为ArrayList,记录所有需要强制移除的进程;
    • mProcessesToGc:数据类型为ArrayList,记录系统进入idle状态需执行gc操作的进程;
    • mPendingPssProcesses:数据类型为ArrayList,记录将要收集内存使用数据PSS的进程;
    • mProcessesOnHold:数据类型为ArrayList,记录刚开机过程,系统还没与偶准备就绪的情况下, 所有需要启动的进程都放入到该队列;
    • mPersistentStartingProcesses:数据类型ArrayList,正在启动的persistent进程;
    • mHomeProcess: 记录包含home Activity所在的进程;
    • mPreviousProcess:记录用户上一次刚访问的进程;其中mPreviousProcessVisibleTime记录上一个进程的用户访问时间;
    • mProcessList: 数据类型ProcessList,用于进程管理,Adj常量定义位于该文件;

    其中最为常见的是mProcessNames,mPidsSelfLocked,mLruProcesses这3个对象

    进程与组件的关系

    系统AMS这边是由ProcessRecord对象记录进程,进程自身比较重要成员变量如下:

    • processName:记录进程名,默认情况下进程名和该进程运行的第一个apk的包名是相同的,当然也可以自定义进程名;
    • pid: 记录进程pid,该值在由进程创建时内核所分配的。
    • thread:执行完attachApplicationLocked()方法,会把客户端进程ApplicationThread的binder服务的代理端传递到 AMS,并保持到ProcessRecord的成员变量thread;
      • ProcessRecord.makeActive,赋值;
      • ProcessRecord.makeInactive,清空;
    • info:记录运行在该进程的第一个应用;
    • pkgList: 记录运行在该进程中所有的包名,比如通过addPackage()添加;
    • pkgDeps:记录该进程所依赖的包名,比如通过addPackageDependency()添加;
    • lastActivityTime:每次updateLruProcessLocked()过程会更新该值;
    • killedByAm:当值为true,意味着该进程是被AMS所杀,而非由于内存低而被LMK所杀;
    • killed:当值为true,意味着该进程被杀,不论是AMS还是其他方式;

    先将进程信息填写完成,然后继续填充组件信息,各个组件在system_server的核心信息记录如下:

    • Service的信息记录在ActiveServices和AMS
    • Broadcast信息记录在BroadcastQueue和AMS
    • Activity信息记录在ActivityStack,ActivityStackSupervisor,以及AMS;
    • Provider信息记录在ProviderMap和AMS;

    APP端的组件信息

    App端的组件信息,都保存在ActivityThread和LoadedApk这两个对象,主要保存信息:

    • ActivityThread:记录provider, activity, service在客户端的相关信息;
    • LoadedApk: 记录动态注册的广播接收器,以及bind方式启动service在客户端的相关信息;

    理解Binder通信

    理解mRemote方法的诞生

    我们理解Binder通信,一般APP开发者一般都是看到ContextImpl对应就截止了

    public ComponentName startService(IApplicationThread caller, Intent service,
                String resolvedType, String callingPackage, int userId) throws RemoteException
    {
        //获取或创建Parcel对象
        Parcel data = Parcel.obtain();
        Parcel reply = Parcel.obtain();
        data.writeInterfaceToken(IActivityManager.descriptor);
        data.writeStrongBinder(caller != null ? caller.asBinder() : null);
        service.writeToParcel(data, 0);
        //写入Parcel数据
        data.writeString(resolvedType);
        data.writeString(callingPackage);
        data.writeInt(userId);
    
        //通过Binder传递数据
        mRemote.transact(START_SERVICE_TRANSACTION, data, reply, 0);
        //读取应答消息的异常情况
        reply.readException();
        //根据reply数据来创建ComponentName对象
        ComponentName res = ComponentName.readFromParcel(reply);
        data.recycle();
        reply.recycle();
        return res;
    }
    

    其中创建了两个Parcel 对象,一个用于读,一个用于写,通过mRemote进行通信,那么mRemote究竟是什么呢?
    这个细节我们就讲讲mRemote究竟是何物。

    首先我们找个切入点说起,显然此时ContextImpl已经不是我们的切入了,我们就从根源说起。我们知道AMP给AMS通信,AMP在什么时候创建呢?


    ActivityManagerNative.java

    static public IActivityManager getDefault() {
        return gDefault.get();
    }
    
     private static final Singleton gDefault = new Singleton() {
            protected IActivityManager create() {
                IBinder b = ServiceManager.getService("activity");
                if (false) {
                    Log.v("ActivityManager", "default service binder = " + b);
                }
                IActivityManager am = asInterface(b);
                if (false) {
                    Log.v("ActivityManager", "default service = " + am);
                }
                return am;
            }
        };
    
        public abstract class Singleton {
            private T mInstance;
            protected abstract T create();
            public final T get() {
                synchronized (this) {
                    if (mInstance == null) {
                        mInstance = create();
                    }
                    return mInstance;
                }
            }
        }
    

    所以gDefault.get()乍看返回的是下面代码:

    IBinder b = ServiceManager.getService("activity");
    IActivityManager am = asInterface(b);
    return am;
    

    我们暂时理解到ServiceManager.getService(“activity”)返回的是指向目标服务AMS的代理对象BinderProxy对象,由该代理对象可以找到目标服务AMS所在进程
    然后看asInterface()方法:

    public abstract class ActivityManagerNative extends Binder implements IActivityManager
    {
        static public IActivityManager asInterface(IBinder obj) {
            if (obj == null) {
                return null;
            }
            //此处obj = BinderProxy,  descriptor = "android.app.IActivityManager"; 
            IActivityManager in = (IActivityManager)obj.queryLocalInterface(descriptor);
            if (in != null) { //此处为null
                return in;
            }
            return new ActivityManagerProxy(obj);
        }
        ...
    }
    

    上面调用的是BinderProxy.queryLocalInterface()

    final class BinderProxy implements IBinder {
        //BinderProxy对象的调用, 则返回值为空
        public IInterface queryLocalInterface(String descriptor) {
            return null;
        }
    }
    

    所以

    IBinder b = ServiceManager.getService("activity");
    IActivityManager am = asInterface(b);
    

    这两句意思是首先得到描述AMS的BinderProxy,然后返回空之后创建一个ActivityManagerProxy对象。最终得到AMP对象返回出去,并且此时AMP已经包装了AMS的BinderProxy了。

    所以我们继续看下AMP的构造:

    class ActivityManagerProxy implements IActivityManager
    {
        public ActivityManagerProxy(IBinder remote)
        {
            mRemote = remote;
        }
    }
    

    我们要的结果出来了mRemote原来就是AMS的BinderProxy对象。所以我们后面研究mRemote就直接在BinderProxy中找代码。

    现在我们要研究transact方法,看看当初在ComtextImp中调用startService()使用了什么:

    mRemote.transact(START_SERVICE_TRANSACTION, data, reply, 0);
    

    mRemote中方法的理解

    public ComponentName startService(IApplicationThread caller, Intent service,
                String resolvedType, String callingPackage, int userId) throws RemoteException
    {
        //获取或创建Parcel对象
        Parcel data = Parcel.obtain();
        Parcel reply = Parcel.obtain();
        data.writeInterfaceToken(IActivityManager.descriptor);
        data.writeStrongBinder(caller != null ? caller.asBinder() : null);
        service.writeToParcel(data, 0);
        //写入Parcel数据
        data.writeString(resolvedType);
        data.writeString(callingPackage);
        data.writeInt(userId);
    
        //通过Binder传递数据
        mRemote.transact(START_SERVICE_TRANSACTION, data, reply, 0);
        //读取应答消息的异常情况
        reply.readException();
        //根据reply数据来创建ComponentName对象
        ComponentName res = ComponentName.readFromParcel(reply);
        data.recycle();
        reply.recycle();
        return res;
    }
    

    Binder.java ::BinderProxy

    final class BinderProxy implements IBinder {
        public boolean transact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
            //用于检测Parcel大小是否大于800k
            Binder.checkParcel(this, code, data, "Unreasonably large binder buffer");
            return transactNative(code, data, reply, flags);
        }
    }
    

    android_util_Binder.cpp::android_os_BinderProxy_transact

    static jboolean android_os_BinderProxy_transact(JNIEnv* env, jobject obj,
        jint code, jobject dataObj, jobject replyObj, jint flags)
    {
        ...
        //将java Parcel转为c++ Parcel
        Parcel* data = parcelForJavaObject(env, dataObj);
        Parcel* reply = parcelForJavaObject(env, replyObj);
        //gBinderProxyOffsets.mObject中保存的是new BpBinder(handle)对象
        IBinder* target = (IBinder*) env->GetLongField(obj, gBinderProxyOffsets.mObject);
        ...
    
        //此处便是BpBinder::transact()
        status_t err = target->transact(code, *data, reply, flags);
        ...
        //最后根据transact执行具体情况,抛出相应的Exception
        signalExceptionForError(env, obj, err, true , data->dataSize());
        return JNI_FALSE;
    }
    
    status_t BpBinder::transact(
        uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
    {
        if (mAlive) {
            status_t status = IPCThreadState::self()->transact(
                mHandle, code, data, reply, flags);
            if (status == DEAD_OBJECT) mAlive = 0;
            return status;
        }
        return DEAD_OBJECT;
    }
    

    IPCThreadState::self()采用单例模式,保证每个线程只有一个实例对象

    status_t IPCThreadState::transact(int32_t handle,
                                      uint32_t code, const Parcel& data,
                                      Parcel* reply, uint32_t flags)
    {
        status_t err = data.errorCheck(); //数据错误检查
        flags |= TF_ACCEPT_FDS;
        ....
        if (err == NO_ERROR) {
             // 传输数据 
            err = writeTransactionData(BC_TRANSACTION, flags, handle, code, data, NULL);
        }
    
        if (err != NO_ERROR) {
            if (reply) reply->setError(err);
            return (mLastError = err);
        }
    
        // 默认情况下,都是采用非oneway的方式, 也就是需要等待服务端的返回结果
        if ((flags & TF_ONE_WAY) == 0) {
            if (reply) {
                //reply对象不为空 
                err = waitForResponse(reply);
            }else {
                Parcel fakeReply;
                err = waitForResponse(&fakeReply);
            }
        } else {
            err = waitForResponse(NULL, NULL);
        }
        return err;
    }
    

    这个过程涉及到通过writeTransactionData()方法传递数据,同时我们注意到分两种方式,一种oneway一种非oneway。还有对于err如果生成返回的。并且在一开头就有代码 flags |= TF_ACCEPT_FDS;是干啥的。

    • 此时我们就理解 flags |= TF_ACCEPT_FDS是处理后的flags吧。
      对于writeTransactionData方法传递数据我们得看源码
    status_t IPCThreadState::writeTransactionData(int32_t cmd, uint32_t binderFlags,
        int32_t handle, uint32_t code, const Parcel& data, status_t* statusBuffer)
    {
        binder_transaction_data tr;
        tr.target.ptr = 0;
        tr.target.handle = handle; // handle指向AMS
        tr.code = code;            // START_SERVICE_TRANSACTION
        tr.flags = binderFlags;    // 0
        tr.cookie = 0;
        tr.sender_pid = 0;
        tr.sender_euid = 0;
    
        const status_t err = data.errorCheck();
        if (err == NO_ERROR) {
            // data为startService相关信息
            tr.data_size = data.ipcDataSize();   // mDataSize
            tr.data.ptr.buffer = data.ipcData(); // mData指针
            tr.offsets_size = data.ipcObjectsCount()*sizeof(binder_size_t); //mObjectsSize
            tr.data.ptr.offsets = data.ipcObjects(); //mObjects指针
        }
        ...
        mOut.writeInt32(cmd);         //cmd = BC_TRANSACTION
        mOut.write(&tr, sizeof(tr));  //写入binder_transaction_data数据
        return NO_ERROR;
    }
    

    也就是说,将我们的data数据还有传递的cmd,flags等数据封装到binder_transaction_data中,然后将此结构体通过mOut写入。
    此时我们还不知道mOut是什么?

    没关系,我们先预留问题在这里稍后会回答。

    那我们继续看那些err怎么传递来的吧

    IPC.waitForResponse

    status_t IPCThreadState::waitForResponse(Parcel *reply, status_t *acquireResult)
    {
        int32_t cmd;
        int32_t err;
        while (1) {
            if ((err=talkWithDriver()) < NO_ERROR) break; 
            err = mIn.errorCheck();
            if (err < NO_ERROR) break; //当存在error则退出循环
             //每当跟Driver交互一次,若mIn收到数据则往下执行一次BR命令
            if (mIn.dataAvail() == 0) continue;
            cmd = mIn.readInt32();
            switch (cmd) {
            case BR_TRANSACTION_COMPLETE:
                //只有当不需要reply, 也就是oneway时 才会跳出循环,否则还需要等待.
                if (!reply && !acquireResult) goto finish; break;
            case BR_DEAD_REPLY:
                err = DEAD_OBJECT;         goto finish;
            case BR_FAILED_REPLY:
                err = FAILED_TRANSACTION;  goto finish;
            case BR_REPLY: ...             goto finish;
            default:
                err = executeCommand(cmd);  
                if (err != NO_ERROR) goto finish;
                break;
            }
        }
    finish:
        if (err != NO_ERROR) {
            if (reply) reply->setError(err); //将发送的错误代码返回给最初的调用者
        }
        return err;
    }
    

    我们比较细致的说下这个方法,首先一直循环,卡在talkWithDriver这里,如果talkWithDriver返回一个err则立马退出循环,如果talkWithDriver没有返回一个err继续执行,也就是此时不存在错误。然后从mIn回写的值里面获取数据,如果首先拿出来的是err则返回,如果不是看dataAvail()这个值如果是0就继续与内核交互调用talkWithDriver(),如果不是0,那么就读一个cmd,然后区分cmd,区分也分情况,比较重要的是executeCommand处理命令,当然如果不需要reply也就是oneway的时候,会走breank。因为oneway是异步的不需要结果,没有错误,不需要结果,那还循环个毛线线。

    里面包含几个命令是直接结束Binder通信的:

    • 非oneway也就是同步并且命令是BR_TRANSACTION_COMPLETE(交互完毕)!reply && !acquireResult时候,意思就是binder认为这次任务完成了,结果也给reply了,也不需要请求了,那还继续给毛线线。
    • BR_DEAD_REPLY
    • BR_FAILED_REPLY
    • BR_REPLY:Binder驱动向Client端发送回应消息; 对于非oneway transaction时,当收到该消息,则完整地完成本次Binder通信;

    都标志着结束。

    status_t IPCThreadState::executeCommand(int32_t cmd)
    {
        BBinder* obj;
        RefBase::weakref_type* refs;
        status_t result = NO_ERROR;
    
        switch ((uint32_t)cmd) {
        case BR_ERROR: ...
        case BR_OK: ...
        case BR_ACQUIRE: ...
        case BR_RELEASE: ...
        case BR_INCREFS: ...
        case BR_TRANSACTION: ... //Binder驱动向Server端发送消息
        case BR_DEAD_BINDER: ...
        case BR_CLEAR_DEATH_NOTIFICATION_DONE: ...
        case BR_NOOP: ...
        case BR_SPAWN_LOOPER: ... //创建新binder线程
        default: ...
        }
    }
    

    那继续看看这些命令如何返回到这里的:

    //mOut有数据,mIn还没有数据。doReceive默认值为true
    status_t IPCThreadState::talkWithDriver(bool doReceive)
    {
        binder_write_read 
        
    // 游标位置大于等于数据大小,说明读入的数据都已经被消化了
        const bool needRead = mIn.dataPosition() >= mIn.dataSize();
        const size_t outAvail = (!doReceive || needRead) ? mOut.dataSize() : 0;
    
        bwr.write_size = outAvail;
        bwr.write_buffer = (uintptr_t)mOut.data();
    
        if (doReceive && needRead) {
            //接收数据缓冲区信息的填充。当收到驱动的数据,则写入mIn
            bwr.read_size = mIn.dataCapacity();
            bwr.read_buffer = (uintptr_t)mIn.data();
        } else {
            bwr.read_size = 0;
            bwr.read_buffer = 0;
        }
    
        // 当同时没有输入和输出数据则直接返回
        if ((bwr.write_size == 0) && (bwr.read_size == 0)) return NO_ERROR;
    
        bwr.write_consumed = 0;
        bwr.read_consumed = 0;
        status_t err;
        do {
            //ioctl执行binder读写操作,经过syscall,进入Binder驱动。调用Binder_ioctl【小节3.1】
            if (ioctl(mProcess->mDriverFD, BINDER_WRITE_READ, &bwr) >= 0)
                err = NO_ERROR;
            else
                err = -errno;
            ...
        } while (err == -EINTR);
    
        if (err >= NO_ERROR) {
            if (bwr.write_consumed > 0) {
                if (bwr.write_consumed < mOut.dataSize())
                    mOut.remove(0, bwr.write_consumed);
                else
                    mOut.setDataSize(0);
            }
            if (bwr.read_consumed > 0) {
                mIn.setDataSize(bwr.read_consumed);
                mIn.setDataPosition(0);
            }
            return NO_ERROR;
        }
        return err;
    }
    

    我们看到是用ioctl与内核进行通信,由于linux系统是完全的驱动注册系统,所以Binder作为一个驱动也不例外,只不过特殊的一点是这个驱动不需要和硬件驱动硬件,操作的是字符设备。

    到目前为止我们至少从流程上已经清楚,从app到除了内核之外的通信过程,是不是感觉还是不明白,因为我们没有接触内核,接触内核怎么写就很清楚,原来Binder如此巧妙高效可爱。。。我编不下去了。。。

    Binder内核通信

    我们到了内核通信,就不用再和framework那样细致了,我们只是简单说流程,让我们明白那些点从而理解通信本质。

    static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
    {
        int ret;
        struct binder_proc *proc = filp->private_data;
        struct binder_thread *thread;
    
        //当binder_stop_on_user_error>=2时,则该线程加入等待队列并进入休眠状态. 该值默认为0
        ret = wait_event_interruptible(binder_user_error_wait, binder_stop_on_user_error < 2);
        ...
        binder_lock(__func__);
        //查找或创建binder_thread结构体
        thread = binder_get_thread(proc);
        ...
        switch (cmd) {
            case BINDER_WRITE_READ:
                ret = binder_ioctl_write_read(filp, cmd, arg, thread);
                break;
            ...
        }
        ret = 0;
    
    err:
        if (thread)
            thread->looper &= ~BINDER_LOOPER_STATE_NEED_RETURN;
        binder_unlock(__func__);
        wait_event_interruptible(binder_user_error_wait, binder_stop_on_user_error < 2);
        return ret;
    }
    

    从文件指针中获取binder_proc,然后通过binder_get_thread获取thread方法。

    我们继续看吧

    static int binder_ioctl_write_read(struct file *filp,
                    unsigned int cmd, unsigned long arg,
                    struct binder_thread *thread)
    {
        int ret = 0;
        struct binder_proc *proc = filp->private_data;
        unsigned int size = _IOC_SIZE(cmd);
        void __user *ubuf = (void __user *)arg;
        struct binder_write_read bwr;
        if (size != sizeof(struct binder_write_read)) {
            ret = -EINVAL;
            goto out;
        }
        //将用户空间bwr结构体拷贝到内核空间
        if (copy_from_user(&bwr, ubuf, sizeof(bwr))) {
            ret = -EFAULT;
            goto out;
        }
    
        if (bwr.write_size > 0) {
            //将数据放入目标进程
            ret = binder_thread_write(proc, thread,
                          bwr.write_buffer,
                          bwr.write_size,
                          &bwr.write_consumed);
            //当执行失败,则直接将内核bwr结构体写回用户空间,并跳出该方法
            if (ret < 0) {
                bwr.read_consumed = 0;
                if (copy_to_user_preempt_disabled(ubuf, &bwr, sizeof(bwr)))
                    ret = -EFAULT;
                goto out;
            }
        }
        if (bwr.read_size > 0) {
            //读取自己队列的数据
            ret = binder_thread_read(proc, thread, bwr.read_buffer,
                 bwr.read_size,
                 &bwr.read_consumed,
                 filp->f_flags & O_NONBLOCK);
            //当进程的todo队列有数据,则唤醒在该队列等待的进程
            if (!list_empty(&proc->todo))
                wake_up_interruptible(&proc->wait);
            //当执行失败,则直接将内核bwr结构体写回用户空间,并跳出该方法
            if (ret < 0) {
                if (copy_to_user_preempt_disabled(ubuf, &bwr, sizeof(bwr)))
                    ret = -EFAULT;
                goto out;
            }
        }
    
        if (copy_to_user(ubuf, &bwr, sizeof(bwr))) {
            ret = -EFAULT;
            goto out;
        }
    out:
        return ret;
    }   
    

    我们首先需要将用户空间的bwr拷贝到内核空间才可以用。
    我们这里也有一个问题binder_thread_write是将数据写入目标进程,那么怎么写到目标进程里面去了,目前我们一直在当前进程运行,只是把数据通过了AMP->AMS也就是system_server进程。哪里来的目标进程
    ,所以这里我们并不懂,需要追寻源码

    然后我们在说说,读取自己数据使用的是binder_thread_read,这里是利用bwr.write_size与bwr.read_size来进行区分读写的。

    static int binder_thread_write(struct binder_proc *proc,
                struct binder_thread *thread,
                binder_uintptr_t binder_buffer, size_t size,
                binder_size_t *consumed)
    {
        uint32_t cmd;
        void __user *buffer = (void __user *)(uintptr_t)binder_buffer;
        void __user *ptr = buffer + *consumed;
        void __user *end = buffer + size;
        while (ptr < end && thread->return_error == BR_OK) {
            //拷贝用户空间的cmd命令,此时为BC_TRANSACTION
            if (get_user(cmd, (uint32_t __user *)ptr)) -EFAULT;
            ptr += sizeof(uint32_t);
            switch (cmd) {
            case BC_TRANSACTION:
            case BC_REPLY: {
                struct binder_transaction_data tr;
                //拷贝用户空间的binder_transaction_data
                if (copy_from_user(&tr, ptr, sizeof(tr)))   return -EFAULT;
                ptr += sizeof(tr);
                binder_transaction(proc, thread, &tr, cmd == BC_REPLY);
                break;
            }
            ...
        }
        *consumed = ptr - buffer;
      }
      return 0;
    }
    

    拷贝用户空间的cmd,然后调用binder_transaction

    static void binder_transaction(struct binder_proc *proc,
                   struct binder_thread *thread,
                   struct binder_transaction_data *tr, int reply){
         struct binder_transaction *t;
         struct binder_work *tcomplete;
         binder_size_t *offp, *off_end;
         binder_size_t off_min;
         struct binder_proc *target_proc;
         struct binder_thread *target_thread = NULL;
         struct binder_node *target_node = NULL;
         struct list_head *target_list;
         wait_queue_head_t *target_wait;
         struct binder_transaction *in_reply_to = NULL;
    
        if (reply) {
            ...
        }else {
            if (tr->target.handle) {
                struct binder_ref *ref;
                // 由handle 找到相应 binder_ref, 由binder_ref 找到相应 binder_node
                ref = binder_get_ref(proc, tr->target.handle);
                target_node = ref->node;
            } else {
                target_node = binder_context_mgr_node;
            }
            // 由binder_node 找到相应 binder_proc
            target_proc = target_node->proc;
        }
    
    
        if (target_thread) {
            e->to_thread = target_thread->pid;
            target_list = &target_thread->todo;
            target_wait = &target_thread->wait;
        } else {
            //首次执行target_thread为空
            target_list = &target_proc->todo;
            target_wait = &target_proc->wait;
        }
    
        t = kzalloc(sizeof(*t), GFP_KERNEL);
        tcomplete = kzalloc(sizeof(*tcomplete), GFP_KERNEL);
    
        //非oneway的通信方式,把当前thread保存到transaction的from字段
        if (!reply && !(tr->flags & TF_ONE_WAY))
            t->from = thread;
        else
            t->from = NULL;
    
        t->sender_euid = task_euid(proc->tsk);
        t->to_proc = target_proc; //此次通信目标进程为system_server
        t->to_thread = target_thread;
        t->code = tr->code;  //此次通信code = START_SERVICE_TRANSACTION
        t->flags = tr->flags;  // 此次通信flags = 0
        t->priority = task_nice(current);
    
        //从目标进程target_proc中分配内存空间=
        t->buffer = binder_alloc_buf(target_proc, tr->data_size,
            tr->offsets_size, !reply && (t->flags & TF_ONE_WAY));
    
        t->buffer->allow_user_free = 0;
        t->buffer->transaction = t;
        t->buffer->target_node = target_node;
    
        if (target_node)
            binder_inc_node(target_node, 1, 0, NULL); //引用计数加1
        //binder对象的偏移量
        offp = (binder_size_t *)(t->buffer->data + ALIGN(tr->data_size, sizeof(void *)));
    
        //分别拷贝用户空间的binder_transaction_data中ptr.buffer和ptr.offsets到目标进程的binder_buffer
        copy_from_user(t->buffer->data,
            (const void __user *)(uintptr_t)tr->data.ptr.buffer, tr->data_size);
        copy_from_user(offp,
            (const void __user *)(uintptr_t)tr->data.ptr.offsets, tr->offsets_size);
    
        off_end = (void *)offp + tr->offsets_size;
    
        for (; offp < off_end; offp++) {
            struct flat_binder_object *fp;
            fp = (struct flat_binder_object *)(t->buffer->data + *offp);
            off_min = *offp + sizeof(struct flat_binder_object);
            switch (fp->type) {
            ...
            case BINDER_TYPE_HANDLE:
            case BINDER_TYPE_WEAK_HANDLE: {
                //处理引用计数情况
                struct binder_ref *ref = binder_get_ref(proc, fp->handle);
                if (ref->node->proc == target_proc) {
                    if (fp->type == BINDER_TYPE_HANDLE)
                        fp->type = BINDER_TYPE_BINDER;
                    else
                        fp->type = BINDER_TYPE_WEAK_BINDER;
                    fp->binder = ref->node->ptr;
                    fp->cookie = ref->node->cookie;
                    binder_inc_node(ref->node, fp->type == BINDER_TYPE_BINDER, 0, NULL);
                } else {    
                    struct binder_ref *new_ref;
                    new_ref = binder_get_ref_for_node(target_proc, ref->node);
                    fp->handle = new_ref->desc;
                    binder_inc_ref(new_ref, fp->type == BINDER_TYPE_HANDLE, NULL);
                }
            } break;
            ...
    
            default:
                return_error = BR_FAILED_REPLY;
                goto err_bad_object_type;
            }
        }
    
        if (reply) {
            //BC_REPLY的过程
            binder_pop_transaction(target_thread, in_reply_to);
        } else if (!(t->flags & TF_ONE_WAY)) {
            //BC_TRANSACTION 且 非oneway,则设置事务栈信息
            t->need_reply = 1;
            t->from_parent = thread->transaction_stack;
            thread->transaction_stack = t;
        } else {
            //BC_TRANSACTION 且 oneway,则加入异步todo队列
            if (target_node->has_async_transaction) {
                target_list = &target_node->async_todo;
                target_wait = NULL;
            } else
                target_node->has_async_transaction = 1;
        }
    
        //将BINDER_WORK_TRANSACTION添加到目标队列,即target_proc->todo
        t->work.type = BINDER_WORK_TRANSACTION;
        list_add_tail(&t->work.entry, target_list);
    
        //将BINDER_WORK_TRANSACTION_COMPLETE添加到当前线程队列,即thread->todo
        tcomplete->type = BINDER_WORK_TRANSACTION_COMPLETE;
        list_add_tail(&tcomplete->entry, &thread->todo);
    
        //唤醒等待队列,本次通信的目标队列为target_proc->wait
        if (target_wait)
            wake_up_interruptible(target_wait);
        return;
    }
    

    tr->target.handle,这个是目标handle,通过这个可以在Binder引用红黑树里面找到对应的binder引用节点。由binder_node找到对应目标节点的进程,对应进程找到目标节点的todo队列,和wait队列。

    如果是非oneway也就是同步的时候要将当前线程保存到t->from:struct binder_transaction *t;

    我们这个时候要明白目标进程是谁,是syatem_server。

    我们将目标进程结构体,目标线程结构体,还有通信的code优先级等信息封装到binder_transaction *t中。然后将我们需要传递的数据拷贝到目标进程中去,剩下的事情就是交给todo队列了。就是按照onyway和非oneway区分,看加入到异步队列还是同步任务栈中,最后将任务类型进行修改需要执行,并且添加到目标进程的目标线程的todo队列中去。然后进行唤醒。

    我们小结这段:

    1. 查询目标进程的过程: handle -> binder_ref -> binder_node -> binder_proc
    2. 将BINDER_WORK_TRANSACTION添加到目标队列target_list:
      1. call事务, 则目标队列target_list=target_proc->todo;
      2. reply事务,则目标队列target_list=target_thread->todo;
      3. async事务,则目标队列target_list=target_node->async_todo.
    3. 数据拷贝
      1. 将用户空间binder_transaction_data中ptr.buffer和ptr.offsets拷贝到目标进程的binder_buffer->data;
      2. 这就是只拷贝一次的真理所在;
    4. 设置事务栈信息
      1. BC_TRANSACTION且非oneway, 则将当前事务添加到thread->transaction_stack;
    5. 事务分发过程:
      1. 将BINDER_WORK_TRANSACTION添加到目标队列(此时为target_proc->todo队列);
      2. 将BINDER_WORK_TRANSACTION_COMPLETE添加到当前线程thread->todo队列;
    6. 唤醒目标进程target_proc开始执行事务。
      该方法中proc/thread是指当前发起方的进程信息,而binder_proc是指目标接收端进程。 此时当前线程thread的todo队列已经有事务, 接下来便会进入binder_thread_read来处理相关的事务.

    这里已经把给target进程写数据说差不多了,我们回过头再看IPCThreadState::talkWithDriver()这个方法巧妙的利用一个结构体binder_write_read 与一个方法ioctl就实现了可以读可以写,是不是比较难理解,因为我们以往的认识都是比如socket有读端也有写端,比如管道也是。而Binder是通过一个结构体内部write_size 与read_size 来区分到底是读数据还是写数据。在内核中也就是binder_ioctl-->binder_ioctl_write_read中如果bwr.write_size > 0则执行binder_thread_write,如果bwr.read_size > 0则执行binder_thread_read,这样的话在一个循环内,就可以把数据写完和读完从而达到进程交互的目的。

    binder内核读数据

    binder_thread_read(){
        //当已使用字节数为0时,将BR_NOOP响应码放入指针ptr
        if (*consumed == 0) {
                if (put_user(BR_NOOP, (uint32_t __user *)ptr))
                    return -EFAULT;
                ptr += sizeof(uint32_t);
            }
    
    retry:
        //binder_transaction()已设置transaction_stack不为空,则wait_for_proc_work为false.
        wait_for_proc_work = thread->transaction_stack == NULL &&
                list_empty(&thread->todo);
    
        thread->looper |= BINDER_LOOPER_STATE_WAITING;
        if (wait_for_proc_work)
          proc->ready_threads++; //进程中空闲binder线程加1
    
        //只有当前线程todo队列为空,并且transaction_stack也为空,才会开始处于当前进程的事务
        if (wait_for_proc_work) {
            if (non_block) {
                ...
            } else
                //当进程todo队列没有数据,则进入休眠等待状态
                ret = wait_event_freezable_exclusive(proc->wait, binder_has_proc_work(proc, thread));
        } else {
            if (non_block) {
                ...
            } else
                //当线程todo队列有数据则执行往下执行;当线程todo队列没有数据,则进入休眠等待状态
                ret = wait_event_freezable(thread->wait, binder_has_thread_work(thread));
        }
    
        if (wait_for_proc_work)
          proc->ready_threads--; //退出等待状态, 则进程中空闲binder线程减1
        thread->looper &= ~BINDER_LOOPER_STATE_WAITING;
        ...
    
        while (1) {
    
            uint32_t cmd;
            struct binder_transaction_data tr;
            struct binder_work *w;
            struct binder_transaction *t = NULL;
            //先从线程todo队列获取事务数据
            if (!list_empty(&thread->todo)) {
                w = list_first_entry(&thread->todo, struct binder_work, entry);
            // 线程todo队列没有数据, 则从进程todo对获取事务数据
            } else if (!list_empty(&proc->todo) && wait_for_proc_work) {
                w = list_first_entry(&proc->todo, struct binder_work, entry);
            } else {
                //没有数据,则返回retry
                if (ptr - buffer == 4 &&
                    !(thread->looper & BINDER_LOOPER_STATE_NEED_RETURN))
                    goto retry;
                break;
            }
    
            switch (w->type) {
                case BINDER_WORK_TRANSACTION:
                    //获取transaction数据
                    t = container_of(w, struct binder_transaction, work);
                    break;
    
                case BINDER_WORK_TRANSACTION_COMPLETE:
                    cmd = BR_TRANSACTION_COMPLETE;
                    //将BR_TRANSACTION_COMPLETE写入*ptr,并跳出循环。
                    put_user(cmd, (uint32_t __user *)ptr);
                    list_del(&w->entry);
                    kfree(w);
                    break;
    
                case BINDER_WORK_NODE: ...    break;
                case BINDER_WORK_DEAD_BINDER:
                case BINDER_WORK_DEAD_BINDER_AND_CLEAR:
                case BINDER_WORK_CLEAR_DEATH_NOTIFICATION: ...   break;
            }
    
            //只有BINDER_WORK_TRANSACTION命令才能继续往下执行
            if (!t)
                continue;
    
            if (t->buffer->target_node) {
                //获取目标node
                struct binder_node *target_node = t->buffer->target_node;
                tr.target.ptr = target_node->ptr;
                tr.cookie =  target_node->cookie;
                t->saved_priority = task_nice(current);
                ...
                cmd = BR_TRANSACTION;  //设置命令为BR_TRANSACTION
            } else {
                tr.target.ptr = NULL;
                tr.cookie = NULL;
                cmd = BR_REPLY; //设置命令为BR_REPLY
            }
            tr.code = t->code;
            tr.flags = t->flags;
            tr.sender_euid = t->sender_euid;
    
            if (t->from) {
                struct task_struct *sender = t->from->proc->tsk;
                //当非oneway的情况下,将调用者进程的pid保存到sender_pid
                tr.sender_pid = task_tgid_nr_ns(sender,
                                current->nsproxy->pid_ns);
            } else {
                //当oneway的的情况下,则该值为0
                tr.sender_pid = 0;
            }
    
            tr.data_size = t->buffer->data_size;
            tr.offsets_size = t->buffer->offsets_size;
            tr.data.ptr.buffer = (void *)t->buffer->data + proc->user_buffer_offset;
            tr.data.ptr.offsets = tr.data.ptr.buffer +
                        ALIGN(t->buffer->data_size, sizeof(void *));
    
            //将cmd和数据写回用户空间
            if (put_user(cmd, (uint32_t __user *)ptr))
                return -EFAULT;
            ptr += sizeof(uint32_t);
            if (copy_to_user(ptr, &tr, sizeof(tr)))
                return -EFAULT;
            ptr += sizeof(tr);
    
            list_del(&t->work.entry);
            t->buffer->allow_user_free = 1;
            if (cmd == BR_TRANSACTION && !(t->flags & TF_ONE_WAY)) {
                t->to_parent = thread->transaction_stack;
                t->to_thread = thread;
                thread->transaction_stack = t;
            } else {
                t->buffer->transaction = NULL;
                kfree(t); //通信完成,则运行释放
            }
            break;
        }
    done:
        *consumed = ptr - buffer;
        //当满足请求线程加已准备线程数等于0,已启动线程数小于最大线程数(15),
        //且looper状态为已注册或已进入时创建新的线程。
        if (proc->requested_threads + proc->ready_threads == 0 &&
            proc->requested_threads_started < proc->max_threads &&
            (thread->looper & (BINDER_LOOPER_STATE_REGISTERED |
             BINDER_LOOPER_STATE_ENTERED))) {
            proc->requested_threads++;
            // 生成BR_SPAWN_LOOPER命令,用于创建新的线程
            put_user(BR_SPAWN_LOOPER, (uint32_t __user *)buffer);
        }
        return 0;
    }
    

    首先当transaction_stack不空且todo队列空闲才执行任务,当进程todo队列没有数据,则会进入休眠状态(wait_event_freezable_exclusive),然后进入循环如果todo有数据则获取事务(binder_work)没有数据则继续到之前的那个步骤再次等待被唤醒。既然现在有数据,则通过type看具体需要对数据做什么操作。如果是BINDER_WORK_TRANSACTION_COMPLETE说明是正常结束,则将数据写到*ptr也就是用户空间,只有命令是BINDER_WORK_TRANSACTION才继续执行,首先找到目标node,将数据都设置tr中,完了cmd变成BR_REPLY,当命令是BR_XX的时候是驱动主动和用户态进行交互。然后用t->from区分是oneway还是非oneway。因为之前在写的时候如果是非oneway也就是同步的时候要将当前线程保存到t->from:struct binder_transaction *t;现在如果是oneway则tr.sender_pid=0,非oneway要讲调用者的进程id保存到sender_pid中。然后还是继续设置tr那些数据,完了将cmd和数据写到用户空间。

    到现在我们推测出每个进程都有一个todo队列,这个todo队列一直在循环,如果有数据则当前进程要把数据放入对方进程的todo队列中,同理。

    当数据返回到用户空间之后,就会执行IPC.executeCommand然后针对那些BR命令去做事情,比如执行BR_TRANSACTION的时候那就说明有数据到达用户空间,用户空间要对这些数据进行处理或者保存之类的操作,当处理完成之后就需要告诉驱动我处理完了所以就通过IPC.sendReply发送给驱动BC_REPLY,当驱动在binder_transaction收到是BC_REPLY消息的时候就会告诉目标进程我这边完成数据接收了。所以驱动就会给对方的目标todo中添加消息。 TIM截图20171113200142.png

    来我们再根据这个图回顾一下:
    首先APP进程给驱动发送BC_TRANSACTION

    static int binder_thread_write(){
    while (ptr < end && thread->return_error == BR_OK) {
            //拷贝用户空间的cmd命令,此时为BC_TRANSACTION
            if (get_user(cmd, (uint32_t __user *)ptr)) -EFAULT;
            ptr += sizeof(uint32_t);
            switch (cmd) {
            case BC_TRANSACTION:
            case BC_REPLY: {
                struct binder_transaction_data tr;
                //拷贝用户空间的binder_transaction_data
                if (copy_from_user(&tr, ptr, sizeof(tr)))   return -EFAULT;
                ptr += sizeof(tr);
                binder_transaction(proc, thread, &tr, cmd == BC_REPLY);
                break;
            }
            ...
        }
    }
    

    此方法处理BC_TRANSACTION,BR_REPLAY是一样的。
    然后在binder_transaction()方法中做处理

    static void binder_transaction(){}
    

    做的事情是:

    1. 查询目标进程的过程: handle -> binder_ref -> binder_node -> binder_proc
    2. 将BINDER_WORK_TRANSACTION添加到目标队列target_list:
      1. call事务, 则目标队列target_list=target_proc->todo;
      2. reply事务,则目标队列target_list=target_thread->todo;
      3. async事务,则目标队列target_list=target_node->async_todo.
    3. 数据拷贝
      • 将用户空间binder_transaction_data中ptr.buffer和ptr.offsets拷贝到目标进程的binder_buffer->data;这就是只拷贝一次的真理所在;
    4. 设置事务栈信息
      • BC_TRANSACTION且非oneway, 则将当前事务添加到thread->transaction_stack;
    5. 事务分发过程:
      1. 将BINDER_WORK_TRANSACTION添加到目标队列(此时为target_proc->todo队列);
      2. 将BINDER_WORK_TRANSACTION_COMPLETE添加到当前线程thread->todo队列;
    6. 唤醒目标进程target_proc开始执行事务。

    接着就是binder_thread_read读数据。

    注意这里将BINDER_WORK_TRANSACTION_COMPLETE添加到当前线程thread->todo队列;


    image.png

    因为:

    static int binder_ioctl_write_read()
    {
        if (bwr.write_size > 0) {
            //将数据放入目标进程
            ret = binder_thread_write(...);
        }
        if (bwr.read_size > 0) {
            //读取自己队列的数据 
            ret = binder_thread_read(...);
            ...
            }
        }
    }
    

    在binder_thread_read中:

    binder_thread_read(){
      switch (w->type) {
             ...
                case BINDER_WORK_TRANSACTION_COMPLETE:
                    cmd = BR_TRANSACTION_COMPLETE;
                    //将BR_TRANSACTION_COMPLETE写入*ptr,并跳出循环。
                    put_user(cmd, (uint32_t __user *)ptr);
                    list_del(&w->entry);
                    kfree(w);
                    break;
              ...
            }
      f (t->buffer->target_node) {
                //获取目标node
                struct binder_node *target_node = t->buffer->target_node;
                tr.target.ptr = target_node->ptr;
                tr.cookie =  target_node->cookie;
                t->saved_priority = task_nice(current);
                ...
                cmd = BR_TRANSACTION;  //设置命令为BR_TRANSACTION
            }
        //将cmd和数据写回用户空间
            if (put_user(cmd, (uint32_t __user *)ptr))
                return -EFAULT;
            ptr += sizeof(uint32_t);
            if (copy_to_user(ptr, &tr, sizeof(tr)))
                return -EFAULT;
            ptr += sizeof(tr);
      ...
    }
    

    看到木有,在写的过程中刚添加完这里就用到了,将命令设置成cmd,先放入到用户空间。

    然后将目标进程节点找出来,将命令设置成BR_TRANSACTION,再后来就将命令回写到用户空间

    这个时候就在用户空间中:

    status_t IPCThreadState::waitForResponse(Parcel *reply, status_t *acquireResult)
    {
        int32_t cmd;
        int32_t err;
    
        while (1) {
            if ((err=talkWithDriver()) < NO_ERROR) break; 
            err = mIn.errorCheck();
            if (err < NO_ERROR) break; //当存在error则退出循环
    
             //每当跟Driver交互一次,若mIn收到数据则往下执行一次BR命令
            if (mIn.dataAvail() == 0) continue;
    
            cmd = mIn.readInt32();
    
            switch (cmd) {
            case BR_TRANSACTION_COMPLETE:
                //只有当不需要reply, 也就是oneway时 才会跳出循环,否则还需要等待.
                if (!reply && !acquireResult) goto finish; break;
    ...
            default:
                err = executeCommand(cmd);  
                if (err != NO_ERROR) goto finish;
                break;
            }
        }
    }
    

    所以这个时候APP这边就进入休眠了。

    那么这个时候在AMS进程这边:

    status_t IPCThreadState::executeCommand(int32_t cmd)
    {
        BBinder* obj;
        RefBase::weakref_type* refs;
        status_t result = NO_ERROR;
    
        switch ((uint32_t)cmd) {
        case BR_ERROR: ...
        case BR_OK: ...
        case BR_ACQUIRE: ...
        case BR_RELEASE: ...
        case BR_INCREFS: ...
        case BR_TRANSACTION: ... //Binder驱动向Server端发送消息
               if ((tr.flags & TF_ONE_WAY) == 0) {
                    if (error < NO_ERROR) reply.setError(error);
                    //对于非oneway, 需要reply通信过程,则向Binder驱动发送BC_REPLY命令
                    sendReply(reply, 0);
                }
        case BR_DEAD_BINDER: ...
        case BR_CLEAR_DEATH_NOTIFICATION_DONE: ...
        case BR_NOOP: ...
        case BR_SPAWN_LOOPER: ... //创建新binder线程
        default: ...
        }
    }
    

    我们看到了BR_TRANSACTION,调用的是sendReply:

    status_t IPCThreadState::sendReply(const Parcel& reply, uint32_t flags)
    {
        status_t err;
        status_t statusBuffer;
        err = writeTransactionData(BC_REPLY, flags, -1, 0, reply, &statusBuffer);
        if (err < NO_ERROR) return err;
        return waitForResponse(NULL, NULL);
    }
    

    上面这行了writeTransactionData和waitForResponse两个方法

    writeTransactionData这个方法中执行时候我们看参数是BC_REPLY,这个方法继续将cmd给驱动,我们继续看看驱动收到BC_REPLY时候做什么:

    static int binder_thread_write(){
    while (ptr < end && thread->return_error == BR_OK) {
            //拷贝用户空间的cmd命令,此时为BC_TRANSACTION
            if (get_user(cmd, (uint32_t __user *)ptr)) -EFAULT;
            ptr += sizeof(uint32_t);
            switch (cmd) {
            case BC_TRANSACTION:
            case BC_REPLY: {
                struct binder_transaction_data tr;
                //拷贝用户空间的binder_transaction_data
                if (copy_from_user(&tr, ptr, sizeof(tr)))   return -EFAULT;
                ptr += sizeof(tr);
                binder_transaction(proc, thread, &tr, cmd == BC_REPLY);
                break;
            }
            ...
        }
    }
    

    其实和BC_TRANSACTION处理一样,

    static void binder_transaction(){
     //将BINDER_WORK_TRANSACTION添加到目标队列,本次通信的目标队列为target_thread->todo
        t->work.type = BINDER_WORK_TRANSACTION;
        list_add_tail(&t->work.entry, target_list);
    
        //将BINDER_WORK_TRANSACTION_COMPLETE添加到当前线程的todo队列
        tcomplete->type = BINDER_WORK_TRANSACTION_COMPLETE;
        list_add_tail(&tcomplete->entry, &thread->todo);
    }
    

    做的事情是:

    1. 查询目标进程的过程: handle -> binder_ref -> binder_node -> binder_proc
    2. 将BINDER_WORK_TRANSACTION添加到目标队列target_list:
      1. call事务, 则目标队列target_list=target_proc->todo;
      2. reply事务,则目标队列target_list=target_thread->todo;
      3. async事务,则目标队列target_list=target_node->async_todo.
    3. 数据拷贝
      • 将用户空间binder_transaction_data中ptr.buffer和ptr.offsets拷贝到目标进程的binder_buffer->data;这就是只拷贝一次的真理所在;
    4. 设置事务栈信息
      • BC_TRANSACTION且非oneway, 则将当前事务添加到thread->transaction_stack;
    5. 事务分发过程:
      1. 将BINDER_WORK_TRANSACTION添加到目标队列(此时为target_proc->todo队列);
      2. 将BINDER_WORK_TRANSACTION_COMPLETE添加到当前线程thread->todo队列;
    6. 唤醒目标进程target_proc开始执行事务。

    注意这里将BINDER_WORK_TRANSACTION_COMPLETE添加到当前线程thread->todo队列;

    image.png

    因为:

    static int binder_ioctl_write_read()
    {
        if (bwr.write_size > 0) {
            //将数据放入目标进程
            ret = binder_thread_write(...);
        }
        if (bwr.read_size > 0) {
            //读取自己队列的数据 
            ret = binder_thread_read(...);
            ...
            }
        }
    }
    

    在binder_thread_read中:

    binder_thread_read(){
      switch (w->type) {
             ...
                case BINDER_WORK_TRANSACTION_COMPLETE:
                    cmd = BR_TRANSACTION_COMPLETE;
                    //将BR_TRANSACTION_COMPLETE写入*ptr,并跳出循环。
                    put_user(cmd, (uint32_t __user *)ptr);
                    list_del(&w->entry);
                    kfree(w);
                    break;
              ...
            }
      f (t->buffer->target_node) {
                //获取目标node
                struct binder_node *target_node = t->buffer->target_node;
                tr.target.ptr = target_node->ptr;
                tr.cookie =  target_node->cookie;
                t->saved_priority = task_nice(current);
                ...
                cmd = BR_TRANSACTION;  //设置命令为BR_TRANSACTION
            }
        //将cmd和数据写回用户空间
            if (put_user(cmd, (uint32_t __user *)ptr))
                return -EFAULT;
            ptr += sizeof(uint32_t);
            if (copy_to_user(ptr, &tr, sizeof(tr)))
                return -EFAULT;
            ptr += sizeof(tr);
      ...
    }
    

    看到木有,在写的过程中刚添加完这里就用到了,将命令设置成cmd,先放入到用户空间。

    然后将目标进程节点找出来,将命令设置成BR_TRANSACTION,再后来就将命令回写到用户空间

    这个时候就在用户空间中:

    status_t IPCThreadState::waitForResponse(Parcel *reply, status_t *acquireResult)
    {
        int32_t cmd;
        int32_t err;
    
        while (1) {
            if ((err=talkWithDriver()) < NO_ERROR) break; 
            err = mIn.errorCheck();
            if (err < NO_ERROR) break; //当存在error则退出循环
    
             //每当跟Driver交互一次,若mIn收到数据则往下执行一次BR命令
            if (mIn.dataAvail() == 0) continue;
    
            cmd = mIn.readInt32();
    
            switch (cmd) {
            case BR_TRANSACTION_COMPLETE:
                //只有当不需要reply, 也就是oneway时 才会跳出循环,否则还需要等待.
                if (!reply && !acquireResult) goto finish; break;
    ...
            default:
                err = executeCommand(cmd);  
                if (err != NO_ERROR) goto finish;
                break;
            }
        }
    }
    

    所以这个时候AMS这边就进入休眠了。

    小结使用接口

    BC协议 调用方法
    BC_TRANSACTION IPC.transact()
    BC_REPLY IPC.sendReply()
    BC_FREE_BUFFER IPC.freeBuffer()
    BC_REQUEST_DEATH_NOTIFICATION IPC.requestDeathNotification()
    BC_CLEAR_DEATH_NOTIFICATION IPC.clearDeathNotification()
    BC_DEAD_BINDER_DONE IPC.execute()

    [图片上传中...(image.png-66ada5-1510577352862-0)]

    相关文章

      网友评论

          本文标题:Binder和进程的关系

          本文链接:https://www.haomeiwen.com/subject/wcabmxtx.html