目录
android_os_Process_sendSignalQuiet
android_os_Process_killProcessGroup
-
应用进程
-
进程状态与生命周期
在Android系统中,进程有五种状态:创建、就绪、运行、阻塞和死亡,进程的生命周期包括创建、运行、暂停、终止等阶段,当一个进程不再需要时,系统会将其杀死以回收资源。
-
进程被杀的原因
Android系统会根据一定的策略来决定是否杀死一个进程,以下是一些常见的原因:
-
系统内存不足:当系统的可用内存不足以满足新的进程需求时,系统会选择杀死一些正在运行的进程以回收内存资源。
-
用户主动操作:用户可以通过任务管理器或第三方应用来杀死不需要的进程。
-
系统优化:为了提高系统性能,系统会定期检查并杀死一些长时间不运行的进程。
-
进程被杀的过程
当系统决定杀死一个进程时,会执行以下步骤:
-
发送SIGTERM信号:系统首先会向目标进程发送SIGTERM信号,通知其即将被杀死,通常情况下,接收到SIGTERM信号的进程会进行清理工作,如释放资源、保存数据等,然后正常退出。
-
等待子进程结束:如果目标进程有子进程,系统会等待子进程结束后再杀死父进程,这是为了避免子进程成为孤儿进程,影响系统的稳定运行。
-
杀死进程:当目标进程没有子进程或者子进程已经结束时,系统会向目标进程发送SIGKILL信号,强制杀死进程,此时,目标进程无法进行任何清理工作,可能会导致数据丢失或其他问题。
-
杀死进程
-
Android 杀前台进程
关闭了所有的 Activity 界面窗口 , 应用进程自然就被杀死了 !
activity.finishAffinity();
上述代码可以关闭所有的本任务栈内的 Activity 窗口界面 , 自然就可以杀死本进程 ; 如果想要杀得更彻底 , 使用该方法与其它的两种杀进程的方法结合使用 ;
杀死前台进程 , 将所有的 Activity 关闭 , 然后调用 android.os.Process.killProcess(android.os.Process.myPid())
或 java.lang.System.exit(0)
中的一种方法即可 ;
关闭 Activity 建议使用 Activity 类的 finishAffinity() 方法 , 该方法可以关闭当前前台任务栈中的所有 Activity , 之后再调用上述两个杀进程的方法 , 即可成功关闭进程 , 应用不会重启 ;
// 1. 调用 Activity 类的 finishAffinity() 方法关闭任务栈中所有 Activity 界面
activity.finishAffinity();
// 2. 使用 Android 的进程 api 类 `android.os.Process` 杀死进程
android.os.Process.killProcess(android.os.Process.myPid());
// 3. 使用 Java 的进程 api 类 `java.lang.System` 杀死进程
java.lang.System.exit(0);
-
Android 杀后台进程
使用 Android 的进程 api 类 android.os.Process
杀死进程 :
android.os.Process.killProcess(android.os.Process.myPid());
如果当前进程处于前台 , 杀死该进程后 , Android 系统会重启该进程 , 这是 Android 系统本身的机制 ;
注意 : 调用该方法杀死的应用 , 不能处于前台任务栈中 ;
使用 Java 的进程 api 类 java.lang.System
杀死进程 : 参数中传入 0 表示正常退出 , 传入其它值表示非正常退出 ;
java.lang.System.exit(0);
ActivityManager 会监听前台进程 , 一旦发现应用不是正常结束 , 就会重启应用 , 使用上述两种方法杀死的进程就属于非正常杀死的进程 , 应用进程会重新启动
-
Android 杀其它进程
获取 ActivityManager , 然后使用 killBackgroundProcesses
方法杀死后台的其它进程 ;
ActivityManager activityManager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
activityManager.killBackgroundProcesses("com.google.example");
需要使用 android.permission.KILL_BACKGROUND_PROCESSES
权限 ;
在清单文件中配置该权限 :
<uses-permission android:name="android.permission.KILL_BACKGROUND_PROCESSES"/>
-
进程杀死的实现原理
kill进程其实是通过发送signal信号的方式来完成的。创建进程从Process.start开始说起,那么杀进程则相应从Process.killProcess开始讲起。
-
用户态Kill
在Process.java
文件有3个方法用于杀进程,下面说说这3个方法的具体工作
Process.killProcess(int pid)
Process.killProcessQuiet(int pid)
Process.killProcessGroup(int uid, int pid)
-
killProcess
-
killProcess
[-> Process.java]
public static final void killProcess(int pid) {
sendSignal(pid, SIGNAL_KILL); //【见小节3.1.1.2】
}
其中SIGNAL_KILL = 9
,这里的sendSignal
是一个Native方法。在Android系统启动过程中,虚拟机会注册各种framework所需的JNI方法,很多时候查询Java层的native方法所对应的native方法,可在路径/framework/base/core/jni
中找到。
这里的sendSignal
所对应的JNI方法在android_util_Process.cpp文件的android_os_Process_SendSignal
方法,接下来进入见流程2.
-
android_os_Process_sendSignal
[- >android_util_Process.cpp]
void android_os_Process_sendSignal(JNIEnv* env, jobject clazz, jint pid, jint sig) {
if (pid > 0) {
//打印Signal信息
ALOGI("Sending signal. PID: %" PRId32 " SIG: %" PRId32, pid, sig);
kill(pid, sig);
}
}
sendSignal
和sendSignalQuiet
的唯一区别就是在于是否有ALOGI()这一行代码。最终杀进程的实现方法都是调用kill(pid, sig)
方法。
-
killProcessQuiet
-
killProcessQuiet
[-> Process.java]
public static final void killProcessQuiet(int pid) {
sendSignalQuiet(pid, SIGNAL_KILL); //【见小节3.1.2.2】
}
-
android_os_Process_sendSignalQuiet
[- >android_util_Process.cpp]
void android_os_Process_sendSignalQuiet(JNIEnv* env, jobject clazz, jint pid, jint sig) {
if (pid > 0) {
kill(pid, sig);
}
}
可见killProcess
和killProcessQuiet
的唯一区别在于是否输出log。最终杀进程的实现方法都是调用kill(pid, sig)
方法。
-
killProcessGroup
-
killProcessGroup
[-> Process.java]
public static final native int killProcessGroup(int uid, int pid);
该Native方法所对应的Jni方法如下:
-
android_os_Process_killProcessGroup
[-> android_util_Process.cpp]
jint android_os_Process_killProcessGroup(JNIEnv* env, jobject clazz, jint uid, jint pid)
{
return killProcessGroup(uid, pid, SIGKILL); //【见小节1.3.3】
}
-
killProcessGroup
[-> processgroup.cpp]
int killProcessGroup(uid_t uid, int initialPid, int signal) {
int processes;
const int sleep_us = 5 * 1000; // 5ms
int64_t startTime = android::uptimeMillis();
int retry = 40;
// 【见Step 1-3-3-1】
while ((processes = killProcessGroupOnce(uid, initialPid, signal)) > 0) {
//当还有进程未被杀死,则重试,最多40次
if (retry > 0) {
usleep(sleep_us);
--retry;
} else {
break; //重试40次,仍然没有杀死进程,代表杀进程失败
}
}
if (processes == 0) {
//移除进程组相应的目录 【见Step 1-3-3-2】
return removeProcessGroup(uid, initialPid);
} else {
return -1;
}
-
killProcessGroupOnce
[-> processgroup.cpp]
static int killProcessGroupOnce(uid_t uid, int initialPid, int signal) {
int processes = 0;
struct ctx ctx;
pid_t pid;
ctx.initialized = false;
while ((pid = getOneAppProcess(uid, initialPid, &ctx)) >= 0) {
processes++;
if (pid == 0) {
continue; //不应该进入该分支
}
int ret = kill(pid, signal); //杀进程组中的进程pid
}
if (ctx.initialized) {
close(ctx.fd);
}
//processes代表总共杀死了进程组中的进程个数
return processes;
}
其中getOneAppProcess
方法的作用是从节点/acct/uid_<uid>/pid_<pid>/cgroup.procs
中获取相应pid,这里是进程,而非线程。
故killProcessGroupOnce
的功能是杀掉uid下,跟initialPid同一个进程组的所有进程。也就意味着通过kill <pid>
,当pid是某个进程的子线程时,那么最终杀的仍是进程。
最终杀进程的实现方法都是调用kill(pid, sig)
方法。
removeProcessGroup
[-> processgroup.cpp]
static int removeProcessGroup(uid_t uid, int pid)
{
int ret;
char path[PROCESSGROUP_MAX_PATH_LEN] = {0};
//删除目录 /acct/uid_<uid>/pid_<pid>/
convertUidPidToPath(path, sizeof(path), uid, pid);
ret = rmdir(path);
//删除目录 /acct/uid_<uid>/
convertUidToPath(path, sizeof(path), uid);
rmdir(path);
return ret;
}
-
小结
流程图:
说明:
-
Process.killProcess(int pid): 杀pid进程
-
Process.killProcessQuiet(int pid):杀pid进程,且不输出log信息
-
Process.killProcessGroup(int uid, int pid):杀同一个uid下同一进程组下的所有进程
以上3个方法,最终杀进程的实现方法都是调用kill(pid, sig)
方法,该方法位于用户空间的Native层,经过系统调用进入到Linux内核的sys_kill方法
。对于杀进程此处的sig=9,其实与在adb里输入的 kill -9 <pid>
效果基本一致。
-
内核态kill
-
sys_kill
[-> syscalls.h]
asmlinkage long sys_kill(int pid, int sig);
sys_kill()
方法在linux内核中没有直接定义,而是通过宏定义SYSCALL_DEFINE2
的方式来实现的。Android内核(Linux)会为每个syscall分配唯一的系统调用号,当执行系统调用时会根据系统调用号从系统调用表中来查看目标函数的入口地址,在calls.S文件中声明了入口地址信息。另外,其中asmlinkage是gcc标签,表明该函数读取的参数位于栈中,而不是寄存器。
[-> signal.c]
SYSCALL_DEFINE2(kill, pid_t, pid, int, sig)
{
struct siginfo info;
info.si_signo = sig;
info.si_errno = 0;
info.si_code = SI_USER;
info.si_pid = task_tgid_vnr(current);
info.si_uid = from_kuid_munged(current_user_ns(), current_uid());
return kill_something_info(sig, &info, pid); //【见流程3.2.2】
}
SYSCALL_DEFINE2
是系统调用的宏定义,方法在此处经层层展开,等价于asmlinkage long sys_kill(int pid, int sig)
。关于宏展开细节就不多说了,就说一点SYSCALL_DEFINE2
中的2是指sys_kill方法有两个参数。
关于系统调用流程比较复杂,还涉及汇编语言,只需要知道 用户空间的kill()
最终调用到内核空间signal.c的kill_something_info()
方法就可以。
-
kill_something_info
[-> signal.c]
static int kill_something_info(int sig, struct siginfo *info, pid_t pid)
{
int ret;
if (pid > 0) {
rcu_read_lock();
//当pid>0时,则发送给pid所对应的进程【见流程2.3】
ret = kill_pid_info(sig, info, find_vpid(pid));
rcu_read_unlock();
return ret;
}
read_lock(&tasklist_lock);
if (pid != -1) {
//当pid=0时,则发送给当前进程组;
//当pid<-1时,则发送给-pid所对应的进程。
ret = __kill_pgrp_info(sig, info,
pid ? find_vpid(-pid) : task_pgrp(current));
} else {
//当pid=-1时,则发送给所有进程
int retval = 0, count = 0;
struct task_struct * p;
for_each_process(p) {
if (task_pid_vnr(p) > 1 &&
!samethreadgroup(p, current)) {
int err = group_send_sig_info(sig, info, p);
++count;
if (err != -EPERM)
retval = err;
}
}
ret = count ? retval : -ESRCH;
}
read_unlock(&tasklist_lock);
return ret;
}
功能:
-
当pid>0 时,则发送给pid所对应的进程;
-
当pid=0 时,则发送给当前进程组;
-
当pid=-1时,则发送给所有进程;
-
当pid<-1时,则发送给-pid所对应的进程。
-
kill_pid_info
[-> signal.c]
int kill_pid_info(int sig, struct siginfo *info, struct pid *pid)
{
int error = -ESRCH;
struct task_struct *p;
rcu_read_lock();
retry:
//根据pid查询到task结构体
p = pid_task(pid, PIDTYPE_PID);
if (p) {
error = group_send_sig_info(sig, info, p); //【见流程2.4】
if (unlikely(error == -ESRCH))
goto retry;
}
rcu_read_unlock();
return error;
}
-
group_send_sig_info
[-> signal.c]
int group_send_sig_info(int sig, struct siginfo *info, struct task_struct *p)
{
int ret;
rcu_read_lock();
//检查sig是否合法以及隐私等权限问题
ret = check_kill_permission(sig, info, p);
rcu_read_unlock();
if (!ret && sig)
ret = do_send_sig_info(sig, info, p, true); //【见流程2.5】
return ret;
}
-
do_send_sig_info
[-> signal.c]
int do_send_sig_info(int sig, struct siginfo *info, struct task_struct *p,
bool group)
{
unsigned long flags;
int ret = -ESRCH;
if (lock_task_sighand(p, &flags)) {
ret = send_signal(sig, info, p, group); //【见流程2.6】
unlock_task_sighand(p, &flags);
}
return ret;
}
-
send_signal
[-> signal.c]
static int send_signal(int sig, struct siginfo *info, struct task_struct *t,
int group)
{
int from_ancestor_ns = 0;
#ifdef CONFIG_PID_NS
from_ancestor_ns = si_fromuser(info) &&
!task_pid_nr_ns(current, task_active_pid_ns(t));
#endif
return __send_signal(sig, info, t, group, from_ancestor_ns); //【见流程2.7】
}
-
__send_signal
[-> signal.c]
static int __send_signal(int sig, struct siginfo *info, struct task_struct *t,
int group, int from_ancestor_ns)
{
struct sigpending *pending;
struct sigqueue *q;
int override_rlimit;
int ret = 0, result;
assert_spin_locked(&t->sighand->siglock);
result = TRACE_SIGNAL_IGNORED;
if (!prepare_signal(sig, t,
from_ancestor_ns || (info == SEND_SIG_FORCED)))
goto ret;
pending = group ? &t->signal->shared_pending : &t->pending;
result = TRACE_SIGNAL_ALREADY_PENDING;
if (legacy_queue(pending, sig))
goto ret;
result = TRACE_SIGNAL_DELIVERED;
if (info == SEND_SIG_FORCED)
goto out_set;
if (sig < SIGRTMIN)
override_rlimit = (is_si_special(info) || info->si_code >= 0);
else
override_rlimit = 0;
q = __sigqueue_alloc(sig, t, GFP_ATOMIC | __GFP_NOTRACK_FALSE_POSITIVE,
override_rlimit);
if (q) {
list_add_tail(&q->list, &pending->list);
switch ((unsigned long) info) {
case (unsigned long) SEND_SIG_NOINFO:
q->info.si_signo = sig;
q->info.si_errno = 0;
q->info.si_code = SI_USER;
q->info.si_pid = task_tgid_nr_ns(current,
task_active_pid_ns(t));
q->info.si_uid = from_kuid_munged(current_user_ns(), current_uid());
break;
case (unsigned long) SEND_SIG_PRIV:
q->info.si_signo = sig;
q->info.si_errno = 0;
q->info.si_code = SI_KERNEL;
q->info.si_pid = 0;
q->info.si_uid = 0;
break;
default:
copy_siginfo(&q->info, info);
if (from_ancestor_ns)
q->info.si_pid = 0;
break;
}
userns_fixup_signal_uid(&q->info, t);
} else if (!is_si_special(info)) {
if (sig >= SIGRTMIN && info->si_code != SI_USER) {
result = TRACE_SIGNAL_OVERFLOW_FAIL;
ret = -EAGAIN;
goto ret;
} else {
result = TRACE_SIGNAL_LOSE_INFO;
}
}
out_set:
//将信号sig传递给正处于监听状态的signalfd
signalfd_notify(t, sig);
//向信号集中加入信号sig
sigaddset(&pending->signal, sig);
//完成信号过程,【见流程2.8】
complete_signal(sig, t, group);
ret:
trace_signal_generate(sig, info, t, group, result);
return ret;
}
-
complete_signal
[-> signal.c]
static void complete_signal(int sig, struct task_struct *p, int group)
{
struct signal_struct *signal = p->signal;
struct task_struct *t;
//查找能处理该信号的线程
if (wants_signal(sig, p))
t = p;
else if (!group || thread_group_empty(p))
return;
else {
// 递归查找适合的线程
t = signal->curr_target;
while (!wants_signal(sig, t)) {
t = next_thread(t);
if (t == signal->curr_target)
return;
}
signal->curr_target = t;
}
//找到一个能被杀掉的线程,如果这个信号是SIGKILL,则立刻干掉整个线程组
if (sig_fatal(p, sig) &&
!(signal->flags & (SIGNAL_UNKILLABLE | SIGNAL_GROUP_EXIT)) &&
!sigismember(&t->real_blocked, sig) &&
(sig == SIGKILL || !t->ptrace)) {
//信号将终结整个线程组
if (!sig_kernel_coredump(sig)) {
signal->flags = SIGNAL_GROUP_EXIT;
signal->group_exit_code = sig;
signal->group_stop_count = 0;
t = p;
//遍历整个线程组,全部结束
do {
task_clear_jobctl_pending(t, JOBCTL_PENDING_MASK);
//向信号集中加入信号SIGKILL
sigaddset(&t->pending.signal, SIGKILL);
signal_wake_up(t, 1);
} while_each_thread(p, t);
return;
}
}
//该信号处于共享队列里(即将要处理的)。唤醒已选中的目标线程,并将该信号移出队列。
signal_wake_up(t, sig == SIGKILL);
return;
}
-
小结
到此Signal信号已发送给目标线程,先用一幅图来小结一下上述流程:
流程分为用户空间(User Space)和内核空间(Kernel Space)。从用户空间进入内核空间需要向内核发出syscall,用户空间的程序通过各种syscall来调用用内核空间相应的服务。系统调用是为了让用户空间的程序陷入内核,该陷入动作是由软中断来完成的。用户态的进程进行系统调用后,CPU切换到内核态,开始执行内核函数。unistd.h
文件中定义了所有的系统中断号,用户态程序通过不同的系统调用号来调用不同的内核服务,通过系统调用号从系统调用表中查看到相应的内核服务。
再回到信号,在Process.java中定义了如下3个信号:
public static final int SIGNAL_QUIT = 3; //用于输出线程trace
public static final int SIGNAL_KILL = 9; //用于杀进程/线程
public static final int SIGNAL_USR1 = 10; //用于强制执行GC
对于kill -9
,信号SIGKILL的处理过程,这是因为SIGKILL是不能被忽略同时也不能被捕获,故不会由目标线程的signal Catcher线程来处理,而是由内核直接处理,到此便完成。
但对于信号3和10,则是交由目标进程(art虚拟机)的SignalCatcher线程来捕获完成相应操作的,接下来进入目标线程来处理相应的信号。