精华内容
下载资源
问答
  • 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
    
    更多相关内容
  • 最近接到一个非【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:08
    enum 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;
    }
    
    
    
    展开全文
  • startActivity启动过程分析 ... 看完这两篇干货,应该一清二楚了。 是否启动应用进程的分支...通过判断当前activity对于的进程名的ProcessRecord是否存在来判断进程是否启动(android-cts-7.1_r20 tag) public fin.

    startActivity启动过程分析

    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进程以及判断进程是否存在 1 androidstudio 的 stop app 是结束 app 下次启动 会从头启动 2 通过 DDMS 结束进程 tools-&gt;android device monitor 等十几秒,弹出新对话框,然后左上角可以...
  • Linux后台运行命令: nohup sh run.sh &...nohup : 不挂断的运行,注意并没有后台运行的功能,,就是指,用nohup运行命令可以使命令永久的执行下去,和用户终端没有关系,例如我们断开SSH连接都不会影响...
  • C#判断当前启动程序进程是否存在

    千次阅读 2012-08-08 10:18:25
     /**** 这种判断方法,有BUG,当第一次以管理员启动,第二次不以管理员启动时,将会有访问被拒绝的异常抛出  if (!item.ProcessName.ToUpper().Contains(current.ProcessNameToUpper()))  {  continue; ...
  • 用shell脚本监控进程是否存在 不存在则启动的实例,先上代码干货: #!/bin/sh ps -fe|grep processString |grep -v grep if [ $? -ne 0 ] then echo "start process....." else echo "runing....." fi ##...
  • init进程是Android系统第一个用户态的进程,init被赋予了很多重要的职责,比如我们熟悉的Zygote孵化器进程就是由init进程启动的。今天我们就来学习init进程启动过程。 1 init进程启动之前分析 在分析init进程之前...
  • 守护进程判断进程是否运行正常

    千次阅读 2016-10-23 22:13:50
    守护进程(Daemon)是运行在后台的一种特殊进程。它独立于控制终端并且周期性地执行某种任务或等待处理某些发生的事件。守护进程是一种很有用的进程。Linux的大多数服务器就是用守护进程实现的。比如,Internet...
  • 然而,大多数情况下你可能不想手动启动服务器,有可能是你安排MySQL服务器在系统引导时自动启动,作为标准引导过程的一部分,在Unix下,该引导过程由系统的Unix用户root执行,并且任何在该过程中运行的进程均以root...
  • 自从进入win7以后,客户端软件如果没有以管理员身份运行或者安装的话,总是有各种问题。 参考下文: http://blog.csdn.net/chenlycly/article/details/45419259
  • Linux / Ubuntu 查询进程所属用户

    千次阅读 2021-03-11 16:13:09
    CPU 进程使用 top 命令可以显示待查询进程所属用户: GPU 进程 1. 查看进程 PID (Process ID) 使用 nvidia-smi 命令查看待查询进程 PID: 2. 查看进程所属用户 使用命令 ps u PID 查看进程所属用户: ...
  • 1、遍历被监控的进程列表,定时(比如1分钟)检测进程列表,如果某个需要被监视的进程未运行,则启动它。 2、防止假死-使用心跳机制(UDP实现后台监控进程与被监控进程的心跳进制) a、被检测进程定时,比如说每隔5秒...
  • 在Zygote 进程启动过程中会把Android 最核心进程SystemServer进程创建并启动
  • Android 常驻进程保活自启动方案总结

    千次阅读 2017-06-01 22:36:29
    Android常驻进程就是进程一直存在,即使被系统外者其他软件人为杀死也能够自启,这种需求一般就是用来常驻接受推送消息时,如何保证进程不被杀死,实时收到推送的消息,与后台保持着链接。那如何保持进程常驻呢,...
  • 场景 我们在开发Windows程序时, 安装程序后一般会以admin方式启动程序, 这时候程序的权限一般是admin权限。可如果重新打开程序时, ... 或者由于某些原因我想看这个程序是否管理员模式启动的,如何快速查看呢? ...
  • 当前登录系统的用户叫做实际用户,而实际运行的进程是以有效用户ID来进行访问权限管理的,正常情况下有效用户就是实际用户,但是如果我们使用了sudo或者使能了设置用户ID位, 按照前面文章的介绍,这个有效用户是...
  • C++ 判断进程是否存在

    万次阅读 2012-09-11 17:09:49
    在做服务应用程序时经常遇到一个服务器多个用户登录时会造成服务应用多个进程同时运行,然后影响系统的正常运行。要解决这个问题就需要在系统启动前判断该应用的进程是否运行,从...//判断进程是否存在 //2012-09-10
  • 如何在qt程序中判断当前应用程序是否用户使用呢?我们先了解一下QApplication这个类 简单的说,QApplication类管理图形用户界面应用程序的控制流和主要设置。可以说 QApplication是Qt的整个后台管理的命脉 它...
  • 有时我们将自定义程序注册为systemd service 进程管理,交由系统管理,可以方便启动停止,亦可以实现服务异常退出重启,开机自启动。 减少自定义程序服务管理的时间消耗。 用法 1、新增配置文件a.service(添...
  • 一些应用中,要用到判断某个进程或者所有进程启动与退出事件。比如,我们做局部代理软件时,就要判断某个进程运行了没有,退出了没有。或者守护进程功能等。 很早以前,大家可能用得多一点的是用枚举进程,或者...
  • 判断进程是否以管理员权限运行,讨论管理员权限与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:59
    Nginx是一个高性能的反向代理服务器,现在一般作为我们网站或其他Web服务的第一层代理,用户在浏览器请求首先经过的就是Nginx服务。 如果Nginx服务没有启动...下面我将在Linux中查看Nginx是否启动的过程记录,分享...
  • PS是LINUX下最常用的也是非常强大的进程查看命令常见的使用方法是检查一个进程是否存在://以下这条命令是检查java 进程是否存在.ps -ef |grep java1. ps简介前面介绍的两个命令都是用于查看当前系统用户的情况,下面...
  • 二、Android 应用进程启动过程 三、 Android Activity的启动流程 一、Android APK的构建过程 通过IDE可以生成可以在android设备中安装的apk文件,Google官方提供的构建APK的过程流程图如下: 打包APK流程总结...
  • 通过测试发现原因:在有些情况下,通过计划任务(通过服务也是如此)调起的进程是system权限的。而在system权限下进程可能会遇到很多问题: 通过注册表或expand 环境变量等方法得到的系统目录并不是我们想要...
  • Android系统启动流程(一)解析init进程启动过程

    万次阅读 多人点赞 2017-02-07 11:29:48
    作为“Android框架层”这个大系列中的第一个系列,我们首先要了解的是Android系统启动流程,在这个流程中会涉及到很多重要的知识点,这个系列我们就来一一讲解它们,这一篇我们就来学习init进程

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 221,179
精华内容 88,471
关键字:

判断进程是否用户启动