傳統 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就這麼幾個,他們的值應該記住的。