-
2020-06-02 19:39:44
1、命令
命令 说明 ps aux | grep 进程名 | grep -v grep 进程存在则输出信息,不存在则没输出 ps -ef | grep 进程名 | grep -v grep | wc -l 进程存在则输出行数(也就是数量),不存在则输出0 其中以上两者输出的不同是由于后者加上了wc命令用于计算,这里-l计算行数。除此之外,ps的参数aux和-ef的区别就在于显示进程的风格(aux是BSD风格,而-ef是System V风格)和信息不同(COMMADN列如果过长,aux会截断,而-ef不会),但如果我们只是用来查看程序是否在运行则无所谓,两者效果一样。
解析:
|
表示管道,左端写入,右端读出;
grep
为搜索命令,-v
表示反向查询,grep -v grep表示去除包含grep的项;
wc -l
表示显示行数;
ps
及以下参数查看进程信息:
-e:显示所有进程
-f:全格式
-a:显示终端上的所有进程,包括其他用户的进程
-u:以用户为主的格式来显示程序状况
-x:显示所有程序,不以终端来区分参考文章:ps -ef 和 ps aux 的区别
2、运用示例
根据前面命令我们可以用在脚本里面进行一系列的自动化操作,比如以下参考例子:
#!/bin/sh while true do # 方式 1 process=`ps aux | grep 进程名 | grep -v grep`; if [ "$process" == "" ];then echo not running else echo running fi # 方式 2 processNum=`ps -fe | grep 进程名 | grep -v grep | wc -l` if [ $processNum -eq 0 ];then echo not running else echo running fi sleep 10 done
更多相关内容 -
Linux Shell 脚本判断进程是否存在
2021-05-14 20:34:24最近接到一个非【iOS】的活儿(生无可恋的表情)。。。是的,每个人都是萝卜,哪里有【坑】去哪里!!...话接前言,这个活儿就是写一个程序在云服务器上做某个软件的...进程守护进程守护就是编写一个脚本,检测程序进...最近接到一个非【iOS】的活儿(生无可恋的表情)。。。是的,每个人都是萝卜,哪里有【坑】去哪里!!
...话接前言,这个活儿就是写一个程序在云服务器上做某个软件的性能测试,什么语言都行(最后选了c++编写,我都不知道为什么。。。),让程序一直跑,所以这个【一直跑】就是今天要说的,一直跑就是让程序在未知原因退出时重新启动。
【运行环境Centos 7.3】
1.进程守护
进程守护就是编写一个脚本,检测程序进程是否存在,如果不存在就启动程序。
想法就是判断某个进程的个数是否为‘0’,这个菜鸡的我遇到了一个坑:就是在程序未启动的情况下统计进程的数竟然不为‘0’,为‘2’,‘3’的情况都有。。。先给个脚本模板(看到代码是不是很开心。。。):
重要提示:所有路径必须是绝对路径!!!包括定时任务!
count=^ps -ef | grep xxx_app | grep -v "grep" | grep -v "xxx_daemon.sh" | wc -l^
if [ 0 -eq $count ]
then
-->启动你的程序,your code here
else
--> 程序正在运行 ...
fi
说明:
【 ^ 】:方括号中的符号要换成【 ` 】就是esc键下面的那个键的英文模式按键,markdown语法问题。。。
count:变量,接收包括进程名为‘xxx_app’的统计行数
xxx_app:程序名,进程名
xxx_daemon.sh:进程守护脚本文件
填坑的两句话:「1」【grep -v "grep"】 和 「2」【grep -v "xxx_daemon.sh"】,意思是显示不包括匹配到的关键字的行,因为ps -ef命令打印所有进程,通过grep正则匹配到含有“xxx_app”关键字的进程,这个会匹配到该命令本身所在行,即grep xxx_app,所以用「1」不显示该行,而「2」则是在将脚本添加至定时任务时需要过滤的,因为脚本的文件名或者脚本文件内容中的某些字符串可能包括xxx_app关键字。
2. 定时任务
写完脚本下一步需要将脚本添加到定时任务中,让脚本周期性地运行,所以前提是要确保机器上安装了【cron】,因为稍后我们将使用【crontab】命令~
借用【/etc/crontab】文件中的内容说一下定时任务的设置规则:
Example of job definition:
.---------------- minute (0 - 59)
| .------------- hour (0 - 23)
| | .---------- day of month (1 - 31)
| | | .------- month (1 - 12) OR jan,feb,mar,apr ...
| | | | .---- day of week (0 - 6) (Sunday=0 or 7) OR sun,mon,tue,wed,thu,fri,sat
| | | | |
* * * * * user-name command to be executed
关心【*】的含义就行,后面先不用管
五个*由左至右分别为:【分钟】、【小时】、【日】、【月】、【星期】,用来设置执行周期
注:感兴趣的朋友可以搜一下【crontab -e】和【vim /etc/crontab】的设置定时任务的区别
设置定时任务
进入定时任务编辑页面:
命令行输入:crontab -e,默认为当前登录用户配置crontab,你也可以指定一个用户:crontab -u xxx -e
最后一行添加:
【*/1 * * * * /bin/sh /aa/bb/xxx_daemon.sh】
含义:每分钟执行一次脚本,如果没有斜杠【 / 】则表示每个小时的第一分钟,e.g.:1 * * * * /bin/sh /aa/bb/xxx_daemon.sh
【 * 】:匹配所有可能的值
【 , 】:或的关系,1,5 * * * *,每个小时的1分钟或5分钟
【 - 】:一个范围,1-5 * * * *,每个小时的1分钟到5分钟,5个时间点
【 / 】:间隔频率,*/1 * * * *,每隔1分钟
在线crontab任务工具
3. 最后的啰嗦
crontab -l,查看当前用户定时任务列表
crontab -r,删除当前用户的crontab文件
可以指定某个用户 crontab -u xx option
安装crontab
yum install vixie-cron,cron主程序
yum install crontabs,cron程序的外壳,用来驱动cron
启动服务
systemctl start crond
停止服务
systemctl stop crond
重新加载服务
systemctl reload crond
重启服务
systemctl restart crond
查看服务运行状态
systemctl status crond
设置开机启动
systemctl enable crond
查看是否开机启动
systemctl list-unit-files | grep crond
-
C++ 判断进程是否退出 代码封装
2020-01-14 17:39:08enum PROCESS_RESULT{ PROCESS_NORMAL_END, //正常退出 PROCESS_ABNORMAL_END, //异常退出 ...//监听某个进程是否结束 PROCESS_RESULT ListenSingleProcessFinish(HANDLE hHandle) { std::cout <&...// DemoTest.cpp : 定义控制台应用程序的入口点。 // #include "stdafx.h" using namespace std; enum PROCESS_RESULT{ PROCESS_NORMAL_END, //正常退出 PROCESS_ABNORMAL_END, //异常退出 PROCESS_UNKNOWN_END, //未知错误 }; //监听某个进程是否结束 PROCESS_RESULT ListenSingleProcessFinish(HANDLE hHandle) { std::cout << "开始监听" << std::endl; DWORD wait_result =::WaitForSingleObject(hHandle,INFINITE); if (wait_result == WAIT_OBJECT_0 + 1) { cout << "正常退出" << endl; return PROCESS_NORMAL_END; } else if (wait_result == WAIT_OBJECT_0) { cout << "异常退出" << endl; return PROCESS_ABNORMAL_END; } else{ return PROCESS_UNKNOWN_END; } } int _tmain(int argc, _TCHAR* argv[]) { HANDLE hhanld = OpenProcess(PROCESS_ALL_ACCESS,NULL,15232); //获取进程句柄 if (hhanld !=NULL) { ListenSingleProcessFinish(hhanld); } system("pause"); return 0; }
-
Activity的启动流程,如何判断当前activity所在进程已经启动了
2020-06-01 15:24:42startActivity启动过程分析 ... 看完这两篇干货,应该一清二楚了。 是否启动应用进程的分支...通过判断当前activity对于的进程名的ProcessRecord是否存在来判断进程是否启动(android-cts-7.1_r20 tag) public fin.https://blog.csdn.net/luoshengyang/article/details/6689748
看完这两篇干货,应该一清二楚了。
是否启动应用进程的分支在ActivityStackSupervisor#startSpecificActivityLocked方法内部
通过判断当前activity对于的进程名的ProcessRecord是否存在来判断进程是否启动(android-cts-7.1_r20 tag)
public final class ActivityStackSupervisor implements DisplayListener { ... void startSpecificActivityLocked(ActivityRecord r, boolean andResume, boolean checkConfig) { // Is this activity's application already running? ProcessRecord app = mService.getProcessRecordLocked(r.processName, r.info.applicationInfo.uid, true); r.task.stack.setLaunchTime(r); if (app != null && app.thread != null) { try { if ((r.info.flags&ActivityInfo.FLAG_MULTIPROCESS) == 0 || !"android".equals(r.info.packageName)) { // Don't add this if it is a platform component that is marked // to run in multiple processes, because this is actually // part of the framework so doesn't make sense to track as a // separate apk in the process. app.addPackage(r.info.packageName, r.info.applicationInfo.versionCode, mService.mProcessStats); } realStartActivityLocked(r, app, andResume, checkConfig); return; } catch (RemoteException e) { Slog.w(TAG, "Exception when starting activity " + r.intent.getComponent().flattenToShortString(), e); } // If a dead object exception was thrown -- fall through to // restart the application. } mService.startProcessLocked(r.processName, r.info.applicationInfo, true, 0, "activity", r.intent.getComponent(), false, false, true); } }
具体启动流程:
1. Launcher:Launcher通知AMS要启动activity。
- startActivitySafely->startActivity->Instrumentation.execStartActivity()(AMP.startActivity)->AMS.startActivity
2. AMS:PMS的resoveIntent验证要启动activity是否匹配。如果匹配,通过ApplicationThread发消息给Launcher所在的主线程,暂停当前Activity(即Launcher)。
3. 暂停完,在该activity还不可见时,通知AMS,根据要启动的Activity配置ActivityStack。然后判断要启动的Activity进程是否存在?
- 存在:发送消息LAUNCH_ACTIVITY给需要启动的Activity主线程,执行handleLaunchActivity
- 不存在:通过socket向zygote请求创建进程。进程启动后,ActivityThread.attach
4. 判断Application是否存在,若不存在,通过LoadApk.makeApplication创建一个。在主线程中通过thread.attach方法来关联ApplicationThread。
5. 在通过ActivityStackSupervisor来获取当前需要显示的ActivityStack。
6. 继续通过ApplicationThread来发送消息给主线程的Handler来启动Activity(handleLaunchActivity)。
7. handleLauchActivity:调用了performLauchActivity,里边Instrumentation生成了新的activity对象,继续调用activity生命周期。
IPC过程:
双方都是通过对方的代理对象来进行通信。
1.app和AMS通信:app通过本进程的AMP和AMS进行Binder通信
2.AMS和新app通信:通过ApplicationThreadProxy来通信,并不直接和ActivityThread通信
参考函数流程
Activity启动流程(从Launcher开始):
第一阶段: Launcher通知AMS要启动新的Activity(在Launcher所在的进程执行)
- Launcher.startActivitySafely //首先Launcher发起启动Activity的请求
- Activity.startActivity
- Activity.startActivityForResult
- Instrumentation.execStartActivity //交由Instrumentation代为发起请求
- ActivityManager.getService().startActivity //通过IActivityManagerSingleton.get()得到一个AMP代理对象
- ActivityManagerProxy.startActivity //通过AMP代理通知AMS启动activity
第二阶段:AMS先校验一下Activity的正确性,如果正确的话,会暂存一下Activity的信息。然后,AMS会通知Launcher程序pause Activity(在AMS所在进程执行)
- ActivityManagerService.startActivity
- ActivityManagerService.startActivityAsUser
- ActivityStackSupervisor.startActivityMayWait
- ActivityStackSupervisor.startActivityLocked :检查有没有在AndroidManifest中注册
- ActivityStackSupervisor.startActivityUncheckedLocked
- ActivityStack.startActivityLocked :判断是否需要创建一个新的任务来启动Activity。
- ActivityStack.resumeTopActivityLocked :获取栈顶的activity,并通知Launcher应该pause掉这个Activity以便启动新的activity。
- ActivityStack.startPausingLocked
- ApplicationThreadProxy.schedulePauseActivity
其中ActivityStackSupervisor.startActivityMayWait中会检查当前匹配到的所有activity并启动一个选择框:
final int startActivityMayWait(IApplicationThread caller, int callingUid, String callingPackage, Intent intent, String resolvedType, IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor, IBinder resultTo, String resultWho, int requestCode, int startFlags, ProfilerInfo profilerInfo, WaitResult outResult, Configuration config, Bundle options, boolean ignoreTargetSecurity, int userId, IActivityContainer iContainer, TaskRecord inTask) { ... boolean componentSpecified = intent.getComponent() != null; //创建新的Intent对象,即便intent被修改也不受影响 intent = new Intent(intent); //收集Intent所指向的Activity信息, 当存在多个可供选择的Activity,则直接向用户弹出resolveActivity [见2.7.1] ActivityInfo aInfo = resolveActivity(intent, resolvedType, startFlags, profilerInfo, userId); ActivityContainer container = (ActivityContainer)iContainer; synchronized (mService) { if (container != null && container.mParentActivity != null && container.mParentActivity.state != RESUMED) { ... //不进入该分支, container == nul } final int realCallingPid = Binder.getCallingPid(); final int realCallingUid = Binder.getCallingUid(); int callingPid; if (callingUid >= 0) { callingPid = -1; } else if (caller == null) { callingPid = realCallingPid; callingUid = realCallingUid; } else { callingPid = callingUid = -1; } final ActivityStack stack; if (container == null || container.mStack.isOnHomeDisplay()) { stack = mFocusedStack; // 进入该分支 } else { stack = container.mStack; } //此时mConfigWillChange = false stack.mConfigWillChange = config != null && mService.mConfiguration.diff(config) != 0; final long origId = Binder.clearCallingIdentity(); if (aInfo != null && (aInfo.applicationInfo.privateFlags &ApplicationInfo.PRIVATE_FLAG_CANT_SAVE_STATE) != 0) { // heavy-weight进程处理流程, 一般情况下不进入该分支 if (aInfo.processName.equals(aInfo.applicationInfo.packageName)) { ... } } //[见流程2.8] int res = startActivityLocked(caller, intent, resolvedType, aInfo, voiceSession, voiceInteractor, resultTo, resultWho, requestCode, callingPid, callingUid, callingPackage, realCallingPid, realCallingUid, startFlags, options, ignoreTargetSecurity, componentSpecified, null, container, inTask); Binder.restoreCallingIdentity(origId); if (stack.mConfigWillChange) { ... //不进入该分支 } if (outResult != null) { ... //不进入该分支 } return res; } }
第三阶段: pause Launcher的Activity,并通知AMS已经paused(在Launcher所在进程执行)
- ApplicationThread.schedulePauseActivity
- ActivityThread.queueOrSendMessage
- H.handleMessage
- ActivityThread.handlePauseActivity
- ActivityManagerProxy.activityPaused
第四阶段:检查activity所在进程是否存在,如果存在,就直接通知这个进程,在该进程中启动Activity;不存在的话,会调用Process.start创建一个新进程(执行在AMS进程)
- ActivityManagerService.activityPaused
- ActivityStack.activityPaused
- ActivityStack.completePauseLocked
- ActivityStack.resumeTopActivityLocked
- ActivityStack.startSpecificActivityLocked
- ActivityManagerService.startProcessLocked
- Process.start //在这里创建了新进程,新的进程会导入ActivityThread类,并执行它的main函数
第五阶段: 创建ActivityThread实例,执行一些初始化操作,并绑定Application。如果Application不存在,会调用LoadedApk.makeApplication创建一个新的Application对象。之后进入Loop循环。(执行在新创建的app进程)
- ActivityThread.main
- ActivityThread.attach(false) //声明不是系统进程
- ActivityManagerProxy.attachApplication
第六阶段:处理新的应用进程发出的创建进程完成的通信请求,并通知新应用程序进程启动目标Activity组件(执行在AMS进程)
- ActivityManagerService.attachApplication //AMS绑定本地ApplicationThread对象,后续通过ApplicationThreadProxy来通信。
- ActivityManagerService.attachApplicationLocked
- ActivityStack.realStartActivityLocked //真正要启动Activity了!
- ApplicationThreadProxy.scheduleLaunchActivity //AMS通过ATP通知app进程启动Activity
第七阶段: 加载MainActivity类,调用onCreate声明周期方法(执行在新启动的app进程)
- ApplicationThread.scheduleLaunchActivity //ApplicationThread发消息给AT
- ActivityThread.queueOrSendMessage
- H.handleMessage //AT的Handler来处理接收到的LAUNCH_ACTIVITY的消息
- ActivityThread.handleLaunchActivity
- ActivityThread.performLaunchActivity
- Instrumentation.newActivity //调用Instrumentation类来新建一个Activity对象
- Instrumentation.callActivityOnCreate
- MainActivity.onCreate
- ActivityThread.handleResumeActivity
- AMP.activityResumed
- AMS.activityResumed(AMS进程)
-
Android结束APP进程以及判断进程是否存在、判断Activity是否在后台
2018-03-24 18:13:38Android结束APP进程以及判断进程是否存在 1 androidstudio 的 stop app 是结束 app 下次启动 会从头启动 2 通过 DDMS 结束进程 tools->android device monitor 等十几秒,弹出新对话框,然后左上角可以... -
shell脚本监控(循环)判断程序进程是否存在,如果存在则不执行,如果不存在则执行启动程序
2018-10-10 17:17:55Linux后台运行命令: nohup sh run.sh &...nohup : 不挂断的运行,注意并没有后台运行的功能,,就是指,用nohup运行命令可以使命令永久的执行下去,和用户终端没有关系,例如我们断开SSH连接都不会影响... -
C#判断当前启动程序进程是否存在
2012-08-08 10:18:25/**** 这种判断方法,有BUG,当第一次以管理员启动,第二次不以管理员启动时,将会有访问被拒绝的异常抛出 if (!item.ProcessName.ToUpper().Contains(current.ProcessNameToUpper())) { continue; ... -
用shell脚本监控进程是否存在 不存在则启动的实例
2017-04-20 13:49:58用shell脚本监控进程是否存在 不存在则启动的实例,先上代码干货: #!/bin/sh ps -fe|grep processString |grep -v grep if [ $? -ne 0 ] then echo "start process....." else echo "runing....." fi ##... -
Android系统启动流程(一) init进程启动过程解析
2020-10-07 15:10:22init进程是Android系统第一个用户态的进程,init被赋予了很多重要的职责,比如我们熟悉的Zygote孵化器进程就是由init进程启动的。今天我们就来学习init进程的启动过程。 1 init进程启动之前分析 在分析init进程之前... -
守护进程判断进程是否运行正常
2016-10-23 22:13:50守护进程(Daemon)是运行在后台的一种特殊进程。它独立于控制终端并且周期性地执行某种任务或等待处理某些发生的事件。守护进程是一种很有用的进程。Linux的大多数服务器就是用守护进程实现的。比如,Internet... -
MySQL服务器的启动与停止的解析
2020-12-14 20:09:05然而,大多数情况下你可能不想手动启动服务器,有可能是你安排MySQL服务器在系统引导时自动启动,作为标准引导过程的一部分,在Unix下,该引导过程由系统的Unix用户root执行,并且任何在该过程中运行的进程均以root... -
判断进程是否以管理员权限运行
2015-09-26 21:41:27自从进入win7以后,客户端软件如果没有以管理员身份运行或者安装的话,总是有各种问题。 参考下文: http://blog.csdn.net/chenlycly/article/details/45419259 -
Linux / Ubuntu 查询进程所属用户
2021-03-11 16:13:09CPU 进程使用 top 命令可以显示待查询进程所属用户: GPU 进程 1. 查看进程 PID (Process ID) 使用 nvidia-smi 命令查看待查询进程 PID: 2. 查看进程所属用户 使用命令 ps u PID 查看进程所属用户: ... -
VC++定时检测进程,被检测进程挂掉后启动
2020-05-27 18:00:101、遍历被监控的进程列表,定时(比如1分钟)检测进程列表,如果某个需要被监视的进程未运行,则启动它。 2、防止假死-使用心跳机制(UDP实现后台监控进程与被监控进程的心跳进制) a、被检测进程定时,比如说每隔5秒... -
Android 进阶——系统启动之核心SystemServer进程启动详解(七)
2021-12-18 23:22:35在Zygote 进程启动过程中会把Android 最核心进程SystemServer进程创建并启动。 -
Android 常驻进程保活自启动方案总结
2017-06-01 22:36:29Android常驻进程就是进程一直存在,即使被系统外者其他软件人为杀死也能够自启,这种需求一般就是用来常驻接受推送消息时,如何保证进程不被杀死,实时收到推送的消息,与后台保持着链接。那如何保持进程常驻呢,... -
[Windows]_[初级]_[如何不编程判断进程是管理员模式启动(UAC)]
2019-06-04 17:17:47场景 我们在开发Windows程序时, 安装程序后一般会以admin方式启动程序, 这时候程序的权限一般是admin权限。可如果重新打开程序时, ... 或者由于某些原因我想看这个程序是否管理员模式启动的,如何快速查看呢? ... -
如何判断当前的进程是否以root权限运行?
2019-03-21 19:17:01当前登录系统的用户叫做实际用户,而实际运行的进程是以有效用户ID来进行访问权限管理的,正常情况下有效用户就是实际用户,但是如果我们使用了sudo或者使能了设置用户ID位, 按照前面文章的介绍,这个有效用户是... -
C++ 判断进程是否存在
2012-09-11 17:09:49在做服务应用程序时经常遇到一个服务器多个用户登录时会造成服务应用多个进程同时运行,然后影响系统的正常运行。要解决这个问题就需要在系统启动前判断该应用的进程是否运行,从...//判断进程是否存在 //2012-09-10 -
qt判断用户是否正在使用当前应用程序
2019-06-25 20:34:18如何在qt程序中判断当前应用程序是否被用户使用呢?我们先了解一下QApplication这个类 简单的说,QApplication类管理图形用户界面应用程序的控制流和主要设置。可以说 QApplication是Qt的整个后台管理的命脉 它... -
【linux】使用systemctl start xxx启动自己的程序|开机启动|守护进程
2022-04-23 03:25:17有时我们将自定义程序注册为systemd service 进程管理,交由系统管理,可以方便启动停止,亦可以实现服务异常退出重启,开机自启动。 减少自定义程序服务管理的时间消耗。 用法 1、新增配置文件a.service(添... -
进程监控,监视进程启动与退出最稳定的方法,不占CPU,不漏进程!
2019-10-15 22:20:13一些应用中,要用到判断某个进程或者所有进程的启动与退出事件。比如,我们做局部代理软件时,就要判断某个进程运行了没有,退出了没有。或者守护进程功能等。 很早以前,大家可能用得多一点的是用枚举进程,或者... -
VC++判断进程是否以管理员权限运行(附源码)
2015-05-01 15:32:07判断进程是否以管理员权限运行,讨论管理员权限与UAC开启和关闭、登录的用户类型之间的关系 -
使用jsvc启动java进程
2020-05-25 20:39:32使用jsvc启动java进程使用jsvc启动java进程启动、关闭java进程时遇到的一些需求1. 在查看java进程信息时,希望能隐藏部分classpath。2. 有时希望能以*类似同步阻塞调用*的方式来启动和关闭java进程。相关概念初识... -
linux判断Nginx是否启动
2018-12-26 22:15:59Nginx是一个高性能的反向代理服务器,现在一般作为我们网站或其他Web服务的第一层代理,用户在浏览器请求首先经过的就是Nginx服务。 如果Nginx服务没有启动...下面我将在Linux中查看Nginx是否启动的过程记录,分享... -
ps -ef |grep java 检查java进程是否存在
2021-02-26 14:28:13PS是LINUX下最常用的也是非常强大的进程查看命令常见的使用方法是检查一个进程是否存在://以下这条命令是检查java 进程是否存在.ps -ef |grep java1. ps简介前面介绍的两个命令都是用于查看当前系统用户的情况,下面... -
Android APK打包安装、应用进程启动过程、Activity启动流程
2018-09-06 19:02:57二、Android 应用进程启动过程 三、 Android Activity的启动流程 一、Android APK的构建过程 通过IDE可以生成可以在android设备中安装的apk文件,Google官方提供的构建APK的过程流程图如下: 打包APK流程总结... -
System权限下获取路径以及使用用户权限启动进程
2018-07-02 17:18:19通过测试发现原因:在有些情况下,通过计划任务(通过服务也是如此)调起的进程是system权限的。而在system权限下进程可能会遇到很多问题: 通过注册表或expand 环境变量等方法得到的系统目录并不是我们想要... -
Android系统启动流程(一)解析init进程启动过程
2017-02-07 11:29:48作为“Android框架层”这个大系列中的第一个系列,我们首先要了解的是Android系统启动流程,在这个流程中会涉及到很多重要的知识点,这个系列我们就来一一讲解它们,这一篇我们就来学习init进程。