Chinaunix首页 | 论坛 | 博客
  • 博客访问: 139598
  • 博文数量: 124
  • 博客积分: 70
  • 博客等级: 民兵
  • 技术积分: 1745
  • 用 户 组: 普通用户
  • 注册时间: 2011-02-24 13:49
文章分类

全部博文(124)

文章存档

2011年(55)

2010年(14)

2009年(30)

2008年(25)

我的朋友

分类: WINDOWS

2009-04-02 09:56:53

Hi all:

 

      昨日从测试同事处获悉,alii存在一定几率的无法退出情况,表现为界面已经消失,但是进程还存在,测试同事提供了dump

分析如下:

 

1.      版本为6.05.09 alitalk.

2.      Vc无法解析符号,均前置显示为*ntdll.dll 类似,使用windbg(饶趣同学提示),采用.reload /f /I 方能显示,始终提示timestamp不匹配,估计msvc就好似这个原因无法加载符号。

3.      据测试同事回报,是退出后发生的。

Stack 如下:(仅有两个线程,一个主线程,一个工作线程)

 

 主线程信息….

工作线程信息

 

因此判断为有两个线程互锁了。

 

然后继续往下分析,从主线程可以看到

1.      因为据测试同事说是退出后一端时间才发先的,因此从aliim!_tmaincrtstartup 这个frame判断这个时候我的winmain的最后一行代码应该已经执行了,因此无法看到,是运行库进行全局反初始化时候发生的

2.      继续跟下去,发现发生问题发生在com库的反初始化,而且从dump中出现的较多的stub/rpc字样,比较容易判定为主线程在释放一个本aparmtment内的代理对象,后者说此com对象的实际运行apartment不是主线程.

3.      然后再看工作线程,工作线程貌似在等待一个criticalsec而没有返回.

 

 我们看下工作线程的ntdll!RtlEnterCriticalSection_0x46 这个frame的第三个参数也就是0x7c99b178

 

这下发现了, 请注意owningthread=0xf74 , 0xf74是谁呢 哈哈,就是主线程,这下就找到了直接原因,就是说主线程拿住了critsec而工作线程拿不到了.

但是从主线程的stack中貌似无法直接看到也有enterciriticalsection的代码? 倒是有一个waitformultipleobject,

因此推断: 主线程进入临界区后在等待另外一个对象,而这个对象等不到。由于!htrace 无法使用因此很难继续推断!

 

分析到了这儿进入了一个困局,因为主线程在等待一个handle,但是从仅有的一个工作线程却并没有发现有蛛丝马迹,因此只能推断:

主线程正在等待的句柄并没有人(线程)进行触发,或者简单的说,由于aliim提出,某个线程退出了,而这个线程可能本来应该设置这个handle.

 

 

下面从两外一个角度来分析,还是围绕主线程退出时候的套件释放对象来说。

从问题的dump以及log可以分析出:

1.      并没有任何进程外的插件运行或者曾经运行过,因此排除掉主线程使用的是远程com对象

2.      ie4_trap.dll 这个对象的stackframe可以怀疑,主线程是不是引用了一个实际创建与其它线程的ie相关对象?

3.      主线程中使用了apartment== FREE 的组件,因此拿到了一个代理对象,而实际对象位于其它线程.

4.      Wwsdkcom有大量的com对象,但是每一个对象都是apartment的。

 

从上面的分析,继而给出解决方案

1. 在return前直接terminateprocess 杀掉自己 ,当然这样导致进程始终处于异常退出状态,如果有些反初始化工作需要进行则会受到影响,非常糟糕的解决办法

2. 在退出前启动一个线程,这个线程等待一段足够长的时间后,杀掉自己所在的进程

 

因此先用第二方案,看看代码大致如下:

unsigned __stdcall ExitMonitorProc(void* p)
{
  Sleep(15 *1000);
  ::TerminateProcess(::GetCurrentProcess() , 0);
}

结果一运行,发现还是有问题,一抓dump和上面那个差不多,并且非常奇怪ExitMonitorproc这个线程在dump中没有,然后将sleep(15 *1000)去掉后,再try,发现进程果然被杀死了。

 

后来终于想到了,因为return 执行后,触发了crt的退出代码,而crt的退出逻辑有一部分就是撤销线程,而exitmonitorproc又处于一个15s的等待中,估计处于此时段时线程被撤销;而线程撤销后,主线程在执行一些全局变量,com库反初始化时候又死锁了。

 

这样继续想方案:

因为当僵死发生时候,通过taskmgr来杀死目标进程是可以的。作为本文就不继续描述此问题了,因为大致有解了,当然为何会引起这个问题比较复杂不再多说了。

大致执行如下代码,由另外一个进程杀自己.

CString strKillCmdParam;
  strKillCmdParam.Format(_T("%d") , ::GetCurrentProcessId());

  ::ShellExecute(NULL,
   _T("open"),
   filesys::GetModuleDir(NULL) + _T("killapp.exe"),
   strKillCmdParam,
   NULL,
   SW_HIDE);

过了两天,又发现另外一个进程出现了类似情况,退出时候死锁。

结果我又用类似方法发现,结果发现依然无效,一抓堆栈:

具体图不贴了,有关细节。

shellexecute执行时候最终会调用getmodulefile这样一个API而这个api最终又死锁了,而这个锁和本来要解决的死锁还是同一个。呵呵。

当然这个问题本身并不说名,这样通过启动进程方式来杀自己还是存在问题,而是这个启动的时机有问题,

这儿说这个细节也是自己的一种经验。作为记忆。

 

 

 

 

 

阅读(575) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~