美文网首页
Service 'zygote' killed by signa

Service 'zygote' killed by signa

作者: 啃着地瓜数星星 | 来源:发表于2018-05-23 11:27 被阅读0次

一、问题描述

01-07 21:57:03.228 1690 2829 D ActivityManager: cleanUpApplicationRecord -- 5762
01-07 21:57:03.232 1690 1702 W WindowManager: Attempted to remove non-existing token: android.os.Binder@333a888
01-07 21:57:03.233 1690 2829 W ActivityManager: Scheduling restart of crashed service com.android.statementservice/.DirectStatementService in 60923ms
01-07 21:57:03.234 2892 3105 E WtProcessController: Error pid or pid not exist
01-07 21:57:03.234 1690 3683 D ActivityManager: cleanUpApplicationRecord -- 5324
01-07 21:57:03.235 1690 3683 I AutoStartManagerService: MIUILOG- Reject RestartService packageName :com.android.email uid : 10064
01-07 21:57:03.236 2892 3105 E WtProcessController: Error pid or pid not exist
01-07 21:57:03.236 1690 2876 D ActivityManager: cleanUpApplicationRecord -- 5303
01-07 21:57:03.237 1690 2876 I AutoStartManagerService: MIUILOG- Reject RestartService packageName :com.miui.personalassistant uid : 10040
01-07 21:57:03.303 10500 10500 W init : type=1400 audit(0.0:2272): avc: denied { write } for name="zygote64_pid" dev="debugfs" ino=12729 scontext=u:r:init:s0 tcontext=u:object_r:debugfs_ktrace:s0 tclass=file permissive=0
01-07 21:57:03.311 739 739 I cnss-daemon: RTM_NEWNEIGH message received: 28
01-07 21:57:03.311 739 739 E cnss-daemon: Stale or unreachable neighbors, ndm state: 32
01-07 21:57:03.314 553 553 I ServiceManager: service 'media.camera' died
01-07 21:57:03.314 553 553 I ServiceManager: service 'media.player' died
01-07 21:57:03.314 553 553 I ServiceManager: service 'media.resource_manager' died
01-07 21:57:03.317 730 980 E OMXNodeInstance: !!! Observer died. Quickly, do something, ... anything...
01-07 21:57:03.373 553 553 I ServiceManager: service 'media.radio' died
01-07 21:57:03.373 553 553 I ServiceManager: service 'media.sound_trigger_hw' died
01-07 21:57:03.373 553 553 I ServiceManager: service 'media.audio_flinger' died
01-07 21:57:03.373 553 553 I ServiceManager: service 'media.audio_policy' died
01-07 21:57:03.388 553 553 I ServiceManager: service 'fingerprints_service' died

1690 是重启之前的 system_server 的 pid,从上面的 log 中可以看出上一步 system_server 还在正常执行操作,下一步各种 service 就开始挂掉,系统开始重启了,中间也没有 system_server 的错误信息。这种情况下,我们会怀疑是其他 service 挂掉直接或间接导致 system_server 重启,譬如说 SurfaceFlinger 重启导致 system_server 重启;查看 log 可以发现 SurfaceFlinger 的 pid 并没有发生改变,并且:

u:r:zygote:s0                  root      1375  1     1613000 25752 20    0     0     0     fg  poll_sched 0000000000 S zygote
u:r:zygote:s0                  root      10500 1     2175000 92232 20    0     0     0     fg  poll_sched 0000000000 S zygote64

service zygote 的 pid 发生了变化,很容易可以推断出 zygote64 发生了重启,并导致 system_server 重启,搜索 log 果然可以发现:

<13>[ 9907.324247] init: Service 'zygote' (pid 1374) killed by signal 1
<13>[ 9907.324349] init: Service 'zygote' (pid 1374) killing any children in process group

zygote64 被 signal 1 杀掉了,那 signal 1 又是什么呢?我们可以通过 "kill -l" 进行查看:


这里写图片描述

signal 1 应为 SIGHUP

二、SIGHUP

从上面的分析可以看出,zygote64 是被 SIGHUP kill 了,下面来具体看一下 SIGHUP 是怎么产生的。
kernel/msm-4.4/kernel/exit.c

/*
 * Check to see if any process groups have become orphaned as
 * a result of our exiting, and if they have any stopped jobs,
 * send them a SIGHUP and then a SIGCONT. (POSIX 3.2.2.2)
 */
static void
kill_orphaned_pgrp(struct task_struct *tsk, struct task_struct *parent)
{
    struct pid *pgrp = task_pgrp(tsk);
    struct task_struct *ignored_task = tsk;
 
    if (!parent)
        /* exit: our father is in a different pgrp than
         * we are and we were the only connection outside.
         */
        parent = tsk->real_parent;
    else
        /* reparent: our child is in a different pgrp than
         * we are, and it was the only connection outside.
         */
        ignored_task = NULL;
 
    if (task_pgrp(parent) != pgrp &&
        task_session(parent) == task_session(tsk) &&
        will_become_orphaned_pgrp(pgrp, ignored_task) &&
        has_stopped_jobs(pgrp)) {
        __kill_pgrp_info(SIGHUP, SEND_SIG_PRIV, pgrp);
        __kill_pgrp_info(SIGCONT, SEND_SIG_PRIV, pgrp);
    }
}

可以看到在满足一系列条件时,会调用 __kill_pgrp_info(SIGHUP, SEND_SIG_PRIV, pgrp) 给 pgrp 中的每个进程发送一个 SIGHUP 信号,那么应该如何解读这些条件呢?

可以看到系统中有两处会调用到 __kill_pgrp_info(...) 函数,见下图:

这里写图片描述
我们先看一下5处 kill_orphaned_pgrp 的调用场景:
kernel/msm-4.4/kernel/exit.c
/*
 * This does two things:
 *
 * A.  Make init inherit all the child processes
 * B.  Check to see if any process groups have become orphaned
 *  as a result of our exiting, and if they have any stopped
 *  jobs, send them a SIGHUP and then a SIGCONT.  (POSIX 3.2.2.2)
 */
static void forget_original_parent(struct task_struct *father,
                    struct list_head *dead)
{
    struct task_struct *p, *t, *reaper;
 
    if (unlikely(!list_empty(&father->ptraced)))
        exit_ptrace(father, dead);
 
    // 为正在退出的进程查找其子进程的 reaper
    reaper = find_child_reaper(father);
    // 如果没有子进程,直接返回
    if (list_empty(&father->children))
        return;
    // 为正在退出的进程查找其子进程的新的 reaper
    reaper = find_new_reaper(father, reaper);
    list_for_each_entry(p, &father->children, sibling) {
        for_each_thread(p, t) {
            t->real_parent = reaper;
            BUG_ON((!t->ptrace) != (t->parent == father));
            if (likely(!t->ptrace))
                t->parent = t->real_parent;
            if (t->pdeath_signal)
                group_send_sig_info(t->pdeath_signal,
                            SEND_SIG_NOINFO, t);
        }
        /*
         * If this is a threaded reparent there is no need to
         * notify anyone anything has happened.
         */
        if (!same_thread_group(reaper, father))
            reparent_leader(father, p, dead);
    }
    list_splice_tail_init(&father->children, &reaper->children);
}

注意,这里传入的参数 father 实际上是 do_exit 中正在退出的 task 的指针,所以这个函数的主要作用是:

  • 为 father(也就是正在退出的 task)的每个子进程以及每个子进程的线程找到他们的新的父亲(real_parent)
  • 如果新的 reaper 与 father 不属于同一线程组,那么对 father 的每个子进程 p 调用 reparent_leader(father, p, dead) (注意这里的 father 并不是我们新找到的 reaper,仍旧是我们这个正在退出的 task)

kernel/msm-4.4/kernel/exit.c

/*
* Any that need to be release_task'd are put on the @dead list.
 */
static void reparent_leader(struct task_struct *father, struct task_struct *p,
                struct list_head *dead)
{
    if (unlikely(p->exit_state == EXIT_DEAD))
        return;
 
    /* We don't want people slaying init. */
    p->exit_signal = SIGCHLD;
 
    /* If it has exited notify the new parent about this child's death. */
    if (!p->ptrace &&
        p->exit_state == EXIT_ZOMBIE && thread_group_empty(p)) {
        if (do_notify_parent(p, p->exit_signal)) {
            p->exit_state = EXIT_DEAD;
            list_add(&p->ptrace_entry, dead);
        }
    }
 
    kill_orphaned_pgrp(p, father);
}

以上是第一处调用 kill_orphaned_pgrp 的地方,下面看一下第二处调用 kill_orphaned_pgrp 的地方:
kernel/msm-4.4/kernel/exit.c

/*
 * Send signals to all our closest relatives so that they know
 * to properly mourn us..
 */
static void exit_notify(struct task_struct *tsk, int group_dead)
{
    bool autoreap;
    struct task_struct *p, *n;
    LIST_HEAD(dead);
 
    write_lock_irq(&tasklist_lock);
    forget_original_parent(tsk, &dead);
 
    if (group_dead)
        kill_orphaned_pgrp(tsk->group_leader, NULL);
 
    if (unlikely(tsk->ptrace)) {
        int sig = thread_group_leader(tsk) &&
                thread_group_empty(tsk) &&
                !ptrace_reparented(tsk) ?
            tsk->exit_signal : SIGCHLD;
        autoreap = do_notify_parent(tsk, sig);
    } else if (thread_group_leader(tsk)) {
        autoreap = thread_group_empty(tsk) &&
            do_notify_parent(tsk, tsk->exit_signal);
    } else {
        autoreap = true;
    }
 
    tsk->exit_state = autoreap ? EXIT_DEAD : EXIT_ZOMBIE;
    if (tsk->exit_state == EXIT_DEAD)
        list_add(&tsk->ptrace_entry, &dead);
 
    /* mt-exec, de_thread() is waiting for group leader */
    if (unlikely(tsk->signal->notify_count < 0))
        wake_up_process(tsk->signal->group_exit_task);
    write_unlock_irq(&tasklist_lock);
 
    list_for_each_entry_safe(p, n, &dead, ptrace_entry) {
        list_del_init(&p->ptrace_entry);
        release_task(p);
    }
}

group_dead 是调用 exit_notify(...) 时传过来的参数,表明是线程组中最后一个退出的 task,tsk->group_leader 即 tgid,这就是第二处调用 kill_orphaned_pgrp 的地方


我们来比较一下两处调用 kill_orphaned_pgrp 的地方有什么不同,假设我们正在退出的 task 是 A(并且 A 是 group_leader 以及线程组中最后一个退出的 task),B 是 A 的子进程(由 A fork 出来),那么两处调用 kill_orphaned_pgrp 的地方分别为:

  • kill_orphaned_pgrp(B, A)
  • kill_orphaned_pgrp(A, NULL)

所以这两处调用 kill_orphaned_pgrp 的地方实际上是针对两种不同的情景:

情景一 kill_orphaned_pgrp(B, A) 如下图所示:


这里写图片描述

所以根据这幅图,可以这样理解 __kill_pgrp_info(SIGHUP, SEND_SIG_PRIV, pgrp) 前需要满足的四个条件:

  • task_pgrp(parent) != pgrp,进程 B 与他的 parent A 不处于同一 process group
  • task_session(parent) == task_session(tsk),A 与 B 在同一 session 中
  • will_become_orphaned_pgrp(pgrp, ignored_task) (这里 ignored_task 为 NULL),这个条件可以很形象地表示为 pgrp2 中除了进程 B 作为 process group 之间的桥梁之外,没有其他进程可以作为这样的桥梁(父进程是 init 的进程除外)
  • has_stopped_jobs(pgrp),pgrp2 中有进程处于 stop 状态 (p->signal->flags & SIGNAL_STOP_STOPPED 为 true)

情景二 kill_orphaned_pgrp(A, NULL) 如下图所示:


这里写图片描述

根据这幅图,可以这样理解 __kill_pgrp_info(SIGHUP, SEND_SIG_PRIV, pgrp) 前需要满足的四个条件:

  • task_pgrp(parent) != pgrp,进程 A 与他的 parent 不处于同一 process group
  • task_session(parent) == task_session(tsk),进程 A 与他的 parent 在同一 session 中
  • will_become_orphaned_pgrp(pgrp, ignored_task) (这里 ignored_task 为 A),这个条件可以很形象地表示为 pgrp2 中除了进程 A 作为 process group 之间的桥梁之外,没有其他进程可以作为这样的桥梁(父进程是 init 的进程除外)
  • has_stopped_jobs(pgrp),pgrp2 中有进程处于 stop 状态 (p->signal->flags & SIGNAL_STOP_STOPPED 为 true)

综合这上面两个情景,可以总结为,进程 A 退出时,会考虑会不会使得其子进程所处的 pgrp 变为孤儿进程组(情景一)以及会不会使得自己退出前所处的 pgrp 变为孤儿进程组(情景二)

三、实践

通过上面的分析,针对 __kill_pgrp_info(SIGHUP, SEND_SIG_PRIV, pgrp) 的两个场景,我们得出了很清晰的结论,下面就通过一些例子来验证一下我们的结论,理论与实践相结合,体验一下花式搞死 zygote 的快感

因为很多份 log 中,系统都是在一键清理 com.tencent.tmgp.speedmobile 这个应用的过程中挂掉的,我们就来看一下这个应用有什么过人之处(我们先在 32 位的机器上实践一下):

1、32 位机器

comm pid ppid tgid pgid sid
com.tencent.tmgp.speedmobile 5999 310 5999 5999 0
xg_service_v2 6076 310 6076 310 0
libxguardian.so 6199 1 6199 310 0
debuggerd 6274 5999 6274 6274 0
debuggerd 6276 6274 6276 310 0
这里写图片描述

可以看到相同 uid 有五个进程存在,我们再通过 cat /proc/pid/stat 和 cat /proc/pid/status 命令查看他们各自的 pid、ppid、pgid、sid 等信息,以 5999 为例,如下所示:


这里写图片描述

其中第一个数字是 pid,S 后面的三个数分别是 ppid、pgid、sid


这里写图片描述
通过上面两个命令,能列出上面5个进程之间的关系:
comm pid ppid tgid pgid sid
com.tencent.tmgp.speedmobile 5999 310 5999 5999 0
xg_service_v2 6076 310 6076 310 0
libxguardian.so 6199 1 6199 310 0
debuggerd 6274 5999 6274 6274 0
debuggerd 6276 6274 6276 310 0



默认情况下 zygote 进程的子进程和孙子进程(即所有 java 进程)的 pgid 应该都等于 zygote 进程的 pid,这里进程 5999 和 6274 应当是自己重新设置了 pgid;五个进程之间的关系如下图所示:

这里写图片描述
从上面五个进程之间的关系,以及前面分析得到的结论,我们可以推测:
  • pgrp 310 与其他 pgrp 沟通的桥梁可以认为有两个,分别是进程 C 和进程 E
  • 给 pgrp 310 中的某个进程发一个 stop 信号(kill -19),再 kill 进程 B 可以模拟出上面的情景一
  • 给 pgrp 310 中的某个进程发一个 stop 信号(除去进程 C),再 kill 进程 C 可以模拟出上面的情景二
  • kill 进程 B 或 C,给 pgrp 310 中的某个进程发一个 stop 信号(除去进程 C、E),再 kill 进程 E 可以模拟出上面的情景二

可以手动验证一下上面推测的三种情况,结论完美,O(∩_∩)O哈哈~

2、64 位机器

comm pid ppid tgid pgid sid
zygote 709 1 709 709 0
zygote64 708 1 708 708 0
com.tencent.tmgp.speedmobile 5013 709 5013 5013 0
xg_service_v2 5101 709 5101 708 0
libxguardian.so 5201 1 5201 708 0
debuggerd 5232 5013 5232 5232 0
debuggerd 5234 5232 5234 708 0
这里写图片描述

7个进程之间的关系:

comm pid ppid tgid pgid sid
zygote 709 1 709 709 0
zygote64 708 1 708 708 0
com.tencent.tmgp.speedmobile 5013 709 5013 5013 0
xg_service_v2 5101 709 5101 708 0
libxguardian.so 5201 1 5201 708 0
debuggerd 5232 5013 5232 5232 0
debuggerd 5234 5232 5234 708 0



默认情况下所有 java 进程(除去 zygote 进程)的 pgid 应该都等于 zygote64 进程的 pid,zygote 进程自己处于一个 pgrp 中,如上 7 个进程之间的关系如下图所示:

这里写图片描述
从上面几个进程之间的关系,以及前面分析得到的结论,我们可以推测:
  • pgrp zygote64 与其他 pgrp 沟通的桥梁可以认为有多个,分别是进程 C 和进程 E,以及 zygote 进程的子进程
  • kill zygote 进程的所有子进程(除去 A),给 pgrp zygote64 中的某个进程发一个 stop 信号(kill -19),再 kill 进程 B 可以模拟出上面的情景一
  • kill zygote 进程的所有子进程(除去 A),给 pgrp zygote64 中的某个进程发一个 stop 信号(除去进程 C),再 kill 进程 C 可以模拟出上面的情景二
  • kill zygote 进程的所有子进程,kill 进程 B 或 C,给 pgrp zygote64 中的某个进程发一个 stop 信号(除去进程 C、E),再 kill 进程 E 可以模拟出上面的情景二,注意这是理论上的,实际操作过程中由于 com.tencent.tmgp.speedmobile 的设置,kill 进程 D 的同时进程 E 也会挂掉
  • kill zygote 进程的所有子进程(除去 D 或者其他的任意一个进程 X),kill 进程 B 或 C,给 pgrp zygote64 中的某个进程发一个 stop 信号(除去会被 kill 的进程),再 kill 进程 D 或 X 可以模拟出上面的情景二

可以手动验证一下上面推测的几种情况,结论完美,_

综上所述,32位和64位机器相差的实际上就是 zygote 这个 process group,如果把 zygote 的子进程都 kill 掉,64位系统的进程关系实际上就相当于32位系统的进程关系;


可以发现,实际上在64位机器上,zygote 进程的子进程很少,大部分 java 进程都是 zygote64 的子进程,这样就很容易出现 zygote 进程的子进程都已经退出的状况了;

四、解决方案

为什么会产生 SIGHUP 这个机制可以参考博客 http://blog.csdn.net/zhangfangew/article/details/27070491
另外,已经就这个问题向 google 提交了 issue https://issuetracker.google.com/issues/71965619 和 change https://android-review.googlesource.com/c/platform/frameworks/base/+/588576

五、知识点补充

list_for_each(pos, head)、list_for_each_entry(pos, head, member)

相关文章

网友评论

      本文标题:Service 'zygote' killed by signa

      本文链接:https://www.haomeiwen.com/subject/cjqrjftx.html