Android Framework原理 -- Binder驱动源码分析-4.2 binder_mmap
binder_mmap,我们之前简单介绍过mmap的原理,那么这里我们看下,Binder驱动内部是如何做的
static int binder_mmap(struct file *filp, struct vm_area_struct *vma)
{
int ret;
//内核空间
struct vm_struct *area;
//当前进程信息
struct binder_proc *proc = filp->private_data;
const char *failure_string;
struct binder_buffer *buffer;
if (proc->tsk != current)
return -EINVAL;
//①
if ((vma->vm_end - vma->vm_start) > SZ_4M)
vma->vm_end = vma->vm_start + SZ_4M;
mutex_lock(&binder_mmap_lock);
if (proc->buffer) {
ret = -EBUSY;
failure_string = "already mapped";
goto err_already_mapped;
}
......
//②
area = get_vm_area(vma->vm_end - vma->vm_start, VM_IOREMAP);
if (area == NULL) {
ret = -ENOMEM;
failure_string = "get_vm_area";
goto err_get_vm_area_failed;
}
proc->buffer = area->addr;
proc->user_buffer_offset = vma->vm_start - (uintptr_t)proc->buffer;
mutex_unlock(&binder_mmap_lock);
#ifdef CONFIG_CPU_CACHE_VIPT
if (cache_is_vipt_aliasing()) {
while (CACHE_COLOUR((vma->vm_start ^ (uint32_t)proc->buffer))) {
pr_info("binder_mmap: %d %lx-%lx maps %p bad alignment\n", proc->pid, vma->vm_start, vma->vm_end, proc->buffer);
vma->vm_start += PAGE_SIZE;
}
}
#endif
proc->pages = kzalloc(sizeof(proc->pages[0]) * ((vma->vm_end - vma->vm_start) / PAGE_SIZE), GFP_KERNEL);
if (proc->pages == NULL) {
ret = -ENOMEM;
failure_string = "alloc page array";
goto err_alloc_pages_failed;
}
proc->buffer_size = vma->vm_end - vma->vm_start;
vma->vm_ops = &binder_vm_ops;
vma->vm_private_data = proc;
//③
if (binder_update_page_range(proc, 1, proc->buffer, proc->buffer + PAGE_SIZE, vma)) {
ret = -ENOMEM;
failure_string = "alloc small buf";
goto err_alloc_small_buf_failed;
}
buffer = proc->buffer;
INIT_LIST_HEAD(&proc->buffers);
list_add(&buffer->entry, &proc->buffers);
buffer->free = 1;
binder_insert_free_buffer(proc, buffer);
proc->free_async_space = proc->buffer_size / 2;
barrier();
proc->files = get_files_struct(current);
proc->vma = vma;
proc->vma_vm_mm = vma->vm_mm;
/*pr_info("binder_mmap: %d %lx-%lx maps %p\n",
proc->pid, vma->vm_start, vma->vm_end, proc->buffer);*/
return 0;
err_alloc_small_buf_failed:
kfree(proc->pages);
proc->pages = NULL;
err_alloc_pages_failed:
mutex_lock(&binder_mmap_lock);
vfree(proc->buffer);
proc->buffer = NULL;
err_get_vm_area_failed:
err_already_mapped:
mutex_unlock(&binder_mmap_lock);
err_bad_arg:
pr_err("binder_mmap: %d %lx-%lx %s failed %d\n",
proc->pid, vma->vm_start, vma->vm_end, failure_string, ret);
return ret;
}
我们先看下binder_mmap的两个入参,它是从service_manager那边传过来的,我们重点关注第二个参数:vma,我们可以把它看做是用户空间,然后在binder_mmap中创建了一个area,就是内核空间
①:首先,会判断用户空间大小是否超过4M,我们可以往前看,当service_manager调用open方法时,传入的mapsize大小为128 * 1024,也就是128K,也就是说在内核空间开辟了一块128K的用户空间内存
②:get_vm_area(vma->vm_end - vma->vm_start, VM_IOREMAP);调用get_vm_area方法,就是在内核空间寻找一块连续的内存,多大呢?就是传进来的用户空间的大小;然后将内核空间的虚拟地址赋值给用户进程
③:调用binder_update_page_range方法,这个方法中主要工作就是创建物理内存并做映射关系,看下源码
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 (allocate == 0)
goto free_range;
if (vma == NULL) {
pr_err("%d: binder_alloc_buf failed to map pages in userspace, no vma\n",
proc->pid);
goto err_no_vma;
}
for (page_addr = start; page_addr < end; page_addr += PAGE_SIZE) {
int ret;
page = &proc->pages[(page_addr - proc->buffer) / PAGE_SIZE];
BUG_ON(*page);
//分配一页的物理内存 4K
*page = alloc_page(GFP_KERNEL | __GFP_HIGHMEM | __GFP_ZERO);
if (*page == NULL) {
pr_err("%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? */;
//将内核空间与其建立映射关系
ret = map_vm_area(&tmp_area, PAGE_KERNEL, page);
if (ret) {
pr_err("%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;
//将用户空间与其建立映射关系
ret = vm_insert_page(vma, user_page_addr, page[0]);
if (ret) {
pr_err("%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;
这里我们看到就是,首先会分配一页的物理内存4K,然后调用map_vm_area将内核空间虚拟地址与物理内存映射;调用vm_insert_page方法,将用户空间与物理内存映射,见下图:
就这样,完成了物理内存与用户空间和内核空间的映射,binder_mmap完成了自己的工作。
接着再回到service_manager的main方法中,我们看到调用了binder_open之后,会调用binder_loop方法,这个有点儿类似Android的Handler,也是开启循环,接收命令去执行任务。
下一篇: 安装debugfs: 情况排查和修复
推荐阅读
-
解析Android Framework原理中的Binder驱动源码
-
Android Framework原理 -- Binder驱动源码分析-4.2 binder_mmap
-
分析Android源码中的Binder驱动(第一部分)
-
Android 11 WiFi开启流程-STA_PRIMARY,如果是打开其他WiFi,则参数2为传入的staId。 frameworks/opt/net/wifi/service/java/com/android/server/wifi/WifiServiceImpl.java public synchronized boolean setWifiEnabled(String packageName, boolean enable) { return setWifiEnabled2(packageName, STA_PRIMARY, enable); } public synchronized boolean setWifiEnabled2(String packageName, int staId,boolean enable) { if (enforceChangePermission(packageName) != MODE_ALLOWED) { return false; } boolean isPrivileged = isPrivileged(Binder.getCallingPid, Binder.getCallingUid); if (!isPrivileged && !isDeviceOrProfileOwner(Binder.getCallingUid, packageName) && !mWifiPermissionsUtil.isTargetSdkLessThan(packageName, Build.VERSION_CODES.Q, Binder.getCallingUid) && !isSystem(packageName, Binder.getCallingUid)) { mLog.info("setWifiEnabled not allowed for uid=%") .c(Binder.getCallingUid).flush; return false; } // If Airplane mode is enabled, only privileged apps are allowed to toggle Wifi if (mSettingsStore.isAirplaneModeOn && !isPrivileged) { mLog.err("setWifiEnabled in Airplane mode: only Settings can toggle wifi").flush; return false; } // If SoftAp is enabled, only privileged apps are allowed to toggle wifi if (!isPrivileged && mTetheredSoftApTracker.getState == WIFI_AP_STATE_ENABLED) { mLog.err("setWifiEnabled with SoftAp enabled: only Settings can toggle wifi").flush; return false; } mLog.info("setWifiEnabled package=% uid=% enable=%").c(packageName) .c(Binder.getCallingUid).c(enable).flush; long ident = Binder.clearCallingIdentity; try { if (staId == STA_PRIMARY && !mSettingsStore.handleWifiToggled(enable)) { // Nothing to do if wifi cannot be toggled return true; } } finally { Binder.restoreCallingIdentity(ident); } if (mWifiPermissionsUtil.checkNetworkSettingsPermission(Binder.getCallingUid)) { mWifiMetrics.logUserActionEvent(enable ? UserActionEvent.EVENT_TOGGLE_WIFI_ON : UserActionEvent.EVENT_TOGGLE_WIFI_OFF); } if (!mIsControllerStarted) { Log.e(TAG,"WifiController is not yet started, abort setWifiEnabled"); return false; } mWifiMetrics.incrementNumWifiToggles(isPrivileged, enable); if(staId == STA_PRIMARY) mActiveModeWarden.wifiToggled; else if(staId == STA_SECONDARY && (getNumConcurrentStaSupported > 1) && (getWifiEnabledState == WifiManager.WIFI_STATE_ENABLED)) mActiveModeWarden.qtiWifiToggled(staId, enable); else Log.e(TAG,"setWifiEnabled not allowed for Id: " + staId); return true; } 四、可以看到wifiservice调用了ActiveModeWarden的wifiToggled,发送了CMD_WIFI_TOGGLED的消息,通知WiFi切换了。 frameworks/opt/net/wifi/service/java/com/android/server/wifi/ActiveModeWarden.java public void wifiToggled { mWifiController.sendMessage(WifiController.CMD_WIFI_TOGGLED); } 五、我们看WifiController是怎么处理这个消息的。WifiController是ActiveModeWarden中的一个状态机,用来管理WiFi的操作,包括热点啊飞行模式什么的。 打开WiFi之前,状态机应该是在Disabled状态,我们看Disable状态里的处理。 class DisabledState extends BaseState { public boolean processMessageFiltered(Message msg) { switch (msg.what) { case CMD_WIFI_TOGGLED: case CMD_SCAN_ALWAYS_MODE_CHANGED: if (shouldEnableSta) { startClientModeManager; transitionTo(mEnabledState); } break; 启动一个新的客户端管理。 private boolean startClientModeManager { Log.d(TAG, "Starting ClientModeManager"); ClientListener listener = new ClientListener; ClientModeManager manager = mWifiInjector.makeClientModeManager(listener); listener.setActiveModeManager(manager); manager.start; if (!switchClientModeManagerRole(manager)) { return false; } mActiveModeManagers.add(manager); return true; } 六、start了ClientModeManager frameworks/opt/net/wifi/service/java/com/android/server/wifi/ClientModeManager.java public void start { Log.d(TAG, "Starting with role ROLE_CLIENT_SCAN_ONLY"); mRole = ROLE_CLIENT_SCAN_ONLY; mTargetRole = ROLE_CLIENT_SCAN_ONLY; mStateMachine.sendMessage(ClientModeStateMachine.CMD_START); } 看一下是谁处理了这个START消息呢 private class IdleState extends State { @Override public boolean processMessage(Message message) { switch (message.what) { case CMD_START: // Always start in scan mode first. mClientInterfaceName = mWifiNative.setupInterfaceForClientInScanMode( mWifiNativeInterfaceCallback); if (TextUtils.isEmpty(mClientInterfaceName)) { Log.e(TAG, "Failed to create ClientInterface. Sit in Idle"); mModeListener.onStartFailure; break; } transitionTo(mScanOnlyModeState); break; } } 七、这里可以看出,WifiNative先去启动HAL frameworks/opt/net/wifi/service/java/com/android/server/wifi/WifiNative.java public String setupInterfaceForClientInScanMode( @NonNull InterfaceCallback interfaceCallback) { synchronized (mLock) { if (!startHal) { mWifiMetrics.incrementNumSetupClientInterfaceFailureDueToHal; return null; } Iface iface = mIfaceMgr.allocateIface(Iface.IFACE_TYPE_STA_FOR_SCAN); iface.externalListener = interfaceCallback; iface.name = createStaIface(iface); if (!mWifiCondManager.setupInterfaceForClientMode(iface.name, Runnable::run, new NormalScanEventCallback(iface.name), new PnoScanEventCallback(iface.name))) { Log.e(TAG, "Failed to setup iface in wificond=" + iface.name); teardownInterface(iface.name); mWifiMetrics.incrementNumSetupClientInterfaceFailureDueToWificond; return null; } iface.networkObserver = new NetworkObserverInternal(iface.id); if (!registerNetworkObserver(iface.networkObserver)) { teardownInterface(iface.name); return null; } mWifiMonitor.startMonitoring(iface.name); onInterfaceStateChanged(iface, isInterfaceUp(iface.name)); iface.featureSet = getSupportedFeatureSetInternal(iface.name); return iface.name; } } 八、启动HAL WifiVendorHal.java-->startVendorHal --> HalDeviceManager.java --> startWifi --> IWifi.start mWifi.start方法是启动实际加载WiFi动作的调用,这里涉及HIDL机制调用。通过获取IWifi接口对象,调用其方法。这里IWifi接口对象是IWifi.hal文件中实现。 android/hardware/interfaces/wifi/1.0/IWifi.hal 在编译时,编译器会将IWifi.hal解析为IWifi.java文件,直接看该文件中的start方法实现即可。 android/out/soong//.intermediates/hardware/interfaces/wifi/1.0/android.hardware.wifi-V1.0-java_gen_java/gen/srcs/android/hardware/wifi/V1_0/IWifi.java public android.hardware.wifi.V1_0.WifiStatus start throws android.os.RemoteException { try { ... ... ... ... mRemote.transact(3 /* start */, _hidl_request, _hidl_reply, 0 /* flags */); _hidl_reply.verifySuccess; _hidl_request.releaseTemporaryStorage; return _hidl_out_status; } finally { _hidl_reply.release; } } 通过binder调用,将调用到wifi.cpp中的start方法. android/hardware/interfaces/wifi/1.4/default/wifi.cpp Return<void> Wifi::start(start_cb hidl_status_cb) { return validateAndCall(this, WifiStatusCode::ERROR_UNKNOWN, &Wifi::startInternal, hidl_status_cb); } wifi.cpp->start ==> wifi.cpp->startInternal ==> wifi.cpp->initializeModeControllerAndLegacyHal ==> WifiModeController->initialize ==> DriverTool->LoadDriver 通过调用DriverTool->LoadDriver将返回到Android framework中。下面是LoadDriver的实现。 android/frameworks/opt/net/wifi/libwifi_hal/include/wifi_hal/driver_tool.cpp bool DriverTool::LoadDriver { return ::wifi_load_driver == 0; } 在wifi_load_driver方法中,将调用系统接口加载WiFi驱动ko。关于系统insmod接口的调用,本文不做分析。到这里,已梳理完在WifiNative类中调用的startHal方法。 android/frameworks/opt/net/wifi/libwifi_hal/wifi_hal_common.cpp int wifi_load_driver { ... ... ... ... insmod(file,args); ... ... ... ... } 调用WifiNl80211Manager类的setupInterfaceForClientMode方法。 该类的主要对WiFi 80211nl管理接口的封装,接口在WiFicond守护进程中呈现给WiFi框架。该类提供的接口仅使用与WiFi框架,访问权限受selinux权限保护。 setupInterfaceForClientMode方法主要为Station模式设置接口。 android/frameworks/base/wifi/java/android/net/wifi/nl80211/WifiNl80211Manager.java public boolean setupInterfaceForClientMode(@NonNull String ifaceName, @NonNull @CallbackExecutor Executor executor, @NonNull ScanEventCallback scanCallback, @NonNull ScanEventCallback pnoScanCallback) { ... ... ... ... // Refresh Handlers mClientInterfaces.put(ifaceName, clientInterface); try { IWifiScannerImpl wificondScanner = clientInterface.getWifiScannerImpl; mWificondScanners.put(ifaceName, wificondScanner); Binder.allowBlocking(wificondScanner.asBinder); ScanEventHandler scanEventHandler = new ScanEventHandler(executor, scanCallback); mScanEventHandlers.put(ifaceName, scanEventHandler); wificondScanner.subscribeScanEvents(scanEventHandler); PnoScanEventHandler pnoScanEventHandler = new PnoScanEventHandler(executor, pnoScanCallback); mPnoScanEventHandlers.put(ifaceName, pnoScanEventHandler); wificondScanner.subscribePnoScanEvents(pnoScanEventHandler); ... ... ... ... } 到这里,ClientModeStateMachine状态机在IdleState状态成功处理完了CMD_START消息。状态机将转到“mScanOnlyModeState”状态,将会执行以下调用流程(具体原因可查看状态机机制)。 IdleState.exit->StartedState.enter->StartedState.exit->ScanOnlyModeState.enter。 九、启动HAL以后,就要启动supplicant了。 在第五步的时候我们调用了ActiveModeWarden.java的startClientModeManagerh函数。start以后会执行switchClientModeManagerRole