1.这几天在处理GPU方面的问题时碰到了在终端执行CTRL+C报错的问题,具体见Vivante GPU驱动报:SAFE mode error错误解决方法这篇帖子,这里主要记录CTRL+C的大致处理流程。
2.当键盘按下CTRL+C时,主机就向目标板发送一个控制字符,其ASSIC码为0x03,在SylixOS下常用的控制字符如下,此定义是在tyLib.c文件中
- /*********************************************************************************************************
- 全局变量(控制字 __LINUX_BACKSPACE_CHAR 直接按 backspace 处理)
- *********************************************************************************************************/
- #define __LINUX_BACKSPACE_CHAR 127 /* linux 下的退格 control-? */
- #define __TTY_BACKSPACE(ptyDev, ch) ((ch) == __LINUX_BACKSPACE_CHAR || (ch) == __TTY_CC(ptyDev, VERASE))
- static CHAR _G_cTyBackspaceChar = 0x08; /* 默认值 control-H */
- static CHAR _G_cTyDeleteLineChar = 0x15; /* 默认值 control-U */
- static CHAR _G_cTyEofChar = 0x04; /* 默认值 control-D */
- static CHAR _G_cTyAbortChar = 0x03; /* 默认值 control-C */
- static CHAR _G_cTyMonTrapChar = 0x18; /* 默认值 control-X */
复制代码
3.开发板收到数据时,就转入uart中断处理函数中执行,中断处理函数如下
- /*********************************************************************************************************
- ** 函数名称: imx6SioIsr
- ** 功能描述: SIO 通道中断处理函数
- ** 输 入 : psiochanChan SIO 通道
- ** 输 出 : NONE
- ** 全局变量:
- ** 调用模块:
- *********************************************************************************************************/
- static irqreturn_t imx6SioIsr (SIO_CHAN *psiochanChan, ULONG ulVector)
- {
- __PSIO_CHANNEL psiochanUart = (__PSIO_CHANNEL)psiochanChan;
- CHAR cChar;
- if (imx6UartIsTxInt(psiochanUart->iChannelNum)) { /* 发送中断 */
- while (!imx6UartTxFifoFull(psiochanUart->iChannelNum)) {
- if (psiochanUart->pcbGetTxChar(psiochanUart->pvGetTxArg, &cChar) != ERROR_NONE) {
- imx6UartTxIntDisable(psiochanUart->iChannelNum); /* 关闭发送中断 */
- break;
- } else {
- imx6UartTxFifoPut(psiochanUart->iChannelNum, cChar); /* 发送数据 */
- }
- }
- }
- while (!imx6UartRxFifoEmpty(psiochanUart->iChannelNum)) { /* 需要接收数据 */
- if (imx6UartRxFifoGet(psiochanUart->iChannelNum, &cChar) /* 接收数据 */
- == ERROR_NONE) {
- psiochanUart->pcbPutRcvChar(psiochanUart->pvPutRcvArg, cChar);
- } else {
- break;
- }
- }
- imx6UartIntClear(psiochanUart->iChannelNum); /* 清除中断 */
- return (LW_IRQ_HANDLED);
- }
复制代码
由代码可知,在uart接受完数据后,执行了psiochanUart->pcbPutRcvChar(psiochanUart->pvPutRcvArg, cChar)这个函数,这个函数是注册SIO设备时所注册好的回调函数,其函数原型是_TyIRd,如下所示
- /*********************************************************************************************************
- ** 函数名称: _TyIRx
- ** 功能描述: 向终端的接收缓冲区中写入一个从硬件接收到的数据, FIFO
- ** 输 入 :
- ** ptyDev, TY 设备
- ** cInChar, 接收到的数据
- ** 输 出 : ERROR_NONE or PX_ERROR
- ** 全局变量:
- ** 调用模块:
- *********************************************************************************************************/
- INT _TyIRd (TY_DEV_ID ptyDev, CHAR cInchar)
复制代码
这个函数的主要功能就是将uart接收到的数据上送到系统的输入缓冲区,然后进行一些处理。
4.在_TyIRd中首先就对接受到的数据是否是CTRL+C字符做了一个判断,如下
- if ((cInchar == __TTY_CC(ptyDev, VINTR)) &&
- (iOpt & OPT_ABORT) &&
- ((_G_pfuncTyAbortFunc != LW_NULL) ||
- (ptyDev->TYDEV_pfuncCtrlC != LW_NULL))) { /* 是否需要进行 ABORT 处理 */
- LW_SPIN_UNLOCK_QUICK(&ptyDev->TYDEV_slLock, iregInterLevel); /* 解锁 spinlock 打开中断 */
- if (ptyDev->TYDEV_pfuncCtrlC) {
- ptyDev->TYDEV_pfuncCtrlC(ptyDev->TYDEV_pvArgCtrlC);
- }
- if (_G_pfuncTyAbortFunc) {
- _G_pfuncTyAbortFunc();
- }
- }
复制代码
如果是CTRL+C字符就执行ptyDev->TYDEV_pfuncCtrlC(ptyDev->TYDEV_pvArgCtrlC),这个也是事先注册好的函数,其原型是__tshellRestart,如下所示
- /*********************************************************************************************************
- ** 函数名称: __tshellRestart
- ** 功能描述: ttiny shell 线程重启 (control-C)
- ** 输 入 : ulThread 线程句柄
- ** 输 出 : NONE
- ** 全局变量:
- ** 调用模块:
- *********************************************************************************************************/
- static VOID __tshellRestart (LW_OBJECT_HANDLE ulThread)
复制代码
5.__tshellRestart函数调用了__tshellRestartEx函数,__tshellRestartEx函数将自己交给了系统的工作队列处理线程来处理,相当于将具体的restart操作放到了中断下半部来处理,如下所示
- if (LW_CPU_GET_CUR_NESTING()) {
- return (_excJobAdd((VOIDFUNCPTR)__tshellRestartEx,
- (PVOID)ulThread, (PVOID)bNeedAuthen, 0, 0, 0, 0));
- }
复制代码
接着会判断本线程是否存在子线程,如果存在则杀死子线程,如果不存在,则重启当前线程,如下所示
- if (ulJoin) {
- #if LW_CFG_SIGNAL_EN > 0
- kill(ulJoin, SIGKILL); /* 杀死等待的线程/进程 */
- #else
- API_ThreadDelete(&ulJoin, LW_NULL);
- #endif /* LW_CFG_SIGNAL_EN > 0 */
- iMsg = API_IoTaskStdGet(ulThread, STD_OUT);
- if (iMsg >= 0) {
- fdprintf(iMsg, "[sh]Warning: Program is killed (SIGKILL) by shell.\n"
- " Restart SylixOS is recommended!\n");
- }
- } else { /* 重启线程 */
- API_ThreadRestart(ulThread, (PVOID)__TTINY_SHELL_GET_STDFILE(ptcbShell));
- }
复制代码
API_ThreadRestart就是重启线程函数了,这里就不具体分析了。