oracle trouble shooting 时看 call stack

call stack 和system call 的区别!
在ORACLE 中我们常用call stack 来匹配是否命中某些bug,这时查看call stack 中的函数调用路径来进一步印证。
在某些情况下,例如进程执行慢,我又会通过strace ,truss 来追踪某个进程,查看其系统调用 情况。
那么call stack 和system call 的区别是什么?
首先说system call:
操作系统通过系统调用为运行于其上的进程提供服务。
当用户态进程发起一个系统调用, CPU 将切换到  内核态 并开始执行一个  内核函数 。 内核函数负责响应应用程序的要求,例如操作文件、进行网络通讯或者申请内存资源等。
那么,在应用程序内,调用一个系统调用的流程是怎样的呢?
我们以一个假设的系统调用  xyz 为例,介绍一次系统调用的所有环节。
如上图,系统调用执行的流程如下:
  1. 应用程序 代码调用系统调用(  xyz ),该函数是一个包装系统调用的 库函数 ;包装装函数的名称与其调用的系统调用的名称相同。例如,glibc包含一个函数truncate(),该函数调用底层的“ truncate”系统调用。
  2. 库函数 (  xyz )负责准备向内核传递的参数,并触发 软中断 以切换到内核;
  3. CPU 被  软中断 打断后,执行  中断处理函数 ,即  系统调用处理函数 ( system_call);
  4. 系统调用处理函数 调用 系统调用服务例程 (  sys_xyz ),真正开始处理该系统调用;
call stack:
调用堆栈是从程序开始到当前程序执行时所调用的函数的名称列表。
system call 和call stack主要区别在于,call stack跟踪包括应用程序(Oracle进程)的被调用方法或函数,而system call跟踪仅包括对操作系统内核的(函数)请求。
以下追踪一个select语句就可以看出差异:
这里采用strace来跟踪系统调用和stack来跟踪调用堆栈。


SQL> select sid from v$mystat where rownum=1;
SID
----------
	37
SQL> select paddr from v$session where sid=37;
PADDR
----------------
00000000902080E8
SQL> select spid from v$process where addr='00000000902080E8';
SPID
------------------------
30091
alter system flush buffer_cache;
SQL>  alter system flush buffer_cache;
System altered.
alter session set "_serial_direct_read" = TRUE;
 select * from cwdtest;
此时抓取pstack,call stack情况:

抓取strace(system call情况)
从以上可以看出system call 是看不到调用它们的应用程序代码或函数。
使用oradebug 会改变代码的常规路径,因此打印出来的call stack后面是无需关注:
再次拿以上的pstack情况来看:
同样的进程,我们使用oradebug 来打印堆栈:
SQL> oradebug SHORT_STACK
ksedsts()+465<-ksdxfstk()+32<-ksdxcb()+1927<-sspuser()+112<-__sighandler()<-read()+14<-ntpfprd()+117<-nsbasic_brc()+376<-nsbrecv()+69<-nioqrc()+495<-opikndf2()+978<-opitsk()+831<-opiino()+969<-opiodr()+917<-opidrv()+570<-sou2o()+103<-opimai_real()+133<-ssthrdmain()+265<-main()+201<-__libc_start_main()+253
SQL>  oradebug SHORT_STACK
ksedsts()+465<-ksdxfstk()+32<-ksdxcb()+1927<-sspuser()+112<-__sighandler()<-read()+14<-ntpfprd()+117<-nsbasic_brc()+376<-nsbrecv()+69<-nioqrc()+495<-opikndf2()+978<-opitsk()+831<-opiino()+969<-opiodr()+917<-opidrv()+570<-sou2o()+103<-opimai_real()+133<-ssthrdmain()+265<-main()+201<-__libc_start_main()+253
整理pstack打印堆栈是:
main-(calls)-> ssthrdmain-(calls)-> opimai_real->(calls)-> sou2o-(calls)-> opidrv-(calls)->opiodr->(calls)->opiino()-> opitsk->(calls)-> opikndf2->(calls)-> nioqrc->(calls)-> nsbrecv->(calls)-> nsbasic_brc->(calls)-> ntpfprd->(calls)-> sntpread ->(calls)->read
整理oradebug打印的堆栈是:
main-(calls)-> ssthrdmain-(calls)-> opimai_real->(calls)-> sou2o-(calls)-> opidrv-(calls)->opiodr->(calls)->opiino()-> opitsk->(calls)-> opikndf2->(calls)-> nioqrc->(calls)-> nsbrecv->(calls)-> nsbasic_brc->(calls)-> ntpfprd->(calls)-> sntpread  -(calls)->read -(calls)-> __sighandler -(calls)-> sspuser -(calls)->ksdxcb -(calls)-> ksdxfstk-(calls)->ksedsts
从上面整理的堆栈调用就可以看出,oradebug 多出了__sighandler -(calls)-> sspuser -(calls)->ksdxcb -(calls)-> ksdxfstk-(calls)->ksedsts的几个函数的调用,这其实是通过oradebug转储堆栈引起的,在oracle 的 diag trace中也是如此,在排查故障时可以忽略这一部分,
oradebug运行“ short_stack”跟踪,则将更改Oracle的常规代码路径。
OS工具的行为有所不同,因为它们通常会暂停进程并转储堆栈。



参考文献

https://blogs.sap.com/2013/06/06/oracle-deeper-insight-into-stack-tracing-sampling-for-a-better-understanding-and-troubleshooting/ [Oracle] A deeper insight into stack tracing / sampling for a better understanding and troubleshooting of Oracle issues
https://www.cnblogs.com/fasionchan/p/9431784.html Linux系统调用原理


请使用浏览器的分享功能分享到微信等