ISR(中断服务程序)是否有单独的堆栈?

Does ISR (Interrupt Service Routine) have a separate stack?

使用 RTOS(例如 FreeRTOS)时,我们为每个线程提供单独的堆栈空间。那么ISR(Interrupt Service Routines)呢,它们在内存中有单独的栈吗?或者这是可配置的?

如果他们没有用于存储在 ISR 中声明的局部变量的堆栈?

我有完全相同的问题,大量搜索使我得出这个结论:答案取决于您的芯片以及您使用的 OS 如何配置该芯片。

所以看看我最喜欢的芯片之一 ARM Cortex-M3(中断是一种例外形式),各个地方的文档如下:

Operating Modes

The Cortex-M3 supports Privileged and User (non-privileged) execution. Code run as Privileged has full access rights whereas code executed as User has limited access rights. The limitations include restrictions on instruction use such as MSR fields, access to memory and peripherals based on system design, and restrictions imposed by the MPU configuration.

The processor supports two operation modes, Thread mode and Handler mode. Thread mode is entered on reset and normally on return from an exception. When in Thread mode, code can be executed as either Privileged or Unprivileged.

Handler mode will be entered as a result of an exception. Code in Handler mode is always executed as Privileged, therefore the core will automatically switch to Privileged mode when exceptions occur. You can change between Privileged Thread mode and User Thread mode when returning from an exception by modifying the EXC_RETURN value in the link register (R14). You can also change from Privileged Thread to User Thread mode by clearing CONTROL[0] using an MSR instruction. However, you cannot directly change to privileged mode from unprivileged mode without going through an exception, for example an SVC.

Main and Process Stacks

The Cortex-M3 supports two different stacks, a main stack and a process stack. To support this the Cortex-M3 has two stack pointers (R13). One of these is banked out depending on the stack in use. This means that only one stack pointer at a time is visible as R13. However, both stack pointers can be accessed using the MRS and MSR instructions. The main stack is used at reset, and is always used in Handler mode (when entering an exception handler). The process stack pointer is only available as the current stack pointer when in Thread mode. You can select which stack pointer (main or process) is used in Thread mode in one of two ways, either by using the EXC_RETURN value when exiting from Handler Mode or while in Thread Mode by writing to CONTROL[1] using an MSR instruction.

还有...

When the processor takes an exception, unless the exception is a tail-chained or a late-arriving exception, the processor pushes information onto the current stack. This operation is referred to as stacking and the structure of eight data words is referred as the stack frame. ...

Immediately after stacking, the stack pointer indicates the lowest address in the stack frame

来自本书"The Definitive Guide to the ARM Cortex-M3":

The MSP, also called SP_main in ARM documentation, is the default SP after power-up; it is used by kernel code and exception handlers. The PSP, or SP_process in ARM documentation, is typically used by thread processes in system with embedded OS running.

Because exception handlers always use the Main Stack Pointer, the main stack memory should contain enough space for the largest number of nesting interrupts.

When an exception takes place, the registers R0–R3, R12, LR, PC, and Program Status (PSR) are pushed to the stack. If the code that is running uses the Process Stack Pointer (PSP), the process stack will be used; if the code that is running uses the Main Stack Pointer (MSP), the main stack will be used. Afterward, the main stack will always be used during the handler, so all nested interrupts will use the main stack.

2017 年 6 月更新:

我之前的回答不正确,我分析了 FreeRTOS 的 cortex 处理器并将我的回答重写为:

Cortex-M3 的标准 FreeRTOS 版本实际上同时配置和使用 MSP 和 PSP。当第一个任务运行时,它修改 MSP 以指向向量中指定的第一个地址 table (0x00000000),这往往是 SRAM 中的最后一个字,然后它触发系统调用,在系统调用中异常处理程序它将 PSP 设置为下一个任务堆栈位置,然后它修改异常 LR 值使得 "return to thread mode and on return use the process stack".

这意味着中断服务例程(AKA 异常处理程序)堆栈从向量 table 中指定的地址向下增长。

您可以配置您的链接器和启动代码以将异常处理程序堆栈放在您喜欢的任何位置,确保您的堆或其他内存区域不与异常处理程序区域重叠并确保该区域足够大。

其他芯片和操作系统的答案可能完全不同!

为了帮助确保您的应用程序在 ISR 堆栈 (MSP) 上具有适当的 space, 这里有一些额外的代码来检查实际的 ISR 堆栈使用情况。除了您已经在 FreeRTOS 任务堆栈上执行的检查之外,还使用:

https://sourceforge.net/p/freertos/discussion/382005/thread/8418dd523e/

更新:我发布了我的 port.c 版本,其中包括 github 上的 ISR 堆栈使用检查:

https://github.com/DRNadler/FreeRTOS_helpers