Binder源码分析:ServiceManager添加服务解析

465 阅读11分钟

我们以添加ActivityManagerService为例分析添加一个Service。

首先画了一下大致涉及到的类以及其中的交互,而实际的流程要更为复杂,就跟我一起来看代码吧。

ServiceManagerProxy请求添加Service

其代码在ActivityManagerServicesetSystemProcess方法当中,具体调用如下:

ServiceManager.addService(Context.ACTIVITY_SERVICE, this, /* allowIsolated= */ true,  
        DUMP_FLAG_PRIORITY_CRITICAL | DUMP_FLAG_PRIORITY_NORMAL | DUMP_FLAG_PROTO);

具体实现如下:

@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)  
public static void addService(String name, IBinder service, boolean allowIsolated,  
        int dumpPriority) {  
    try {  
        getIServiceManager().addService(name, service, allowIsolated, dumpPriority);  
    } catch (RemoteException e) {  
    }  
}

其中getIServiceManager的代码我们已经分析过了,我们之前拿到的IServiceManager的实例为ServiceManagerProxy,这里可以直接去看它的这个方法:

public void addService(String name, IBinder service, boolean allowIsolated, int dumpPriority)  
        throws RemoteException {  
    mServiceManager.addService(name, service, allowIsolated, dumpPriority);  
}

这里调用了mServiceManager的同名方法,而这个成员变量的初始化如下:

mServiceManager = IServiceManager.Stub.asInterface(remote);

这里的remote就是我们之前构造函数传入的BinderProxy,而上面的函数后我们发获取到的对象则为IServiceMaanager.Stub.Proxy,我们可以看一下它的同名方法:

@Override public void addService(java.lang.String name, android.os.IBinder service, boolean allowIsolated, int dumpPriority) throws android.os.RemoteException  
{  
  android.os.Parcel _data = android.os.Parcel.obtain();  
  android.os.Parcel _reply = android.os.Parcel.obtain();  
  try {  
    _data.writeInterfaceToken(DESCRIPTOR);  
    _data.writeString(name);  
    _data.writeStrongBinder(service);  
    _data.writeBoolean(allowIsolated);  
    _data.writeInt(dumpPriority);  
    boolean _status = mRemote.transact(Stub.TRANSACTION_addService, _data, _reply, 0);  
    _reply.readException();  
  }  
  finally {  
    _reply.recycle();  
    _data.recycle();  
  }  
}

这里我们看到就是把设置的数据和binder写入到Parcel之后 调用transact。这里我们可以看一下Parcel首先是写入了InterfaceToken,也就是IServiceManager的描述符,其次才是其他内容。我们主要关注一下如何写入Binder的。其最终调用的方法在android_os_Parcel.cpp中,代码如下:

static void android_os_Parcel_writeStrongBinder(JNIEnv* env, jclass clazz, jlong nativePtr, jobject object)  
{  
    Parcel* parcel = reinterpret_cast<Parcel*>(nativePtr);  
    if (parcel != NULL) {  
        const status_t err = parcel->writeStrongBinder(ibinderForJavaObject(env, object));  
        if (err != NO_ERROR) {  
            signalExceptionForError(env, clazz, err);  
        }  
    }  
}

我们主要关注第5行,这里有一个ibinderForJavaObject,用于从javaobject中拿到binder的native对象,我们可以看一下其源码:

sp<IBinder> ibinderForJavaObject(JNIEnv* env, jobject obj)  
{  
    if (obj == NULL) return NULL;  
  
    // 如果是Binder实例
    if (env->IsInstanceOf(obj, gBinderOffsets.mClass)) {  
        JavaBBinderHolder* jbh = (JavaBBinderHolder*)  
            env->GetLongField(obj, gBinderOffsets.mObject);  
        return jbh->get(env, obj);  
    }  
  
    // 如果是BinderProxy实例
    if (env->IsInstanceOf(obj, gBinderProxyOffsets.mClass)) {  
        return getBPNativeData(env, obj)->mObject;  
    }  
  
    return NULL;  
}

BinderProxyNativeData* getBPNativeData(JNIEnv* env, jobject obj) {  
    return (BinderProxyNativeData *) env->GetLongField(obj, gBinderProxyOffsets.mNativeData);  
}


这个地方会判断我们的传过来的javaobject是Binder的实例还是BinderProxy的实例,前者对应Binder的服务端,后者对应的是客户端,我们刚刚传过来的AMS则是服务端。这里是从javaobject拿到mObject成员变量,对应native的类JavaBBinderHolder,最后调用它的get方法拿到JavaBinder对象。此处算是完成了我们Java层的Binder在Native层的对应对象的获取。现在就可以看看ParcelwriteStrongBinder方法了:

status_t Parcel::writeStrongBinder(const sp<IBinder>& val)  
{  
    return flattenBinder(val);  
}

其中又调用了flattenBinder,这个方法比较长,我们先一点点的贴代码:

BBinder* local = nullptr;  
if (binder) local = binder->localBinder();  
if (local) local->setParceled();

if (isForRpc()) {
...
}

这里binder是我们刚刚的拿到的JavaBBinder,它的localBinder()实现如下:

BBinder* BBinder::localBinder()  
{  
    return this;  
}

也就是说返回了自己。另外这里我们不是RPC,所以其中的代码我们不需要关注,继续看后面的代码:

flat_binder_object obj;
if (binder != nullptr) {  
    if (!local) {  
       ...//如果我们传入的不是BBinder,而是BpBinder执行这里的逻辑,省略
    } else {  
        int policy = local->getMinSchedulerPolicy();  
        int priority = local->getMinSchedulerPriority();  
  
        if (policy != 0 || priority != 0) {  
            // override value, since it is set explicitly  
            schedBits = schedPolicyMask(policy, priority);  
        }  
        obj.flags = FLAT_BINDER_FLAG_ACCEPTS_FDS;  
        if (local->isRequestingSid()) {  
            obj.flags |= FLAT_BINDER_FLAG_TXN_SECURITY_CTX;  
        }  
        if (local->isInheritRt()) {  
            obj.flags |= FLAT_BINDER_FLAG_INHERIT_RT;  
        }  
        obj.hdr.type = BINDER_TYPE_BINDER;  
        obj.binder = reinterpret_cast<uintptr_t>(local->getWeakRefs());  
        obj.cookie = reinterpret_cast<uintptr_t>(local);  
    }  
} else {  
    ...
}
obj.flags |= schedBits;

上面的代码主要是将binder的一些参数拍平放到flat_binder_object当中。其中binder是放置到cookie字段,binder的弱引用放到了binder字段。

status_t status = writeObject(obj, false);  
if (status != OK) return status;  
  
return finishFlattenBinder(binder);

这里才开始真正的把数据写入 ,可以先看看这个writeObject方法:

status_t Parcel::writeObject(const flat_binder_object& val, bool nullMetaData)  
{  
    const bool enoughData = (mDataPos+sizeof(val)) <= mDataCapacity;  
    const bool enoughObjects = mObjectsSize < mObjectsCapacity;  
    if (enoughData && enoughObjects) {  
restart_write:  
        *reinterpret_cast<flat_binder_object*>(mData+mDataPos) = val; //把数据写如内存  
        ...
        // Need to write meta-data?  
        if (nullMetaData || val.binder != 0) {  
            mObjects[mObjectsSize] = mDataPos;  
            acquire_object(ProcessState::self(), val, this);  
            mObjectsSize++;  
        }  
  
        return finishWrite(sizeof(flat_binder_object));  //调整dataPos和当前DateSize
    }  
  
    if (!enoughData) {  
       ....
    }  
    if (!enoughObjects) {  
        ...
    }  
  
    goto restart_write;  
}

上面主要就是把binder写入内存当中,其他的则是处理内存不足的情况,有申请内存的代码,这里我们无须关注。可以在看一下前面的finishFlattenBinder方法:

status_t Parcel::finishFlattenBinder(const sp<IBinder>& binder)  
{  
    internal::Stability::tryMarkCompilationUnit(binder.get());  
    int16_t rep = internal::Stability::getRepr(binder.get());  
    return writeInt32(rep);  
}

这个方法主要为binder设置Repr,并且把值也写入到Parcel当中去,默认值为Level::SYSTEM,我们不再深入看其代码。

到这里大概就看完了Parcel写数据的代码了。可以看看transact方法,这里的mRemoteBinderProxy,它的transact方法中主要调用了一下代码:

final boolean result = transactNative(code, data, reply, flags);  
if (reply != null && !warnOnBlocking) {  
    reply.addFlags(Parcel.FLAG_IS_REPLY_FROM_BLOCKING_ALLOWED_OBJECT);  
}  
return result;

它的native实现在android_util_Binder.cpp中,代码如下:

static jboolean android_os_BinderProxy_transact(JNIEnv* env, jobject obj,  
        jint code, jobject dataObj, jobject replyObj, jint flags) 
{  

    Parcel* data = parcelForJavaObject(env, dataObj);  //从java层的Parcel对象获取native层的Parcel对象
    ...
    Parcel* reply = parcelForJavaObject(env, replyObj);  
    ...
    IBinder* target = getBPNativeData(env, obj)->mObject.get(); //获取native层的BinderProxy对象 
    if (target == NULL) {  
        ...
        return JNI_FALSE;  
    }  
  
    status_t err = target->transact(code, *data, reply, flags);  
    ...
    return JNI_FALSE;
}

上面主要就 是获取native层的Parcel对象和Binder对象,并且调用binder的transact方法。这里的Binder对象是什么呢,回顾之前分析的javaObjectForIBinder方法,可知此处拿到的应该是BpBinder对象。我们就可以看它的代码了:

status_t BpBinder::transact(  
    uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)  
{  
    // 只有binder或者的时候才能执行 
    if (mAlive) {  
        bool privateVendor = flags & FLAG_PRIVATE_VENDOR;  
        // 用户层的flag移除
        flags = flags & ~FLAG_PRIVATE_VENDOR;  
  
        if (code >= FIRST_CALL_TRANSACTION && code <= LAST_CALL_TRANSACTION) {  
            ... //Stability 相等判断,此处略过
        }  
  
        status_t status;  
        if (CC_UNLIKELY(isRpcBinder())) {  
            ...
        } else {  
            status = IPCThreadState::self()->transact(binderHandle(), code, data, reply, flags);  
        }  
        ....
        if (status == DEAD_OBJECT) mAlive = 0;  
  
        return status;  
    }  
  
    return DEAD_OBJECT;  
}

因为ServiceManager的id为0,此处binderHandle()拿到的值应为0。此处主要也是调用了IPCThreadStatetransact方法:

status_t IPCThreadState::transact(int32_t handle,  
                                  uint32_t code, const Parcel& data,  
                                  Parcel* reply, uint32_t flags)  
{  
    status_t err;  
  
    flags |= TF_ACCEPT_FDS;  
    err = writeTransactionData(BC_TRANSACTION, flags, handle, code, data, nullptr);  
  
    if (err != NO_ERROR) {  
        if (reply) reply->setError(err);  
        return (mLastError = err);  
    }  
  
    if ((flags & TF_ONE_WAY) == 0) {  
        if (reply) {  
            err = waitForResponse(reply);  
        } else {  
            Parcel fakeReply;  
            err = waitForResponse(&fakeReply);  
        }  
    } else {  
        err = waitForResponse(nullptr, nullptr);
    }  
  
    return err;  
}

这里主要调用了两个方法,分别是writeTransactionDatawaitForResponse,我们分别看一下。首先是writeTransactionData,它的第一个参数为BC_TRANSACTION,这是用于与Binder驱动交互的命令,除了这个之外还有其他一些,可以在binder.h当中找到。现在可以看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; /* Don't pass uninitialized stack data to a remote process */  
    tr.target.handle = handle;  
    tr.code = code;  
    tr.flags = binderFlags;  
    tr.cookie = 0;  
    tr.sender_pid = 0;  
    tr.sender_euid = 0;  
  
    const status_t err = data.errorCheck();  
    if (err == NO_ERROR) {  
        tr.data_size = data.ipcDataSize();  
        tr.data.ptr.buffer = data.ipcData();  
        tr.offsets_size = data.ipcObjectsCount()*sizeof(binder_size_t);  
        tr.data.ptr.offsets = data.ipcObjects();  
    } else if (statusBuffer) {  
        tr.flags |= TF_STATUS_CODE;  
        *statusBuffer = err;  
        tr.data_size = sizeof(status_t);  
        tr.data.ptr.buffer = reinterpret_cast<uintptr_t>(statusBuffer);  
        tr.offsets_size = 0;  
        tr.data.ptr.offsets = 0;  
    } else {  
        return (mLastError = err);  
    }  
  
    mOut.writeInt32(cmd);  
    mOut.write(&tr, sizeof(tr));  
  
    return NO_ERROR;  
}

这个方法中所做的事情为,把我们传入的data和code以及要调用的binder的id等都放到binder_transaction_data中去,同时又把这个tr和调用binder驱动的命令BC_TRANSACTION一起写入到mOut当中去,这个mOut也是一个Parcel对象。到这里,数据都写完了,但是binder驱动在那里读取处理这个数据呢,我们继续看waitForResponse,前面我们因为有传reply过来,因此会调用到第17行的waitForResponse方法,代码如下:

status_t IPCThreadState::waitForResponse(Parcel *reply, status_t *acquireResult)  
{  
    uint32_t cmd;  
    int32_t err;  
  
    while (1) {  
        if ((err=talkWithDriver()) < NO_ERROR) break;  
        err = mIn.errorCheck();  
        if (err < NO_ERROR) break;  
        if (mIn.dataAvail() == 0) continue;  
  
        cmd = (uint32_t)mIn.readInt32();  
  
        switch (cmd) {  
        case BR_ONEWAY_SPAM_SUSPECT:  
            [[fallthrough]];  
        ......
        case BR_REPLY:  
            {  
                binder_transaction_data tr;  
                err = mIn.read(&tr, sizeof(tr));  
                if (err != NO_ERROR) goto finish;  
  
                if (reply) {  
                    if ((tr.flags & TF_STATUS_CODE) == 0) {  
                        reply->ipcSetDataReference(  
                            reinterpret_cast<const uint8_t*>(tr.data.ptr.buffer),  
                            tr.data_size,  
                            reinterpret_cast<const binder_size_t*>(tr.data.ptr.offsets),  
                            tr.offsets_size/sizeof(binder_size_t),  
                            freeBuffer);  
                    } else {  
                        ....
                    }  
                } else {  
                   ...
                    continue;  
                }  
            }  
            goto finish;  
  
        default:  
            err = executeCommand(cmd);  
            if (err != NO_ERROR) goto finish;  
            break;  
        }  
    }  
  
finish:  
    if (err != NO_ERROR) {  
        if (acquireResult) *acquireResult = err;  
        if (reply) reply->setError(err);  
        mLastError = err;  
    }  
  
    return err;  
}

talkWithDriver分析

这里开启了一个while的无限循环,首先调用talkWithDriver,看名字就知道是与Binder驱动进行交互,这里首先会看看这个方法有没有报错,没有报错又会检查mIn是否有报错。我们前面看到过mOut,这里又有mIn,它们是用来做什么的呢,我们看一下talkWithDriver,可以发现一些东西:

status_t IPCThreadState::talkWithDriver(bool doReceive)  
{  
    if (mProcess->mDriverFD < 0) {  
        return -EBADF;  
    }  
  
    binder_write_read bwr;  
  
    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();  
  
    // This is what we'll read.  
    if (doReceive && needRead) {  
        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 {  
        
#if defined(__ANDROID__)  
        if (ioctl(mProcess->mDriverFD, BINDER_WRITE_READ, &bwr) >= 0)  
            err = NO_ERROR;  
        ...
#endif  
    } while (err == -EINTR);  

  
    if (err >= NO_ERROR) {  
        if (bwr.write_consumed > 0) {  
            if (bwr.write_consumed < mOut.dataSize())  
                ...
            else {  
                mOut.setDataSize(0);  
                processPostWriteDerefs();  
            }  
        }  
        if (bwr.read_consumed > 0) {  
            mIn.setDataSize(bwr.read_consumed);  
            mIn.setDataPosition(0);  
        }  
        ...
        return NO_ERROR;  
    }  
  
    return err;  
}

这个方法传参的默认值为true,也就是需要接受结果。在这里我们看到有一个新的数据结构binder_write_read,此处会把mOut中的数据指针写入到它的write_buffer当中,同时把mIn的数据指针写入到read_buffer中,此处的写指的是向binder驱动中写。随后我们看到是在一个循环当中调用系统调用ioctl来与binder驱动进行交互,这里使用循环的原因是,当我们调用这个系统调用的时候可能会遇到遇到中断,我们之前的调用未能执行,因此需要一直等待到执行为止。

到这里我们就分析完了添加Service调用端的所有代码,此时我们需要看一下ServiceManager服务端与Binder进行交互的代码。

ServiceManager服务端处理添加Service

我们前面分析ServiceManager启动的时候,知道最后会注册Looper的监听,当Binder驱动有消息的时候,BinderCallbak的handleEvent就会执行去处理,那么当我们在客户端请求添加Binder服务的时候,这里也会执行。这个方法中执行了如下代码:

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

这里我们可以看一下详细的源码:

status_t IPCThreadState::handlePolledCommands()  
{  
    status_t result;  
    do {  
	    //读取缓存数据知道处理完成
        result = getAndExecuteCommand();  
    } while (mIn.dataPosition() < mIn.dataSize());  
	//减少binder的引用数量,此处也会和驱动交互
    processPendingDerefs();  
    //若有为执行的命令,全部执行
    flushCommands();  
    return result;  
}

此处我们主要关注getAndExecuteCommand方法,后面都已经加了注释,此处不需要详细关注。getAndExecuteCommand方法当中也是首先调用talkWithDriver方法,这个方法前面分析过了,不再分析,这样执行完之后,mIn当中就会拿到客户端请求传输过来的数据了,之后就从数据中拿取命令和数据进行执行,代码如下:

size_t IN = mIn.dataAvail();  
if (IN < sizeof(int32_t)) return result;  
cmd = mIn.readInt32();   //读取命令
pthread_mutex_lock(&mProcess->mThreadCountLock);  //为了增加线程计数上锁
mProcess->mExecutingThreadsCount++;  
if (mProcess->mExecutingThreadsCount >= mProcess->mMaxThreads &&  
        mProcess->mStarvationStartTimeMs == 0) {  
    mProcess->mStarvationStartTimeMs = uptimeMillis();  
}  
pthread_mutex_unlock(&mProcess->mThreadCountLock);  
  
result = executeCommand(cmd);  //执行命令
  
pthread_mutex_lock(&mProcess->mThreadCountLock);  
mProcess->mExecutingThreadsCount--;
if (mProcess->mWaitingForThreads > 0) {  
    pthread_cond_broadcast(&mProcess->mThreadCountDecrement);  
}  
pthread_mutex_unlock(&mProcess->mThreadCountLock);

代码很多,但是大多都是为了给binder线程计数增减的。我们主要去看一下executeCommand中的代码,该方法中代码很多,而我们在客户端执行的是BC_TRANSACTION,因此这里应该收到的是BR_TRANSACTION命令,因此只需要看该分支的代码:

BBinder* obj;  
RefBase::weakref_type* refs;  
status_t result = NO_ERROR;  
  
switch ((uint32_t)cmd) {
	...
	case BR_TRANSACTION:
		binder_transaction_data_secctx tr_secctx;  
		binder_transaction_data& tr = tr_secctx.transaction_data;
		result = mIn.read(&tr, sizeof(tr)); //读取binder携带过来的数据到tr中

		Parcel buffer;  
		//将数据的引用放入Parcel当中
		buffer.ipcSetDataReference(  
		    reinterpret_cast<const uint8_t*>(tr.data.ptr.buffer),  
		    tr.data_size,  
		    reinterpret_cast<const binder_size_t*>(tr.data.ptr.offsets),  
		    tr.offsets_size/sizeof(binder_size_t), freeBuffer);
		//设置调用这的uid,pid,flag等信息
		mCallingPid = tr.sender_pid;  
		mCallingSid = reinterpret_cast<const char*>(tr_secctx.secctx);  
		mCallingUid = tr.sender_euid;  
		mLastTransactionBinderFlags = tr.flags;
		if (tr.target.ptr) {  //ServiceManager的binder无ptr
			//非serviceManager的binder,tr.cookie为本地的BBinder对象指针
		    if (reinterpret_cast<RefBase::weakref_type*>(  
	            tr.target.ptr)->attemptIncStrong(this)) {  
			        error = reinterpret_cast<BBinder*>(tr.cookie)->transact(tr.code, buffer,  
                &reply, tr.flags);  
	        reinterpret_cast<BBinder*>(tr.cookie)->decStrong(this);  
		    } else {  
		        error = UNKNOWN_TRANSACTION;  
		    }  
  
		} else {  
		//ServiceManager使用the_context_object这个BBinder对象。
		    error = the_context_object->transact(tr.code, buffer, &reply, tr.flags);  
		}
		if ((tr.flags & TF_ONE_WAY) == 0) {  
		    if (error < NO_ERROR) reply.setError(error);  
  
		    constexpr uint32_t kForwardReplyFlags = TF_CLEAR_BUF;  
		    sendReply(reply, (tr.flags & kForwardReplyFlags));  //写入回复
		} else {
		...
		}
		...
}

return result;

上面的代码已经做了省略,逻辑就是首先从mIn这块内存中拿到数据,并且放Parcel中,随后把uid,pid相关的属性设置到当前进程。之后是获取BBinder对象去执行transact方法,对于普通的binder,对于普通的binder,会ptr这个字段,并且tr.cookie就是本地的BBinder对象指针,而对于ServiceManager,这里就会使用在启动ServiceManager时候调用setTheContextObject所设置的BBinder对象,也就是服务端的ServiceManager。这里transact执行完成之后会调用sendReply将执行结果通过binder驱动传递回binder调用端,从而完成整个流程。这里先看transact,分析完再来分析sendReply

transact方法在BBinder类当中,在其中会调用onTransact方法,而到ServiceManager,它的onTransact的实现在BnServiceManager当中,这个类则是通过AIDL工具生成的。因为没有源码,根据经验我们这边可以知道它会调用ServiceManageraddService方法,而其中最重要的代码如下:

mNameToService[name] = Service {  
    .binder = binder,  
    .allowIsolated = allowIsolated,  
    .dumpPriority = dumpPriority,  
    .debugPid = ctx.debugPid,  
};  
  
auto it = mNameToRegistrationCallback.find(name);  
if (it != mNameToRegistrationCallback.end()) {  
    for (const sp<IServiceCallback>& cb : it->second) {  
        mNameToService[name].guaranteeClient = true;  
        // permission checked in registerForNotifications  
        cb->onRegistration(name, binder);  
    }  
}

看代码可知道,这里把Binder放到Service结构体当中,随后放入mNameToService当中,mNameToService是一个map。而mNameToRegistrationCallback中为服务注册的回调,当注册完成之后会调用它的onRegistration方法。

前面我们还有一个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(nullptr, nullptr);  
}

writeTransactionData当中就是把我们的reply打包成为binder_transaction_data写入mOut当中,这里的命令为BC_REPLY,执行完之后调用waitForResponse,其中会调用talkWithDriver来回应,之后便结束了服务端的相应。客户端随后可以读取客户端的mIn数据可以获取reply的数据。

到这里就分析完了Service注册的流程。文章仅为个人阅读代码的思考和理解,可能出现错误,欢迎读者与我交流。