鸿蒙内核源码分析(中断管理篇) | 江湖从此不再怕中断 | 百篇博客分析OpenHarmony源码 | v44.02
百篇博客系列篇.本篇为:
硬件架构相关篇为:
- v22.xx 鸿蒙内核源码分析(汇编基础篇) | CPU在哪里打卡上班 | 51.c.h .o
- v23.xx 鸿蒙内核源码分析(汇编传参篇) | 如何传递复杂的参数 | 51.c.h .o
- v36.xx 鸿蒙内核源码分析(工作模式篇) | CPU是韦小宝,七个老婆 | 51.c.h .o
- v38.xx 鸿蒙内核源码分析(寄存器篇) | 小强乃宇宙最忙存储器 | 51.c.h .o
- v39.xx 鸿蒙内核源码分析(异常接管篇) | 社会很单纯,复杂的是人 | 51.c.h .o
- v40.xx 鸿蒙内核源码分析(汇编汇总篇) | 汇编可爱如邻家女孩 | 51.c.h .o
- v42.xx 鸿蒙内核源码分析(中断切换篇) | 系统因中断活力四射 | 51.c.h .o
- v43.xx 鸿蒙内核源码分析(中断概念篇) | 海公公的日常工作 | 51.c.h .o
- v44.xx 鸿蒙内核源码分析(中断管理篇) | 江湖从此不再怕中断 | 51.c.h .o
关于中断部分系列篇将用三篇详细说明整个过程.
中断概念篇 中断概念很多,比如中断控制器,中断源,中断向量,中断共享,中断处理程序等等.本篇做一次整理.先了解透概念才好理解中断过程.用海公公打比方说明白中断各个概念.可前往鸿蒙内核源码分析(总目录)查看.
中断管理篇(本篇) 从中断初始化
HalIrqInit
开始,到注册中断的LOS_HwiCreate
函数,到消费中断函数的HalIrqHandler
,剖析鸿蒙内核实现中断的过程,很像设计模式中的观察者模式.中断切换篇 用自下而上的方式,从中断源头纯汇编代码往上跟踪代码细节.说清楚保存和恢复中断现场
TaskIrqContext
过程.可前往鸿蒙内核源码分析(总目录)查看.
编译开关
系列篇编译平台为 hi3516dv300,整个工程可前往查看.
预编译处理过程会自动生成编译开关 menuconfig.h ,供编译阶段选择编译,可前往查看.
//....
#define LOSCFG_ARCH_ARM_VER "armv7-a"
#define LOSCFG_ARCH_CPU "cortex-a7"
#define LOSCFG_PLATFORM "hi3516dv300"
#define LOSCFG_PLATFORM_BSP_GIC_V2 1
#define LOSCFG_PLATFORM_ROOTFS 1
#define LOSCFG_KERNEL_CPPSUPPORT 1
#define LOSCFG_HW_RANDOM_ENABLE 1
#define LOSCFG_ARCH_CORTEX_A7 1
#define LOSCFG_DRIVERS_HDF_PLATFORM_RTC 1
#define LOSCFG_DRIVERS_HDF_PLATFORM_UART 1
中断初始化
hi3516dv300 中断控制器选择了 LOSCFG_PLATFORM_BSP_GIC_V2
,对应代码为 gic_v2.c
GIC(Generic Interrupt Controller)是ARM公司提供的一个通用的中断控制器.
看这种代码因为涉及硬件部分,需要对照ARM中断控制器 gic_v2.pdf文档看.可前往地址下载查看.
//硬件中断初始化
VOID HalIrqInit(VOID)
{
UINT32 i;
/* set externel interrupts to be level triggered, active low. */ //将外部中断设置为电平触发,低电平激活
for (i = 32; i < OS_HWI_MAX_NUM; i += 16) {
GIC_REG_32(GICD_ICFGR(i / 16)) = 0;
}
/* set externel interrupts to CPU 0 */ //将外部中断设置为CPU 0
for (i = 32; i < OS_HWI_MAX_NUM; i += 4) {
GIC_REG_32(GICD_ITARGETSR(i / 4)) = 0x01010101;
}
/* set priority on all interrupts */ //设置所有中断的优先级
for (i = 0; i < OS_HWI_MAX_NUM; i += 4) {
GIC_REG_32(GICD_IPRIORITYR(i / 4)) = GICD_INT_DEF_PRI_X4;
}
/* disable all interrupts. */ //禁用所有中断。
for (i = 0; i < OS_HWI_MAX_NUM; i += 32) {
GIC_REG_32(GICD_ICENABLER(i / 32)) = ~0;
}
HalIrqInitPercpu();//初始化当前CPU中断信息
/* enable gic distributor control */
GIC_REG_32(GICD_CTLR) = 1; //使能分发中断寄存器,该寄存器作用是允许给CPU发送中断信号
#if (LOSCFG_KERNEL_SMP == YES)
/* register inter-processor interrupt *///注册核间中断,啥意思?就是CPU各核直接可以发送中断信号
//处理器间中断允许一个CPU向系统其他的CPU发送中断信号,处理器间中断(IPI)不是通过IRQ线传输的,而是作为信号直接放在连接所有CPU本地APIC的总线上。
LOS_HwiCreate(LOS_MP_IPI_WAKEUP, 0xa0, 0, OsMpWakeHandler, 0);//注册唤醒CPU的中断处理函数
LOS_HwiCreate(LOS_MP_IPI_SCHEDULE, 0xa0, 0, OsMpScheduleHandler, 0);//注册调度CPU的中断处理函数
LOS_HwiCreate(LOS_MP_IPI_HALT, 0xa0, 0, OsMpScheduleHandler, 0);//注册停止CPU的中断处理函数
#endif
}
//给每个CPU core初始化硬件中断
VOID HalIrqInitPercpu(VOID)
{
/* unmask interrupts */ //取消中断屏蔽
GIC_REG_32(GICC_PMR) = 0xFF;
/* enable gic cpu interface */ //启用gic cpu接口
GIC_REG_32(GICC_CTLR) = 1;
}
解读
上来四个循环,是对中断控制器寄存器组的初始化,也就是驱动程序,驱动程序是配置硬件寄存器的过程.寄存器分通用和专用寄存器.下图为 gic_v2 的寄存器功能 ,这里对照代码和datasheet重点说下中断配置寄存器(
GICD_ICFGRn
)
以下是GICD_ICFGRn的介绍
The GICD_ICFGRs provide a 2-bit Int_config field for each interrupt supported by the GIC.
This field identifies whether the corresponding interrupt is edge-triggered or level-sensitiveGICD_ICFGRs为GIC支持的每个中断提供一个2位配置字段。此字段标识相应的中断是边缘触发的还是电平触发的
0xC00 - 0xCFC GICD_ICFGRn RW IMPLEMENTATION DEFINED Interrupt Configuration Registers
#define GICD_ICFGR(n) (GICD_OFFSET + 0xc00 + (n) * 4) /* Interrupt Configuration Registers */ //中断配置寄存器
如此一个32位寄存器可以记录16个中断的信息,这也是代码中出现
GIC_REG_32(GICD_ICFGR(i / 16))
的原因.GIC-v2支持三种类型的中断
- PPI:私有外设中断(Private Peripheral Interrupt),是每个CPU私有的中断。最多支持16个PPI中断,硬件中断号从ID16~ID31。PPI通常会送达到指定的CPU上,应用场景有CPU本地时钟。
- SPI:公用外设中断(Shared Peripheral Interrupt),最多可以支持988个外设中断,硬件中断号从ID32~ID1019。
- SGI:软件触发中断(Software Generated Interrupt)通常用于多核间通讯,最多支持16个SGI中断,硬件中断号从ID0~ID15。SGI通常在内核中被用作 IPI 中断(inter-processor interrupts),并会送达到系统指定的CPU上,函数的最后就注册了三个核间中断的函数.
typedef enum {//核间中断
LOS_MP_IPI_WAKEUP, //唤醒CPU
LOS_MP_IPI_SCHEDULE,//调度CPU
LOS_MP_IPI_HALT, //停止CPU
} MP_IPI_TYPE;
中断相关的结构体
size_t g_intCount[LOSCFG_KERNEL_CORE_NUM] = {0};//记录每个CPUcore的中断数量
HwiHandleForm g_hwiForm[OS_HWI_MAX_NUM];//中断注册表 @note_why 用 form 来表示?有种写 HTML的感觉 :P
STATIC CHAR *g_hwiFormName[OS_HWI_MAX_NUM] = {0};//记录每个硬中断的名称
STATIC UINT32 g_hwiFormCnt[OS_HWI_MAX_NUM] = {0};//记录每个硬中断的总数量
STATIC UINT32 g_curIrqNum = 0; //记录当前中断号
typedef VOID (*HWI_PROC_FUNC)(VOID); //中断函数指针
typedef struct tagHwiHandleForm {
HWI_PROC_FUNC pfnHook; //中断处理函数
HWI_ARG_T uwParam; //中断处理函数参数
struct tagHwiHandleForm *pstNext; //节点,指向下一个中断,用于共享中断的情况
} HwiHandleForm;
typedef struct tagIrqParam { //中断参数
int swIrq; // 软件中断
VOID *pDevId; // 设备ID
const CHAR *pName; //名称
} HwiIrqParam;
注册硬中断
/******************************************************************************
创建一个硬中断
中断创建,注册中断号、中断触发模式、中断优先级、中断处理程序。中断被触发时,
handleIrq会调用该中断处理程序
******************************************************************************/
LITE_OS_SEC_TEXT_INIT UINT32 LOS_HwiCreate(HWI_HANDLE_T hwiNum, //硬中断句柄编号 默认范围[0-127]
HWI_PRIOR_T hwiPrio, //硬中断优先级
HWI_MODE_T hwiMode, //硬中断模式 共享和非共享
HWI_PROC_FUNC hwiHandler,//硬中断处理函数
HwiIrqParam *irqParam) //硬中断处理函数参数
{
UINT32 ret;
(VOID)hwiPrio;
if (hwiHandler == NULL) {//中断处理函数不能为NULL
return OS_ERRNO_HWI_PROC_FUNC_NULL;
}
if ((hwiNum > OS_USER_HWI_MAX) || ((INT32)hwiNum < OS_USER_HWI_MIN)) {//中断数区间限制 [32,96]
return OS_ERRNO_HWI_NUM_INVALID;
}
#ifdef LOSCFG_NO_SHARED_IRQ //不支持共享中断
ret = OsHwiCreateNoShared(hwiNum, hwiMode, hwiHandler, irqParam);
#else
ret = OsHwiCreateShared(hwiNum, hwiMode, hwiHandler, irqParam);
#endif
return ret;
}
//创建一个共享硬件中断,共享中断就是一个中断能触发多个响应函数
STATIC UINT32 OsHwiCreateShared(HWI_HANDLE_T hwiNum, HWI_MODE_T hwiMode,
HWI_PROC_FUNC hwiHandler, const HwiIrqParam *irqParam)
{
UINT32 intSave;
HwiHandleForm *hwiFormNode = NULL;
HwiHandleForm *hwiForm = NULL;
HwiIrqParam *hwiParam = NULL;
HWI_MODE_T modeResult = hwiMode & IRQF_SHARED;
if (modeResult && ((irqParam == NULL) || (irqParam->pDevId == NULL))) {
return OS_ERRNO_HWI_SHARED_ERROR;
}
HWI_LOCK(intSave);//中断自旋锁
hwiForm = &g_hwiForm[hwiNum];//获取中断处理项
if ((hwiForm->pstNext != NULL) && ((modeResult == 0) || (!(hwiForm->uwParam & IRQF_SHARED)))) {
HWI_UNLOCK(intSave);
return OS_ERRNO_HWI_SHARED_ERROR;
}
while (hwiForm->pstNext != NULL) {//pstNext指向 共享中断的各处理函数节点,此处一直撸到最后一个
hwiForm = hwiForm->pstNext;//找下一个中断
hwiParam = (HwiIrqParam *)(hwiForm->uwParam);//获取中断参数,用于检测该设备ID是否已经有中断处理函数
if (hwiParam->pDevId == irqParam->pDevId) {//设备ID一致时,说明设备对应的中断处理函数已经存在了.
HWI_UNLOCK(intSave);
return OS_ERRNO_HWI_ALREADY_CREATED;
}
}
hwiFormNode = (HwiHandleForm *)LOS_MemAlloc(m_aucSysMem0, sizeof(HwiHandleForm));//创建一个中断处理节点
if (hwiFormNode == NULL) {
HWI_UNLOCK(intSave);
return OS_ERRNO_HWI_NO_MEMORY;
}
hwiFormNode->uwParam = OsHwiCpIrqParam(irqParam);//获取中断处理函数的参数
if (hwiFormNode->uwParam == LOS_NOK) {
HWI_UNLOCK(intSave);
(VOID)LOS_MemFree(m_aucSysMem0, hwiFormNode);
return OS_ERRNO_HWI_NO_MEMORY;
}
hwiFormNode->pfnHook = hwiHandler;//绑定中断处理函数
hwiFormNode->pstNext = (struct tagHwiHandleForm *)NULL;//指定下一个中断为NULL,用于后续遍历找到最后一个中断项(见于以上 while (hwiForm->pstNext != NULL)处)
hwiForm->pstNext = hwiFormNode;//共享中断
if ((irqParam != NULL) && (irqParam->pName != NULL)) {
g_hwiFormName[hwiNum] = (CHAR *)irqParam->pName;
}
g_hwiForm[hwiNum].uwParam = modeResult;
HWI_UNLOCK(intSave);
return LOS_OK;
}
解读
- 内核将硬中断进行编号,如:
#define NUM_HAL_INTERRUPT_TIMER0 33
#define NUM_HAL_INTERRUPT_TIMER1 33
#define NUM_HAL_INTERRUPT_TIMER2 34
#define NUM_HAL_INTERRUPT_TIMER3 34
#define NUM_HAL_INTERRUPT_TIMER4 35
#define NUM_HAL_INTERRUPT_TIMER5 35
#define NUM_HAL_INTERRUPT_TIMER6 36
#define NUM_HAL_INTERRUPT_TIMER7 36
#define NUM_HAL_INTERRUPT_DMAC 60
#define NUM_HAL_INTERRUPT_UART0 38
#define NUM_HAL_INTERRUPT_UART1 39
#define NUM_HAL_INTERRUPT_UART2 40
#define NUM_HAL_INTERRUPT_UART3 41
#define NUM_HAL_INTERRUPT_UART4 42
#define NUM_HAL_INTERRUPT_TIMER NUM_HAL_INTERRUPT_TIMER4
例如:时钟节拍处理函数
OsTickHandler
就是在HalClockInit
中注册的//硬时钟初始化
VOID HalClockInit(VOID)
{
// ...
(void)LOS_HwiCreate(NUM_HAL_INTERRUPT_TIMER, 0xa0, 0, OsTickHandler, 0);//注册OsTickHandler到中断向量表
}
//节拍中断处理函数 ,鸿蒙默认10ms触发一次
LITE_OS_SEC_TEXT VOID OsTickHandler(VOID)
{
UINT32 intSave;
TICK_LOCK(intSave);//tick自旋锁
g_tickCount[ArchCurrCpuid()]++;// 累加当前CPU核tick数
TICK_UNLOCK(intSave);
OsTimesliceCheck();//时间片检查
OsTaskScan(); /* task timeout scan *///扫描超时任务 例如:delay(300)
#if (LOSCFG_BASE_CORE_SWTMR == YES)
OsSwtmrScan();//扫描定时器,查看是否有超时定时器,加入队列
#endif
}
- 鸿蒙是支持中断共享的,在
OsHwiCreateShared
中,将函数注册到g_hwiForm
中.中断向量完成注册后,就是如何触发和回调的问题.触发在 鸿蒙内核源码分析(总目录)中断切换篇中已经讲清楚,触发是从底层汇编向上调用,调用的C函数就是HalIrqHandler
中断怎么触发的?
分两种情况:
通过硬件触发,比如按键,USB的插拔这些中断源向中断控制器发送电信号(高低电平触发或是上升/下降沿触发),中断控制器经过过滤后将信号发给对应的CPU处理,通过硬件改变PC和CPSR寄存值,直接跳转到中断向量(固定地址)执行.
b reset_vector @开机代码
b _osExceptUndefInstrHdl @异常处理之CPU碰到不认识的指令
b _osExceptSwiHdl @异常处理之:软中断
b _osExceptPrefetchAbortHdl @异常处理之:取指异常
b _osExceptDataAbortHdl @异常处理之:数据异常
b _osExceptAddrAbortHdl @异常处理之:地址异常
b OsIrqHandler @异常处理之:硬中断
b _osExceptFiqHdl @异常处理之:快中断
通过软件触发,常见于核间中断的情况, 核间中断指的是几个CPU之间相互通讯的过程.以下为某一个CPU向其他CPU(可以是多个)发起让这些CPU重新调度
LOS_MpSchedule
的中断请求信号.最终是写了中断控制器的GICD_SGIR
寄存器,这是一个由软件触发中断的寄存器.中断控制器会将请求分发给对应的CPU处理中断,即触发了OsIrqHandler
.//给参数CPU发送调度信号
VOID LOS_MpSchedule(UINT32 target)//target每位对应CPU core
{
UINT32 cpuid = ArchCurrCpuid();
target &= ~(1U << cpuid);//获取除了自身之外的其他CPU
HalIrqSendIpi(target, LOS_MP_IPI_SCHEDULE);//向目标CPU发送调度信号,核间中断(Inter-Processor Interrupts),IPI
}
//SGI软件触发中断(Software Generated Interrupt)通常用于多核间通讯
STATIC VOID GicWriteSgi(UINT32 vector, UINT32 cpuMask, UINT32 filter)
{
UINT32 val = ((filter & 0x3) << 24) | ((cpuMask & 0xFF) << 16) |
(vector & 0xF); GIC_REG_32(GICD_SGIR) = val;//写SGI寄存器
}
//向指定核发送核间中断
VOID HalIrqSendIpi(UINT32 target, UINT32 ipi)
{
GicWriteSgi(ipi, target, 0);
}
中断统一处理入口函数 HalIrqHandler
//硬中断统一处理函数,这里由硬件触发,调用见于 ..\arch\arm\arm\src\los_dispatch.S
VOID HalIrqHandler(VOID)
{
UINT32 iar = GIC_REG_32(GICC_IAR);//从中断确认寄存器获取中断ID号
UINT32 vector = iar & 0x3FFU;//计算中断向量号
/*
* invalid irq number, mainly the spurious interrupts 0x3ff,
* gicv2 valid irq ranges from 0~1019, we use OS_HWI_MAX_NUM
* to do the checking.
*/
if (vector >= OS_HWI_MAX_NUM) {
return;
}
g_curIrqNum = vector;//记录当前中断ID号
OsInterrupt(vector);//调用上层中断处理函数
/* use orignal iar to do the EOI */
GIC_REG_32(GICC_EOIR) = iar;//更新中断结束寄存器
}
VOID OsInterrupt(UINT32 intNum)//中断实际处理函数
{
HwiHandleForm *hwiForm = NULL;
UINT32 *intCnt = NULL;
intCnt = &g_intCount[ArchCurrCpuid()];//当前CPU的中断总数量 ++
*intCnt = *intCnt + 1;//@note_why 这里没看明白为什么要 +1
#ifdef LOSCFG_CPUP_INCLUDE_IRQ //开启查询系统CPU的占用率的中断
OsCpupIrqStart();//记录本次中断处理开始时间
#endif
#ifdef LOSCFG_KERNEL_TICKLESS
OsTicklessUpdate(intNum);
#endif
hwiForm = (&g_hwiForm[intNum]);//获取对应中断的实体
#ifndef LOSCFG_NO_SHARED_IRQ //如果没有定义不共享中断 ,意思就是如果是共享中断
while (hwiForm->pstNext != NULL) { //一直撸到最后
hwiForm = hwiForm->pstNext;//下一个继续撸
#endif
if (hwiForm->uwParam) {//有参数的情况
HWI_PROC_FUNC2 func = (HWI_PROC_FUNC2)hwiForm->pfnHook;//获取回调函数
if (func != NULL) {
UINTPTR *param = (UINTPTR *)(hwiForm->uwParam);
func((INT32)(*param), (VOID *)(*(param + 1)));//运行带参数的回调函数
}
} else {//木有参数的情况
HWI_PROC_FUNC0 func = (HWI_PROC_FUNC0)hwiForm->pfnHook;//获取回调函数
if (func != NULL) {
func();//运行回调函数
}
}
#ifndef LOSCFG_NO_SHARED_IRQ
}
#endif
++g_hwiFormCnt[intNum];//中断号计数器总数累加
*intCnt = *intCnt - 1; //@note_why 这里没看明白为什么要 -1
#ifdef LOSCFG_CPUP_INCLUDE_IRQ //开启查询系统CPU的占用率的中断
OsCpupIrqEnd(intNum);//记录中断处理时间完成时间
#endif
}
解读
统一中断处理函数是一个通过一个中断号去找到注册函数的过程,分四步走:
- 第一步:取号,这号是由中断控制器的
GICC_IAR
寄存器提供的,这是一个专门保存当前中断号的寄存器. - 第二步:从注册表
g_hwiForm
中查询注册函数,同时取出参数. - 第三步:执行函数,也就是回调注册函数,分有参和无参两种情况
func(...)
,在中断共享的情况,注册函数会指向 next 注册函数pstNext
,依次执行回调函数,这是中断共享的实现细节.typedef struct tagHwiHandleForm {
HWI_PROC_FUNC pfnHook; //中断处理函数
HWI_ARG_T uwParam; //中断处理函数参数
struct tagHwiHandleForm *pstNext; //节点,指向next中断,用于共享中断的情况
} HwiHandleForm;
- 第四步:销号,本次中断完成了就需要消除记录,中断控制器也有专门的销号寄存器
GICC_EOIR
- 另外的是一些统一数据,每次中断号处理内核都会记录次数,和耗时,以便定位/跟踪/诊断问题.
鸿蒙内核源码分析.总目录
v08.xx 鸿蒙内核源码分析(总目录) | 百万汉字注解 百篇博客分析 | 51.c.h .o
百万汉字注解.百篇博客分析
百万汉字注解 >> 精读鸿蒙源码,中文注解分析, 深挖地基工程,大脑永久记忆,四大码仓每日同步更新< gitee| github| csdn| coding >
百篇博客分析 >> 故事说内核,问答式导读,生活式比喻,表格化说明,图形化展示,主流站点定期更新中< 51cto| csdn| harmony| osc >
关注不迷路.代码即人生
QQ群:790015635 | 入群密码: 666
原创不易,欢迎转载,但请注明出处.
鸿蒙内核源码分析(中断管理篇) | 江湖从此不再怕中断 | 百篇博客分析OpenHarmony源码 | v44.02的更多相关文章
- 鸿蒙内核源码分析(内存管理篇) | 虚拟内存全景图是怎样的 | 百篇博客分析OpenHarmony源码 | v12.04
百篇博客系列篇.本篇为: v12.xx 鸿蒙内核源码分析(内存管理篇) | 虚拟内存全景图是怎样的 | 51.c.h .o 内存管理相关篇为: v11.xx 鸿蒙内核源码分析(内存分配篇) | 内存有 ...
- 鸿蒙内核源码分析(任务管理篇) | 任务池是如何管理的 | 百篇博客分析OpenHarmony源码 | v5.05
百篇博客系列篇.本篇为: v05.xx 鸿蒙内核源码分析(任务管理篇) | 任务池是如何管理的 | 51.c.h .o 任务管理相关篇为: v03.xx 鸿蒙内核源码分析(时钟任务篇) | 触发调度谁 ...
- 鸿蒙内核源码分析(中断概念篇) | 海公公的日常工作 | 百篇博客分析OpenHarmony源码 | v43.02
百篇博客系列篇.本篇为: v43.xx 鸿蒙内核源码分析(中断概念篇) | 海公公的日常工作 | 51.c.h .o 硬件架构相关篇为: v22.xx 鸿蒙内核源码分析(汇编基础篇) | CPU在哪里 ...
- 鸿蒙内核源码分析(中断切换篇) | 系统因中断活力四射 | 百篇博客分析OpenHarmony源码 | v42.02
百篇博客系列篇.本篇为: v42.xx 鸿蒙内核源码分析(中断切换篇) | 系统因中断活力四射 | 51.c.h .o 硬件架构相关篇为: v22.xx 鸿蒙内核源码分析(汇编基础篇) | CPU在哪 ...
- 鸿蒙内核源码分析(汇编汇总篇) | 所有的汇编代码都在这里 | 百篇博客分析OpenHarmony源码 | v40.03
百篇博客系列篇.本篇为: v40.xx 鸿蒙内核源码分析(汇编汇总篇) | 汇编可爱如邻家女孩 | 51.c.h .o 硬件架构相关篇为: v22.xx 鸿蒙内核源码分析(汇编基础篇) | CPU在哪 ...
- 鸿蒙内核源码分析(异常接管篇) | 社会很单纯 , 复杂的是人 | 百篇博客分析OpenHarmony源码 | v39.03
百篇博客系列篇.本篇为: v39.xx 鸿蒙内核源码分析(异常接管篇) | 社会很单纯,复杂的是人 | 51.c.h .o 硬件架构相关篇为: v22.xx 鸿蒙内核源码分析(汇编基础篇) | CPU ...
- 鸿蒙内核源码分析(寄存器篇) | 小强乃宇宙最忙存储器 | 百篇博客分析OpenHarmony源码 | v38.02
百篇博客系列篇.本篇为: v38.xx 鸿蒙内核源码分析(寄存器篇) | 小强乃宇宙最忙存储器 | 51.c.h .o 硬件架构相关篇为: v22.xx 鸿蒙内核源码分析(汇编基础篇) | CPU在哪 ...
- 鸿蒙内核源码分析(工作模式篇) | CPU是韦小宝,七个老婆 | 百篇博客分析OpenHarmony源码 | v36.04
百篇博客系列篇.本篇为: v36.xx 鸿蒙内核源码分析(工作模式篇) | CPU是韦小宝,七个老婆 | 51.c.h .o 硬件架构相关篇为: v22.xx 鸿蒙内核源码分析(汇编基础篇) | CP ...
- 鸿蒙内核源码分析(汇编传参篇) | 如何传递复杂的参数 | 百篇博客分析OpenHarmony源码 | v23.02
百篇博客系列篇.本篇为: v23.xx 鸿蒙内核源码分析(汇编传参篇) | 如何传递复杂的参数 | 51.c.h .o 硬件架构相关篇为: v22.xx 鸿蒙内核源码分析(汇编基础篇) | CPU在哪 ...
随机推荐
- 题解 Connect
传送门 各种骗分无果,特殊性质还手残写挂了-- 首先完全图上直接输出边权 \(\times (n-2)\) 就行了,然而我脑残乘的 \(n-1\) 看数据范围肯定是状压,但是压边肯定炸了,考虑压点 因 ...
- ASP.NET Core教程:使用Supervisor做ASP.NET Core应用程序守护进程
一.前言 在上一篇文章中,我们讲解了如何在Linux服务器上面部署ASP.NET Core应用程序,并且使用Nginx作为反向代理.我们在Linux服务器上面,是通过ASP.NET Core自宿主的方 ...
- 获取sim 卡的IMEI 和 IMSI
IReadOnlyList<string> networkAccIds = Windows.Networking.NetworkOperators.MobileBroadbandAccou ...
- java多线程的一些面试题
8.callable与fature Callable与Runnable类似,但是Callable有返回值,并且有一个参数化的类型. Fature保存异步计算的结果.9.执行器 Executor.10. ...
- CompletionService简介、原理以及小案例
博客1:http://www.oschina.net/question/12_11255 博客2: CompletionService简介 CompletionService与ExecutorServ ...
- xv6学习笔记(5) : 锁与管道与多cpu
xv6学习笔记(5) : 锁与管道与多cpu 1. xv6锁结构 1. xv6操作系统要求在内核临界区操作时中断必须关闭. 如果此时中断开启,那么可能会出现以下死锁情况: 进程A在内核态运行并拿下了p ...
- 不用调整Nginx,SpringBoot也能解决前端访问的跨域问题
1.什么情况下会出现跨域问题 通常,在前端工程师的开发过程中,往往在本地机器启动前端服务, 而调用的后端接口服务是在另外一台机器运行,这时就会出现跨域问题,让接口无法调通. 而到了测试环境和生产环境, ...
- vue@cli3 项目模板怎么使用public目录下的静态文件,找了好久都不对,郁闷!
作为图片最好放在static目录下,但是vue@cli3没有static,网上都说放在public目录下,行,那就放吧,可问题是图片放了怎么使用 第一次尝试 肯定用绝对路径这就不说了,用相对路径,we ...
- ES6对象扩展——扩展运算符
1.复制对象:使用扩展运算符复制对象时是浅拷贝 //复制对象:使用扩展运算符复制对象时是浅拷贝 const obj1 = { a:1, b:2, d:{ aa:1, bb:2 } } const ob ...
- MySQL-SQL基础-查询1
#子查询-某些情况下,当进行查询的时候,需要的条件是另外一个select语句的结果,这个时候就要用到子查询.用于子查询的关键字主要包括: in.not in.=.!=.exists.not exist ...