Android 信号处理面面观 之 信号定义、行为和来源

< Version 0.1  2011-11-21  Init draft>


传统 Unix系统的信号定义和行为

所有的符合Unix规范(如POSIX)的系统都统一定义了SIGNAL的数量、含义和行为。 作为Linux系统,Android自然不会更改SIGNAL的定义。在Android代码中,signal的定义一般在 signum.h (prebuilt/linux-x86/toolchain/i686-linux-glibc2.7-4.4.3/sysroot/usr/include/bits/signum.h)中:

/* Signals.  */
#define	SIGHUP		1	/* Hangup (POSIX).  */
#define	SIGINT		2	/* Interrupt (ANSI).  */
#define	SIGQUIT		3	/* Quit (POSIX).  */
#define	SIGILL		4	/* Illegal instruction (ANSI).  */
#define	SIGTRAP		5	/* Trace trap (POSIX).  */
#define	SIGABRT		6	/* Abort (ANSI).  */
#define	SIGIOT		6	/* IOT trap (4.2 BSD).  */
#define	SIGBUS		7	/* BUS error (4.2 BSD).  */
#define	SIGFPE		8	/* Floating-point exception (ANSI).  */
#define	SIGKILL		9	/* Kill, unblockable (POSIX).  */
#define	SIGUSR1		10	/* User-defined signal 1 (POSIX).  */
#define	SIGSEGV		11	/* Segmentation violation (ANSI).  */
#define	SIGUSR2		12	/* User-defined signal 2 (POSIX).  */
#define	SIGPIPE		13	/* Broken pipe (POSIX).  */
#define	SIGALRM		14	/* Alarm clock (POSIX).  */
#define	SIGTERM		15	/* Termination (ANSI).  */
#define	SIGSTKFLT	16	/* Stack fault.  */
#define	SIGCLD		SIGCHLD	/* Same as SIGCHLD (System V).  */
#define	SIGCHLD		17	/* Child status has changed (POSIX).  */
#define	SIGCONT		18	/* Continue (POSIX).  */
#define	SIGSTOP		19	/* Stop, unblockable (POSIX).  */
#define	SIGTSTP		20	/* Keyboard stop (POSIX).  */
#define	SIGTTIN		21	/* Background read from tty (POSIX).  */
#define	SIGTTOU		22	/* Background write to tty (POSIX).  */
#define	SIGURG		23	/* Urgent condition on socket (4.2 BSD).  */
#define	SIGXCPU		24	/* CPU limit exceeded (4.2 BSD).  */
#define	SIGXFSZ		25	/* File size limit exceeded (4.2 BSD).  */
#define	SIGVTALRM	26	/* Virtual alarm clock (4.2 BSD).  */
#define	SIGPROF		27	/* Profiling alarm clock (4.2 BSD).  */
#define	SIGWINCH	28	/* Window size change (4.3 BSD, Sun).  */
#define	SIGPOLL		SIGIO	/* Pollable event occurred (System V).  */
#define	SIGIO		29	/* I/O now possible (4.2 BSD).  */
#define	SIGPWR		30	/* Power failure restart (System V).  */
#define SIGSYS		31	/* Bad system call.  */
#define SIGUNUSED	31

我们知道,信号处理的方式一般有三种:

1. 忽略  接收到信号后不做任何反应。

2. 自定义  用自定义的信号处理函数来执行特定的动作

3. 默认  接收到信号后按默认得行为处理该信号。 这是多数应用采取的处理方式。


而 传统 UNIX系统对以上信号的默认处理如下图所示 (来自 APUT ):


Android 系统 信号处理的行为

我们知道,信号处理的行为是以进程级的。就是说不同的进程可以分别设置不同的信号处理方式而互不干扰。同一进程中的不同线程虽然可以设置不同的信号屏蔽字,但是却共享相同的信号处理方式 (也就是说 在一个线程里改变信号处理方式,将作用于该进程中的所有线程)。


Android也是Linux系统。所以其信号处理方式不会有本质的改变。但是为了开发和调试的需要,android对一些信号的处理定义了额外的行为。 下面是这些典型的信号在Android系统上的行为:

1. SIGQUIT ( 整型值为 3)

上面的表10-1显示,传统UNIX系统应用,对SIGQUIT信号的默认行为是 "终止 + CORE"。也就是产生core dump文件后,立即终于运行。

Android Dalvik应用收到该信号后,会 打印改应用中所有线程的当前状态,并且并不是强制退出。这些状态通常保存在一个特定的叫做trace的文件中。一般的路径是/data/anr/trace.txt. 下面是一个典型的trace文件的内容:

----- pid 503 at 2011-11-21 21:59:12 -----
Cmd line: com.android.phone

DALVIK THREADS:
(mutexes: tll=0 tsl=0 tscl=0 ghl=0 hwl=0 hwll=0)
"main" prio=5 tid=1 NATIVE
  | group="main" sCount=1 dsCount=0 obj=0x400246a0 self=0x12770
  | sysTid=503 nice=0 sched=0/0 cgrp=default handle=-1342909272
  | schedstat=( 15165039025 12197235258 23068 ) utm=182 stm=1334 core=0
  at android.os.MessageQueue.nativePollOnce(Native Method)
  at android.os.MessageQueue.next(MessageQueue.java:119)
  at android.os.Looper.loop(Looper.java:122)
  at android.app.ActivityThread.main(ActivityThread.java:4134)
  at java.lang.reflect.Method.invokeNative(Native Method)
  at java.lang.reflect.Method.invoke(Method.java:491)
  at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:841)
  at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:599)
  at dalvik.system.NativeStart.main(Native Method)

"Thread-29" prio=5 tid=24 WAIT
  | group="main" sCount=1 dsCount=0 obj=0x406f0d50 self=0x208c18
  | sysTid=1095 nice=0 sched=0/0 cgrp=default handle=2133304
  | schedstat=( 9521483 7029937750 720 ) utm=0 stm=0 core=0
  at java.lang.Object.wait(Native Method)
  - waiting on <0x406f0d50> (a com.motorola.android.telephony.cdma.OemCdmaTelephonyManager$Watchdog)
  at java.lang.Object.wait(Object.java:361)
  at com.motorola.android.telephony.cdma.OemCdmaTelephonyManager$Watchdog.run(OemCdmaTelephonyManager.java:229)

"FileObserver" prio=5 tid=23 NATIVE
  | group="main" sCount=1 dsCount=0 obj=0x4068b2f8 self=0x1ed278
  | sysTid=909 nice=0 sched=0/0 cgrp=default handle=2019248
  | schedstat=( 11810291 7018493670 720 ) utm=0 stm=0 core=0
  at android.os.FileObserver$ObserverThread.observe(Native Method)
  at android.os.FileObserver$ObserverThread.run(FileObserver.java:88)

"android.hardware.SensorManager$SensorThread" prio=5 tid=22 NATIVE
  | group="main" sCount=1 dsCount=0 obj=0x406bbd90 self=0x1b2ec0
  | sysTid=869 nice=-8 sched=0/0 cgrp=default handle=1974064
  | schedstat=( 3014251483 8295989933 15621 ) utm=171 stm=128 core=0
  at android.hardware.SensorManager.sensors_data_poll(Native Method)
  at android.hardware.SensorManager$SensorThread$SensorThreadRunnable.run(SensorManager.java:498)
  at java.lang.Thread.run(Thread.java:1020)
...
该文件包好很多重要的信息,可以说明在发生异常是,当前进程的状态 (后面有单独的一篇文章分析改文件)


2. 对于很多其他的异常信号 (SIGILL, SIGABRT, SIGBUS, SIGFPE, SIGSEGV, SIGSTKFLT ), Android进程 在退出前,会生成 tombstone文件。记录该进程退出前的轨迹。一个典型的tombstone文件内容如下:

*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
Build fingerprint: 'verizon/pasteur/pasteur:3.2.2/1.6.0_241/eng.drmn68.20111115.094123:eng/test-keys'
pid: 181, tid: 322  >>> /system/bin/mediaserver <<<
signal 8 (SIGFPE), code 0 (?), fault addr 000000b5
 r0 00000000  r1 00000008  r2 ffffffff  r3 00000020
 r4 00000008  r5 00000000  r6 000000a5  r7 00000025
 r8 662f9c00  r9 662f9c00  10 00000001  fp 00000000
 ip aff17699  sp 4057f9dc  lr aff176a7  pc aff0c684  cpsr 00000010
 d0  6f762f6f69647502  d1  0000562202000000
 d2  0000000400000300  d3  400120dc00000000
 d4  0000000000000000  d5  0000000000000000
 d6  3ce449db86666666  d7  3e4ccccd3e4ccccd
 d8  000000000035c6a8  d9  000000000035c6a8
 d10 0000000000000000  d11 0000000000000000
 d12 0000000000000000  d13 0000000000000000
 d14 0000000000000000  d15 0000000000000000
 d16 0000000000000000  d17 3e582f8f86b6a000
 d18 3fe0000000000000  d19 3fe000000c17c7c3
 d20 3f11504c292739d4  d21 bebbb371092382c4
 d22 3ff0000000000000  d23 3ff43d135cda918c
 d24 3e66376972bea4d0  d25 0000000000000000
 d26 0000000000000000  d27 0000000000000000
 d28 0000000000000000  d29 0000000000000000
 d30 0000000000000000  d31 0000000000000000
 scr 20000010

         #00  pc 0000c684  /system/lib/libc.so (kill)
         #01  pc 000176a4  /system/lib/libc.so (raise)

libc base address: aff00000

code around pc:
aff0c664 e2601000 e0100001 116f0f10 12600020 
aff0c674 e12fff1e e92d50f0 e3a07025 ef000000 
aff0c684 e8bd50f0 e1b00000 512fff1e ea00ade7 
aff0c694 e92d50f0 e3a070ee ef000000 e8bd50f0 
aff0c6a4 e1b00000 512fff1e ea00ade0 f5d0f000 

code around lr:
aff17684 00029e2e 461cb537 e9cd17dd f7f34500 
aff17694 bd3eef02 4604b510 ed5ef7f3 f7f44621 
aff176a4 bd10efea 49034602 2300b510 f7f44802 
aff176b4 bd10edf6 28121969 fee1dead 2400b513 
aff176c4 94019400 ec9cf7f4 bf00bd1c 4c11b570 

stack:
    4057f99c  a2b6fd15  /system/lib/libstagefright.so
    4057f9a0  00000000  
    4057f9a4  a2b6fe51  /system/lib/libstagefright.so
    4057f9a8  000fb02c  
    4057f9ac  a2b6fde7  /system/lib/libstagefright.so
    4057f9b0  4057fa14  
    4057f9b4  000fb030  
    4057f9b8  00000000  
    4057f9bc  a2b6fe79  /system/lib/libstagefright.so
    4057f9c0  000fafe0  
    4057f9c4  00000000  
    4057f9c8  4057fa14  
    4057f9cc  a2b6fe59  /system/lib/libstagefright.so
    4057f9d0  00000001  
    4057f9d4  a801e509  /system/lib/libutils.so
    4057f9d8  4057fa14  
#01 4057f9dc  00000008  
    4057f9e0  00000000  
    4057f9e4  000000a5  
    4057f9e8  00000000  
    4057f9ec  aff17699  /system/lib/libc.so
    4057f9f0  aff176a7  /system/lib/libc.so
    4057f9f4  00000000  
    4057f9f8  aff0e154  /system/lib/libc.so
    4057f9fc  00000000  
    4057fa00  aff0cf84  /system/lib/libc.so
    4057fa04  aff0cf94  /system/lib/libc.so
    4057fa08  00000000  
    4057fa0c  000000a5  
    4057fa10  00000000  
    4057fa14  aff0fca4  /system/lib/libc.so
    4057fa18  662f9c00  
    4057fa1c  000000a5  
    4057fa20  00000000  
--- --- --- --- --- --- --- --- --- --- --- --- --- --- --- ---
pid: 181, tid: 181
 r0 fffffe00  r1 c0186201  r2 be8b8b98  r3 be8b8b94
 r4 0000f5e0  r5 0000f5b0  r6 0000f610  r7 00000036
 r8 00000001  r9 0000f5cc  10 0000f5b8  fp 00000000
 ip a812336c  sp be8b8b78  lr aff25e19  pc aff0b680  cpsr 80000010
 d0  000f891000000000  d1  00000004be8b8b00
 d2  0069006400650000  d3  00410049002e0000
 d4  0000000000000000  d5  0000000000000000
 d6  4208000041880000  d7  0000000041a00000
 d8  0000000000000000  d9  0000000000000000
 d10 0000000000000000  d11 0000000000000000
 d12 0000000000000000  d13 0000000000000000
 d14 0000000000000000  d15 0000000000000000
 d16 0000000000000000  d17 0000000000000000
 d18 4000000000000000  d19 3fcce7359d4792d9
 d20 3f11504c292739d4  d21 bebbb371092382c4
 d22 3ff0000000000000  d23 3ff43d135cda918c
 d24 3e66376972bea4d0  d25 0000000000000000
 d26 0000000000000000  d27 0000000000000000
 d28 0000000000000000  d29 0000000000000000
 d30 0000000000000000  d31 0000000000000000
 scr 60000010

         #00  pc 0000b680  /system/lib/libc.so (__ioctl)
         #01  pc 00025e16  /system/lib/libc.so (ioctl)
         #02  pc 00016202  /system/lib/libbinder.so (_ZN7android14IPCThreadState14talkWithDriverEb)
         #03  pc 00016afc  /system/lib/libbinder.so (_ZN7android14IPCThreadState14joinThreadPoolEb)
         #04  pc 00008a94  /system/bin/mediaserver
         #05  pc 00014aa0  /system/lib/libc.so (__libc_init)

libc base address: aff00000

code around pc:
aff0b660 ef000000 e8bd0090 e1b00000 512fff1e 
aff0b670 ea00b1ef e92d0090 e3a07036 ef000000 
aff0b680 e8bd0090 e1b00000 512fff1e ea00b1e8 
aff0b690 e92d0090 e3a07091 ef000000 e8bd0090 
aff0b6a0 e1b00000 512fff1e ea00b1e1 e92d0090 
...
可以看出,它同样包含很多重要的信息(特别是 stack )来帮助我们查找异常的原因。分析tombstone的方法,将单独成篇。


Android信号的产生和测试

我们看到,多数signal的产生是由于某种内部错误。我们在在开发过程中,当然也可以通过系统调用故意生成signal给某进程。主要的方法如果:

1. 在kernel里 使用 kill_proc_info()

2. 在native应用中 使用 kill() 或者raise()

3. java 应用中使用 Procees.sendSignal()等


但是在测试中,最简单的方法某过于通过 adb 工具了。一个典型场景是:

adb root
adb shell ps
adb shell kill -3 513

首先是切换到root用户 (普通进程只能发个自己或者同组进程,而root可以发送signal给任何进程)。然后用 ps命令查看当前系统中所有的进程信息。最后用kill命令发送SIGQUIT给进程号为513的进程。


android kill程序的实现很简单,他只能支持发送signal的值(如上例中的 “3”)给进程,而不能用名字(如“SIGQUIT”)。 android 中kill程序的代码在system/core/toolbox/kill.c 中。虽然移植linux中kill的实现就能支持名字,但是那个完全没有必要,android需要的signal就这么几个,他们的值应该记住的。


对于终端发送 SIGQUIT,大多数用户可以直接得到预期的结果 (生成相应的trace文件)。 最困惑的行为来自于终端发送SIGILL, SIGABRT, SIGBUS, SIGFPE, SIGSEGV, SIGSTKFLT等信号,我们常常看到 “不确定” 的行为:有时候能够看到 process 终止,有时候却不能。core dump 也不是总能产生。 例如如下测试场景:

adb root
adb shell ps
adb shell kill -11 299
从 log里看到 进程299 并没有终止, ps 看到进程还在。Log只有如下输出:

F/libc    (  244): Fatal signal 11 (SIGSEGV) at 0x000001c0 (code=0)
I/DEBUG   (   31): timed out waiting for pid=244 tid=244 uid=10009 to die

再试一次:

adb shell ps
adb shell kill -11 299

这次 进程299被终止掉了,却没有产生 core dump文件:

D/Zygote  (   34): Process 244 terminated by signal (11)
I/ActivityManager(   78): Process com.android.calendar (pid 244) has died.

其实,我们观察到得不确定行为是因为没有了解 android 这些信号的处理。他的行为的确是确定的,要点是:

要产生core dump并终止某进程,我们需要 连续发送两次改信号,并且中间间隔在0.2秒到3秒之间。

如果间隔过小, Android可能无法接收第一个signal。如果时间过久,android将简单的终止进程,而没有 core dump产生。 我们会在后面 详细的介绍产生这种行为的原因。


好了,了解到上面的行为后,我们再次试验,连续两次发送 SIGSEGV给进程,看看行为:

adb shell ps
adb shell kill -11 307
adb shell kill -11 307

我们看到, 该进程被终止, tombstone被打到log里, 并且存储在/data/tombstone_00 中:

F/libc    (  387): Fatal signal 11 (SIGSEGV) at 0x000001cf (code=0)
I/DEBUG   (   31): *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
I/DEBUG   (   31): Build fingerprint: 'generic/sdk/generic:4.0.1/ICS_MR0/eng.drmn68.20111115.224016:eng/test-keys'
I/DEBUG   (   31): pid: 387, tid: 387  >>> com.android.mms <<<
I/DEBUG   (   31): signal 11 (SIGSEGV), code 0 (?), fault addr 000001d2
I/DEBUG   (   31):  r0 fffffffc  r1 beb2b4e0  r2 00000010  r3 ffffffff
I/DEBUG   (   31):  r4 001d0268  r5 001d027c  r6 00000000  r7 000000fc
I/DEBUG   (   31):  r8 00000000  r9 00000014  10 00012820  fp beb2b674
I/DEBUG   (   31):  ip 400c11a4  sp beb2b4b8  lr 400b9993  pc 40011384  cpsr 20000010
I/DEBUG   (   31):  d0  000000f043700000  d1  3ff0000043700000
I/DEBUG   (   31):  d2  457ff80000000fff  d3  000000003f000000
I/DEBUG   (   31):  d4  00001fff00000000  d5  3fe999999999999a
I/DEBUG   (   31):  d6  3fe8000000000000  d7  000000f000000000
I/DEBUG   (   31):  d8  0000000000000000  d9  0000000000000000
I/DEBUG   (   31):  d10 0000000000000000  d11 0000000000000000
I/DEBUG   (   31):  d12 0000000000000000  d13 0000000000000000
I/DEBUG   (   31):  d14 0000000000000000  d15 0000000000000000
I/DEBUG   (   31):  scr 60000012
I/DEBUG   (   31):
I/DEBUG   (   31):          #00  pc 0000d384  /system/lib/libc.so (epoll_wait)
I/DEBUG   (   31):          #01  pc 00026990  /system/lib/libutils.so (_ZN7android6Looper9pollInnerEi)
I/DEBUG   (   31):          #02  pc 00026bbe  /system/lib/libutils.so (_ZN7android6Looper8pollOnceEiPiS1_PPv)

...

至此,我们已经掌握 android信号的定义和行为。下面就可以研究产生这种行为的原因了。

你可能感兴趣的:(android)