DualOS多进程资源管理说明
1. 概述¶
DualOS多进程是基于Purelinux多进程方案上做兼容的,核心设计思想为进程资源谁创建就由谁来销毁,由一个主进程构建核心Pipeline,子进程负责取流、取Output Buffer或创建子码流等行为。套用到DualOS上,将RTOS创建的App Preload也当成一个进程去处理,并且该进程为主进程负责创建核心Pipeline,在Linux起来所跑的App为子进程,负责取流、取Output buffer或创建子码流等行为。
图1-1 DualOS多进程架构图
基于该核心思想,Linux所跑的App不能去跨进程销毁RTOS App所创建的资源,可以去获取或设置属性,在Linux起来后,RTOS App基本上是不会主动去做任何事情的,如果想要销毁RTOS创建的资源,需要通过Linux这边发送Cli Cmd(基于Rpmsg实现的Linux to RTOS通信手段),RTOS接收到Cli Cmd,根据Cli Cmd定义的子串去做对应的销毁动作,基本架构如图1-1所示。
2. MI资源¶
核心的规则为多进程中资源谁创建就由谁来销毁,本章节会阐述清楚哪些为MI资源,怎么理解销毁创建。
对于MI SYS会拥有Init、Bind、MMa、InputPortBuffer、OutputPortBuffer等资源,每个与stream pipe相关(需要使用到MI模块)的进程都需要在初始化时呼叫一次MI SYS的init API(MI_SYS_Init)、并且退出时呼叫一次MI SYS的Exit API(MI_SYS_Exit);对于同样Src和Dest的Bind和Unbind动作需要在同一个进程进行;对于同一块MMaBuffer的Alloc和Free需要在同一个进程进行;任意进程都可以Get InputPortBuffer或OutputPortBuffer。
对于MI Video/Audio相关的模块大部分会拥有Device/Channel/Port资源。
- Device资源支持多进程,跨进程使用该Device下的Channel时,需要先Recreate Device,此时Create Device使用的参数不会生效,只会参考第一次创建Device使用的参数,退出时需要Destroy Device。
- Channel和Port资源不支持多进程,不允许跨进程做Recreate和Destroy的动作。部分模块能跨进程Dup Chn使用,参考特殊模块注意。
表1-1总结了常用MI模块拥有的资源和对应创建销毁API,其中规则中提到需在同一进程进行指的是操作同一份资源,例如以MI SYS的Bind为例,以同样的SrcChnPort和DstChnPort建立的绑定关系为同一份资源:
MI_SYS_ChnPort_t stSrcChnPort; MI_SYS_ChnPort_t stDstChnPort; stSrcChnPort.eModId = E_MI_MODULE_ID_ISP; stSrcChnPort.u32DevId = 0; stSrcChnPort.u32ChnId = 0; stSrcChnPort.u32PortId = 0; stDstChnPort.eModId = E_MI_MODULE_ID_SCL; stDstChnPort.u32DevId = 0; stDstChnPort.u32ChnId = 0; stDstChnPort.u32PortId = 0;
表1-1 MI资源总结
MI模块 | 资源 | 创建和销毁的API | 规则 |
---|---|---|---|
SYS | Init | MI_SYS_Init MI_SYS_Exit |
每一个stream pipe进程都需要呼叫一次Init和Exit |
Bind | MI_SYS_BindChnPort2 MI_SYS_UnbindChnPort2 |
Bind/Unbind同一份资源需在同一进程进行 | |
MMa | MI_SYS_MMA_Alloc MI_SYS_MMA_Free |
Alloc和Free同一块Buffer需在同一进程进行 | |
PrivateMMAHeap | MI_SYS_ConfigPrivateMMAHeap | Alloc和Free同一块Buffer需在同一进程进行 | |
PortBuffer | MI_SYS_ChnInputPortGetBuf MI_SYS_ChnInputPortPutBuf MI_SYS_ChnOutputPortGetBuf MI_SYS_ChnOutputPortPutBuf |
任意进程都可以GetBuf,同一块Buffer的Get和Put需在同一进程进行 | |
VIF | Group | MI_VIF_CreateDevGroup MI_VIF_DestroyDevGroup |
不支持多进程,需在同一进程做创建和销毁 |
Device | MI_VIF_EnableDev MI_VIF_DisableDev |
||
Port | MI_VIF_EnableOutputPort MI_VIF_DisableOutputPort |
||
ISP SCL VENC JPD VDEC LDC AI AO RGN (部分模块没有Port请忽略) |
Device | MI_XXX_CreateDevice MI_XXX_DestroyDevice |
支持多进程,跨进程使用时需要做创建和销毁 |
Channel | MI_XXX_CreateChannel MI_XXX_DestroyChannel |
不支持多进程,需在同一进程做创建和销毁 | |
Port | MI_XXX_EnableOutputPort MI_XXX_DisableOutputPort |
不支持多进程,需在同一进程做创建和销毁 |
AI、AO模块将Open划分为Device资源,对应API接口MI_XXX_Open/MI_XXX_Close,参考表格中Device资源规则。
RGN模块将Init划分为Device资源,对应MI_RGN_Init/MI_RGN_Deinit,参考表格中Device资源规则;Handle划分为Channel资源,对应MI_RGN_Create/MI_RGN_Destroy,参考格中Channel资源规则。
以下跨进程操作资源的行为是允许的:
- 获取和设置资源属性,例如呼叫MI API:MI_XXX_SetDevAttr、MI_XXX_SetChnAttr、MI_XXX_SetOutputPortAttr、MI_XXX_GetDevAttr、MI_XXX_GetChnAttr、MI_XXX_GetOutputPortAttr等。
- Device资源的使用,呼叫MI API:MI_XXX_CreateDevice、MI_XXX_DestroyDevice,需要成对呼叫。
- Stop和Start Chn,例如呼叫MI API:MI_XXX_StopChannel、MI_XXX_StartChannel。
- 获取MI模块的Input/OutputBuffer,例如呼叫MI API:MI_SYS_ChnInputPortGetBuf、MI_SYS_ChnInputPortPutBuf、MI_SYS_ChnOutputPortGetBuf、MI_SYS_ChnOutputPortPutBuf。
3. 退出和销毁¶
3.1 常用Pipeline举例¶
以DualOS中常用Pipeline举例,如图1-2所示
图1-2 DualOS多进程常用Pipeline
- RTOS Preload 属于进程1构建VIF->ISP->SCL->VENC的基本MI Pipeline,确保video数据流正常出流。
- Linux APP1属于进程2,用于创建VENC子码流,并且Get VENC的output stream给到Rtsp做Preview画面
- Linux APP2属于进程3,用于Get SCL的Output Buffer(通过
MI SYS_ChnOutputPortGetBuf
接口),将Buffer给到算法做处理。
在此多进程场景中,RTOS Preload APP会把Video Pipeline基本构建完成,Linux起来后所运行的APP都是在此基础上进行拓展。
3.2 退出和销毁流程¶
RTOS提供了MI_DEVICE_RegMiSysExitCall
这个API,用于Preload APP注册Exit Function。或者Preload APP自行定义Cli Cmd,通过Linux发送Cli Cmd主动退出。
RTOS Preload APP行为 | 退出流程 |
---|---|
注册了Exit Function | Linux stream pipeline最后一个进程退出后,RTOS Preload APP自动退出。 在异常退出或进程被Kill掉场景,Linux的资源由MI SYS回收,RTOS的资源由APP通过Exit Function回收。 |
没有注册对应的Exit Function | Rtos Preload APP不会自动退出。 在异常退出或进程被Kill掉场景,RTOS的资源不会主动回收。 |
定义Cli Cmd,主动调用Exit Function(与上述是否注册Exit Function不冲突) | 由Linux APP自行决定退出 |
退出和销毁流程:
重点在于描述注册了Exit Function的情况,对应到上述举例的Pipeline中:
1、假如Linux APP1进程退出,对RTOS Preload APP 和Linux APP2无影响,RTOS APP能正常出流,Linux APP2也能正常获取SCL OuputBuffer。
2、Linux APP2进程再退出,此时Linux APP2先退出,退出完成后RTOS Preload APP也会自动退出。
注意:Linux APP在使用RTOS资源时,RTOS如果退出了,Linux APP再去使用会出现异常,例如Get不到Buffer,取流失败等。
3.3 异常问题除错¶
退出和销毁时,如果资源销毁不正确,容易导致系统crash问题,以下提供基本的除错思路:
1、打开RTOS log,更改env参数bootargs_rtos的loglevel参数和logmode参数。
#修改前 bootargs_rtos=rtos_size=0x01000000 limit_dram_size=0x10000000 mma_base=0x24000000 mma_size=0x0B000000 rtosrd_addr=0x2FA00000 rtosrd_size=0x120000 loglevel=3 #修改后 bootargs_rtos=rtos_size=0x01000000 limit_dram_size=0x10000000 mma_base=0x24000000 mma_size=0x0B000000 rtosrd_addr=0x2FA00000 rtosrd_size=0x120000 loglevel=7 logmode=direct
确认是否有[MI ERR]的log,根据log排查资源销毁情况,是否重复调用Unbind/Destroy等接口。
2、加debug信息定位跑到哪里死机,尽可能确认跑到哪一个接口会出现问题。
3、排查是否RTOS端的资源没有完全释放,导致Linux端再跑应用时出错。
4、排查资源释放接口调用顺序是否正确。
5、排查接口的参数设置是否正确。
4. 切换参数的做法¶
切换参数分为两种流程:
1、切换过程中不涉及资源的销毁和创建,这种情况下可以在任意进程完成切换,不会有多进程的限制。
对于流程1,Chn属性参数和Port的切换、Port属性的切换会走此流程,例如ISP模块需要切换3Dnr Level,可以参考以下流程:
MI_ISP_DEV devId; MI_ISP_CHANNEL chnId; MI_ISP_ChnParam_t stChnParam; MI_ISP_StopChannel(devId, chnId); MI_ISP_GetChnParam(devId, chnId, stChnParam); stChnParam.e3DNRLevel = 2; MI_ISP_SetChnParam(devId, chnId, stChnParam); MI_ISP_StartChannel(devId, chnId);
例如SCL模块需要切换Output Size,可以参考以下流程:
Port属于进程资源,如果要保证实效性,通过Disable Port => Set PortAttr => Enable Port流程做切换,需搭配cli cmd实现。
MI_SCL_DEV devId; MI_SCL_CHANNEL chnId; MI_SCL_PORT portId; MI_SCL_OutputParam_t stSclOutputParam; MI_SCL_GetOutputPortParam(devId, chnId, portId, &stSclOutputParam); stSclOutputParam.stSclOutputSize.u16Width = 1280; stSclOutputParam.stSclOutputSize.u16Height = 720; MI_SCL_SetOutputPortParam(devId, chnId, portId, &stSclOutputPara);
2、切换过程会需要先做Dev/Chn/Port等资源的销毁,设置参数属性,再重新创建。
在DualOS场景下,RTOS Preload APP也当成一个进程加入到多进程处理中,Linux就不能够直接跨进程去销毁RTOS所创建的资源,需要RTOS自行销毁资源,并且RTOS的开发界面并不如Linux便捷,在Linux端操作RTOS APP要通过Rpmsg的通信方法去实现。
切换参数的具体做法依赖于RTOS端定义Cli Cmd,Linux发送指定子串到RTOS,RTOS解析后实现对应的资源销毁,销毁完成后,由Linux去再创建和设定参数属性动作。
例如Sensor/VIF/ISP切换HDR的流程,可参考以下流程,rtos端接收Cli Cmd具体接口自行定义:
RTOS端:
主要依赖用户RTOS Preload APP如何设计,在这里自行定义一套解析Cli Cmd子串,呼叫对应模块的Deinit/Destroy/Unbind相关API
static int ST_Deinit(MI_U32 mod, MI_U32 dev, MI_U32 chn) { if(mod == E_MI_MODULE_ID_SNR) { MI_SNR_Disable } if(mod == E_MI_MODULE_ID_VIF) { MI_VIF_DisableOutputPort MI_VIF_DisableDev MI_VIF_DestroyDevGroup } ... ... return 0; } static int ST_Unbind(MI_U32 mod, MI_U32 dev, MI_U32 chn) { if(mod == E_MI_MODULE_ID_ISP) { MI_SYS_ChnPort_t stSrcChnPort; MI_SYS_ChnPort_t stDstChnPort; stSrcChnPort.eModId = E_MI_MODULE_ID_VIF; stSrcChnPort.u32DevId = 0; stSrcChnPort.u32ChnId = 0; stSrcChnPort.u32PortId = 0; stDstChnPort.eModId = E_MI_MODULE_ID_ISP; stDstChnPort.u32DevId = dev; stDstChnPort.u32ChnId = chn; stDstChnPort.u32PortId = 0; MI_SYS_UnBindChnPort2 } ... ... return 0; } static int _L2RData(CLI_t * pCli, char * p) { MI_U32 mod = 0; MI_U32 dev = 0; MI_U32 chn = 0; int i = 0; int cnt = 0; char *cmd = NULL; cnt = CliTokenCount(pCli); if(cnt == 0 || cnt < 4) goto HELP_EXIT; CliTokenPopNum(pCli, &mod, 0); CliTokenPopNum(pCli, &dev, 0); CliTokenPopNum(pCli, &chn, 0); cmd = CliTokenPop(pCli); if(!(strcmp(cmd, "deinit"))) { ST_Deinit(mod, dev, chn); } else if(!(strcmp(cmd, "unbind"))) { ST_Unbind(mod, dev, chn); } return eCLI_PARSE_OK; HELP_EXIT: return eCLI_PARSE_ERROR; } SS_RTOS_CLI_CMD(pipe_cmd, "get linux 2 rtos data", "Usage: pipe_cmd [p1:mod p2:dev p3:chn p4:args...]\n", _L2RData);
Linux端:
1、echo cli cmd,unbind ISP和VIF system("echo pipe_cmd ISP dev chn unbind > /proc/dualos/rtos"); 2、呼叫MI ISP API Stop Chn MI_ISP_StopChannel; 3、echo cli cmd,deinit VIF system("echo pipe_cmd VIF dev chn deinit > /proc/dualos/rtos"); 4、echo cli cmd,deinit SENSOR system("echo pipe_cmd VIF dev chn deinit > /proc/dualos/rtos"); 5、呼叫MI SENSOR Init API MI_SNR_SetPlaneMode MI_SNR_SetRes MI_SNR_Enable 6、呼叫MI VIF Init API MI_VIF_CreateDevGroup MI_VIF_EnableDev MI_VIF_EnableOutputPort 7、呼叫MI ISP SetChnParam API MI_ISP_GetChnParam MI_ISP_SetChnParam 8、呼叫MI ISP StartChn API MI_ISP_StartChannel 9、bind ISP和VIF MI_SYS_BindChnPort2
5. 特殊模块注意¶
前面章节2中提到Channel资源是不支持多进程,不能跨进程创建使用,但MI VENC和MI AI模块比较特殊,在跨进程使用时,需要先做Dup Chn的动作,Dup Chn就想当于做了Channel资源的Recreate,Create和Destroy是需要成对呼叫,Dup Chn的API接口为MI_AI_DupChnGroup、MI_VENC_DupChn。
总的来说需要有以下注意的地方:
-
Channel被创建后,在另外的进程内Dup Chn只能被调用一次。
-
在创建Channel的进程中可以直接使用当前资源,无需Dup。
-
无论进程中使用的是CreateChn还是Dup Chn,在退出进程时都必须呼叫DestroyChn API去销毁资源。
-
只允许在同一个进程内调用GetStream接口(MI_VENC_GetStraem、MI_AI_Read),不同进程间不支持同时调用GetStream。
6. 规则限制总结¶
- 进程资源默认谁创建就由谁来销毁。
- Linux不能直接通过MI API去销毁RTOS创建的资源,只能通过Cli Cmd,发送到RTOS,通知RTOS去呼叫MI API做销毁的动作。
- Device资源支持多进程,跨进程使用同一个Device的时候需做创建和销毁,Channel资源和Port资源不支持多进程,不能跨进程做创建和销毁,细节说明参考章节2。
- RTOS端创建的资源,需要调用
MI_DEVICE_RegMiSysExitCall
接口注册对应的Exit Function并在Exit Function中做对应的销毁,否则在Kill掉Linux进程的情况下,RTOS资源不会主动回收,或者跑其他Linux应用会出现异常问题。 - 在Linux端切换参数时如果涉及资源销毁,需搭配Cli Cmd实现,第一次切换将RTOS资源销毁,在Linux上创建,后续再次切换就只需要在Linux端操作,详细做法可参考章节4。
- MI模块涉及Dup Chn的动作需额外注意,Dup Chn也属于进程资源的一种,Dup后要做相应的销毁动作,细节要求查看章节5。
7. 对APP行为的影响¶
相比之前的做法,目前MI内部会将跨进程销毁资源的动作挡掉,呼叫这类行为的API时会执行失败,返回MI ERR。目前做法只是将之前理应遵循的规则,写在code里面,确保规则执行到位。
Linux APP需清楚哪些资源是RTOS创建的,哪些资源是Linux创建,对应的只需要做Linux资源的销毁。
如果要在Linux起来后做参数切换,涉及资源的销毁和创建,需要RTOS APP定义Cli Cmd和Linux配合实现,切换的流程由Linux APP去保证。
之前允许Linux任意去创建/销毁RTOS所使用的资源存在安全性和运行稳定性的风险。
举个例子,如图1-3所示:
在RTOS创建了VIF->ISP->SCL->VENC的Pipeline,在使用RGN Attach到SCL上面画框,在Linux起来后,Linux APP同样使用了RGN,将RGN Attach到VENC上面贴时间戳,Linux这边用完RGN后,将其整个Deinit,就会导致RTOS那边的RGN也跟随着Deinit,影响到RTOS创建Piepline的正常出流效果。诸如此类问题,对MI来说以前的做法做不了防守。
图1-3 异常问题举例