传统 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)中:
-
- #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文件内容如下:
可以看出,它同样包含很多重要的信息(特别是 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就这么几个,他们的值应该记住的。