在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) |