周鑫箭优秀作者
原创内容 来源:小居数码网 时间:2024-08-12 09:43:01 阅读() 收藏:46 分享:80 爆
导读:您正在阅读的是关于【数码知识】的问题,本文由科普作家协会,生活小能手,著名生活达人等整理监督编写。本文有3783个文字,大小约为14KB,预计阅读时间10分钟。
上一篇文章从Native角度讲解了Android进程管理的相关概念,本文将继续从上层的Framework中的进程启动、销毁场景和优先级处理、以及它们与四大组件的种种关联,来逐步解析Android进程管理的其他关键要素。
Android Framework层是运行在ART虚拟机之上的,对于一般的Java进程是不具备主动创建进程的能力的。我们都知道Android中存在四大组件:Activity、Service、BroadcastReceiver和ContentProvider,那么为什么这样划分呢,一个主要的原因是在四大组件的启动阶段,如果宿主进程没有正在运行,那么系统服务进程system_server将会先拉起进程,然后才会继续启动组件。简而言之,正因为AMS将安卓中进程的创建、销毁及优先级进行了封装,应用侧开发者才能够在对进程无感知的情况的同时使用四大组件。
启动四大组件方法 startActivity, sendBroadcast, startService/bindService 和 getContentProviderImpl 之所以能够启动进程,是因为它们都会通过发送 binder 消息到 AMS 进行处理,当 AMS 发现对应组件没有在 xml 声明的进程启动时,会先拉起该进程,然后再启动对应组件。
也就是说,四大组件正是APP进程的入口。它们拉起进程的方式都是一致的,基本流程:
彩蛋:实用的进程相关adb命令
Java进程的创建与销毁实时
adb logcat -b events | grep proc**07-16 14:19:23.357 4774 4800 I am_proc_start: [0,30314,10063,com.koushikdutta.vysor,activity,com.koushikdutta.vysor/.StartActivity]07-16 14:19:23.375 4774 6027 I am_proc_bound: [0,30314,com.koushikdutta.vysor]07-16 14:19:31.621 4774 5281 I am_proc_died: [0,30314,com.koushikdutta.vysor,900,17]
获取应用进程中组件详细信息
adb shell dumpsys activity p <packageName>*APP* UID 10115 ProcessRecord{4114d9d 18987:com.ss.android.ugc.aweme/u0a115} user #0 uid=10115 gids={50115, 20115, 9997, 3002, 3003, 3001} requiredAbi=armeabi-v7a instructionSet=arm class=com.ss.android.ugc.aweme.app.host.HostApplication ... // adj, procState优先级信息 oom: max=1001 curRaw=0 setRaw=0 cur=0 set=0 curSchedGroup=2 setSchedGroup=2 systemNoUi=false trimMemoryLevel=0 curProcState=2 repProcState=2 pssProcState=5 setProcState=2 ... **Activities**: - ActivityRecord{5e38d19 u0 com.ss.android.ugc.aweme/.main.MainActivity t948} **Services**: - ServiceRecord{13f23bc u0 com.ss.android.ugc.aweme/com.tt.miniapphost.process.base.HostCrossProcessCallService} ... **Connections**: - ConnectionRecord{77ae579 u0 CR com.ss.android.ugc.aweme/com.ss.android.socialbase.downloader.downloader.IndependentProcessDownloadService:@30b4240} ... **Published Providers**: - com.umeng.message.provider.MessageProvider -> ContentProviderRecord{76d000a u0 com.ss.android.ugc.aweme/com.umeng.message.provider.MessageProvider} ... **Connected Providers**: - c887614/com.android.providers.settings/.SettingsProvider->18987:com.ss.android.ugc.aweme/u0a115 s1/1 u0/0 +19h23m3s147ms - c4d7fba/com.android.providers.media/.MediaProvider->18987:com.ss.android.ugc.aweme/u0a115 s0/1 u1/2 +1m10s935ms **Receivers**: - ReceiverList{4afbc5 18987 com.ss.android.ugc.aweme/10115/u0 remote:687d23c} ...
获取应用优先级信息
adb shell dumpsys activity oProc 0: fore T/A/TOP trm: 0 18987:com.ss.android.ugc.aweme/u0a115 (top-activity) oom: max=1001 curRaw=0 setRaw=0 cur=0 set=0 state: cur=TOP set=TOP lastPss=268MB lastSwapPss=0.00 lastCachedPss=0.00 cached=false empty=false hasAboveClient=false
Android Framework中进程优先级的衡量单位有两种,除了 adj (对应 lmk 的 oom_score_adj)之外,新添了 procState 变量。这两个优先级相辅相成,adj 更多的用在给 lmk 判断该进程是否应该被杀死,而procState 更多的给 Framework 层的服务给进程状态"定级",例如,AMS可以简单粗暴的通过判断 procState 是否小于等于 PROCESS_STATE_IMPORTANT_FOREGROUND 来判断该进程是否为"前台进程":
[ActivityManagerService.java]
@OverridepublicbooleanisAppForeground(intuid){synchronized(this){UidRecorduidRec=mActiveUids.get(uid);if(uidRec==null||uidRec.idle){returnfalse;}returnuidRec.curProcState<=ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND;}}
这里的 UidRecord 是 Android 常用的权限管理方案,如果没有做修改的话,普通应用创建的进程都是在同一个 uid 里的。在系统侧,Android 常常通过 uid 来划分对应的进程是否具有相应的优先级。
关于Android中adj与procState具体值的含义,可以查看文章最后的附录表格,具体场景仅供参考。如果需要细致判定当前进程优先级的状态,通过源码分析最为直观。
adj、schedGroup、procState和adjType的核心计算步骤是在AMS中的computeOomAdjLocked方法完成的,如图:
这个方法的核心执行无外乎以下几个功能:
彩蛋:使用framework中的"自相矛盾"进行保活
进程保活无外乎也是一种普通应用对操作系统的攻击手段,对于一般的攻击手段,我把它理解成使用开发者约定俗成的规则漏洞来突破普通用户原有权限的行为。
比如说 buffer overflow,是利用了旧版本C编译器在切换函数堆栈时,没有有效检查堆栈信息作用范围的漏洞。使得普通应用用户可以利用超长的栈内信息覆盖调原有的堆栈跳转信息,使得应用执行其它意想不到的程序。通过这种方式可以获取到root权限;又比如说 TCP 劫持,是利用了发送方与接收方没有很好的 SEQ和ACK 序列的校验手段,使得中间者可以通过控制 SEQ和ACK的数值来控制正常通信方的流程。
这种优先级方式分为两种
第一种,依赖高优先级 client:
if(mayBeTop&&procState>ActivityManager.PROCESS_STATE_TOP){switch(procState){caseActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE:caseActivityManager.PROCESS_STATE_FOREGROUND_SERVICE://Somethingelseiskeepingitatthislevel,justleaveit.break;caseActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND:caseActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND:caseActivityManager.PROCESS_STATE_TRANSIENT_BACKGROUND:caseActivityManager.PROCESS_STATE_SERVICE:procState=ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE;app.adjType=mayBeTopType;app.adjSource=mayBeTopSource;app.adjTarget=mayBeTopTarget;...}}
maybeTop 为 true 需要 ContentProvider 或者 Service 的 client 正在前台显示 Activity
同理,通过让用户选择该client来提供服务,例如WallPaperService,可以让system_server成为client,以此来提高进程优先级。
第二种,两个进程互相绑定:
if((cr.flags&Context.BIND_FOREGROUND_SERVICE)!=0){clientProcState=ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE;}elseif(mWakefulness==PowerManagerInternal.WAKEFULNESS_AWAKE&&(cr.flags&Context.BIND_FOREGROUND_SERVICE_WHILE_AWAKE)!=0){clientProcState=ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE;}else{clientProcState=ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND;}...if(procState>clientProcState){procState=clientProcState;if(adjType==null){adjType="service";}}
通过设置 flag,两个普通的进程可以通过互相"攀升"来提升优先级
具体步骤:
adb shell ps | grep <pacakgeName>ACTIVITY MANAGER RUNNING PROCESSES (dumpsys activity processes) All known processes: *APP* UID 10064 ProcessRecord{f59eb2f 30917:com.bytedance.gcsuppression.demo/u0a64} ... oom: max=1001 curRaw=100 setRaw=100 cur=100 set=100 curSchedGroup=1 setSchedGroup=1 systemNoUi=false trimMemoryLevel=0 curProcState=5 repProcState=5 pssProcState=5 setProcState=5 lastStateTime=-23s716ms
(该方法在 Android 8.0 上测试过仍有效,更高版本尚未测试;因为是利用了 service 与动态广播的特性,所以 framework 很难对这个逻辑做出应对,除非是在系统侧维护一个 service 黑名单)
虽然杀进程有这么多的花样,但常见的方式就三种:
清理名称触发场景功能kill粒度最细,用于进程自杀、AMS 查杀单个缓存进程等杀死单个 java 进程killBackground普通应用也可以通过 AM 服务调用杀死系统中 background 进程force-stop以 package 为单位,强力杀死和该包有关联的进程并禁止大部分自启杀死该包下所有进程,关闭并销毁相关组件
普通的查杀方式如kill和killBackground都只是杀死单个或部分进程,当进程死亡后,会因为 binder fd 的释放通过死亡回调通知 AMS 进程已死,这期间 AMS 也有可能因为 Service 等组件重新将进程拉起。考虑到拉活方案花样多,应用中的其它 java 进程或 c 进程仍然可以通过启动组件的方式重新将进程拉起来。
在一般的原生机器上,force-stop虽然很少使用,一般只在设置里的"强制停止"按钮触发。除了杀死进程之外也会销毁组件等信息,同时也会更改 PMS 中对应包名的状态,避免再次拉活。其主要功能如下:
彩蛋:当我们上滑应用卡片时Android在做什么
voidcleanUpRemovedTaskLocked(TaskRecordtr,booleankillProcess,booleanremoveFromRecents){//1.在最近任务中移除Taskif(removeFromRecents){mRecentTasks.remove(tr);}...//2.选择需要查杀的进程finalStringpkg=component.getPackageName();ArrayList<ProcessRecord>procsToKill=newArrayList<>();ArrayMap<String,SparseArray<ProcessRecord>>pmap=mService.mProcessNames.getMap();for(inti=0;i<pmap.size();i++){SparseArray<ProcessRecord>uids=pmap.valueAt(i);for(intj=0;j<uids.size();j++){ProcessRecordproc=uids.valueAt(j);...//2.1如果Task所在进程还有别的Task在最近任务中显示,那么该进程不会被杀for(intk=0;k<proc.activities.size();k++){TaskRecordotherTask=proc.activities.get(k).getTask();if(tr.taskId!=otherTask.taskId&&otherTask.inRecents){return;}}//2.2进程有前台service的不会被杀,典型的有音乐播放软件if(proc.foregroundServices){return;}//Addprocesstokilllist.procsToKill.add(proc);}}//3.正式查杀for(inti=0;i<procsToKill.size();i++){ProcessRecordpr=procsToKill.get(i);//如果满足这两个条件才会立即被杀if(pr.setSchedGroup==ProcessList.SCHED_GROUP_BACKGROUND&&pr.curReceivers.isEmpty()){pr.kill("removetask",true);}else{pr.waitingToKill="removetask";}}}
彩蛋:反保活的终结者
Android 中的进程查杀最强武器是force-stop,本质上它会通过遍历并杀死所有和应用有关的Java进程:
privatefinalbooleankillPackageProcessesLocked(StringpackageName,intappId,intuserId,intminOomAdj,booleancallerWillRestart,booleanallowRestart,booleandoit,booleanevenPersistent,Stringreason){ArrayList<ProcessRecord>procs=newArrayList<>();finalintNP=mProcessNames.getMap().size();for(intip=0;ip<NP;ip++){SparseArray<ProcessRecord>apps=mProcessNames.getMap().valueAt(ip);finalintNA=apps.size();for(intia=0;ia<NA;ia++){ProcessRecordapp=apps.valueAt(ia);...//continueiftheprocessdonotneedtokillapp.removed=true;procs.add(app);}}intN=procs.size();//force-stop的本质是通过遍历所有与应用有关的进程,依次查杀来实现杀死所有进程的for(inti=0;i<N;i++){removeProcessLocked(procs.get(i),callerWillRestart,allowRestart,reason);}updateOomAdjLocked();returnN>0;}
想要做反保活的话,我们只需要在 removeProcessLocked 的遍历之前,将 java 进程和在其同一cgroup的c进程一起通过发送信号hang住,再通过循环依次杀死所有进程,这种方法基本能杜绝所有拉活方案。
进程管理与大家日常开发息息相关,也是在Android系统中是个举足轻重的模块。相信通过本系列的两篇文章,大家已经对进程启动期间涉及到fork、对进程的优先级管理涉及到adj、对进程的调度这些概念已经有了更加深入的理解了。
上面就是小居数码小编今天给大家介绍的关于(androidframework层)的全部内容,希望可以帮助到你,想了解更多关于数码知识的问题,欢迎关注我们,并收藏,转发,分享。
94%的朋友还想知道的:
(499)个朋友认为回复得到帮助。
部分文章信息来源于以及网友投稿,转载请说明出处。
本文标题:Framework层概念(androidframework层):http://sjzlt.cn/shuma/154851.html