📄 qanda.html
字号:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"><html><head><meta http-equiv="Content-Type" content="text/html;charset=UTF-8"><title>YAVRTOS: Questions and Answers</title><link href="doxygen.css" rel="stylesheet" type="text/css"><link href="tabs.css" rel="stylesheet" type="text/css"></head><body><!-- Generated by Doxygen 1.5.4 --><div class="tabs"> <ul> <li><a href="index.html"><span>Main Page</span></a></li> <li><a href="modules.html"><span>Modules</span></a></li> <li><a href="annotated.html"><span>Data Structures</span></a></li> <li><a href="files.html"><span>Files</span></a></li> <li><a href="pages.html"><span>Related Pages</span></a></li> </ul></div><h1><a class="anchor" name="QandA">Questions and Answers </a></h1><h2><a class="anchor" name="qanda-int">What happens if I disable interrupts, then suspend myself (e.g. by waiting on a semaphore)?</a></h2>If you disable interrupts, then the task switcher cannot run, as it depends on the tick interrupt. However, if you then use an API call that can run the task switcher as a side-effect, the task switcher could decide to run a task that didn't disable interrupts Since the task switcher remembers which tasks have disabled interrupts and which tasks haven't, interrupts would be re-enabled.<p>However, the next time the task switcher chooses your task, it will re-disable interrupts before resuming your task. Indeed, it will disable interrupts before it even decides that your task is the next task that should run.<h2><a class="anchor" name="qanda-int2">Why would I want to disable interrupts anyway?</a></h2>When interrupts are enabled, your task can be interrupted literally in the middle of a statement. If that statement was accessing a shared resource, then the state of that shared resource could change right in the middle of your statement. The only way to ensure that you are acting on a consistent shared resource is to disable interrupts as you are accessing it.<p>The classic example of this problem is a bank.<p><div class="fragment"><pre class="fragment">int32_t balance = 80;<span class="keywordtype">void</span> deduct_task_1(<span class="keywordtype">void</span> *p) { <span class="keywordflow">if</span> (balance >= 20) { balance -= 20; }}<span class="keywordtype">void</span> deduct_task_2(<span class="keywordtype">void</span> *p) { <span class="keywordflow">if</span> (balance >= 50) { balance -= 50; }}</pre></div><p>Assuming that the two tasks are executing simultaneously, and <code>deduct_task_1</code> is the first one to run. It will see that the balance is sufficient, and will execute the "balance -= 20" statement. What that statement actually means, of course, is "balance = balance - 20". In other words, "read the value of balance, subtract 20, and store the result back in balance".<p>The problem arises when the task is interrupted, for instance, between "subtract 20" and "store the result back in balance". When it has subtracted 20, the new value is 60, and it is prepared to store that result back. However, if the task is interrupted, and <code>deduct_task_2</code> is run, it will put a value of 30 into balance. Then, when <code>deduct_task_1</code> resumes, it will over-write the value of balance with 60, with the result that the customer has gained 50 cent.<p>The only way to fix this is to make sure that the two tasks cannot interrupt each other -<p><div class="fragment"><pre class="fragment">int32_t balance = 80;<span class="keywordtype">void</span> deduct_task_1(<span class="keywordtype">void</span> *p) { <a class="code" href="group__task.html#g42c4528688cb31827aa5efb696995642" title="A flag indicating whether interrupts were enabled - used by disable_interrupts()...">interrupt_store_t</a> interrupts = <a class="code" href="group__task.html#g6984a5336b36a4ca99682a2cf5ae044f" title="Disable interrupts system-wide.">disable_interrupts</a>(); <span class="keywordflow">if</span> (balance >= 20) { balance -= 20; } <a class="code" href="group__task.html#gecf8a8e43c2afae89e46503ff1c887bf" title="Restore the state of the system-wide interrupts.">restore_interrupts</a>(interrupts);}<span class="keywordtype">void</span> deduct_task_2(<span class="keywordtype">void</span> *p) { <a class="code" href="group__task.html#g42c4528688cb31827aa5efb696995642" title="A flag indicating whether interrupts were enabled - used by disable_interrupts()...">interrupt_store_t</a> interrupts = <a class="code" href="group__task.html#g6984a5336b36a4ca99682a2cf5ae044f" title="Disable interrupts system-wide.">disable_interrupts</a>(); <span class="keywordflow">if</span> (balance >= 50) { balance -= 50; } <a class="code" href="group__task.html#gecf8a8e43c2afae89e46503ff1c887bf" title="Restore the state of the system-wide interrupts.">restore_interrupts</a>(interrupts);}</pre></div><p>You will notice that we disable interrupts before we check the customers balance - if we didn't, the balance could change between when we check it and when we do the deduction, and so we could end up giving the customer credit on their balance.<h2><a class="anchor" name="qanda-int3">Must I therefore disable interrupts every time I access a shared resource?</a></h2>You can either disable interrupts, or create a <a class="el" href="group__mutex.html">mutex</a> for the shared resource and lock on that whenever you access the resource. However, since mutexes are not available to ISRs, the only way to access a shared resource that is also used by an ISR is by disabling interrupts.<p>The only exception to this rule is if your C compiler compiles the statement that accesses the resource into a single CPU instruction. This can happen, but is rare.<p>Note that all YAVRTOS functions disable interrupts as appropriate, so if you are using <a class="el" href="group__mailbox.html">mailboxes</a> or <a class="el" href="group__semaphore.html">semaphores</a> in an ISR, there is no need to worry about interrupts in your tasks when you are accessing those resources.<h2><a class="anchor" name="qanda-stop">Why can a task only be stopped by a higher-priority task?</a></h2>The only way that task A can know if it has stopped task B completely is if task A is a higher-priority task than task B. Why? Because a task C, of higher-priority than both A and B, could re-start task B as soon as it has died. But if task B is of lower priority than task A, task B will not be able to re-start until task A has finished processing the fact that task B has died.<p>(Therefore, it is in fact possible for a lower-priority task to stop a higher-priority task - it just isn't possible for that task to know when the higher-priority task has completely stopped. However, I think that is bad practice, so it is disabled in YAVRTOS).<h2><a class="anchor" name="qanda-pri">Can I change the priority of a task?</a></h2>No. This has to do with the stopping of tasks - if a task is being stopped by a higher-priority task, and it is re-started with an even higher priority, the task may get to start before the stopping task is informed that it has stopped.<h2><a class="anchor" name="qanda-sei">When my ISR runs, interrupts are disabled - may I re-enable them?</a></h2>Yes. If your ISR is interrupted by another ISR, the second ISR will continue to use the system stack. When the last ISR has completed, a task switch will be performed if any of the ISRs requested it.<h2><a class="anchor" name="qanda-cleanup">What is the task cleanup function about?</a></h2>The task cleanup procedure is called whenever a (non-zero-priority) task is stopped. A task is stopped by someone calling <a class="el" href="group__task.html#ge1337adc1d63d7cd874a376d6513c04a" title="Stop a task.">stop_task()</a>, or when the task procedure finishes.<p>The task cleanup procedure should be used to release any resources that the task may have reserved. Since <a class="el" href="group__task.html#ge1337adc1d63d7cd874a376d6513c04a" title="Stop a task.">stop_task()</a> may be called at any time, the task may have reserved resources (e.g. memory) that it would need to release before it goes away.<p>See <a class="el" href="task_8c.html#b1c4cc247bd379e340e25bd86911919d" title="The entry point for all tasks that are stopping">task_stopper()</a> for more information.<h2><a class="anchor" name="qanda-cleanup2">Can the task cleanup function be stopped?</a></h2>No, though it is subject to the normal rules of the task scheduler.<h2><a class="anchor" name="qanda-task-inf">Does a task have to have an infinite loop?</a></h2>No - if a task returns, and it is a non-zero-priority task, it will be stopped. If it is a zero-priority "idle" task, it will be re-started.<h2><a class="anchor" name="qanda-cpuctx">In task.h, what do the save_cpu_context() and restore_cpu_context() macros do?</a></h2>These macros are the central part of a pre-emptive RTOS - what they do is save the state of the CPU to the stack, and restore it from the stack. So, when the CPU state is restored, the CPU is able to continue doing whatever it was doing as if it was never interrupted.<p>In <a class="el" href="task_8h.html#bedcaab91615f58a55ed4135438618e8" title="Save the CPU context to the stack, and disable interrupts">save_cpu_context()</a>, the R0 register is the first to be saved. Then the status register contents are copied to the R0 register, and interrupts are disabled. Then, the registers R1 to R31 are saved. Finally, R0 (i.e. the status register contents) is saved. Why do I do it this way? I want to disable interrupts as quickly as possible (otherwise, I could be interrupted in the middle of saving the CPU context to the stack, which would initiate another save of the CPU context to the stack, which could quickly overflow the stack). However, I also need to store the state of the global interrupts enabled flag in the status register. So I save R0 to the stack, then load the status register to R0, then disable interrupts, then save everything else.<p>In <a class="el" href="task_8h.html#7e11bac5a51572c5db72502c47cf90d3" title="Restore the CPU context from the stack, possibly re-enabling interrupts">restore_cpu_context()</a>, the R0 register (which contains the status register contents - including the global interrupt enable flag) is retrieved from the stack. Then the registers R31 to R1 are restored. Then R0 is copied into the status register - this may have the effect of re-enabling interrupts. Finally, the R0 register is retrieved from the stack.<h2><a class="anchor" name="qanda-saveint">In task.h, what is "*(((uint8_t*)SP)+1) |= _BV(SREG_I);" about?</a></h2>In a nutshell, that statement means "set the global interrupt enabled bit in the last byte on the stack". The last byte to be saved to the stack is the status register - the one that contains the global interrupt enabled bit. So why do we set this bit? Because we want interrupts to be enabled when we restore the CPU context. Why? Because interrupts were enabled when the ISR started (otherwise it wouldn't have started!) and we want to restore the state of the interrupts when we are finished. <hr><p align="center"><font size="-1">YAVRTOS and YAVRTOS documentation Copyright © 2007-2008 Chris O'Byrne. Email - chris <at> obyrne <dot> com</font></p></body></html>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -