Binder和进程的关系

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)]

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,294评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,493评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,790评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,595评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,718评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,906评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,053评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,797评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,250评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,570评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,711评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,388评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,018评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,796评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,023评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,461评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,595评论 2 350

推荐阅读更多精彩内容