diff --git a/SUMMARY.md b/SUMMARY.md index 17af6d006e356a3e0928c028713a80185e7de645..ddc316d63b247292c85f90e1a499006ec1b8d207 100644 --- a/SUMMARY.md +++ b/SUMMARY.md @@ -19,8 +19,8 @@ - [SMP多核调度](programing-manual/sched/smp.md) - [进程管理](programing-manual/process/process.md) - [系统调用](programing-manual/process/syscall.md) - - [中断系统](programing-manual/irq/irq.md) - - [延迟队列](programing-manual/irq/delay_queue.md) + - [中断系统](programing-manual/interrupt/irq.md) + - [延迟队列](programing-manual/interrupt/delay_queue.md) - [定时器](programing-manual/timer/timer.md) - [驱动框架](programing-manual/driver/framework.md) - [单元测试](programing-manual/test/utest.md) diff --git a/programing-manual/interrupt/delay_queue.md b/programing-manual/interrupt/delay_queue.md new file mode 100644 index 0000000000000000000000000000000000000000..a14f435aef9e5b604fd04e8aa593ba486620de85 --- /dev/null +++ b/programing-manual/interrupt/delay_queue.md @@ -0,0 +1,69 @@ +# 中断延迟队列 + +## 一、描述 + +中断延迟队列是一种延迟处理中断的机制,类似于 `linux` 中断机制中的中断下半部分,用来进行具体的中断处理。 +中断的要求是快速地响应中断,并且是关闭了全局中断的,然后回到之前的工作。而延迟队列就是来完成这个工作的,产生了中断后,只进行最少的工作, +然后激活中断队列工作,然后就返回,完成了本次中断。等到没有中断的时候,再去从延迟队列中获取一个工作来处理其对应的中断下半部分处理工作。 + +## 二、原理 + +中断延迟队列在某个驱动或者内核组件中会进行初始化。当产生中断的时候,在中断处理函数中,只对中断产生时的数据进行保存,不对中断产生时的数据进行处理, +而是处理中断延迟工作,然后就直接返回。等到响应了该中断之后,再去调用延迟队列中的工作。 + +由于中断是具有一定优先级的,有的中断需要先处理,而有的中断则可以后处理,因此就有了多个中断队列,每个队列的优先级是不同的。至于需要加入哪种队列, +是在该延迟中断工作初始化的时候设定的。 + +工作队列的优先级如下,其中 `NX_IRQ_FAST_QUEUE` 优先级最高, `NX_IRQ_SLOW_QUEUE` 优先级最低。 + +```c +enum NX_IRQ_DelayQueue +{ + NX_IRQ_FAST_QUEUE = 0, + NX_IRQ_NORMAL_QUEUE, + NX_IRQ_PERIOD_QUEUE, + NX_IRQ_SCHED_QUEUE, + NX_IRQ_SLOW_QUEUE, + NX_IRQ_QUEUE_NR, +}; +``` + +内核的定时器属于 `NX_IRQ_FAST_QUEUE` ,这样才能保证时间的精度。 +而内核的任务调度属于 `NX_IRQ_SCHED_QUEUE` ,及时调度的精度低一点也没有太大影响。 + +## 三、框架图 + +![框架图](figures/delay_queue.png) + +## 四、接口 + +创建一个延迟的中断工作,需要传入中断工作处理函数入口,以及函数执行时的参数,就可以创建一个延迟的工作对象。 +```c +NX_IRQ_DelayWork *NX_IRQ_DelayWorkCreate(NX_IRQ_WorkHandler handler, void *arg, NX_U32 flags); +``` + +如果是一个静态对象,不需要创建,那么可以直接调用初始化,并传入该对象的地址即可,参数同上。 +```c +NX_Error NX_IRQ_DelayWorkInit(NX_IRQ_DelayWork *work, NX_IRQ_WorkHandler handler, void *arg, NX_U32 flags); +``` + +有创建工作,那么就有销毁工作,只需要传入工作对象的地址即可。 +```c +NX_Error NX_IRQ_DelayWorkDestroy(NX_IRQ_DelayWork *work); +``` + +创建了一个中断工作对象,就需要将其加入到某个延迟队列中,只需要传入队列和工作对象的地址即可。 +```c +NX_Error NX_IRQ_DelayQueueEnter(NX_IRQ_DelayQueue queue, NX_IRQ_DelayWork *work); +``` + +和加入延迟队列相反,有离开延迟队列,那么和加入的参数一样,只是意思相反而已。 +```c +NX_Error NX_IRQ_DelayQueueLeave(NX_IRQ_DelayQueue queue, NX_IRQ_DelayWork *work); +``` + +最后,当有中断产生的时候,就可以去激活中断工作,那么则需要调用 `NX_IRQ_DelayWorkHandle` 来处理工作 +对象即可,传入工作对象的地址即可。 +```c +NX_Error NX_IRQ_DelayWorkHandle(NX_IRQ_DelayWork *work); +``` diff --git a/programing-manual/interrupt/figures/delay_queue.png b/programing-manual/interrupt/figures/delay_queue.png new file mode 100644 index 0000000000000000000000000000000000000000..7db6f4c32d8b873a7fc1b0061d48daadb82e234f Binary files /dev/null and b/programing-manual/interrupt/figures/delay_queue.png differ diff --git a/programing-manual/interrupt/figures/interrupt.png b/programing-manual/interrupt/figures/interrupt.png new file mode 100644 index 0000000000000000000000000000000000000000..2f343db312be8dd4bbe218cb147681e5a2b7c070 Binary files /dev/null and b/programing-manual/interrupt/figures/interrupt.png differ diff --git a/programing-manual/interrupt/irq.md b/programing-manual/interrupt/irq.md new file mode 100644 index 0000000000000000000000000000000000000000..c4bcf10c9cf5a2e8a739b156c6364e4f2790eca5 --- /dev/null +++ b/programing-manual/interrupt/irq.md @@ -0,0 +1,49 @@ +# 中断管理 + +## 一、描述 + +中断是由处理器支持的一个临时打断当前执行流的功能,可分为设备中断和处理器异常。设备中断就是当网卡设备收到数据后会产生一个中断,打断处理器当前在处理的事情,去处理网络数据包。异常中断就是当处理器执行了一个不存在的指令或者访问了一个没有权限的内存地址,处理器就会中断当前的执行流,去对异常做一个处理,一般都是 `kill` 当前的进程。 + +## 二、原理 + +中断管理的实现是基于处理器的中断管理机制的,不同的架构具体实现机制可能有差别,但是在大体逻辑上都是一致的,因此可以抽象出一套统一的接口来供上层使用。 + +由于中断数量会比较多,因此,都会有一个中断表来注册中断。然后由一个寄存器或者多个寄存器来记录该表的地址。产生中断的时候会去查表,根据具体的中断号去找对应的中断处理函数,简单来说就是去调用中断的回调函数。 + +中断是可以全局打开和关闭的,打开后才会响应中断。除此之外,对于每一个具体的中断,都可以打开和关闭,在禁止了网卡中断后,即使打开了全局中断,也收不到网络数据包了。但是,异常是不可以屏蔽的,异常是来告诉内核出现一些问题了,需要根据具体的异常做处理。有的异常是不可以修复的,会导致系统死机,有的异常时可以修复的,比如页故障,可以拿来实现页面延迟映射, `COW` 等机制。 + +## 三、框架图 + +![框架图](figures/interrupt.png) + +## 四、接口 + +需要注册某个中断的时候,需要传入中断号,绑定中断处理函数,设置中断的参数数据,设置中断的名字。 +```c +NX_Error NX_IRQ_Bind(NX_IRQ_Number irqno, + NX_IRQ_Handler handler, + void *data, + char *name, + NX_U32 flags); +``` + +当需要注销某个中断的时候,只需要传入中断号以及注册时的数据即可。相当于解除了中断函数的绑定。 +```c +NX_Error NX_IRQ_Unbind(NX_IRQ_Number irqno, void *data); +``` + +注册了中断后,并不能产生中断,还需要解除中断的屏蔽(unmask),相当于打开中断。除此之外,如果不希望产生该中断,则需要屏蔽某个中断(mask),相当于关闭中断。 +```c +NX_Error NX_IRQ_Unmask(NX_IRQ_Number irqno); +NX_Error NX_IRQ_Mask(NX_IRQ_Number irqno); +``` + +前面有讲到关于全局中断的打开和关闭,全局中断的打开是 `NX_IRQ_Enable` 函数,相反关闭则是 + `NX_IRQ_Disable` 。由于有的时候只是为了暂时关闭中断,处理完后需要恢复中断的状态,于是则需要使用保存中断的状态和恢复中断的状态,即 `NX_IRQ_SaveLevel` 和 `NX_IRQ_RestoreLevel` 。 + +```c +#define NX_IRQ_Enable() NX_IRQ_ControllerInterface.enable() +#define NX_IRQ_Disable() NX_IRQ_ControllerInterface.disable() +#define NX_IRQ_SaveLevel() NX_IRQ_ControllerInterface.saveLevel() +#define NX_IRQ_RestoreLevel(level) NX_IRQ_ControllerInterface.restoreLevel(level) +```