如何创建中断堆栈?

时间:2015-10-06 07:58:31

标签: c stack interrupt

我希望我的中断服务程序使用不同的堆栈(可能是它自己的)&不使用调用者线程的堆栈。

thread_entry (){
    do_something();
    --> Interrupt occurs
    do_otherstuff();    

}

void interrupt_routine ()
{
    uint8_t read_byte;     // I don't want this to be part of caller thread's stack
    read_byte= hw_read();
}

是否可能&如何实现这个目标?

3 个答案:

答案 0 :(得分:1)

The stacks required for OS and interrupt handlers is set up at initialization itself. This is again architecture specific code. For case of ARM processors it has a distinct R13 that is used when the processor is in the interrupt mode. Again this register is initialized at bootup. What is the problem you want to address with this design.

答案 1 :(得分:0)

The GNU C library for Linux has methods to control the stack in which the signal executes. Refer to the documentation for full details.

The basic idea is that you allocate memory for the stack and the call the function

sigstack()

to specify that this stack is available to be used for signal handling. You then use the

sigaction()

function to register a handler for a particular signal and specify the flag value

SA_ONSTACK

that this handler runs on the special stack

Here is a code snippet showing the pattern, it's "borrowed" from the Linux Programming Interface examples

 sigstack.ss_sp = malloc(SIGSTKSZ);
 if (sigstack.ss_sp == NULL)
     errExit("malloc");

 sigstack.ss_size = SIGSTKSZ;
 sigstack.ss_flags = 0;
 if (sigaltstack(&sigstack, NULL) == -1)
     errExit("sigaltstack");
 printf("Alternate stack is at         %10p-%p\n",
         sigstack.ss_sp, (char *) sbrk(0) - 1);

 sa.sa_handler = sigsegvHandler;     /* Establish handler for SIGSEGV */
 sigemptyset(&sa.sa_mask);
 sa.sa_flags = SA_ONSTACK;           /* Handler uses alternate stack */
 if (sigaction(SIGSEGV, &sa, NULL) == -1)
     errExit("sigaction");

答案 2 :(得分:0)

Here's a simple x86 inline assembly implementation. You have a wrapper function which changes the stack, and calls your real routine.

const uint32_t interrupt_stack_size = 4096;
uint8_t interrupt_stack[interrupt_stack_size];

void interrupt_routine_wrap()
{
    static int thread_esp;
    // Stack grows towards lower addresses, so start at the bottom
    static int irq_esp = (int) interrupt_stack + interrupt_stack_size;

    // Store the old esp
    asm mov dword ptr thread_esp, esp;

    // Set the new esp
    asm mov esp, dword ptr irq_esp;

    // Execute the real interrupt routine
    interrupt_routine(); 

    // Restore old esp
    asm mov esp, dword ptr thread_esp;
}

I'm completely ignoring the segment register here (ss), but different memory models may need to store that along with sp.

You can get rid of the inline assembly by using setjmp/longjmp to read/write all registers. That's a more portable way to do it.

Also note that I'm not preserving any registers here, and inline assembly may confuse the compiler. Perhaps it'd be worth it to add a pusha/popa pair around the wrapper routine. Compiler may do this for you if you specify the function as interrupt. Check the resulting binary to be certain.