从用户态程序开发的角度,我们并不需要理解操作系统复杂的内存管理机制,这是和硬件平台相关的。但是了解内核发送SIGSEGV信号的流程,对我们理解SIGSEGV是很有帮助的。 红色部分展示了内核发送SIGSEGV 信号给用户态程序的总体流程。当用户态 程序访问一个会引发SIGSEGV 的地址时,硬件首先产生一个page fault,即“缺页异常”。 在内核的page fault 处理函数中,首先判断该地址是否属于用户态程序的地址空间。32位系统中虚拟存储空间占4GB空间。linux内核将这4G字节的空间分为两部分。用户态程序的地址空间为[0x00000000,0xBFFFFFFF],内核地址空间为[0xC0000000,0xFFFFFFFF]。如果该地址属于用户态地址空间,检查访问的类型是否和该内存区域的类型是否匹配,不匹配,则发送SIGSEGV 信号;如果该地址不属于用户态地址空间,检查访问该地址的操作是否发生在用户态,如果是,发送SIGSEGV 信号。 当程序不正常退出时,内核会在当前目录产生core文件(一个内存的映像,同时加上调试信息),然后利用gdb查看core文件,可以指示出导致程序出错位置的文件和行数。
确定系统是否配置支持dump core的功能。通过ulimit -c或ulimit -a,如果为0,则不会产生对应的coredump,需要进行修改和设置。使用 ulimit –c [size]这里的size的单位是blocks,一般1block=512bytes在不知 size的大小的时候,可以ulimit -c unlimited。
1.用gcc(或者交叉编译工具)进行编译时,选择-g选项。 2.不能进行strip操作,否则你将看不见程序的函数名、变量名,代替这些将是运行时的内存地址。
core文件默认的存储位置与对应的可执行程序在同一目录下,文件名是core,大家可以通过下面的命令看到core文件的存储位置和格式:cat /PRoc/sys/kernel/core_pattern。 其中core_pattern的格式见下面说明: %% 单个%字符 %p 所dump进程的进程ID %u 所dump进程的实际用户ID %g 所dump进程的实际组ID %s 导致本次core dump的信号 %t core dump的时间 (由1970年1月1日计起的秒数) %h 主机名 %e 程序文件名 其中/proc/sys/kernel/core_uses_pid。如果这个文件的内容被配置成1,即使core_pattern中没有设置%p,最后生成的core dump文件名仍会加上进程ID。以下是参数设置例子:
echo "1" > /proc/sys/kernel/core_uses_pid sysctl -w kernel.core_uses_pid=1; echo "/persistent/core-%e-%p-%t" > core_pattern; sysctl -w kernel.core_pattern=/persistent/core-%e-%p-%t注意:这里是指在进程当前工作目录的下创建。通常与程序在相同的路径下。但如果程序中调用了chdir函数,则有可能改变了当前工作目录。这时core文件创建在chdir指定的路径下。有好多程序崩溃了,我们却找不到core文件放在什么位置。和chdir函数就有关系。当然程序崩溃了不一定都产生core文件。
( a )进程是设置-用户-ID,而且当前用户并非程序文件的所有者 ( b )进程是设置-组-ID,而且当前用户并非该程序文件的组所有者 ( c )用户没有写当前工作目录的许可权 ( d )文件太大。core文件的许可权(假定该文件在此之前并不存在)通常是用户读/,组读和其他读 友情提醒:当环境当好后,可以使用在终端执行 “kill -11 应用程序PID”命令,然后查看是否回生成core文件。
发生core dump之后, 用gdb进行查看core文件的内容, 以定位文件中引发段错误的行。如下:
gdb [exec file] [core file]如: arm-none-linux-gnueabi-gdb queuetest core执行结果如下:root@silent:/home/lianxi# arm-none-linux-gnueabi-gdb queuetest coreGNU gdb (Sourcery G++ Lite 2009q1-203) 6.8.50.20081022-cvsCopyright (C) 2008 Free Software Foundation, Inc.License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>This is free software: you are free to change and redistribute it.There is NO WARRANTY, to the extent permitted by law. Type "show copying"and "show warranty" for details.This GDB was configured as "--host=i686-pc-linux-gnu --target=arm-none-linux-gnueabi".For bug reporting instructions, please see:<https://support.codesourcery.com/GNUToolchain/>...Error while mapping shared library sections:lib/libdl.so.2: No such file or directory.Error while mapping shared library sections:lib/libgcc_s.so.1: No such file or directory.Error while mapping shared library sections:lib/libc.so.6: No such file or directory.Error while mapping shared library sections:lib/ld-linux.so.3: No such file or directory.Symbol file not found for /lib/libdl.so.2Symbol file not found for /lib/libgcc_s.so.1Symbol file not found for /lib/libc.so.6Symbol file not found for /lib/ld-linux.so.3warning: Unable to find dynamic linker.GDB will be unable to debug shared library initializersand track explicitly loaded dynamic code.Core was generated by `./queuetest'.Program terminated with signal 11, Segmentation fault.#0 0x00008548 in main (argc=1, argv=0xbeb26e24) at main.c:3838 printf("ptr = %d/n", *ptr);(gdb) where#0 0x00008548 in main (argc=1, argv=0xbeb26e24) at main.c:38gdb中键入where,就会看到程序崩溃时堆栈信息(当前函数之前的所有已调用函数的列表),显然错误出现main.c:38中。 但往往调试没这么简单,所以还得记住几个常用的gdb命令,结合查看分析 bt 查看堆栈信息 bt full 完全显示函数之间相互调用时传递的参数值和函数的内部变量值 where 显示在哪儿down掉 info locals 显示目前的区域参数 info threads 显示当前可调试的所有线程,每个线程会有一个gdb为其分配的ID,后面操作线程的时候会用这个ID,前面有*的是当前调试的线程。 thread id 切换当前调试的线程为指定ID的线程 list 往下列出代码 info frame 查看桢的详细信息 thread apply all command 让所有被调试线程执行gdb命令command x/nfu 查看内存信息
注:在交叉编译环境中,经常发现库找不到的情况,这是因为没有指定库路径的原因。可以通过以下两个命令设置。
set solib-absolute-prefix 路径set solib-search-path 路径新闻热点
疑难解答