博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Android 信号处理面面观 之 信号定义、行为和来源
阅读量:6650 次
发布时间:2019-06-25

本文共 7675 字,大约阅读时间需要 25 分钟。

原文:http://blog.csdn.net/rambo2188/article/details/6998349

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

所有的符合Unix规范(如POSIX)的系统都统一定义了SIGNAL的数量、含义和行为。 作为Linux系统,自然不会更改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.phoneDALVIK 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 <<

可以看出,它同样包含很多重要的信息(特别是 stack )来帮助我们查找异常的原因。分析tombstone的方法,将单独成篇。 

Android信号的产生和测试

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

1. 在kernel里 使用 kill_proc_info()

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

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

 

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

adb rootadb shell psadb 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 rootadb shell psadb 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 psadb 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 psadb shell kill -11 307adb 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 <<

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

你可能感兴趣的文章
adb命令
查看>>
HDU 2680 Choose the best route(多起点单终点最短路问题)题解
查看>>
js经典试题之原型与继承
查看>>
iPod nano将何去何从?
查看>>
南阳42--一笔画问题
查看>>
win10 1803 频繁死机,卡死不动
查看>>
zabbix(x)
查看>>
【ccf- csp201509-4】高速公路
查看>>
Restful Api 的好与坏
查看>>
Python 特殊函数(filter, map, reduce等)
查看>>
[BZOJ4198][Noi2015]荷马史诗
查看>>
杭电 1155 Bungee Jumping(物理题)
查看>>
单链表倒置算法
查看>>
北京达人2011春装秀
查看>>
[转载] 信息系统项目管理师教程——06 信息化基础知识
查看>>
数据之路 Day9 Pandas包
查看>>
构建第一个Spring Boot2.0应用之Controller(三)
查看>>
oracle物化视图介绍
查看>>
在Eclipse中使用建立使用Gradle做依赖管理的Spring Boot工程
查看>>
js页面跳转常用的几种方式(转)
查看>>