定位 UNIX 上常见问题的经验总结.docx
- 文档编号:18011277
- 上传时间:2023-08-05
- 格式:DOCX
- 页数:23
- 大小:27.80KB
定位 UNIX 上常见问题的经验总结.docx
《定位 UNIX 上常见问题的经验总结.docx》由会员分享,可在线阅读,更多相关《定位 UNIX 上常见问题的经验总结.docx(23页珍藏版)》请在冰点文库上搜索。
定位UNIX上常见问题的经验总结
定位UNIX上常见问题的经验总结
同时通过对下面两个例子的介绍,巩固了上面问题分析的介绍:
一个多线程应用的性能问题的分析一个crash问题的分析UNIX程序常见问题分类UNIX下运行程序,经常会遇到以下几类问题:
Crash
内存泄露
句柄泄露
进程不响应
性能不满足预期
逻辑错误回页首UNIX程序常见问题的分析方法UNIX下Crash问题的分析方法crash原理和core文件生成原因(信号的介绍)Crash是进程崩溃,是由于应用进程做了错误的操作(例如,数组拷贝越界导致对系统内存进行了写操作,使用了错误的指针地址),操作系统向应用进程发送了信号,如果应用进程没有做特殊处理,应用进程将coredump在进程当前的工作目录下生成一个core文件,core文件复制了该进程的存储图像,是一个内存映像。
不是所有的信号默认行为都是crash,常见默认crash信号主要有:
SIGABRT
SIGBUS
SIGSEGV
SIGILL
SIGPIPE可以通过kill–l(适用所有UNIX平台)查看信号的信息。
查看针对某个进程的所有信号的默认行为(例如:
在Solaris平台使用psigpid命令查看,其他平台的命令略有不同,请参考各自平台用户手册).[root@svs4qa09SunOSa]#psig25040
25040:
/qatest/ModelerServer/5.0.0.0.64/modelersrv_15_0-server
HUPcaught0x10002958c0
INTcaught0x1000295800
QUITdefault
ILLdefault
TRAPdefault
ABRTdefault
EMTdefault
FPEdefault
KILLdefault
BUSdefault
SEGVdefault
SYSdefault
PIPEignored
ALRMdefault
TERMcaught0x1000295800
USR1default
USR2default
CLDcaught0x100067f44NOCLDSTOP下面列举一些常见信号的默认操作以及可能产生的原因:
例如:
Solaris平台如下。
下面的信息参考Solaris内核结构第2版第二章(Solaris进程模型)第75页,其他平台基本相同,请参考各自平台用户手册:
信号值处理动作发出信号的原因SIGHUP缺省的动作是终止进程终端挂起或者控制进程终止SIGINT缺省的动作是终止进程键盘中断(如break键被按下)SIGQUIT缺省的动作是终止进程并进行内核映像转储(dumpcore)键盘的退出键被按下SIGILL缺省的动作是终止进程并进行内核映像转储(dumpcore)非法指令SIGABRT缺省的动作是终止进程并进行内核映像转储(dumpcore)由abort(3)发出的退出指令SIGFPE缺省的动作是终止进程并进行内核映像转储(dumpcore)浮点异常SIGKILL9AEFKill信号终止信号SIGSEGV缺省的动作是终止进程并进行内核映像转储(dumpcore)无效的内存引用SIGPIPE缺省的动作是终止进程管道破裂:
写一个没有读端口的管道SIGALRM缺省的动作是终止进程由alarm
(2)发出的信号SIGTERM缺省的动作是终止进程终止信号SIGUSR1缺省的动作是终止进程用户自定义信号1SIGUSR2缺省的动作是终止进程用户自定义信号2SIGCHLD缺省的动作是忽略此信号子进程结束信号SIGSTOPDEF终止进程SIGBUS缺省的动作是终止进程并进行内核映像转储(dumpcore)总线错误(错误的内存访问)core文件分析一般思路首先使用file命令(所有UNIX平台适用)查看core文件生成的源程序bash-3.00$filecore
core:
ELF64-bitMSBcorefileSPARCV9Version1,from'qatest'从以上结果可以看出,该core文件是由64位程序qatest生成的。
然后使用gdb(或者dbx)对core文件进行分析:
bash-2.05$dbx./qatest./core再使用where命令查看core的位置:
t@1(l@1)programterminatedbysignalBUS(invalidaddressalignment)
CurrentfunctionisMCXML_700:
:
MCSetting:
:
MCSetting
87fpValue=s.GetValue()->Clone();
(dbx)where从这个core文件可以看到,它收到了BUS信号,crash的位置在=s.GetValue()->Clone()函数。
更多有关gdb,dbx的使用请参考gdb,dbx用户手册。
core文件无法生成常见原因当程序崩溃时,并不是总会生成core文件。
经常有下面的情况导致core文件没有产生:
对core文件大小做了限制,可以通过ulimit(所有UNIX平台适用)的命令进行查看:
bash-3.00$ulimit-a
corefilesize(blocks,-c)unlimited
datasegsize(kbytes,-d)unlimited
filesize(blocks,-f)unlimited
openfiles(-n)256
pipesize(512bytes,-p)10
stacksize(kbytes,-s)unlimited
cputime(seconds,-t)unlimited
maxuserprocesses(-u)29995
virtualmemory(kbytes,-v)unlimited建议使用下面的命令将这个限制改为unlimitedbash-3.00$ulimit–cunlimited
磁盘空间是否充足,通过df命令(所有UNIX平台适用)查看Available的空间是否充足。
bash-3.00$df-k
Filesystem1024-blocksUsedAvailableCapacityMountedon
/0409751179939450930%/
/dev140369626409751179939450930%/dev
查看信号是否被捕获(例如:
Solaris平台使用psig进行查看,见上面的例子,其他平台的命令略有不同,请参考各自平台用户手册)。
如果上面的情况导致core文件没有生成,请修改它。
没有core文件产生,如何分析crash
有时候经常发现进程crash了,但是coredump文件没有产生。
这时可以使用dbx,gdb等调试工具首先attach到运行的进程上,然后再执行业务,如果进程crash,dbx或者gdb将终止在crash的位置,我们便可以根据这个堆栈信息对crash进行分析,与分析core文件相同。
回页首UNIX下内存泄露问题分析方法内存泄露简单的说就是申请了一块内存空间,使用完毕后没有释放掉。
它的一般表现方式是程序运行时间越长,占用内存越多,最终用尽全部内存,整个系统崩溃。
封装new和delete对内存泄漏进行分析通过对new和delete的封装,将new和delete的过程通过日志文件的保存记录下来。
然后对日志文件进行分析,是否new和delete是匹配的,有哪些内存申请,但是没有释放。
下面通过一个简单的测试程序(此代码使用C++语言实现,目前没有考虑申请数组的情况)进行演示:
这个测试程序申请了pTemp1,pTemp2,pTemp3的三块内存,但是仅仅释放了pTemp3,存在pTemp1和pTemp2的内存泄露。
程序解释:
在每次内存申请时,将内存申请的信息注册到MAP表中,在每次内存释放时,将对应的内存信息从注册表中删除,这样注册表中将保存未释放的内存信息,按照一定的规则将注册表中的信息输出(定时或者进程退出等)。
然后我们从输出信息中便可以分析出内存泄漏点。
通过自定义宏DEMONEW和DEMODELETE申请内存和释放内存,在这两个宏中,我们将内存的申请和释放做了记录,从而可以得到未释放内存的信息,请参考下面的程序实现流程图:
图1.内存申请释放流程:
图2.DEMONEW实现流程:
图3.DEMODELETE实现流程:
测试程序代码:
#include
#include
#include
#include
//申请内存时,存储new位置的数据结构
typedefstruct{
std:
:
stringfilename;
intline;
}MEMINFO;
//输出文件
std:
:
ofstreamloginfo("//tmp/memory.log");
typedefstd:
:
mapMemMap;
//存储内存申请记录(在每次内存申请时,将内存申请的地址作为键值,
//内存申请操作所在的文件名和行号作为内容,存储到下面的数据结构memmap中)
MemMapmemmap;
//注册内存申请信息到上面的map容器中,输入的参数分别为内存地址,文件名,行号
voidRegMemInfo(longlongaddr,constchar*fname,longlonglnum)
{
MEMINFOinfo;
if(fname)
{
info.filename=fname;
}
info.line=lnum;
memmap.insert(MemMap:
:
value_type(addr,info));
};
//卸载内存申请信息从上面的map容器中,输入的参数为内存地址
voidUnRegMemInfo(longlongaddr)
{
if(memmap.end()!
=memmap.find(addr))
{
memmap.erase(addr);
}
}
//定义宏DEMONEW,封装了内存申请的操作,在内存申请成功后,调用RegMemInfo功能,
//将内存信息注册到map容器中
#defineDEMONEW(p,ptype)\
do\
{\
p=newptype;\
if(p)\
{\
RegMemInfo((longlong)p,__FILE__,__LINE__);\
}\
else\
{\
std:
:
cout<<"NEWfailed"< : endl;\ }\ }\ while(0) //定义宏DEMODELETE,封装了内存释放的操作,在内存释放时,调用UnRegMemInfo //功能,将内存信息从map容器中删除 #defineDEMODELETE(p)\ do\ {\ if(p)\ {\ UnRegMemInfo((longlong)p);\ deletep;\ p=0;\ }\ }while(0) //写信息流内容到文件 voidWriteString(std: : stringbuf) { loginfo< : endl; } //将整数转换为字符串 std: : stringInt2Str(intvalue) { charbuf[16]={0}; sprintf(buf,"%d",value); returnbuf; } //输出map容器中存储的内存没有释放的信息 voidOutput() { loginfo.clear(); if(memmap.empty()) { WriteString("NoMemoryleak."); return; } MemMap: : iteratoriter; WriteString("TheMemoryleakisbelow: "); for(iter=memmap.begin();iter! =memmap.end();++iter) { std: : stringbuf; std: : stringsAddr=Int2Str(iter->first); std: : stringsLine=Int2Str(iter->second.line); buf+="memoryAddress"; buf+=sAddr; buf+=": FILE"; buf+=iter->second.filename; buf+=",LINE"; buf+=sLine; buf+="nofreed"; WriteString(buf); } } //测试程序主入口函数 intmain(intargc,char*argv[]) { char*pTemp1=0; DEMONEW(pTemp1,char); char*pTemp2=0; DEMONEW(pTemp2,char); char*pTemp3=0; DEMONEW(pTemp3,char); DEMODELETE(pTemp1); Output(); loginfo.close(); return0; }上面测试程序的输出是: [dyu@xilinuxbldsrv~]$vi/tmp/memory.log TheMemoryleakisbelow: memoryAddress280929008: FILEtest.cpp,LINE109nofreed memoryAddress280929152: FILEtest.cpp,LINE111nofreed输出分析: 从输出结果我们可以发现,此测试程序在test.cpp文件的109和111行各有一处内存泄漏,查看源代码,它们分别是pTemp1和pTemp2。 使用Purify(适用所有UNIX平台)或者valgrind(适用Linux平台)工具对内存泄漏进行分析 使用Purify对内存泄漏进行分析 Purify是IBMRationalPurifyPlus的工具之一,是一个面向VC、VB或者Java开发的测试VisualC/C++和Java代码中与内存有关的错误的工具,它确保整个应用程序的质量和可靠性。 在查找典型的C/C++程序中的传统内存访问错误,RationalPurify可以大显身手。 在UNIX系统中,使用Purify需要重新编译程序。 通常的做法是修改Makefile中的编译器变量。 例如定义CC变量为purifygccCC=purifygcc首先运行Purify安装目录下的purifyplus_setup.sh来设置环境变量,然后运行make重新编译程序。 需要指出的是,程序必须编译成调试版本。 在编译器命令(例如Solaris的CC编译器,Linux的gcc编译器等)后,也就是必须使用"-g"选项。 在重新编译的程序运行结束后,Purify会打印出一个分析报告。 测试程序(此代码使用C++语言实现): #include voidfunc1() { //char*pBuf=newchar; } voidfunc2() { char*pBuf=newchar; } voidfunc3() { char*pBuf=newchar; } intmain() { func1(); func2(); func3(); return0; }编译程序: [dyu@xilinuxbldsrvpurify]$purifyg++-gtst.cpp-otst1Purify输出: [dyu@xilinuxbldsrvpurify]$./tst1 16: 50: 59(rational)OUT: "PurifyPlusUNIX"dyu@xilinuxbldsrv ****Purifyinstrumented./tst1(pid530atFriApr616: 50: 592012) *Purify7.0.0.0-014090319Linux(64-bit)(C)CopyrightIBMCorporation.1992, *2009AllRightsReserved. *Forcontactinformationtype: "purify-help" *ForPurifyVieweroutput,settheDISPLAYenvironmentvariable. *Licensesuccessfullycheckedout. *Command-line: ./tst1 *Optionssettings: -g++=yes-purify\ -purify-home= /home/dyu/purify/PurifyPlus.7.0.0.0-014/Rational/releases/purify.i386_linux2.7.0.0.0-014 -process-large-objects=yes-gcc3_path=/usr/bin/g++\ -cache-dir= /home/dyu/purify/PurifyPlus.7.0.0.0-014/Rational/releases/purify.i386_linux2.7.0.0.0-014/cache ****Purifyinstrumented./tst1(pid530)**** Currentfiledescriptorsinuse: 5 FIU: filedescriptor0: FIU: filedescriptor1: FIU: filedescriptor2: FIU: filedescriptor26: FIU: filedescriptor27: ****Purifyinstrumented./tst1(pid530)**** Purify: Searchingforallmemoryleaks... Memoryleaked: 2bytes(100%);potentiallyleaked: 0bytes(0%) MLK: 1byteleakedat0xa457098 *Thismemorywasallocatedfrom: malloc[rtlib.o] operatornew(unsignedlong)[libstdc++.so.6] operatornew(unsignedlong)[rtlib.o] func2()[tst.cpp: 9] main[tst.cpp: 20] __libc_start_main[libc.so.6] _start[crt1.o] MLK: 1byteleakedat0xa457138 *Thismemorywasallocatedfrom: malloc[rtlib.o] operatornew(unsignedlong)[libstdc++.so.6] operatornew(unsignedlong)[rtlib.o] func3()[tst.cpp: 14] main[tst.cpp: 21] __libc_start_main[libc.so.6] _start[crt1.o] PurifyHeapAnalysis(combiningsuppressedandunsuppressedblocks) BlocksBytes Leaked22 PotentiallyLeaked00 In-Use00 ---------------------------------------- TotalAllocated22Purify图形输出: 安装Xmanager等工具,设置DISPLAY为本机IP,见下图: [dyu@xilinuxbldsrvpurify]$exportDISPLAY=9.119.131.33: 0输出分析: 从purify的输出可以看出,此测试程序存在两处内存泄漏,它分别是func2和func3,在tst.cpp文件的第9和第14行。 使用valgrind(现在仅仅支持Linux平台)对内存泄漏进行分析 Valgrind是一套Linux下,开放源代码(GPLV2)的仿真调试工具的集合。 Valgrind由内核(core)以及基于内核的其他调试工具组成。 内核类似于一个框架,它模拟了一个CPU环境,并提供服务给其他工具;而其他工具则类似于插件(plug-in),利用内核提供的服务完成各种特定的内存调试任务。 Valgrind在对程序进行侦测的时候,不需要对程序进行重新编译。 下面使用valgrind对一个简单的测试程序进行。 测试程序: 同Purify的测试程序相同。 编译程序: [dyu@xilinuxbldsrvpurify]$g++-gtst.cpp-otstvalgrind输出: [dyu@xilinuxbldsrvpurify]$valgrind--leak-check=full./tst ==25396==Memcheck,amemoryerrordetector ==25396==C
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 定位 UNIX 上常见问题的经验总结 常见问题 经验总结