Chinaunix首页 | 论坛 | 博客
  • 博客访问: 588758
  • 博文数量: 169
  • 博客积分: 2656
  • 博客等级: 少校
  • 技术积分: 1685
  • 用 户 组: 普通用户
  • 注册时间: 2009-07-30 13:03
文章分类

全部博文(169)

文章存档

2011年(1)

2010年(135)

2009年(33)

我的朋友

分类: 嵌入式

2010-05-14 13:12:22

在target shell上输入lkup "l",会打印所有查到的符号表,以分页方式显示,此时输入ctrl+c,会挂死shell,这是一个bug。
-> lkup "l"
qPriListCalibrate         0x001b2780 text     
msgQDelete                0x001c58d0 text     
iosDevDelete              0x0019a30c text     
ifioctl                   0x0013f08c text     
tcp_close                 0x001818f8 text     
setlocale                 0x0018af94 text     
ipLibMultiInit            0x001600c0 text     
if_slowtimoRestart        0x0013eeb0 text     
hashFuncMultiply          0x00198ed0 text     
semEvIsFreeTbl            0x001fab80 data     
_savegprx_25_l            0x0012a2e0 text     
_savegprx_16_l            0x0012a2bc text     
_savefprx_26_l            0x0012a21c text     
_savefprx_17_l            0x0012a1f8 text     
sysClkRateSet             0x0010324c text     
moduleLibInit             0x001a6980 text     
mcastHashTblInsert        0x00145c50 text     
ifRouteDelete             0x0015f054 text     
arpioctl                  0x001417a4 text     
tcp_template              0x001812d4 text     
soo_select                0x0017c964 text     
motFccRxStallCheck        0x00130b40 text     
Type to continue, Q to stop:
 
初步分析原因如下:
在执行lkup的时候,会先申请符号表的互斥信号量,打印完以后释放。但是在执行的过程中由于需要翻页,所以存在任务切换。
    semTake (&symTblId->symMutex, WAIT_FOREVER);
    pSymbol = (SYMBOL *) hashTblEach (symTblId->nameHashId, symEachRtn, (int) &rtnDesc);
    semGive (&symTblId->symMutex);  /* release exclusion to table */

如果执行了ctrl+c,实际上在操作系统内部执行的是dbgTlTyAbort,dbgTlTyAbort要先执行tt然后才重启shell,tt也要查符号表,由于lkup在shell任务中执行,ctrl+c在exec任务中执行,二者任务不同,tt同样要申请符号表的互斥信号量。所以exec任务被阻塞了,ctrl+c无法执行完毕。

LOCAL void dbgTlTyAbort (void)
    {
    tyAbortFuncSet ((FUNCPTR) NULL); /* cancel abort routine */

    /* reset the standard input and output channels in case they are hung up */

    ioctl (STD_IN,  FIOFLUSH, 0);
    ioctl (STD_OUT, FIOFLUSH, 0);
    ioctl (STD_ERR, FIOFLUSH, 0);

    tt (shellTaskId);

    if ((taskRestart (shellTaskId)) != ERROR)
printErr ("%s restarted.\n", taskName (shellTaskId));
    else
{
printErr ("spawning new shell.\n");

if (shellInit (0, TRUE) == ERROR)
     printErr ("shell spawn failed!\n");
}

    tyAbortFuncSet ((FUNCPTR) dbgTyAbort);  /* set abort routine */
    }
 
前面的分析中有一个疑点,tExcTask应该挂在symTblId->symMutex上,这是一个互斥信号量,tExcTask的优先级是 0,tShell的优先级是1,应该会引起任务优先级翻转。抱着好奇的心理,我做了一个定时任务打印优先级,我看到的结果是tShell任务优先级仍然是 1,并未翻转。
怎么回事呢?
原因在于优先级翻转的条件,除了互斥信号量以外,还有一个必要条件,信号量创建的时候必须设置有SEM_INVERSION_SAFE属性。下面是涉及到翻转的代码:
STATUS semMPendQPut
    (
    FAST SEM_ID semId,  /* semaphore ID to take */
    int timeout         /* timeout in ticks */
    )
    {

#ifdef WV_INSTRUMENTATION
    /* windview - level 2 event logging */
    EVT_TASK_1 (EVENT_OBJ_SEMTAKE, semId);
#endif

    if (windPendQPut (&semId->qHead, timeout) != OK)
        return (ERROR);

    /* if taskIdCurrent is of a higher priority than the task that owns
     * this semaphore, reprioritize the owner.
     */

    if (semId->options & SEM_INVERSION_SAFE)
        {
        taskIdCurrent->pPriMutex = semId;        /* track mutex we pend on */

        if (taskIdCurrent->priority < semId->semOwner->priority)
            {
            windPrioritySet (semId->semOwner, taskIdCurrent->priority);
            }
        }

    return (OK);
    }
 
虽然没有翻转,但是tExcTask已经被阻塞了,有字符输入的时候tShell仍然可以被激活,应该可以继续输出lkup命令的信息。但是为什么没有输出呢?
查了一下挂起时刻的tShell任务的调用栈:
tShell Call Stack:
1c4de8   semBTake
1c5aa0   semTake
11ab2c   tyRead
194ed0   iosRead
1938e4   read
1906cc   fioRdString
11bed0   symSysTblPrint
19cae0   symEachRtn
1b0294   sllEach
193184   hashTblEach
19ca78   symEach
11bc78   symShow
197954   objShow
1a82c4   show
1a8e44   lkup
1b642c   funcCall
1b7ee4   yyparse
1a5874   execute
1a56f0   execShell
1a54c4   shell
1cf5c0   vxTaskEntry

应该挂在了tyRead的第一个信号量pTyDev->rdSyncSem上,这个信号量在有字符输入的时候是会被释放的,从这里查起。在定时任务里增加了对这个信号量的semShow,发现在ctrl+c输入之前,这里都是挂着一个任务的:
->
Semaphore Id        : 0x17ff5d4   
Semaphore Type      : BINARY   
Task Queuing        : PRIORITY  
Pended Tasks        : 1         
State               : EMPTY     
Options             : 0x1       SEM_Q_PRIORITY

VxWorks Events
--------------
Registered Task     : NONE
Event(s) to Send    : N/A
Options             : N/A

Pended Tasks
------------
   NAME      TID    PRI TIMEOUT
---------- -------- --- -------
tShell      16c7e18   1       0

但是在ctrl+c输入完之后再查就没有任务了。可以断定ctrl+c的处理函数影响了信号量的队列。仔细查看ctrl+c的处理,发现问题出在tt上。
tt的处理过程是先挂起目标任务,然后输入调用堆栈的符号表,最后再解挂任务。ctrl+c的执行在tExcTask任务中,这个任务由于申请不到符号表的信号量被挂起了。挂起的位置正好是tt的第二个阶段,即tt已经把tShell 挂起还未解挂的时候。
串口输入字符激活了信号量挂起的任务链,但是在获取任务的时候调用了windPendQGet,请看windPendQGet的代码:
void windPendQGet
    (
    Q_HEAD *pQHead      /* pend queue to get first task off */
    )
    {
    FAST WIND_TCB *pTcb = (WIND_TCB *) Q_GET (pQHead);

#ifdef WV_INSTRUMENTATION
    /* windview - level 2 event logging */
    EVT_TASK_1 (EVENT_WINDPENDQGET, (int) pTcb);         /* log event */
#endif

    pTcb->status &= ~WIND_PEND;                        /* clear pended flag */
    taskRtnValueSet (pTcb, OK);                        /* return OK */

    if (pTcb->status & WIND_DELAY)                /* task was timing out */
        {
        pTcb->status &= ~WIND_DELAY;
        Q_REMOVE (&tickQHead, &pTcb->tickNode);        /* remove from queue */
        }

    if (pTcb->status == WIND_READY)                /* task is now ready */
        Q_PUT (&readyQHead, pTcb, pTcb->priority);
    }


红色部分要求必须是等于ready才能被激活,而此时的tShell是被赋了一个suspend的属性的。所以无法激活,仅仅是把它从队列中摘走,自此查 semShow看不到挂起的任务。
 
分析到这里,基本就清楚了。
ctrl+c把执行的action挂到了tExcTask里,在执行的时候由于符号表的信号量已经被tShell任务占有所以被block在信号量上,而 tExcTask在被阻塞前执行了tt函数的一半,挂起了tShell没有解挂。tShell任务虽然能够收到字符但是由于被suspend而无法激活。最终导致tExcTask和tShell都无法运行。系统显示shell挂死。
阅读(11997) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~