第二步:通过命令 pidof java 找到已经启动的java进程的ID,选择需要查看的java程序的进程ID
第三步:使用命令 kill -3 <java进行的 pid>打印出java程序的线程堆栈信息
第四步:通常情况下运行的项目可能会比较大,那么这个时候打印的堆栈信息可能会有几千到几万行,为了方便查看,我们往往需要将输出内容进行重定向
使用linux下的重定向命令方式即可:例如: demo.sh >run.log 2>&1 将输出信息重定向到 run.log中。
注:在 *** 作系统中,0 1 2分别对应着不同的含义, 如下:
0 : 标准输入,即:C中的stdin , java中的System.in
1 : 标准输出, 即:C中的stdout ,java中的System.out
2 : 错误输出, 即:C中的stderr , java中的System.err
Demo:
----------------------------------------------------------------------------------------------
Sources Code :
public class PrintThreadTrace {
Object obj1 = new Object()
Object obj2 = new Object()
public void func1(){
synchronized (obj1){
func2()
}
}
public void func2(){
synchronized (obj2){
while(true){
System.out.print("")
}
}
}
public static void main(String[] args){
PrintThreadTrace ptt = new PrintThreadTrace()
ptt.func1()
}
}
----------------------------------------------------------------------------------------------------------------
按照步骤 *** 作后的打印输出信息:
Full thread dump Java HotSpot(TM) 64-Bit Server VM (24.79-b02 mixed mode):
"Service Thread" daemon prio=10 tid=0x00007fdc880a9000 nid=0x12a4 runnable [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"C2 CompilerThread1" daemon prio=10 tid=0x00007fdc880a7000 nid=0x12a3 waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"C2 CompilerThread0" daemon prio=10 tid=0x00007fdc880a4000 nid=0x12a2 waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"JDWP Command Reader" daemon prio=10 tid=0x00007fdc50001000 nid=0x1299 runnable [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"JDWP Event Helper Thread" daemon prio=10 tid=0x00007fdc880a1800 nid=0x1298 runnable [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"JDWP Transport Listener: dt_socket" daemon prio=10 tid=0x00007fdc8809e000 nid=0x1297 runnable [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"Signal Dispatcher" daemon prio=10 tid=0x00007fdc88091000 nid=0x1296 waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"Finalizer" daemon prio=10 tid=0x00007fdc88071800 nid=0x1295 in Object.wait() [0x00007fdc77ffe000]
java.lang.Thread.State: WAITING (on object monitor)
at java.lang.Object.wait(Native Method)
- waiting on <0x00000000ecb04858>(a java.lang.ref.ReferenceQueue$Lock)
at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:135)
- locked <0x00000000ecb04858>(a java.lang.ref.ReferenceQueue$Lock)
at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:151)
at java.lang.ref.Finalizer$FinalizerThread.run(Finalizer.java:209)
"Reference Handler" daemon prio=10 tid=0x00007fdc8806f800 nid=0x1294 in Object.wait() [0x00007fdc7c10b000]
java.lang.Thread.State: WAITING (on object monitor)
at java.lang.Object.wait(Native Method)
- waiting on <0x00000000ecb04470>(a java.lang.ref.Reference$Lock)
at java.lang.Object.wait(Object.java:503)
at java.lang.ref.Reference$ReferenceHandler.run(Reference.java:133)
- locked <0x00000000ecb04470>(a java.lang.ref.Reference$Lock)
"main" prio=10 tid=0x00007fdc8800b800 nid=0x128e runnable [0x00007fdc8fef7000]
java.lang.Thread.State: RUNNABLE
at com.wenchain.study.PrintThreadTrace.func2(PrintThreadTrace.java:20)
- locked <0x00000000ecc04b20>(a java.lang.Object)
at com.wenchain.study.PrintThreadTrace.func1(PrintThreadTrace.java:13)
- locked <0x00000000ecc04b10>(a java.lang.Object)
at com.wenchain.study.PrintThreadTrace.main(PrintThreadTrace.java:27)
"VM Thread" prio=10 tid=0x00007fdc8806b000 nid=0x1293 runnable
"GC task thread#0 (ParallelGC)" prio=10 tid=0x00007fdc88021000 nid=0x128f runnable
"GC task thread#1 (ParallelGC)" prio=10 tid=0x00007fdc88023000 nid=0x1290 runnable
"GC task thread#2 (ParallelGC)" prio=10 tid=0x00007fdc88024800 nid=0x1291 runnable
"GC task thread#3 (ParallelGC)" prio=10 tid=0x00007fdc88026800 nid=0x1292 runnable
"VM Periodic Task Thread" prio=10 tid=0x00007fdc880b3800 nid=0x12a5 waiting on condition
JNI global references: 1391
Heap
PSYoungGen total 17920K, used 1270K [0x00000000ecb00000, 0x00000000ede80000, 0x0000000100000000)
eden space 15872K, 8% used [0x00000000ecb00000,0x00000000ecc3d898,0x00000000eda80000)
from space 2048K, 0% used [0x00000000edc80000,0x00000000edc80000,0x00000000ede80000)
to space 2048K, 0% used [0x00000000eda80000,0x00000000eda80000,0x00000000edc80000)
ParOldGen total 39424K, used 0K [0x00000000c6200000, 0x00000000c8880000, 0x00000000ecb00000)
object space 39424K, 0% used [0x00000000c6200000,0x00000000c6200000,0x00000000c8880000)
PSPermGen total 21504K, used 2619K [0x00000000c1000000, 0x00000000c2500000, 0x00000000c6200000)
object space 21504K, 12% used [0x00000000c1000000,0x00000000c128edd8,0x00000000c2500000)
----------------------------------------------------------------------------------------------------------------------------
上面的信息中包含了当前JVM中所有运行的线程信息,其中在示例中我们启动的线程为main线程,其余的都是JVM自己创建的。
在打印的信息中,我们可以清楚的看见当前线程的调用上下文,可以很清楚的知道程序的运行情况。
并且我们在最后面还能看见当前虚拟机中的内存使用情况,青年世代,老年世代的信息等等...
PS: 在JDK1.5以上,我们可以通过在Java程序中调用Thread.getStackTrace()方法来进行堆栈的自动打印,使得线程堆栈的打印时机可编程控制。
文章知识点与官方知识档案匹配
Java技能树首页概览
89841 人正在系统学习中
点击阅读全文
打开CSDN,阅读体验更佳
jstack-查看Java进程的线程堆栈信息,锁定高消耗资源代码
jstack主要用来查看某个Java进程内的线程堆栈信息。语法格式如下: jstack[option]pid jstack[option]executable core jstack[option][server-id@]remote-hostname-or-ip 命令行参数选项说明如下: ...
011Java并发包018查看线程堆栈信息_执笔未来的博客
java.util.concurrent.ScheduledThreadPoolExecutor$DelayedWorkQueue.take(ScheduledThreadPoolExecutor.java:1088) java.util.concurrent.ScheduledThreadPoolExecutor$DelayedWorkQueue.take(ScheduledThreadPoolExecutor.java:809) java.util.concurre...
最新发布 jstack -- java堆栈常用排查指令
jstack -- java堆栈常用排查指令
继续访问
热门推荐 jstack 命令查看JAVA线程堆栈
JAVA堆栈信息实际生产中,可能由于开发以及测试未能全面覆盖的代码质量、性能问题,而引致线程挂起甚至崩溃。可能就需要查看堆栈信息来排查问题了。jps -lvmjps -lvm 用于查看当前机器上运行的java进程。C:\Users\Administrator>jps -lvm 7348 -Dosgi.requiredJavaVersion=1.8 -Dosgi.instance.area.defa
继续访问
Java多线程——查看线程堆栈信息
Java多线程——查看线程堆栈信息 摘要:本文主要介绍了查看线程堆栈信息的方法。 使用Thread类的getAllStackTraces()方法 方法定义 可以看到getAllStackTraces()方法的返回值是一个Map对象,key是Thread的实例,value是一个StackTraceElement实例数组: 1 public static Map<Thread, S...
继续访问
java堆栈常用排查指令
java 异常排查四板斧 1、查看java 堆栈线程信息 说明 jstack命令打印指定Java进程、核心文件或远程调试服务器的Java线程的Java堆栈跟踪信息。 对于每个Java框架,完整的类名,方法名, 字节码索引(BCI)和行号(如果有的话)被打印出来。 使用-m选项,jstack命令打印程序中所有线程的Java和本机帧 计数器(PC)。 对于每个本机帧,当可用时,将打印离PC最近的本机符号。 c++乱码的名字不会被修改。 要demangle c++名称,输出这个 命令可以管道到c++filt。 当
继续访问
java诊断工具-Arthas(thread命令)查看当前线程堆栈
cpu使用率与linux 命令top -H -p <pid>的线程CPU类似 1、支持一键展示当前最忙的前N个线程并打印堆栈 thread -n 3 没有线程ID,包含[Internal]表示为JVM内部线程,参考dashboard命令的介绍。 cpuUsage为采样间隔时间内线程的CPU使用率,与dashboard命令的数据一致。 deltaTime为采样间隔时间内线程的增量CPU时间,小于1ms时被取整显示为0ms。 time线程运行总CPU...
继续访问
java查看线程的堆栈信息
通过使用jps 命令获取需要监控的进程的pid,然后使用jstackpid 命令查看线程的堆栈信息。 通过jstack命令可以获取当前进程的所有线程信息。 每个线程堆中信息中,都可以查看到线程ID、线程的状态(wait、sleep、running 等状态)、是否持有锁信息等。 jstack -l <pid>>jvm_listlocks.txt 转...
继续访问
java 查看线程堆栈信息_Java多线程——查看线程堆栈信息
java多线程——查看线程堆栈信息摘要:本文主要介绍了查看线程堆栈信息的方法。使用thread类的getallstacktraces()方法方法定义可以看到getallstacktraces()方法的返回值是一个map对象,key是thread的实例,value是一个stacktraceelement实例数组:1 public static map getallstacktraces()使用可以使...
继续访问
java线程堆栈信息分析
java堆栈信息分析
继续访问
java 查看堆栈_javap 命令查看堆栈中信息
javap命令是对.java文件进行反编译,通过这个命令可以看到堆栈中是怎么压栈和出栈的已经执行顺序,这里简单解释下javap的简单的使用,下面举个例子:题目:i++ 和++i的区别解释:简单点说 这个问题都不难回答,这里就不说了,但是实际上堆栈中区别也是老大了(这里就用到了javap命令), 步骤:1.在任意一个盘下面建一个名为Test.java的文件(文件名可以随意命名)代码如下:public...
继续访问
java 查看线程堆栈信息_jstack-查看Java进程的线程堆栈信息,锁定高消耗资源代码。...
jstack主要用来查看某个Java进程内的线程堆栈信息。语法格式如下:jstack[option]pidjstack[option]executablecorejstack[option][server-id@]remote-hostname-or-ip命令行参数选项说明如下:-llonglistings,会打印出额外的锁信息,在发生死锁时可以用jstack-lpid来观察...
继续访问
java堆栈信息怎么看_线程堆栈信息怎么看? - cs_person的个人空间 - OSCHINA - 中文开源技术交流社区...
一条线程堆栈信息大概长成下面这个样子:RMI TCP Connection(267865)-172.16.5.25" daemon prio=10 tid=0x00007fd508371000 nid=0x55ae waiting for monitor entry [0x00007fd4f8684000]java.lang.Thread.State: BLOCKED (on object m...
继续访问
线程堆栈信息怎么看?
一条线程堆栈信息大概长成下面这个样子: RMI TCP Connection(267865)-172.16.5.25" daemon prio=10 tid=0x00007fd508371000 nid=0x55ae waiting for monitor entry [0x00007fd...
继续访问
java的栈和堆
栈与堆都是Java用来在Ram中存放数据的地方。与C++不同,Java自动管理栈和堆,程序员不能直接地设置栈或堆。 Java 的堆是一个运行时数据区,类的(对象从中分配空间。这些对象通过new、newarray、anewarray和multianewarray等指令建立,它们不需要程序代码来显式的释放。堆是由垃圾回收来负责的,堆的优势是可以动态地分配内存大小,生存期也不必事先告诉编译器,因为它是在...
继续访问
查看java线程_【JAVA】Java线程堆栈信息查看
如何获得线程的堆栈信息?线上服务器cpu 100%了,该如何排查问题?1.top命令查询哪个pid进程占用cpu高(ps -ef|grep java 获取PID号)2.通过 top -Hp pid 可以查看该进程下各个线程的cpu使用情况,获取占用cpu高的线程id3.执行命令:printf "%X\n" 线程tid(用于获取占用cpu高的线程id的16进制数)4.执行命令:jstack pid ...
继续访问
kill -3 java_kill -3 PID命令获取java应用堆栈信息
一、应用场景:当linux服务器出现异常情况(响应缓慢,负载持续飙升)并且服务器没有安装对应的包而无法使用jstack等命令时,可以使用linux的kill相关命令打印堆栈信息。命令格式:kill -3 PID二、执行步骤:2.1、获取java进程的PIDps -ef|grep java结果的第二列数字就是进程对应的pid。2.2、kill -3 PID(1)如果项目通过Tomcat进行发布(普通...
继续访问
jstack 工具 查看JVM堆栈信息
1|0介绍 jstack是java虚拟机自带的一种堆栈跟踪工具。jstack用于打印出给定的java进程ID或corefile或远程调试服务的Java堆栈信息,如果是在64位机器上,需要指定选项"-J-d64",Windows的jstack使用方式只支持以下的这种方式: jstack [-l] pid 主要分为两个功能: a. 针对活着的进程做本地的或远程的线程dump; b. 针对core文件做线程dump。 jstack用于生成java虚拟机当前时刻的线程快照。线程快照是...
继续访问
linux查看java堆栈
1、查看JAVA进程JVM参数 jinfo -flags pid(进程号) -XX:CICompilerCount=2 最大的并行编译数 -XX:InitialHeapSize=16777216 JVM 的初始堆内存大小 -XX:MaxHeapSize=257949696 JVM 的最大堆内存大小 -XX:MaxNewSize=85983232 -XX:MinHeapDeltaBytes=196608 -XX:NewSize=5570560 -XX:OldSize=11206656 2、JVM 查看.
继续访问
Linux 如何查看一个进程的堆栈
有两种方法:第一种:pstack 进程ID第二种,使用gdb 然后attach 进程ID,然后再使用命令 thread apply all bt 两种方法都可以列出进程所有的线程的当前的调用栈。不过,使用gdb的方法,还可以查看某些信息,例如局部变量,指针等。不过,如果只看调用栈的话,pstack还是很方便的。
继续访问
JAVA获取堆栈信息
1. 通过Throwable获取 StackTraceElement[] stackTrace = new Throwable().getStackTrace()2. 通过Thread获取 StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace()
继续访问
java 查看线程栈大小_基于 Java 线程栈的问题排查
除日志外,还有没有别的方式跟踪线上服务问题呢?或者,跟踪并排除日志里无法发现的问题?方法当然是有的,就是通过现场快照定位并发现问题。我们所说的现场,主要指这两方面:Java 线程栈。线程栈是Java线程工作的快照,可以获得当前线程在做什么;Java 内存堆。堆是JVM的内存快照,可以获取内存分配相关信息。
在Linux的多目录命令提示符中工作是一种痛苦的事情,但以下这些利用cd和pushd切换目录的技巧有助于你节省时间和精力 。在Linux命令提示中,用cd命令来改变当前目录。这是cd命令的一些基本用法:
改变你的根路径,键入cd,按回车键。
进入一个子目录,键入cd,空格,然后是子路径名(例如:cd Documents),再按回车键。
进入当前目录的上一级目录,键入cd,空格,两个点,然后按回车键。
进入一个特定的目录,键入cd,空格,路径名(例如 cd /usr/local/lib),再按回车键。
为了确定你所在的目录,你可以键入pwd,按回车键,你将看到你所在的当前目录名称。
用pushd实现在不同目录间切换。
在命令行模式下,当你工作在不同目录中,你将发现你有很多时间都浪费在重复输入上。如果这些目录不在同一个根目录中,你不得不在转换时输入完整的路径名,这难免让人有些难以忍受。但你可以用以下的一个或两个步骤来避免所有多余的输入:用命令行解释器中的历史记录,或者用命令行函数pushd。
用命令行解释器中的历史记录的好处是只需按很少的键。在命令行中用向上的箭头来查找你用过的命令,直到你找到,然后按回车键。如果你所切换的两个目录在整个驱动器的子目录结构中很接近,那用解释器中的历史记录可能是你最好的选择。
然而,如果你在两个截然不同的路径间转换的话,你可能很希望利用pushd这个函数,你可以用它创建一个目录堆栈(在内存中的一个列表)。
注释:缺省情况下,pushd函数可能不包括在你的Linux中;但它包涵在Red Hat和用Red Hat驱动的系统中。如果你的系统中没有pushd函数,你可以在ibiblio.org网站上下载相关的函数工具。
这里说一下怎么用pushd。假设你现在工作在/usr/share/fonts目录下。你需要对/usr/share/fonts做一些改动,你将频繁的在两个目录间切换。开始在一个目录下,用pushd函数切换到另一个目录。在我们的例子中,开始在/usr/share/fonts下,你键入pushd/opt/wonderword/fonts,然后按回车键。现在,你将在下一行看到堆栈中的内容:/opt/wonderword/fonts /usr/share/fonts。
正如你所看到的,当你键入pushd和一个路径名时,将自动产生一个堆栈,内容是你键入的目录名和你当前工作的目录名。在我们的例子中,你所键入的路径(/opt/wonderword/fonts)在堆栈的顶部。
快速返回上一级目录,你可以直接键入pushd,如果不跟路径名,你将返回到堆栈中前一个目录的上一层目录。
如果你需要从堆栈中删除一个目录,键入popd,然后是目录名称,再按回车键。想查看堆栈中目录列表,键入dirs,然后按回车键。popd和dirs命令也是常用函数中的一部分。
1. Linux Kernel Panic的产生的原因panic是英文中是惊慌的意思,Linux Kernel panic正如其名,linux kernel不知道如何走了,它会尽可能把它此时能获取的全部信息都打印出来。
有两种主要类型kernel panic,后面会对这两类panic做详细说明:
1.hard panic(也就是Aieee信息输出)
2.soft panic (也就是Oops信息输出)
2. 常见Linux Kernel Panic报错内容:
(1) Kernel panic-not syncing fatal exception in interrupt
(2) kernel panic – not syncing: Attempted to kill the idle task!
(3) kernel panic – not syncing: killing interrupt handler!
(4) Kernel Panic – not syncing:Attempted to kill init !
3. 什么会导致Linux Kernel Panic?
只有加载到内核空间的驱动模块才能直接导致kernel panic,你可以在系统正常的情况下,使用lsmod查看当前系统加载了哪些模块。
除此之外,内建在内核里的组件(比如memory map等)也能导致panic。
因为hard panic和soft panic本质上不同,因此我们分别讨论。
4. hard panic
一般出现下面的情况,就认为是发生了kernel panic:
机器彻底被锁定,不能使用
数字键(Num Lock),大写锁定键(Caps Lock),滚动锁定键(Scroll Lock)不停闪烁。
如果在终端下,应该可以看到内核dump出来的信息(包括一段”Aieee”信息或者”Oops”信息)
和Windows蓝屏相似
4.1 原因
对于hard panic而言,最大的可能性是驱动模块的中断处理(interrupt handler)导致的,一般是因为驱动模块在中断处理程序中访问一个空指针(null pointre)。一旦发生这种情况,驱动模块就无法处理新的中断请求,最终导致系统崩溃。
本人就曾遇到过这样一个例子:在多核系统中,包括AP应用处理器、mcu微控制器和modem处理器等系统中,mcu控制器用于系统的低功耗控制,mcu微控制器由于某种原因超时向AP应用处理器发送一个超时中断,AP接受中断后调用中断处理函数读取mcu的状态寄存器,发现是mcu的超时中断,就在中断处理程序中主动引用一个空指针,迫使AP处理器打印堆栈信息然后重启linux系统。这就是一个典型的hard panic,这里不对mcu超时原因做深入的分析,只是用来说明hard panic产生的机理。
3.2 信息收集
根据panic的状态不同,内核将记录所有在系统锁定之前的信息。因为kenrel panic是一种很严重的错误,不能确定系统能记录多少信息,下面是一些需要收集的关键信息,他们非常重要,因此尽可能收集全,当然如果系统启动的时候就kernel panic,那就无法只知道能收集到多少有用的信息了。
/var/log/messages: 幸运的时候,整个kernel panic栈跟踪信息都能记录在这里,当然对于嵌入式linux系统,kernel panic的内核打印信息被放到/data/dontpanic目录下,包括两个文件:apanic_console存放的是内核控制台的log,apanic_threads存放的是linux kernel发生panic时的所有内核线程的堆栈信息。
应用程序/库 日志: 可能可以从这些日志信息里能看到发生panic之前发生了什么。
其他发生panic之前的信息,或者知道如何重现panic那一刻的状态
终端屏幕dump信息,一般OS被锁定后,复制,粘贴肯定是没戏了,因此这类信息,你可以需要借助数码相机或者原始的纸笔工具了。
如果kernel dump信息既没有在/var/log/message里,也没有在屏幕上,那么尝试下面的方法来获取(当然是在还没有死机的情况下):
如果在图形界面,切换到终端界面,dump信息是不会出现在图形界面的,甚至都不会在图形模式下的虚拟终端里。
确保屏幕不黑屏,可以使用下面的几个方法:
setterm -blank 0
setterm -powerdown 0
setvesablank off
从终端,拷贝屏幕信息(方法见上)
实际上,当内核发生panic时,linux系统会默认立即重启系统,当然这只是默认情况,除非你修改了产生panic时重启定时时间,这个值默认情况下是0,即立刻重启系统。所以当panic时没有把kernel信息导入文件的话,那么可能你很难再找到panic产生的地方。
3.3 完整栈跟踪信息的排查方法
栈跟踪信息(stack trace)是排查kernel panic最重要的信息,该信息如果在/var/log/messages日志里当然最好,因为可以看到全部的信息,如果仅仅只是在屏幕上,那么最上面的信息可能因为滚屏消失了,只剩下栈跟踪信息的一部分。如果你有一个完整栈跟踪信息的话,那么就可能根据这些充分的信息来定位panic的根本原因。要确认是否有一个足够的栈跟踪信息,你只要查找包含”EIP”的一行,它显示了是什么函数和模块调用时导致panic。
使用内核调试工具(kenrel debugger ,aka KDB)
如果跟踪信息只有一部分且不足以用来定位问题的根本原因时,kernel debugger(KDB)就需要请出来了。
KDB编译到内核里,panic发生时,他将内核引导到一个shell环境而不是锁定。这样,我们就可以收集一些与panic相关的信息了,这对我们定位问题的根本原因有很大的帮助。
使用KDB需要注意,内核必须是基本核心版本,比如是2.4.18,而不是2.4.18-5这样子的,因为KDB仅对基本核心有效。
4. soft panic
4.1 症状:
没有hard panic严重
通常导致段错误(segmentation fault)
可以看到一个oops信息,/var/log/messages里可以搜索到’Oops’
机器稍微还能用(但是收集信息后,应该重启系统)
4.2 原因
凡是非中断处理引发的模块崩溃都将导致soft panic。在这种情况下,驱动本身会崩溃,但是还不至于让系统出现致命性失败,因为它没有锁定中断处理例程。导致hard panic的原因同样对soft panic也有用(比如在运行时访问一个空指针)
4.3 信息收集
当soft panic发生时,内核将产生一个包含内核符号(kernel symbols)信息的dump数据,这个将记录在/var/log/messages里。为了开始排查故障,可以使用ksymoops工具来把内核符号信息转成有意义的数据。
为了生成ksymoops文件,需要:
从/var/log/messages里找到的堆栈跟踪文本信息保存为一个新文件。确保删除了时间戳(timestamp),否则ksymoops会失败。
运行ksymoops程序(如果没有,请安装)
详细的ksymoops执行用法,可以参考ksymoops(8)手册。
5. Kernel panic实例:
今天就遇到 一个客户机器内核报错:“Kernel panic-not syncing fatal exception”,重启后正常,几个小时后出现同样报错,系统down了,有时重启后可恢复有时重启后仍然报同样的错误。
什么是fatal exception?
“致命异常(fatal exception)表示一种例外情况,这种情况要求导致其发生的程序关闭。通常,异常(exception)可能是任何意想不到的情况(它不仅仅包括程序错误)。致命异常简单地说就是异常不能被妥善处理以至于程序不能继续运行。
软件应用程序通过几个不同的代码层与 *** 作系统及其他应用程序相联系。当异常(exception)在某个代码层发生时,为了查找所有异常处理的代码,各个代码层都会将这个异常发送给下一层,这样就能够处理这种异常。如果在所有层都没有这种异常处理的代码,致命异常(fatal exception)错误信息就会由 *** 作系统显示出来。这个信息可能还包含一些关于该致命异常错误发生位置的秘密信息(比如在程序存储范围中的十六进制的位置)。这些额外的信息对用户而言没有什么价值,但是可以帮助技术支持人员或开发人员调试程序。
当致命异常(fatal exception)发生时, *** 作系统没有其他的求助方式只能关闭应用程序,并且在有些情况下是关闭 *** 作系统本身。当使用一种特殊的应用程序时,如果反复出现致命异常错误的话,应将这个问题报告给软件供应商。 ” 而且此时键盘无任何反应,必然使用reset键硬重启。
panic.c源文件有个方法,当panic挂起后,指定超时时间,可以重新启动机器,这就是前面说的panic超时重启。如果你的机器事先配置好了魔法键的使用,就可以在超时之前通过魔法键使系统在重启前尽可能多的为你多做些事情,当然这些事情不是用来使系统恢复正常,而是尽量避免损失或导出一些有用信息来帮助后面的定位。
方法:
#vi /etc/sysctl.conf 添加
kernel.panic = 20 #panic error中自动重启,等待timeout为20秒
kernel.sysrq=1 #激活Magic SysRq 否则,键盘鼠标没有响应
按住 [ALT]+[SysRq]+[COMMAND], 这里SysRq是Print SCR键,而COMMAND按以下来解释!
b – 立即重启
e – 发送SIGTERM给init之外的系统进程
o – 关机
s – sync同步所有的文件系统
u – 试图重新挂载文件系统
配置一下以防万一。
很多网友安装linux出现“Kernel panic-not syncing fatal exception in interrupt”是由于网卡驱动原因。解决方法:将选项“Onboard Lan”的选项“Disabled”,重启从光驱启动即可。等安装完系统之后,再进入BIOS将“Onboard Lan”的选项给“enable”,下载相应的网卡驱动安装。
如出现以下报错:
init() r8168 …
… …
… :Kernel panic: Fatal exception
r8168是网卡型号。
在BIOS中禁用网卡,从光驱启动安装系统。再从网上下载网卡驱动安装。
#tar vjxf r8168-8.014.00.tar.bz2
# make clean modules (as root or with sudo)
# make install
# depmod -a
# modprobe r8168
安装好系统后reboot进入BIOS把网卡打开。
另有网友在Kernel panic出错信息中看到“alc880”,这是个声卡类型。尝试着将声卡关闭,重启系统,搞定。
安装linux系统遇到安装完成之后,无法启动系统出现Kernel panic-not syncing fatal exception。很多情况是由于板载声卡、网卡、或是cpu 超线程功能(Hyper-Threading )引起的。这类问题的解决办法就是先查看错误代码中的信息,找到错误所指向的硬件,将其禁用。系统启动后,安装好相应的驱动,再启用该硬件即可。
另外出现“Kernel Panic — not syncing: attempted to kill init”和“Kernel Panic — not syncing: attempted to kill idle task”有时把内存互相换下位置或重新插拔下可以解决问题。
6. 一个kernel panic的解决之法
相信使用linux kernel开发过驱动的兄弟都知道,kernel panic对系统带来的危害要比应用程序panic大的多,甚至可以用灾难来形容。对于应用程序的panic最多导致linux系统杀掉该用户进程,但对于kernel panic就没办法了,因为kernel是整个系统的管理者,自己出现问题了(当然是不可恢复的异常)就只能等待重启了。
kernel panic的最大问题就是难于定位,对于一个开发者来说,有些kernel panic那简直就像是一场噩梦,上面主要说明了如何抓取kernel panic的方法和一些panic实例,当然,抓取panic的打印信息是解决panic的第一步也是关键一步,下面就根据自己曾碰到过的一个kernel panic做为实例来说明从出现panic到解决panic的一般方法。
6.1 抓取kernel panic信息
没错,正如前面说的,这是第一步也是非常关键的一步,如果要解决一个kernel panic当然必须首先要知道它产生的地方,也就是说产生panic的内核函数调用栈,当前的内核调用栈记录了产生kernel panic时的函数调用关系链,这里我不在贴出相关的打印实例,这样的kernel panic网上也到处都是,而且还有很多的文章来说明如何确定是哪个源文件的哪一行导致的panic,因此感兴趣的同学可以搜索一些这样的文章看看,这里指说明一下解决kernel panic的一般步骤和注意事项。
对于抓取kernel log的方法前面有介绍,这里不赘述,但想强调两点:
(1) 不管是什么样的panic,首先要抓取足够的内核打印信息,当然必要的情况下还需要搜集产生kernel panic时的应用程序的打印信息,对于Android系统来说就是logcat信息,在android嵌入式软件平台上其实有更好更全面的log搜集方法,那就是bugreport,它将产生此刻系统全方位的信息,对,没错,就是全方位的信息,包括内核、应用、内存、进程和处理器等所有相关信息,是一个非常好的调试工具,至于bugreport的工作原理感兴趣的同学自己查找下资料。
注意:bugreport的使用需注意两点:第一,它只能在系统正常运行的情况下使用,第二,正因为第一点,你需要在系统产生kernel panic重启系统后的第一时间使用bugreport导出所有信息,因为这所有信息中包含了上次系统重启的原因的相关log信息。
(2) 既然是抓取panic log信息,必然少不了复现panic这个过程,有的panic的产生时概率性随机的,就是说你不知道什么时候就可能会产生panic,因此请珍惜每一次复现panic的机会,起码要在复现panic之前准备好你要抓取的是那些信息,这些信息能否帮助你进一步定位panic,否则,不要在出现panic时手忙脚乱,不知道自己要什么,最好每次复现panic前计划好这次你要那些信息(可能每次抓取信息的重点不一样)。
注意:在工作中经常碰到这样一个现象:测试部门的同学好不容易发现一个问题,请开发同学定位,开发同学基本上没怎么分析问题就嚷着信息抓的不够没法定位,结果让测试同学半天甚至一天来复现这个问题,等复现了问题开发同学还没搞清楚自己到底要什么信息来定位,有的问题复现时的环境只能保持几分钟甚至几十秒钟,这势必会浪费了测试同学的劳动成果。
6.2 分析kernel panic
搜集了足够的panic信息,下面就是分析panic的时候了,对于一个panic问题,你要知道三点:
(1) 首先要对汇编语言有一定的了解,定位panic产生的C代码位置
其实就是根据当前内核线程的内核调用栈查找产生panic调用链,在panic log的前面几行已经显示了kernel panic的代码位置,但这个位置是相对于产生panic函数的偏移,你并不知道它到底是哪一行,这个时候你需要objdump反汇编器来对那个产生panic的镜像文件反汇编,然后根据panic信息的指示找到对应的汇编代码,对照C代码根据汇编上下文确定C代码行,其实,kernel panic的产生一般都是非法地址的引用,尤其是NULL指针的引用,这也比较容易定位出panic的C代码行。
(2) 分析导致panic的C代码行上下文,确定panic引入点
第一步应该会比较容易找到导致panic 的C代码行,根据产生panic的代码进一步找到panic的引入点,这一步可以搭配printk来定位(如果是大概率panic就更容易定位了),这一步相对第一步花费多一点的时间,如果是应用代码分析到这里已经差不多结束了,确定了panic引入点就可以修改代码进行回归测试了,但对于kernel来说要复杂的多。
正如之前曾碰到的panic,复现虽然不容易但是基本上在固定时间点左右就可以复现,我是用的脚本循环加载卸载wifi模块,每次都是大约500次左右产生panic,要知道必现的panic就容易解决的多了,但当时因为这500次的循环就要花费2个小时左右,而且环境还经常出现问题,导致我花费很长时间才定位出问题所在:每次的加载和卸载wifi模块都导致devices kset节点引用计数多减一,当devices kset的引用计数变为0的时候被系统回收,linux系统随后可能会出现N种panic现象,之后发现是因为wifi模块每次加载下载时对应的设备节点的引用计数增减失衡导致devices kset被多减一,然后发现是linux 内核核心代码的问题。
(3) 最好不要怀疑linux的核心代码,也不要试图去修改
正因为这一点,让我迟迟不敢确定是不是真的核心代码问题,linux的核心代码那可是数以万计的大牛经过千锤百炼的代码,岂容你轻易修改,经过进一步的分析这个panic是因为我们用的wifi卡是非标准媒体卡,走的是非标准流程,在这个流程中对wifi设备初始化时少了一次wifi设备节点的引用,但在卸载模块时同标准卡一样被解引用了。
(4)不要坚定的以为围绕着panic信息就能解决panic问题
还是上面的panic,实际上,上面提到的panic问题其实应该是很多的panic,这也是在后期复现panic时发现的,在加载卸载500次左右时必现panic,但却不是同一个panic,如果按照正常思路:既然是panic,就应该从panic信息下手,顺藤摸瓜一直追下去。如果是这样,这个问题恐怕永远也解决不了,因为你在复现一个panic时总是会有其他的panic出现,这回让你无所适从的。
通过对这些panic的log的分析,发现他们都有一个共性,在产生panic之前都有一段WARNING打印,也正是对这段打印的分析找出了问题的根源,对于这段WARNING的内容和分析过程不在这里说明,只为表达下面的观点:
事实证明,在碰到panic问题定位时,如果想当一段时间内定位不出来,又没有什么更好的思路时,你应该回头看看在panic之前kernel是否产生了哪些不太正常的log,这也许就是导致kernel panic的前兆或推手。
(5) 尽可能多的把握linux kernel的行为,对一些难啃的panic大胆猜测
这里的大胆猜测是建立在想当了解linux kernel行为上的有理性的推理,尽管有些猜测并不是完全正确的,但在你证明它是正确的过程中或许会有意外收获。对于wifi模块加载卸载的panic问题来说,我曾有过两次错误的猜测:
第一次,因为长时间的加载卸载都会出现panic,而且开始发现的panic是在kmem_cache_alloc函数中,因此猜测是内存泄露导致的内存耗尽,因此在后面的复现过程中我写了个脚本循环打印内存的使用情况,发现内存的占用一直稳定在一个正常的范围,证明了我的第一个猜测是错误的。
第二次,在看到devices 的内核对象kobject的名字在panic之前出现乱码的情况下(printk打印了名字),我曾大胆地做过猜测:linux kernel发生了踩内存现象,导致devices kset对象被破坏。随后做了各方面的努力来证明我的想法,结果发现几乎都是在第493次加载wifi模块时出现的panic问题,这让我很迷惑,如果是踩内存怎么可能固定在493次发生,虽然不能完全证明这个猜想是错误的,但这足以说明我的方向有问题。
如此反反复复,花费了我两个星期的时间才搞定这个panic,因此,kernel panic虽然难啃,但只要你愿意去尝试愿意去努力,就算最后拿不下这个panic,你也会学到很多很多的东西,包括linux kernel行为,这些会对你以后的学习产生很大的影响,在碰到这类问题一定是信心满满的。
7. 小结
一直想总结一点kernel panic的解决之法,在网上也搜索了很多资料,基本上都一样,本文前面也引用了这些文章中的一篇,曾经做过的总结过的东西能记录下来给别人看和给以后自己复习都是很有意义的事情,以前kernel panic的问题总让我不敢靠的太近,现在我还是可以比较自信的面对他们,这里也只是给同学们一些解决panic的建议,个人觉得分析一个具体的实例的意义也不是太大,所以也没有对一个具体的实例做详细分析,希望可以找到更多的相关文章来拜读,夜已深沉,还有什么人...
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)