转换与梳理:进程状态的变化概览
简介
我们经常会说道Android存在几种进程状(前台进程,可见进程,服务进程,后台进程,空进程 )等等做app一般最多就是五种状态,但是具体Android系统将进程分成多少状态呢?每种状态到底有什么含义呢?今天我们就大概聊聊Android系统中的进程状态。
我们先来看下面这个类
ProcessList.java
final class ProcessList {
第一阶段定义的是一些系统标注进程不同状态的一些值
//进程crash的最小时间间隔,当一个进程连续crash的时间间隔小雨60s,这个进程就是bad进程
static final int MIN_CRASH_INTERVAL = 60*1000;
//未知进程
static final int UNKNOWN_ADJ = 16;
//如果某个进程有不可见的Activity,根据LRU原则,这个进程的adj值就会在[9,15]之间
static final int CACHED_APP_MAX_ADJ = 15;
static final int CACHED_APP_MIN_ADJ = 9;
//最近没有任务的Service进程的adj的值
static final int SERVICE_B_ADJ = 8;
//表示previous进程的adj值(previous:之前)
static final int PREVIOUS_APP_ADJ = 7;
//Home进程的adj的值,也就是Launcher进程
static final int HOME_APP_ADJ = 6;
//service正在运行的进程
static final int SERVICE_ADJ = 5;
//重量级进程,就是android:cantSaveState属性为true的进程,目前没有用到的地方
static final int HEAVY_WEIGHT_APP_ADJ = 4;
//正在执行备份操作的进程
static final int BACKUP_APP_ADJ = 3;
//perceptible进程指的是那些没有在前台现实但是用户能感觉到比如音乐
static final int PERCEPTIBLE_APP_ADJ = 2;
//Activity的onStart()周期的进程,注意Activity弹出dialog这个activity也是可见的,只是没有焦点
static final int VISIBLE_APP_ADJ = 1;
//foreground进程,Activity在前台显示,recevire在onReceive()都是前台进程
static final int FOREGROUND_APP_ADJ = 0;
//关联着系统或persistent进程(service关联)
static final int PERSISTENT_SERVICE_ADJ = -11;
//系统persistent进程,比如telephony,这类app进程 android:persistent为true
static final int PERSISTENT_PROC_ADJ = -12;
//系统进程,但是这个值不是systemserver,这个是init进程
//由init进程启动的daemon和service进程的adj值也等于-16
//由于systemServer进程由Zygote启动所以Zygote=SystemServer=Init=16
static final int SYSTEM_ADJ = -16;
//native进程的adj的值
static final int NATIVE_ADJ = -17;
定义一些规定的常量
//内存大小为4*1024
static final int PAGE_SIZE = 4*1024;
//只拥有activity并且activity不可见的进程最少只能是2
static final int MIN_CACHED_APPS = 2;
//允许空进程最大时间
static final long MAX_EMPTY_TIME = 30*60*1000;
//定义LMK的6个adj的值,这个和内核中的值是需要转换的
private final int[] mOomAdj = new int[] {
FOREGROUND_APP_ADJ, VISIBLE_APP_ADJ, PERCEPTIBLE_APP_ADJ,
BACKUP_APP_ADJ, CACHED_APP_MIN_ADJ, CACHED_APP_MAX_ADJ
};
还会定义一些不同内存的手机对应的一些阀值
基本命令
查看adj
可以通过命令:
adb shell
//筛选进程
ps | grep <包名|pid>
//然后,其中oom_score_adj是内核计算过后的adj
cat proc/<pid>/oom_score_adj
方法2
adb shell
dumpsys activity o
//然后查看
ProcessRecord中下面这些属性反应了oom_score_adj的值
int maxAdj; // Maximum OOM adjustment for this process
int curRawAdj; // Current OOM unlimited adjustment for this process
int setRawAdj; // Last set OOM unlimited adjustment for this process
int curAdj; // Current OOM adjustment for this process
int setAdj; // Last set OOM adjustment for this process
可以通过命令:
adb shell
//筛选进程
ps | grep <包名|pid>
//然后,其中oom_score_adj是内核计算过后的adj
cat proc/<pid>/oom_score_adj
方法2
adb shell
dumpsys activity o
//然后查看
int maxAdj; // Maximum OOM adjustment for this process
int curRawAdj; // Current OOM unlimited adjustment for this process
int setRawAdj; // Last set OOM unlimited adjustment for this process
int curAdj; // Current OOM adjustment for this process
int setAdj; // Last set OOM adjustment for this process
其中:
- maxAdj 指定了该进程允许的oom_score_adj最大值(主要给系统应用和常驻内存使用,通过maxAdj保证这些进程拥有较高优先级)
- curXXX这一组记录了这一次优先级计算的结果,会将curXXX复制给对应的setXXX这一组上进行备份。
- xxxRawAdj记录了没有经过限制的adj值,“没有经过限制”是指这其中的值可能是超过了oom_score_adj文件所允许的范围(-1000 ~ 1000)
- ProcessList.Java中预定义了oom_score_adj的可能取值。
static final int UNKNOWN_ADJ = 1001; // 未知进程
static final int PREVIOUS_APP_ADJ = 700; // 前一个应用
static final int HOME_APP_ADJ = 600; // 桌面进程
static final int SERVICE_ADJ = 500; // 包含了Service的进程
static final int HEAVY_WEIGHT_APP_ADJ = 400; // 重量级进程
static final int BACKUP_APP_ADJ = 300; // 备份应用进程
static final int PERCEPTIBLE_APP_ADJ = 200; // 可感知的进程
static final int VISIBLE_APP_ADJ = 100; // 可见进程
static final int VISIBLE_APP_LAYER_MAX = PERCEPTIBLE_APP_ADJ - VISIBLE_APP_ADJ - 1;
static final int FOREGROUND_APP_ADJ = 0; // 前台进程
static final int PERSISTENT_SERVICE_ADJ = -700; // 常驻服务进程
static final int PERSISTENT_PROC_ADJ = -800; // 常驻应用进程
static final int SYSTEM_ADJ = -900; // 系统进程
static final int NATIVE_ADJ = -1000; // native系统进程
解释上面这些可能的情况:
- FOREGROUND_APP_ADJ = 0,这个是前台应用进程的优先级,正在和用户交互的进程(普通应用获得的最高优先级)
- VISIBLE_APP_ADJ是具有可见Activity进程的优先级,前台的Activity设置透明,或者小窗口后面的Activity也可见这种情况
- PERCEPTIBLE_APP_ADJ是指用户可感知的进程,可感知的进程包括:
1.进程中包含了处于pause状态或者正在pause的Activity
2.进程中包含了正在stop的Activity
3.进程中包含了前台的Service
- PREVIOUS_APP_ADJ描述的是前一个应用的优先级。在启动新的Activity时,如果新启动的Activity是属于一个新的进程的,那么当前即将被stop的Activity所在的进程便会成为“前一个应用”进程。
- HEAVY_WEIGHT_APP_ADJ 描述的重量级进程是指那些通过Manifest指明不能保存状态的应用进程。
- PERSISTENT_SERVICE_ADJ = -700,PERSISTENT_PROC_ADJ = -800,系统中的一些进程,比如System_ADJ,NATIVE_ADJ=-1000
进程分类
- 前台进程 (Foreground process)
- 包含用户正在交互的 Activity(已调用 Activity 的 onResume() 方法)
- 包含某个 Service,后者绑定到用户正在交互的 Activity
- 包含正在“前台”运行的 Service(已调用 startForeground())
- 包含正执行一个生命周期回调的 Service(onCreate()、onStart() 或 onDestroy())
- 包含正执行其 onReceive() 方法的 BroadcastReceiver
- 可见进程(Visible process)
- 不再前台,但对前台有影响,比如如果前台 Activity 启动了一个对话框,允许在其后显示上一 Activity,则有可能会发生这种情况。
- 托管绑定到可见(或前台)Activity 的 Service
- 服务进程 (Service process)
- 使用startservice()方法启动服务,服务与可见内容没直接关联(下载,播放音乐等)属于可感知
- 后台进程 (Background process)
- 不可见的Activity进程(调用onStop()方法)这类进程保存在LRU列表中。
- 空进程 (Empty process)
- 不包含任何活动应用组件的进程。目的是通过应用缓存进程,而不浪费时间在启动新的进程。
Android中进程管理的机制
platform/frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java)
platform/frameworks/base/services/core/java/com/android/server/am/ProcessList.java
platform/system/core/lmkd/lmkd.c
kernel/common/drivers/staging/Android/lowmemorykiller.c
- 包含用户正在交互的 Activity(已调用 Activity 的 onResume() 方法)
- 包含某个 Service,后者绑定到用户正在交互的 Activity
- 包含正在“前台”运行的 Service(已调用 startForeground())
- 包含正执行一个生命周期回调的 Service(onCreate()、onStart() 或 onDestroy())
- 包含正执行其 onReceive() 方法的 BroadcastReceiver
- 不再前台,但对前台有影响,比如如果前台 Activity 启动了一个对话框,允许在其后显示上一 Activity,则有可能会发生这种情况。
- 托管绑定到可见(或前台)Activity 的 Service
- 使用startservice()方法启动服务,服务与可见内容没直接关联(下载,播放音乐等)属于可感知
- 不可见的Activity进程(调用onStop()方法)这类进程保存在LRU列表中。
- 不包含任何活动应用组件的进程。目的是通过应用缓存进程,而不浪费时间在启动新的进程。
platform/frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java)
platform/frameworks/base/services/core/java/com/android/server/am/ProcessList.java
platform/system/core/lmkd/lmkd.c
kernel/common/drivers/staging/Android/lowmemorykiller.c
通过输入命令:
adb shell
dumpsys activity o
查看有关进程状态
Android m
杀进程与阶段对应
杀进程与阶段对应
对应在ProcessList.java
中定义
级别 | 常量名称 | 内存大小 | 简述 |
---|---|---|---|
-16 | SYSTEM_ADJ | 61440 kB | 系统进程 |
-12 | PERSISTENT_PROC_ADJ | 61440 kB | 系统persistent进程,比如telephony |
-11 | PERSISTENT_SERVICE_ADJ | 61440 kB | 关联着系统或persistent进程 |
0 | FOREGROUND_APP_ADJ | 61440 kB | 前台进程(Foreground process |
1 | VISIBLE_APP_ADJ | 76800 kB | 可见进程(Visible process) |
2 | PERCEPTIBLE_APP_ADJ | 92160 kB | 可感知进程,比如后台音乐播放 |
3 | BACKUP_APP_ADJ | 107520 kB | 备份进程 |
4 | HEAVY_WEIGHT_APP_ADJ | 137660 kB | 后台的重量级进程,system/rootdir/init.rc文件中设置 |
5 | SERVICE_ADJ | 137660 kB | 服务进程(Service process) |
6 | HOME_APP_ADJ | 137660 kB | Home进程 |
7 | PREVIOUS_APP_ADJ | 137660 kB | 上一个App的进程(往往通过按返回键) |
8 | SERVICE_B_ADJ | 137660 kB | List中的Service(较老的、使用可能性更小) |
9 | CACHED_APP_MIN_ADJ | 137660 kB | 不可见进程的adj最小值 |
15 | CACHED_APP_MAX_ADJ | 174948 kB | 不可见进程的adj最大值 |
Android n
杀进程与阶段对应
对应在ProcessList.java
中定义
杀进程与阶段对应对应在ProcessList.java
中定义
级别 | 常量名称 | 内存大小 | lmk杀进程根据adj6档 |
---|---|---|---|
-900 | SYSTEM_ADJ | 73,728k | |
-800 | PERSISTENT_PROC_ADJ | 73,728K | |
-700 | PERSISTENT_SERVICE_ADJ | 73,728K | |
0 | FOREGROUND_APP_ADJ | 73,728K | + |
100 | VISIBLE_APP_ADJ | 92,160K | + |
200 | PERCEPTIBLE_APP_ADJ | 110,592K | + |
300 | BACKUP_APP_ADJ | 129,024K | + |
400 | HEAVY_WEIGHT_APP_ADJ | 221,184K | |
500 | SERVICE_ADJ | 221,184K | |
600 | HOME_APP_ADJ | 221,184K | |
700 | PREVIOUS_APP_ADJ | 221,184K | |
800 | SERVICE_B_ADJ | 221,184K | |
900 | CACHED_APP_MIN_ADJ | 221,184K | + |
906 | CACHED_APP_MAX_ADJ | 322,560K | + |
Process state(m and n)
对应在ActivityManager中定义一下几种进程的状态
state级别 | 取值 | 解释 | 优先级(大于state)return IMPORTANCE_XXX |
---|---|---|---|
PROCESS_STATE_CACHED_EMPTY | 16 | 进程处于cached状态,且为空进程 | 400 |
PROCESS_STATE_CACHED_ACTIVITY_CLIENT | 15 | 进程处于cached状态,且为另一个cached进程(内含Activity)的client进程 | 400 |
PROCESS_STATE_CACHED_ACTIVITY | 14 | 进程处于cached状态,且内含Activity | 400 |
PROCESS_STATE_LAST_ACTIVITY | 13 | 后台进程,且拥有上一次显示的Activity | 400 |
PROCESS_STATE_HOME | 12 | 后台进程,且拥有home Activity | 400 |
PROCESS_STATE_RECEIVER | 11 | 后台进程,且正在运行receiver | 300 |
PROCESS_STATE_SERVICE | 10 | 后台进程,且正在运行service | 300 |
PROCESS_STATE_HEAVY_WEIGHT | 9 | 后台进程,但无法执行restore,因此尽量避免kill该进程 | 170 |
PROCESS_STATE_BACKUP | 8 | 后台进程,正在运行backup/restore操作 | 130 |
PROCESS_STATE_IMPORTANT_BACKGROUND | 7 | 对用户很重要的进程,用户不可感知其存在 | 130 |
PROCESS_STATE_IMPORTANT_FOREGROUND | 6 | 对用户很重要的进程,用户可感知其存在 | 200 |
PROCESS_STATE_TOP_SLEEPING | 5 | 与PROCESS_STATE_TOP一样,但此时设备正处于休眠状态 | 150 |
PROCESS_STATE_FOREGROUND_SERVICE | 4 | 拥有给一个前台Service | 125 |
PROCESS_STATE_BOUND_FOREGROUND_SERVICE | 3 | 拥有给一个前台Service,且由系统绑定 | 100 |
PROCESS_STATE_TOP | 2 | 拥有当前用户可见的top Activity | 100 |
PROCESS_STATE_PERSISTENT_UI | 1 | persistent系统进程,并正在执行UI操作 | 100 |
PROCESS_STATE_PERSISTENT | 0 | persistent系统进程 | 100 |
PROCESS_STATE_NONEXISTENT | -1 | 不存在的进程 | 1000 |
进程优先级定义
AMS中有三个核心方法
- updateOomAdjLocked:更新adj,当目标进程为空,或者被杀则返回false;否则返回true;
- computeOomAdjLocked:计算adj,返回计算后RawAdj值;
- applyOomAdjLocked:应用adj,当需要杀掉目标进程则返回false;否则返回true。
AMS中有三个核心方法
updateOomAdjLocked 中会调用 computeOomAdjLocked 和 applyOomAdjLocked。
LowMemoryKiller 的阈值的设定
阈值的设定
- /sys/module/lowmemorykiller/parameters/adj
- /sys/module/lowmemorykiller/parameters/minfree
shamu:/ # cat /sys/module/lowmemorykiller/parameters/adj
0,100,200,300,900,906
shamu:/ # cat /sys/module/lowmemorykiller/parameters/minfree
18432,23040,27648,32256,36864,46080
阈值的设定
shamu:/ # cat /sys/module/lowmemorykiller/parameters/adj
0,100,200,300,900,906
shamu:/ # cat /sys/module/lowmemorykiller/parameters/minfree
18432,23040,27648,32256,36864,46080
minfree中数值的单位是内存中的页面数量,一般情况下一个页面是4KB。
例如:将1,6写入节点/sys/module/lowmemorykiller/parameters/adj,将1024,8192写入节点/sys/module/lowmemorykiller/parameters/minfree。
策略:当系统可用内存低于8192个pages时,则会杀掉oom_score_adj>=6的进程;当系统可用内存低于1024个pages时,则会杀掉oom_score_adj>=1的进程。
lmkd 守护进程
system/core/lmkd/lmkd.c
system/core/lmkd/lmkd.c
lmkd创建名称为lmkd的socket,节点位于/dev/socket/lmkd,接受命令如下:
功能 | 命令 | 对应方法 |
---|---|---|
LMK_PROCPRIO | 设置进程adj | PL.setOomAdj() |
LMK_TARGET | 更新oom_adj | PL.updateOomLevels() |
LMK_PROCREMOVE | 移除进程 | PL.remove() |
设置adj
- 向节点/proc/<pid>/oom_score_adj写入oom_adj。
framework与lmkd对应方法:
static void ctrl_command_handler(void) {
int ibuf[CTRL_PACKET_MAX / sizeof(int)];
int len;
int cmd = -1;
int nargs;
int targets;
len = ctrl_data_read((char *)ibuf, CTRL_PACKET_MAX);
if (len <= 0)
return;
nargs = len / sizeof(int) - 1;
if (nargs < 0)
goto wronglen;
//将网络字节顺序转换为主机字节顺序
cmd = ntohl(ibuf[0]);
switch(cmd) {
case LMK_TARGET:
targets = nargs / 2;
if (nargs & 0x1 || targets > (int)ARRAY_SIZE(lowmem_adj))
goto wronglen;
cmd_target(targets, &ibuf[1]);
break;
case LMK_PROCPRIO:
if (nargs != 3)
goto wronglen;
//设置进程adj
cmd_procprio(ntohl(ibuf[1]), ntohl(ibuf[2]), ntohl(ibuf[3]));
break;
case LMK_PROCREMOVE:
if (nargs != 1)
goto wronglen;
cmd_procremove(ntohl(ibuf[1]));
break;
default:
ALOGE("Received unknown command code %d", cmd);
return;
}
return;
wronglen:
ALOGE("Wrong control socket read length cmd=%d len=%d", cmd, len);
}
- LMK_TARGET:AMS.updateConfiguration()的过程中调用updateOomLevels()方法, 分别向/sys/module/lowmemorykiller/parameters目录下的minfree和adj节点写入相应信息;
- LMK_PROCPRIO: AMS.applyOomAdjLocked()的过程中调用setOomAdj(),向/proc/<pid>/oom_score_adj写入oomadj 后直接返回;
- LMK_PROCREMOVE:AMS.handleAppDiedLocked或者 AMS.cleanUpApplicationRecordLocked()的过程,调用remove(),目前不做任何事,直接返回;
LowMemoryKiller Kernel driver
lowmemorykiller driver
位于 drivers/staging/android/lowmemorykiller.c
lowmemorykiller driver
位于 drivers/staging/android/lowmemorykiller.c
核心在于:通过 register_shrinker
和unregister_shrinker
分别用于初始化和退出。
LMK通过注册shrinker
来实现。其中shrinker
是Linux kernel标准的回收page的机制,由内核线程kswapd负责监控。
核心思想是
- 选择oom_score_adj最大的进程中,并且rss内存最大的进程作为选中要杀的进程。
- 杀进程方式:send_sig(SIGKILL, selected, 0)向选中的目标进程发送signal 9来杀掉目标进程。
内部使用:
lmkd参数
- oom_adj:代表进程的优先级, 数值越大,优先级越低,越容易被杀. 取值范围[-16, 15]
- oom_score_adj: 取值范围[-1000, 1000]
- oom_score:lmk策略中貌似并没有看到使用的地方,这个应该是oom才会使用。
lowmem_oom_adj_to_oom_score_adj 计算:
//OOM_SCORE_ADJ_MAX=1000
//OOM_DISABLE=-17
static int lowmem_oom_adj_to_oom_score_adj(int oom_adj)
{
if (oom_adj == OOM_ADJUST_MAX)
return OOM_SCORE_ADJ_MAX;
else
return (oom_adj * OOM_SCORE_ADJ_MAX) / -OOM_DISABLE;
}
- 当oom_adj = 15, 则 oom_score_adj = 1000;
- 当oom_adj < 15, 则 oom_score_adj = oom_adj * 1000/17;
小结
- 系统framework根据不同类型进程生命周期控制,动态分配不同的adj,并在不同时机进行更新。
- 更新adj时候在framework层会和lmkd守护进程进行通信,修改lmk deiver配置参数,设置/proc/pid/oom_score_adj;
- lowmemorykiller 驱动会被 linux 内核的内存 shrinker 机制调度,在 shrinker 操作中,计算进程 adj 和 rss,依据 driver 的 oom_adj 和 minfree 配置,进行 kill 进程操作
上一篇: 美国为什么限制中国的超算发展?轻松理解超算的运用、构造与软件奥秘
下一篇: 西门子S7-200 SMART:一步步教你配置PID向导、调用子程序与制作状态图测试,以及自适应PID参数设置实例指南
推荐阅读
-
转换与梳理:进程状态的变化概览
-
梳理Linux中进程与线程间的通信方法概览
-
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
-
[DFT+Experimental] Xiaolin Li AM:对质子化的新理解促进钠电富集镍结构的稳定 - 引言 通过氧化还原反应实现化学能和电能转换的可充电插层电池几乎肯定会发生与离子/电子转移相关的本体结构和/或界面变化。因此,离子/电子转移过程的可逆性和材料结构的演变决定了这些电池的可充电性或循环寿命。由于离子/电子扩散和结构演变在很大程度上受电极材料的内在特性以及电解质和电极材料之间的动态界面反应控制,因此保持结构完整性和界面稳定性对于实现高性能电池至关重要。 早期关于质子/锂离子交换的研究表明,在充电状态下,电解质中的质子可以结合到层状锂材料的主体中,但人们对阳极主体结构在连续循环中如何演变知之甚少。层状金属氧化物正极,尤其是富镍材料,已成为提高可充电碱性离子电池能量密度的有前途的候选材料;因此,为了提高性能,迫切需要解释为什么与电解质的界面极不稳定。最近,有人认为富镍阳极与晶界电解质的相互作用与体球的晶间开裂有关。研究还发现,富镍阳极在高浓度电解质、局部高浓度电解质(LHCE)和传统碳酸盐电解质中表现出明显不同的表面降解率。 成果简介 最近,西北太平洋国家实验室的研究人员肖必伟和李小林与德克萨斯农工大学的 Perla B. Balbuena 等人一起,使用 NaNiO2 在不同质子产生水平的电解质中作为模型系统,展示了质子掺杂效应的全貌。晶格氧的质子化会刺激过渡金属向碱性层迁移,并加速层状岩盐的相变,从而导致块体结构的分解和各向异性表面重构层的形成。
-
理解进程基本状态、转变、阻塞与挂起:一张揭示进程状态转换的图表
-
进程与线程状态的变更过程 - 线程状态转换详解
-
第二章:深入理解Linux编程 - 进程工作原理:剖析进程定义与特性、状态转换、关键数据结构、从创建到结束的过程、睡眠与唤醒机制、暂停与重启操作,以及处理器调度的核心概念