Binder的简单分析

Binder是一种进程间通信机制

Binder是一个虚拟物理设备驱动

Binder是一个能发起通信的java类

Linux是一切皆文件

那么都在什么时候需要多进程操作?

突破进程内存限制,如图库占用内存过多

功能稳定性,独立的通信进程保持长连接稳定性

规避系统内存泄漏,独立的webView进程阻隔内存泄漏导致的问题

隔离风险,对于不稳定的功能放在独立进程避免主内存崩溃

surferView、webView本身设计就有问题,可以考虑多进程保证内存泄漏风险

那么为什么选择binder?

性能 只需要一次拷贝 

特点 C/S架构

安全性为每个进程分配UID 支持实名和匿名

什么是实名?什么是匿名?

有在ServiceManager 注册称为实名, 没有在ServiceManager注册,称为匿名,比如自己的Service就是匿名,可以变成实名么?也可以变成实名

共享内存

性能 无需拷贝性能更好

特点 控制复杂,易用性差

依赖上层协议

那么什么叫依赖上层协议?简单说就是如果恶意软件发起的,用假的协议也是一样能访问

不安全 而 Binder 分配UID 更好追踪 所以binder更安全 而且 共享内存直接修改数据没有同步控制,很容易访问絮乱,安全性太差,没有经过拷贝数据

Socket

性能 需要拷贝两次

特点 C/S架构 效率低,开销大

依赖上层协议

这里有个问题 为什么socket既可以访问网络也可以跨进程通信

Socket 是一种通用的网络通信协议,它可以在不同的网络节点之间进行通信。在网络通信中,Socket 提供了一种标准的接口,使得不同主机之间可以建立连接、发送和接收数据。

而在跨进程通信中,Android 系统采用了 Binder 机制。Binder 是一种进程间通信(IPC)机制,通过将对象和方法的调用传递给其他进程来实现通信。Binder 通过底层的 Linux 进程间通信(IPC)机制来传输数据,实现了进程间的数据共享和通信。

那么为什么 Socket 可以用于网络通信和跨进程通信呢?这是因为在底层实现上,网络通信和跨进程通信都依赖于操作系统提供的 IPC 机制。Socket 是一种通用的接口,它可以在不同的层级上实现不同的传输协议(如 TCP、UDP),而底层的实现细节则由操作系统负责处理。

在网络通信中,Socket 通过 TCP/IP 协议栈与网络进行交互,发送和接收网络数据包。而在跨进程通信中,Socket 可以通过底层的 Binder 机制与其他进程进行通信,传递数据和调用方法。

因此,虽然 Socket 在应用层面上被用于网络通信和跨进程通信,但它们的底层实现机制是不同的。网络通信依赖于 TCP/IP 协议栈,而跨进程通信依赖于 Binder 机制。

socket比binder更消耗性能,所以为什么Android系统中的通信大部分使用socket?

Android系统中使用socket作为通信方式的主要原因其更加通用和灵活。虽然在性能方面,Socket确实会消耗一些额外的资源,但它在设备、跨平台和跨网络环境的通信中具有广泛的应用。

首先,Socket是一种标准的网络编程接口,几乎所有的操作系统都支持它。这意味着使用Socket可以实现跨平台的通信,而不仅限于Android设备之间的通信。这对于开发跨平台的应用程序和与其他设备进行通信非常有用。

其次,Socket提供了一种基于网络的通信模型,可以通过TCP或UDP协议进行数据传输。这种灵活性使得开发者可以根据具体需求选择合适的协议和通信方式。例如,对于需要可靠传输和有序数据的场景,可以使用TCP协议;而对于实时性要求较高的场景,可以选择UDP协议。

另外,Socket还能够支持各种网络通信模式,如客户端-服务器模式、对等模式等。这使得开发者可以根据应用程序的需求选择合适的通信模式,从而更好地满足应用程序的要求。

虽然Binder在Android系统中主要用于进程间通信(IPC),但它的使用场景主要集中在同一设备上的进程通信,而Socket则更适用于网络通信和跨设备通信。因此,在Android系统中,Socket被广泛应用于网络通信和与其他设备进行通信的场景,以实现更大的灵活性和通用性。

还有一个问题就是安全性,binder会开启一个自己的线程池,但是binder也存在死锁的风险

如果说线程有死锁,有数据不安全性,那么多进程存在这种问题嘛?答案是肯定的 后面分析

Binder是一次拷贝,那么什么叫做一次拷贝?

那么首先看下传统的IPC机制

 64、32的区别是什么?

寻址就是所谓的门牌编号

  1. 寻址能力:64位系统的主要优势之一是能够支持更大的内存寻址空间。32位系统的寻址空间限制在4GB以内,而64位系统可以支持更大的内存容量(最多可达16EB)。
  2. 整数运算:64位系统可以对更大范围的整数进行计算,具有更高的精度和更大的整数范围。这对于一些需要处理大数据量或需要高精度计算的应用程序很重要。
  3. 浮点运算:64位系统在浮点运算方面也具有优势,可以进行更精确的浮点计算,提供更高的计算精度。
  4. 指令集扩展:64位架构引入了一些新的指令集扩展,如SSE2、AVX等,可以提供更高效的数据处理能力和并行计算能力。

那么一般来讲用户空间寻址占3g,而内核占一g

那么问题来了?一个内存才8G,那么进程就占4G如何处理?那么这里涉及一个概念,虚拟内存和物理内存

软件工程师说的内存都是虚拟内存

物理内存就是内存条

比如一个数组,而内存不一定会有连续的地址,为了解决这种问题,就分为了虚拟内存和物理内存,这是一个比较简单的比喻

那么内存管理单元就会处理虚拟地址和物理地址的映射关系

 内核空间映射都是同一块内存,用户空间映射的是不同的

如果一个进程调用另一个进程首先要copy_form_user 系统首先要看是否有权限

我们所说的拷贝就是这个copy_form_user调用了几次 在用户空间运行的称用户态 内核成为内核态

转化非常消耗时间,但是为什么?

因为需要保存当前进程的状态,耗时且消耗资源

内核空间和内核空间是相通的,但是和用户空间是不通的,所以需要系统调用将内存调用到用户空间如下所示 传统IPC两次拷贝如下所示

那么binder为啥是一次拷贝??还有为啥既然mmap能映射还需要拷贝?

是为了安全性考虑,所以才进行拷贝一次的目的

 通过这种mmap形式去映射内存,到达跨进程通信的目的

那为什么两边不能都直接映射?那就变成了内存共享,实现难度加大性能会差一点但是使用更加方便

那么问题来了 Android用户态切换内核态非常耗时 所以Binder是开启了新的线程执行的吗?

是的,Binder 在 Android 中会开启新的线程执行跨进程通信的操作。这是因为在进行 Binder 调用时,涉及到用户态和内核态之间的切换,而这种切换是相对耗时的。为了避免阻塞应用程序主线程,Android 系统会将 Binder 相关的操作放在独立的线程中执行。

具体来说,Android 的 Binder 机制在底层使用了线程池来管理 Binder 线程。当应用程序发起跨进程通信时,系统会在线程池中选择一个空闲的 Binder 线程来执行相应的操作,例如发送请求、等待响应等。通过将 Binder 相关的操作放在独立的线程中执行,可以避免阻塞应用程序主线程,提高应用程序的响应性能。

需要注意的是,由于 Binder 线程是由系统管理的,因此开发者无法直接控制和操作它。开发者只需要使用正确的方式进行跨进程通信,并遵循异步操作的原则,让系统自行管理 Binder 线程的创建和销毁。这样可以确保应用程序在进行跨进程通信时获得较好的性能和响应性。

Binder 并发环境下是如何保证安全的?

在 Binder 并发环境下,安全性是通过以下方式得到保证的:

  1. 进程隔离:Binder 使用进程间通信(IPC)机制来实现组件之间的通信,每个组件运行在单独的进程中。这种进程隔离确保了组件之间的相互独立性,一个组件的错误不会影响到其他组件。

  2. 线程安全:在每个进程中,Binder 会为每个线程提供一个独立的 Binder 线程池。这样可以避免多个线程同时访问 Binder 对象,从而保证了线程安全性。

  3. 引用计数:Binder 使用引用计数来管理对象的生命周期。当一个组件需要使用另一个组件提供的服务时,它会增加对该组件的引用计数;当不再需要该服务时,它会减少对该组件的引用计数。只有当引用计数为零时,该组件才会被销毁,这样可以避免资源泄漏和访问无效对象的情况发生。

  4. 权限控制:Binder 支持权限控制来限制对特定服务的访问。每个 Binder 组件都可以定义自己的权限级别,并在注册时进行声明。只有具有相应权限的组件才能访问该服务,这样可以确保只有经过授权的组件才能执行敏感操作。

综上所述,Binder 在并发环境下通过进程隔离、线程安全、引用计数和权限控制等机制来保证安全性。

为什么binder线程池就可以维护安全,或者说为什么新开一个线程池就能保证线程安全?

新开一个 Binder 线程池可以有助于维护线程安全的原因如下:

  1. 独立的线程:每个进程都有自己的 Binder 线程池,不同进程之间的线程是相互独立的。这意味着每个组件在自己所在的进程中拥有独立的线程池,可以避免不同组件之间的线程冲突和竞争条件。

  2. 串行执行:Binder 线程池中的线程一般是串行执行的,即每次只处理一个请求。这种串行执行的机制确保了在同一时刻只有一个线程在访问 Binder 对象,避免了多个线程同时对对象进行操作导致的线程安全问题,如竞态条件和数据不一致性。

  3. 同步机制:Binder 线程池使用了同步机制来保证线程安全。例如,可以使用锁来控制对共享资源的访问,或者使用信号量来限制并发访问的数量。这样可以确保在并发环境下对关键资源的访问是互斥的,避免了数据竞争和访问冲突。

总的来说,新开一个 Binder 线程池可以提供独立的线程环境,实现线程的串行执行和使用同步机制来保证线程安全。这样可以有效地避免并发访问导致的线程安全问题,确保 Binder 在并发环境下的安全性。

ServerManager 我们在网络请求的时候会先通过URL去访问DNS服务器去获取到域名ip,拿到ip后再去访问服务器,那么ServerManager就是类似的流程

网络请求流程 

ServerManager就跟DNS一样会有路由表(显式通信)

ActivityTaskManagerService 的Lifecycle 代码 publishBinderService 传入的是 service因为ActivityTaskManagerService 实现了stub接口 onstart是在反射创建服务的时候调用方调用的

    public static final class Lifecycle extends SystemService {
        private final ActivityTaskManagerService mService;

        public Lifecycle(Context context) {
            super(context);
            mService = new ActivityTaskManagerService(context);
        }

        @Override
        public void onStart() {
            publishBinderService(Context.ACTIVITY_TASK_SERVICE, mService);
            mService.start();
        }
 

binder一般是在系统启动时候进行启动的,进程启动最终会调用到open_driver 

static int open_driver(const char *driver)
382{
        //打开binder驱动
383    int fd = open(driver, O_RDWR | O_CLOEXEC);
384    if (fd >= 0) {
385        int vers = 0;
386        status_t result = ioctl(fd, BINDER_VERSION, &vers);
387        if (result == -1) {
388            ALOGE("Binder ioctl to obtain version failed: %s", strerror(errno));
389            close(fd);
390            fd = -1;
391        }
392        if (result != 0 || vers != BINDER_CURRENT_PROTOCOL_VERSION) {
393          ALOGE("Binder driver protocol(%d) does not match user space protocol(%d)! ioctl() return value: %d",
394                vers, BINDER_CURRENT_PROTOCOL_VERSION, result);
395            close(fd);
396            fd = -1;
397        }
398        size_t maxThreads = DEFAULT_MAX_BINDER_THREADS;
399        result = ioctl(fd, BINDER_SET_MAX_THREADS, &maxThreads);
400        if (result == -1) {
401            ALOGE("Binder ioctl to set max threads failed: %s", strerror(errno));
402        }
403    } else {
404        ALOGW("Opening '%s' failed: %s\n", driver, strerror(errno));
405    }
406    return fd;
407}

那么这里有个问题 为什么binder在系统初始化时候第一时间初始化?

就比如AMS,如果启动一个子app,如果第一时间没有进行初始化,那么想开启一个Activity如何开启?只能通过binder通信去开启Activity

那么zygote为什么没有binder?使用socket?安全性,有可能会死锁

Android sdk专门为了aidl在SDK内部使用了 aidl工具可以生成c++文件

aidl的流程图大概

接下来分析下绑定流程

 private boolean bindServiceCommon(Intent service, ServiceConnection conn, int flags,
            String instanceName, Handler handler, Executor executor, UserHandle user) {

         ..省略一车代码

             //ActivityManager.getService() 先拿到对应的binder对象
            int res = ActivityManager.getService().bindIsolatedService(
                mMainThread.getApplicationThread(), getActivityToken(), service,
                service.resolveTypeIfNeeded(getContentResolver()),
                sd, flags, instanceName, getOpPackageName(), user.getIdentifier());
            if (res < 0) {
                throw new SecurityException(
                        "Not allowed to bind to service " + service);
            }
            return res != 0;
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }
    @UnsupportedAppUsage
    public static IActivityManager getService() {
        return IActivityManagerSingleton.get();
    }

    private static IActivityTaskManager getTaskService() {
        return ActivityTaskManager.getService();
    }

    @UnsupportedAppUsage
    private static final Singleton<IActivityManager> IActivityManagerSingleton =
            new Singleton<IActivityManager>() {
                @Override
                protected IActivityManager create() {
                    //首先先拿到 ServiceManager 里的 binder对象
                    final IBinder b = ServiceManager.getService(Context.ACTIVITY_SERVICE);
                    final IActivityManager am = IActivityManager.Stub.asInterface(b);
                    return am;
                }
            };

然后经过一系列跨进程调用就到了

    private final boolean requestServiceBindingLocked(ServiceRecord r, IntentBindRecord i,
            boolean execInFg, boolean rebind) throws TransactionTooLargeException {
        if (r.app == null || r.app.thread == null) {
            // If service is not currently running, can't yet bind.
            return false;
        }
        if (DEBUG_SERVICE) Slog.d(TAG_SERVICE, "requestBind " + i + ": requested=" + i.requested
                + " rebind=" + rebind);
        if ((!i.requested || rebind) && i.apps.size() > 0) {
            try {
                bumpServiceExecutingLocked(r, execInFg, "bind");
                r.app.forceProcessStateUpTo(ActivityManager.PROCESS_STATE_SERVICE);

                //这里的thread就是我们之前跨进程说到的在main函数里会创建appThread对象
              
                r.app.thread.scheduleBindService(r, i.intent.getIntent(), rebind,
                        r.app.getReportedProcState());
                if (!rebind) {
                    i.requested = true;
                }
                i.hasBound = true;
                i.doRebind = false;
            } catch (TransactionTooLargeException e) {

然后进行进程切换

   public final void scheduleBindService(IBinder token, Intent intent,
                boolean rebind, int processState) {
            updateProcessState(processState, false);
            BindServiceData s = new BindServiceData();
            s.token = token;
            s.intent = intent;
            s.rebind = rebind;

            if (DEBUG_SERVICE)
                Slog.v(TAG, "scheduleBindService token=" + token + " intent=" + intent + " uid="
                        + Binder.getCallingUid() + " pid=" + Binder.getCallingPid());
            sendMessage(H.BIND_SERVICE, s);
        }

Handler才是真正的切换了进程 就实现了跨进程执行

那么绑定流程搞定后看下Binder的底层逻辑

 AIDL调用了接口就会走到这 这里是调用逻辑

   @Override public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, java.lang.String aString) throws android.os.RemoteException
      {
        android.os.Parcel _data = android.os.Parcel.obtain();
        android.os.Parcel _reply = android.os.Parcel.obtain();

           //这里是一个IBinder接口,实现类是BinderProxy
          boolean _status = mRemote.transact(Stub.TRANSACTION_basicTypes, _data, _reply, 0);

          if (!_status && getDefaultImpl() != null) {
            getDefaultImpl().basicTypes(anInt, aLong, aBoolean, aFloat, aDouble, aString);
            return;
          }
          _reply.readException();
        }
        finally {
          _reply.recycle();
          _data.recycle();
        }
      }

在BinderProxy里

  public boolean transact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
        Binder.checkParcel(this, code, data, "Unreasonably large binder buffer");

         //我们只看主线流程走到了 transactNative 

        try {
            return transactNative(code, data, reply, flags);
        } finally {
          
        }
    }

 在JNI调用之后就走到了 android_os_BinderProxy_transact 方法

1235static jboolean android_os_BinderProxy_transact(JNIEnv* env, jobject obj,
1236        jint code, jobject dataObj, jobject replyObj, jint flags) // throws RemoteException
1237{
1238    if (dataObj == NULL) {
1239        jniThrowNullPointerException(env, NULL);
1240        return JNI_FALSE;
1241    }
1242
1243    Parcel* data = parcelForJavaObject(env, dataObj);
1244    if (data == NULL) {
1245        return JNI_FALSE;
1246    }
1247    Parcel* reply = parcelForJavaObject(env, replyObj);
1248    if (reply == NULL && replyObj != NULL) {
1249        return JNI_FALSE;
1250    }
1251
        在这里调用getBPNativeData函数实际上获取的是BinderProxyNativeData
        实际上是BpBinder
1252    IBinder* target = getBPNativeData(env, obj)->mObject.get();
1253    if (target == NULL) {
1254        jniThrowException(env, "java/lang/IllegalStateException", "Binder has been finalized!");
1255        return JNI_FALSE;
1256    }
1257
1258    ALOGV("Java code calling transact on %p in Java object %p with code %" PRId32 "\n",
1259            target, obj, code);
1260
1261
1262    bool time_binder_calls;
1263    int64_t start_millis;
1264    if (kEnableBinderSample) {
1265        // Only log the binder call duration for things on the Java-level main thread.
1266        // But if we don't
1267        time_binder_calls = should_time_binder_calls();
1268
1269        if (time_binder_calls) {
1270            start_millis = uptimeMillis();
1271        }
1272    }
1273
1274    //printf("Transact from Java code to %p sending: ", target); data->print();
1275    
        接下来在这边传入参数进入下一步
        status_t err = target->transact(code, *data, reply, flags);
1276    //if (reply) printf("Transact from Java code to %p received: ", target); reply->print();
1277
1278    if (kEnableBinderSample) {
1279        if (time_binder_calls) {
1280            conditionally_log_binder_call(start_millis, target, code);
1281        }
1282    }
1283
1284    if (err == NO_ERROR) {
1285        return JNI_TRUE;
1286    } else if (err == UNKNOWN_TRANSACTION) {
1287        return JNI_FALSE;
1288    }
1289
1290    signalExceptionForError(env, obj, err, true /*canThrowRemoteException*/, data->dataSize());
1291    return JNI_FALSE;
1292}

这里的getBPNativeData 实际上是反射实现 

(BinderProxyNativeData *) env->GetLongField(obj, gBinderProxyOffsets.mNativeData);

这段代码是通过 JNI(Java Native Interface)实现的反射代码。它使用了JNI函数GetLongField来获取obj对象的gBinderProxyOffsets.mNativeData字段的值,并将其转换为BinderProxyNativeData*类型。通过这种方式,可以在Java代码中访问和操作底层的原生数据。

接下来到了BPBinder里的 transact 方法

209status_t BpBinder::transact(
210    uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
211{
212    // Once a binder has died, it will never come back to life.
213    if (mAlive) {
            创建了一个线程的操作
214        status_t status = IPCThreadState::self()->transact(
215            mHandle, code, data, reply, flags);
216        if (status == DEAD_OBJECT) mAlive = 0;
217        return status;
218    }
219
220    return DEAD_OBJECT;
221}
594status_t IPCThreadState::transact(int32_t handle,
595                                  uint32_t code, const Parcel& data,
596                                  Parcel* reply, uint32_t flags)
597{
598       //省略一些代码
          为了整合一些数据,将数据写入到结构体
         err = writeTransactionData(BC_TRANSACTION_SG, flags, handle, code, data, nullptr);
627  
628      if (err != NO_ERROR) {
629          if (reply) reply->setError(err);
630          return (mLastError = err);
631      }
617
618    if ((flags & TF_ONE_WAY) == 0) {
619        #if 0
620        if (code == 4) { // relayout
621            ALOGI(">>>>>> CALLING transaction 4");
622        } else {
623            ALOGI(">>>>>> CALLING transaction %d", code);
624        }
625        #endif
626        if (reply) {
                接下来在这里
627            err = waitForResponse(reply);
628        } else {
629            Parcel fakeReply;
630            err = waitForResponse(&fakeReply);
631        }
632        #if 0
633        if (code == 4) { // relayout
634            ALOGI("<<<<<< RETURNING transaction 4");
635        } else {
636            ALOGI("<<<<<< RETURNING transaction %d", code);
637        }
638 
650
651    return err;
652}

一步一图人才能看得懂,那么 这个时序就走到了IPCThreadState这个类,这个类干嘛的,都说晕了,BPBinder是客户端联系Binder驱动设备的,那IPCThreadState干嘛的?

ProcessState 专门管理每个应用进程的 Binder 操作,同一个进程中只有一个 ProcessState 实例存在,且只在 ProcessState 对象创建时才打开 Binder 设备以及内存映射。相关代码如下:

sp<ProcessState> ProcessState::self(){
    Mutex::Autolock _l(gProcessMutex);
    if (gProcess != NULL) { //如果创建过 ProcessState 就直接返回
        return gProcess;
    }
    gProcess = new ProcessState;
    return gProcess;
}

外部统一通过 ProcessState::self() 方法获取 ProcessState,以此保证 ProcessState 的进程单例,ProcessState 的构造函数如下:

rocessState::ProcessState()
    : mDriverFD(open_driver()) //打开 binder 设备
    , mVMStart(MAP_FAILED) //初始化为 MAP_FAILED,映射成功后会变更
    , mThreadCountLock(PTHREAD_MUTEX_INITIALIZER)
    , mThreadCountDecrement(PTHREAD_COND_INITIALIZER)
    , mExecutingThreadsCount(0)
    , mMaxThreads(DEFAULT_MAX_BINDER_THREADS) //binder 线程最大数量
    , mStarvationStartTimeMs(0)
    , mManagesContexts(false)
    , mBinderContextCheckFunc(NULL)
    , mBinderContextUserData(NULL)
    , mThreadPoolStarted(false)
    , mThreadPoolSeq(1){
       if (mDriverFD >= 0) { //已经成功打开 binder 驱动设备
           // 将应用进程一块虚拟内存空间与 binder 驱动映射,在此内存块上进行数据通信
           mVMStart = mmap(0, BINDER_VM_SIZE, PROT_READ, MAP_PRIVATE | MAP_NORESERVE, mDriverFD, 0);
           if (mVMStart == MAP_FAILED) { //映射失败处理
               ALOGE("Using /dev/binder failed: unable to mmap transaction memory.\n");
               close(mDriverFD);
               mDriverFD = -1;
           }
       }
}

 OK,我不懂 open_driver打开了什么binder设备?mmap是这个类初始化就做的,那么mmap怎么连上的?ok这些问题留到后面,binder_open mmap这三大重要流程我们慢慢分析,我们先关心主线,搞清楚结构再搞清楚代码细节

989  status_t IPCThreadState::writeTransactionData(int32_t cmd, uint32_t binderFlags,
990      int32_t handle, uint32_t code, const Parcel& data, status_t* statusBuffer)
991  {
992      ...
         通知对应的驱动来写入这些数据
995      tr_sg.transaction_data.target.handle = handle;
996     
1001  
1002      const status_t err = data.errorCheck();
1003      if (err == NO_ERROR) {
1004          tr_sg.transaction_data.data_size = data.ipcDataSize();
              取出数据 另一个进程也可以进行解析数据
1005          tr_sg.transaction_data.data.ptr.buffer = data.ipcData();
1006          tr_sg.transaction_data.offsets_size = data.ipcObjectsCount()*sizeof(binder_size_t);
1007          tr_sg.transaction_data.data.ptr.offsets = data.ipcObjects();
1008          tr_sg.buffers_size = data.ipcBufferSize();
1009      } 
          ....
1020      
1021      mOut.writeInt32(cmd);
1022      mOut.write(&tr_sg, sizeof(tr_sg));
1023  
1024      return NO_ERROR;
1025  }

循环读取驱动 

761status_t IPCThreadState::waitForResponse(Parcel *reply, status_t *acquireResult)
762{
763    uint32_t cmd;
764    int32_t err;
765
766    while (1) {
            在这里读取驱动

767        if ((err=talkWithDriver()) < NO_ERROR) break;
768
840    }
        //省略一些与主线无关的代码

 在这里进行ioctl 进行与内存打交道

852status_t IPCThreadState::talkWithDriver(bool doReceive)
853{
854     //省略无关流程
900    do {
901        IF_LOG_COMMANDS() {
902            alog << "About to read/write, write size = " << mOut.dataSize() << endl;
903        }
904#if defined(__ANDROID__)
905        if (ioctl(mProcess->mDriverFD, BINDER_WRITE_READ, &bwr) >= 0)
906            err = NO_ERROR;
907        else

908     //...... 
951
952    return err;
953}

ioctl方法资料:【通俗易懂】详解 ioctl 函数是如何操控底层的_ioctl函数详解-CSDN博客

一步一张图,时序图大概如下 

那么接下来,我们要多问几个问题,我们看源码看到现在,binder的一次拷贝在哪?它的线程池在哪?为什么需要一个线程池?怎么映射的?

那么先回答第一个问题 binder的一次拷贝在哪?

binder调用 ioctl 后会进行拷贝

ioctl 操作内核层的地址

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;
	void __user *ubuf = (void __user *)arg;

	/*pr_info("binder_ioctl: %d:%d %x %lx\n",
			proc->pid, current->pid, cmd, arg);*/

	binder_selftest_alloc(&proc->alloc);

	trace_binder_ioctl(cmd, arg);

	ret = wait_event_interruptible(binder_user_error_wait, binder_stop_on_user_error < 2);
	if (ret)
		goto err_unlocked;

	thread = binder_get_thread(proc);
	if (thread == NULL) {
		ret = -ENOMEM;
		goto err;
	}

	switch (cmd) {
	case BINDER_WRITE_READ:
		ret = binder_ioctl_write_read(filp, arg, thread);
		if (ret)
			goto err;
		break;
	case BINDER_SET_MAX_THREADS: {
		int max_threads;

		if (copy_from_user(&max_threads, ubuf,
				   sizeof(max_threads))) {
			ret = -EINVAL;
			goto err;
		}
		binder_inner_proc_lock(proc);
		proc->max_threads = max_threads;
		binder_inner_proc_unlock(proc);
		break;
	}

copy_from_user进行一次复制 

ServiceManager 里的binder 获取方式和 Service的绑定不同 这里是读取逻辑

ServiceManager里的binder需要通过 getContextObject来获取 

    private static IServiceManager getIServiceManager() {
102        if (sServiceManager != null) {
103            return sServiceManager;
104        }
105
106        // Find the service manager
107        sServiceManager = ServiceManagerNative
108                .asInterface(Binder.allowBlocking(BinderInternal.getContextObject()));
109        return sServiceManager;
110    }
976
977static jobject android_os_BinderInternal_getContextObject(JNIEnv* env, jobject clazz)
978{
979    sp<IBinder> b = ProcessState::self()->getContextObject(NULL);
       建造java对象 下面有做分析
980    return javaObjectForIBinder(env, b);
981}

ProcessState就是进程状态,启动进程会第一时间启动ProcessState 会进行初始化binder,为Binder通信提供支持初始化Binder线程池等

那么为什么ProcessState 来提供这些底层能力?

因为 ProcessState self 函数 

sp<ProcessState> ProcessState::self()
69{
70    Mutex::Autolock _l(gProcessMutex);
71    if (gProcess != NULL) {
72        return gProcess;
73    }
74    gProcess = new ProcessState("/dev/binder");
75    return gProcess;
76}

 因为在这里初始化了binder驱动等一系列事情

409ProcessState::ProcessState(const char *driver)
410    : mDriverName(String8(driver))
411    , mDriverFD(open_driver(driver))
412    , mVMStart(MAP_FAILED)
413    , mThreadCountLock(PTHREAD_MUTEX_INITIALIZER)
414    , mThreadCountDecrement(PTHREAD_COND_INITIALIZER)
415    , mExecutingThreadsCount(0)
416    , mMaxThreads(DEFAULT_MAX_BINDER_THREADS)
417    , mStarvationStartTimeMs(0)
418    , mManagesContexts(false)
419    , mBinderContextCheckFunc(NULL)
420    , mBinderContextUserData(NULL)
421    , mThreadPoolStarted(false)
422    , mThreadPoolSeq(1)
423{
424    if (mDriverFD >= 0) {
            调用mmap接口向binder驱动中申请内核空间
425        // mmap the binder, providing a chunk of virtual address space to receive transactions.
426        mVMStart = mmap(0, BINDER_VM_SIZE, PROT_READ, MAP_PRIVATE | MAP_NORESERVE, mDriverFD, 0);
427        if (mVMStart == MAP_FAILED) {
428            // *sigh*
429            ALOGE("Using %s failed: unable to mmap transaction memory.\n", mDriverName.c_str());
430            close(mDriverFD);
431            mDriverFD = -1;
432            mDriverName.clear();
433        }
434    }
435
436    LOG_ALWAYS_FATAL_IF(mDriverFD < 0, "Binder driver could not be opened.  Terminating.");
437}
438

继续分析getContextObject,我们知道ServiceManager的路由表最后走到这里我们看下这个方法是如何实现的

110sp<IBinder> ProcessState::getContextObject(const sp<IBinder>& /*caller*/)
111{
112    return getStrongProxyForHandle(0);
113}

为什么是第0个?第0个就代表了ServiceManager所在的进程 固定的地址

243
244sp<IBinder> ProcessState::getStrongProxyForHandle(int32_t handle)
245{
251
252    if (e != NULL) {
253        // We need to create a new BpBinder if there isn't currently one, OR we
254        // are unable to acquire a weak reference on this current one.  See comment
255        // in getWeakProxyForHandle() for more info about this.
256        IBinder* b = e->binder;
257           省略无关代码
284           如果第0个没有binder 就返回一个新的binder对象
285            b = BpBinder::create(handle);
286            e->binder = b;
287            if (b) e->refs = b->getWeakRefs();
288            result = b;
289        } else {
290            // This little bit of nastyness is to allow us to add a primary
291            // reference to the remote proxy when this team doesn't have one
292            // but another team is sending the handle to us.
293            result.force_set(b);
294            e->refs->decWeak(this);
295        }
296    }
297
298    return result;
299}
300

那么BPbinder代表什么?它就代表了驱动的封装 这个BPbinder是native的对象,不能直接给上层使用,所以需要JNI进行封装,如果想把native的对象变成java层的对象,那就需要通过反射将native对象转成java对象 javaObjectForIBinder

644jobject javaObjectForIBinder(JNIEnv* env, const sp<IBinder>& val)
645{
646    .....省略无关代码
658
659    BinderProxyNativeData* nativeData = gNativeDataCache;
660    if (nativeData == nullptr) {
661        nativeData = new BinderProxyNativeData();
662    }
663    // gNativeDataCache is now logically empty.
       通过反射制造java对象
664    jobject object = env->CallStaticObjectMethod(gBinderProxyOffsets.mClass,
665            gBinderProxyOffsets.mGetInstance, (jlong) nativeData, (jlong) val.get());

       省略无关代码
666    ......
686
687    return object;
688}

由此可见,ServiceManager 的 getService 本质上也是跨进程去 底层获取保存的BPBinder

client 端的调用流程结束,看下binder的启动流程

init.rc文件 serviceManager 是自己的进程

116  int main(int argc, char** argv) {
117      if (argc > 2) {
118          LOG(FATAL) << "usage: " << argv[0] << " [binder driver]";
119      }
120      要使用的binder驱动为 /dev/binder
121      const char* driver = argc == 2 ? argv[1] : "/dev/binder";
122      
         初始化binder驱动
123      sp<ProcessState> ps = ProcessState::initWithDriver(driver);
124      ps->setThreadPoolMaxThreadCount(0);
125      ps->setCallRestriction(ProcessState::CallRestriction::FATAL_IF_NOT_ONEWAY);
126  
         实例化ServiceManager 现在是在ServiceManager自己的进程
127      sp<ServiceManager> manager = new ServiceManager(std::make_unique<Access>());
         添加自身服务
128      if (!manager->addService("manager", manager, false /*allowIsolated*/, IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT).isOk()) {
129          LOG(ERROR) << "Could not self register servicemanager";
130      }
131      设置服务端为bbinder
132      IPCThreadState::self()->setTheContextObject(manager);
133      ps->becomeContextManager(nullptr, nullptr);
134      通过loop来处理binder事务
135      sp<Looper> looper = Looper::prepare(false /*allowNonCallbacks*/);
136  
137      BinderCallback::setupTo(looper);
138      ClientCallbackCallback::setupTo(looper, manager);
139  
140      while(true) {
141          looper->pollAll(-1);
142      }
143  
144      // should not be reached
145      return EXIT_FAILURE;
146  }

初始化binder驱动 

349  static int open_driver(const char *driver)
350  {
         打开binder驱动
351      int fd = open(driver, O_RDWR | O_CLOEXEC);
352      if (fd >= 0) {
353          int vers = 0;
             验证Binder版本
354          status_t result = ioctl(fd, BINDER_VERSION, &vers);
355          if (result == -1) {
356              ALOGE("Binder ioctl to obtain version failed: %s", strerror(errno));
357              close(fd);
358              fd = -1;
359          }
360          if (result != 0 || vers != BINDER_CURRENT_PROTOCOL_VERSION) {
361            ALOGE("Binder driver protocol(%d) does not match user space protocol(%d)! ioctl() return value: %d",
362                  vers, BINDER_CURRENT_PROTOCOL_VERSION, result);
363              close(fd);
364              fd = -1;
365          }
             设置binder最大线程数
366          size_t maxThreads = DEFAULT_MAX_BINDER_THREADS;
367          result = ioctl(fd, BINDER_SET_MAX_THREADS, &maxThreads);
368          if (result == -1) {
369              ALOGE("Binder ioctl to set max threads failed: %s", strerror(errno));
370          }
371      } else {
372          ALOGW("Opening '%s' failed: %s\n", driver, strerror(errno));
373      }
374      return fd;
375  }

如果ServiceManager想提供服务就需要类似aidl的sub类,实现后才会提供服务

ServiceManager实现BnServiceManager才能够提供服务

68  // LooperCallback for IClientCallback
69  class ClientCallbackCallback : public LooperCallback {
70  public:
71      static sp<ClientCallbackCallback> setupTo(const sp<Looper>& looper, const sp<ServiceManager>& manager) {
72          sp<ClientCallbackCallback> cb = new ClientCallbackCallback(manager);
73  
74          int fdTimer = timerfd_create(CLOCK_MONOTONIC, 0 /*flags*/);
75          LOG_ALWAYS_FATAL_IF(fdTimer < 0, "Failed to timerfd_create: fd: %d err: %d", fdTimer, errno);
76  
77          itimerspec timespec {
78              .it_interval = {
79                  .tv_sec = 5,
80                  .tv_nsec = 0,
81              },
82              .it_value = {
83                  .tv_sec = 5,
84                  .tv_nsec = 0,
85              },
86          };
87  
88          int timeRes = timerfd_settime(fdTimer, 0 /*flags*/, &timespec, nullptr);
89          LOG_ALWAYS_FATAL_IF(timeRes < 0, "Failed to timerfd_settime: res: %d err: %d", timeRes, errno);
90          监听binder描述符
91          int addRes = looper->addFd(fdTimer,
92                                     Looper::POLL_CALLBACK,
93                                     Looper::EVENT_INPUT,
94                                     cb,
95                                     nullptr);
96          LOG_ALWAYS_FATAL_IF(addRes != 1, "Failed to add client callback FD to Looper");
97  
98          return cb;
99      }
100      当binder驱动发来消息后就可以通过handleEvent函数接收处理
101      int handleEvent(int fd, int /*events*/, void* /*data*/) override {
102          uint64_t expirations;
103          int ret = read(fd, &expirations, sizeof(expirations));
104          if (ret != sizeof(expirations)) {
105              ALOGE("Read failed to callback FD: ret: %d err: %d", ret, errno);
106          }
107  
108          mManager->handleClientCallbacks();
109          return 1;  // Continue receiving callbacks.
110      }

然后通过前面setTheContextObject 传进来的ServiceManager的onTransact传递 给BnServiceManager

接下来看几个Binder的方法

static int binder_open(struct inode *nodp, struct file *filp)
{
    //结构体1 下边有分析内部结构
	struct binder_proc *proc;
	struct binder_device *binder_dev;
	binder_debug(BINDER_DEBUG_OPEN_CLOSE, "%s: %d:%d\n", __func__,
		     current->group_leader->pid, current->pid);
	proc = kzalloc(sizeof(*proc), GFP_KERNEL);
	if (proc == NULL)
		return -ENOMEM;
	spin_lock_init(&proc->inner_lock);
	spin_lock_init(&proc->outer_lock);
	get_task_struct(current->group_leader);
	proc->tsk = current->group_leader;
	mutex_init(&proc->files_lock);
	INIT_LIST_HEAD(&proc->todo);
	if (binder_supported_policy(current->policy)) {
		proc->default_priority.sched_policy = current->policy;
		proc->default_priority.prio = current->normal_prio;
	} else {
		proc->default_priority.sched_policy = SCHED_NORMAL;
		proc->default_priority.prio = NICE_TO_PRIO(0);
	}
	binder_dev = container_of(filp->private_data, struct binder_device,
				  miscdev);
	proc->context = &binder_dev->context;
    //与binder申请内存地址息息相关
    //初始化对应binder_alloc
	binder_alloc_init(&proc->alloc);
	binder_stats_created(BINDER_STAT_PROC);
    //应用程序的pid,调用方的pid
	proc->pid = current->group_leader->pid;
	INIT_LIST_HEAD(&proc->delivered_death);
	INIT_LIST_HEAD(&proc->waiting_threads);
    //private_data 被赋值为 proc 接下来mmap会用到
	filp->private_data = proc;
	mutex_lock(&binder_procs_lock);
    //将数据放入列表头(不重要)
	hlist_add_head(&proc->proc_node, &binder_procs);
	mutex_unlock(&binder_procs_lock);
    //类似debug类型的不重要
	if (binder_debugfs_dir_entry_proc) {
		char strbuf[11];
		snprintf(strbuf, sizeof(strbuf), "%u", proc->pid);
		/*
		 * proc debug entries are shared between contexts, so
		 * this will fail if the process tries to open the driver
		 * again with a different context. The priting code will
		 * anyway print all contexts that a given PID has, so this
		 * is not a problem.
		 */
		proc->debugfs_entry = debugfs_create_file(strbuf, 0444,
			binder_debugfs_dir_entry_proc,
			(void *)(unsigned long)proc->pid,
			&binder_proc_fops);
	}
	return 0;
}

struct binder_proc {
	struct hlist_node proc_node;
    //binder的线程数
	struct rb_root threads;
    //所谓bpbinder rb_root 是一个红黑树,提高效率
	struct rb_root nodes;
    //引索
	struct rb_root refs_by_desc;
    //节点
	struct rb_root refs_by_node;
	int pid;
	struct vm_area_struct *vma;
	struct task_struct *tsk;
	struct files_struct *files;
	struct hlist_node deferred_work_node;
	int deferred_work;
	void *buffer;
	ptrdiff_t user_buffer_offset;
	struct list_head buffers;
	struct rb_root free_buffers;
	struct rb_root allocated_buffers;
	size_t free_async_space;
	struct page **pages;
	size_t buffer_size;
	uint32_t buffer_free;
	struct list_head todo;
	wait_queue_head_t wait;
	struct binder_stats stats;
	struct list_head delivered_death;
	int max_threads;
	int requested_threads;
	int requested_threads_started;
	int ready_threads;
	long default_priority;
};

MMap

binder-map内存 与 server 进程会进行mmap进行映射

//vm_area_struct linux 0-3G 连续的虚拟的结构体
static int binder_mmap(struct file *filp, struct vm_area_struct *vma)
{
	int ret;
    //先拿到 binder_open里赋值的 private_data 也就是申请内存的
	struct binder_proc *proc = filp->private_data;
	const char *failure_string;
    
	if (proc->tsk != current->group_leader)
		return -EINVAL;
    //如果传入的参数超过4m就只能给4m
	if ((vma->vm_end - vma->vm_start) > SZ_4M)
		vma->vm_end = vma->vm_start + SZ_4M;
    //log
	binder_debug(BINDER_DEBUG_OPEN_CLOSE,
		     "%s: %d %lx-%lx (%ld K) vma %lx pagep %lx\n",
		     __func__, proc->pid, vma->vm_start, vma->vm_end,
		     (vma->vm_end - vma->vm_start) / SZ_1K, vma->vm_flags,
		     (unsigned long)pgprot_val(vma->vm_page_prot));
    //检测是否失败 
	if (vma->vm_flags & FORBIDDEN_MMAP_FLAGS) {
		ret = -EPERM;
		failure_string = "bad vm_flags";
		goto err_bad_arg;
	}
    
	vma->vm_flags |= VM_DONTCOPY | VM_MIXEDMAP;
	vma->vm_flags &= ~VM_MAYWRITE;
    //这里是回调方法 但是里面没有做什么事情 只是用于打印
	vma->vm_ops = &binder_vm_ops;
	vma->vm_private_data = proc;
    


    关键方法,初始化的alloc 和内存区域 都传入到了 binder_alloc_mmap_handler方法里
	ret = binder_alloc_mmap_handler(&proc->alloc, vma);
	if (ret)
		return ret;
	mutex_lock(&proc->files_lock);
	proc->files = get_files_struct(current);
	mutex_unlock(&proc->files_lock);
	return 0;
err_bad_arg:
	pr_err("%s: %d %lx-%lx %s failed %d\n", __func__,
	       proc->pid, vma->vm_start, vma->vm_end, failure_string, ret);
	return ret;
}
int binder_alloc_mmap_handler(struct binder_alloc *alloc,
			      struct vm_area_struct *vma)
{
	int ret;
	struct vm_struct *area;
	const char *failure_string;
	struct binder_buffer *buffer;
	mutex_lock(&binder_alloc_mmap_lock);
    //先判断一下alloc 是否被map过,第一次进入是不会被map过的
	if (alloc->buffer) {
		ret = -EBUSY;
		failure_string = "already mapped";
		goto err_already_mapped;
	}
    //构造一个内核虚拟地址的方法 申请空间
	area = get_vm_area(vma->vm_end - vma->vm_start, VM_ALLOC);
	if (area == NULL) {
		ret = -ENOMEM;
		failure_string = "get_vm_area";
		goto err_get_vm_area_failed;
	}
    //初始化为内核的一个虚拟的首地址,这里已经初始化完成
	alloc->buffer = area->addr;
    //用户空间对内核空间的偏移量
    //申请到的空间减去 buffer 也就是内核空间 用户空间首地址 - 内核空间首地址
	alloc->user_buffer_offset =
		vma->vm_start - (uintptr_t)alloc->buffer;
	mutex_unlock(&binder_alloc_mmap_lock);
#ifdef CONFIG_CPU_CACHE_VIPT
	if (cache_is_vipt_aliasing()) {
		while (CACHE_COLOUR(
				(vma->vm_start ^ (uint32_t)alloc->buffer))) {
			pr_info("binder_mmap: %d %lx-%lx maps %pK bad alignment\n",
				alloc->pid, vma->vm_start, vma->vm_end,
				alloc->buffer);
			vma->vm_start += PAGE_SIZE;
		}
	}
#endif
    //需要多少页?初始化为0 一般是4096
	alloc->pages = kzalloc(sizeof(alloc->pages[0]) *
				   ((vma->vm_end - vma->vm_start) / PAGE_SIZE),
			       GFP_KERNEL);
	if (alloc->pages == NULL) {
		ret = -ENOMEM;
		failure_string = "alloc page array";
		goto err_alloc_pages_failed;
	}
    //大小
	alloc->buffer_size = vma->vm_end - vma->vm_start;
    //申请好了一个壳
	buffer = kzalloc(sizeof(*buffer), GFP_KERNEL);
	if (!buffer) {
		ret = -ENOMEM;
		failure_string = "alloc buffer struct";
		goto err_alloc_buf_struct_failed;
	}
	buffer->data = alloc->buffer;
    //添加到list里
	list_add(&buffer->entry, &alloc->buffers);
	buffer->free = 1;
	binder_insert_free_buffer(alloc, buffer);
    //异步空间只有同步空间的一半
	alloc->free_async_space = alloc->buffer_size / 2;
	barrier();
	alloc->vma = vma;
	alloc->vma_vm_mm = vma->vm_mm;
	/* Same as mmgrab() in later kernel versions */
	atomic_inc(&alloc->vma_vm_mm->mm_count);
	return 0;
err_alloc_buf_struct_failed:
	kfree(alloc->pages);
	alloc->pages = NULL;
err_alloc_pages_failed:
	mutex_lock(&binder_alloc_mmap_lock);
	vfree(alloc->buffer);
	alloc->buffer = NULL;
err_get_vm_area_failed:
err_already_mapped:
	mutex_unlock(&binder_alloc_mmap_lock);
	pr_err("%s: %d %lx-%lx %s failed %d\n", __func__,
	       alloc->pid, vma->vm_start, vma->vm_end, failure_string, ret);
	return ret;
}

 那mmap到底干了啥?首先进行内核偏移的计算,在内核把用户空间的内存区域和内核空间的内存区域都进行了申请,将内核空间和用户空间的内存偏移计算出来干什么?后面如果通过内核空间寻找用户空间只需要知道偏移量就可以进行映射

但是好像没有申请内存?

新的版本是当你发起申请的时候动态给你内存,而不是一开始给你的1m-8k的内存

当你需要的时候,会按照物理页(page)给你内存 那么是如何做到的?

static int binder_update_page_range(struct binder_proc *proc, int allocate,
	void *start, void *end, struct vm_area_struct *vma)
{
	void *page_addr;
	unsigned long user_page_addr;
	struct vm_struct tmp_area;
	struct page **page;
	struct mm_struct *mm;
	if (binder_debug_mask & BINDER_DEBUG_BUFFER_ALLOC)
		printk(KERN_INFO "binder: %d: %s pages %p-%p\n",
		       proc->pid, allocate ? "allocate" : "free", start, end);
	if (end <= start)
		return 0;
	if (vma)
		mm = NULL;
	else
		mm = get_task_mm(proc->tsk);
	if (mm) {
		down_write(&mm->mmap_sem);
		vma = proc->vma;
	}
	if (allocate == 0)
		goto free_range;
	if (vma == NULL) {
		printk(KERN_ERR "binder: %d: binder_alloc_buf failed to "
		       "map pages in userspace, no vma\n", proc->pid);
		goto err_no_vma;
	}

    //根据start和end 的大小 计算申请对应的内存页
	for (page_addr = start; page_addr < end; page_addr += PAGE_SIZE) {
		int ret;
		struct page **page_array_ptr;
        算出需要多少物理页
		page = &proc->pages[(page_addr - proc->buffer) / PAGE_SIZE];
		BUG_ON(*page);
		*page = alloc_page(GFP_KERNEL | __GFP_ZERO);
        
		if (*page == NULL) {
			printk(KERN_ERR "binder: %d: binder_alloc_buf failed "
			       "for page at %p\n", proc->pid, page_addr);
			goto err_alloc_page_failed;
		}
		tmp_area.addr = page_addr;
		tmp_area.size = PAGE_SIZE + PAGE_SIZE /* guard page? */;
		page_array_ptr = page;
        算出内存空间的内存地址
		ret = map_vm_area(&tmp_area, PAGE_KERNEL, &page_array_ptr);
		if (ret) {
			printk(KERN_ERR "binder: %d: binder_alloc_buf failed "
			       "to map page at %p in kernel\n",
			       proc->pid, page_addr);
			goto err_map_kernel_failed;
		}

        将用户空间的偏移地址计算出
		user_page_addr =
			(uintptr_t)page_addr + proc->user_buffer_offset;

        vma是用户空间的内存地址 进行赋值
		ret = vm_insert_page(vma, user_page_addr, page[0]);
		if (ret) {
			printk(KERN_ERR "binder: %d: binder_alloc_buf failed "
			       "to map page at %lx in userspace\n",
			       proc->pid, user_page_addr);
			goto err_vm_insert_page_failed;
		}
		/* vm_insert_page does not seem to increment the refcount */
	}
	if (mm) {
		up_write(&mm->mmap_sem);
		mmput(mm);
	}
	return 0;
free_range:

    那为什么这里又有一个类似的行为?
    
	for (page_addr = end - PAGE_SIZE; page_addr >= start;
	     page_addr -= PAGE_SIZE) {
		page = &proc->pages[(page_addr - proc->buffer) / PAGE_SIZE];
		if (vma)
			zap_page_range(vma, (uintptr_t)page_addr +
				proc->user_buffer_offset, PAGE_SIZE, NULL);
err_vm_insert_page_failed:

        最后释放
		unmap_kernel_range((unsigned long)page_addr, PAGE_SIZE);
err_map_kernel_failed:
		__free_page(*page);
		*page = NULL;
err_alloc_page_failed:
		;
	}
err_no_vma:
	if (mm) {
		up_write(&mm->mmap_sem);
		mmput(mm);
	}
	return -ENOMEM;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值