⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 kernel-mutexes.html

📁 ecos 文档
💻 HTML
📖 第 1 页 / 共 2 页
字号:
levels. However this may not always be possible even at the
application level. In addition mutexes may be used internally by
underlying code, for example the memory allocation package, so careful
analysis of the whole system would be needed to be sure that priority
inversion cannot occur. Instead it is common practice to use one of
two techniques: priority ceilings and priority inheritance.
      </P
><P
>Priority ceilings involve associating a priority with each mutex.
Usually this will match the highest priority thread that will ever
lock the mutex. When a thread running at a lower priority makes a
successful call to <TT
CLASS="FUNCTION"
>cyg_mutex_lock</TT
> or
<TT
CLASS="FUNCTION"
>cyg_mutex_trylock</TT
> its priority will be boosted to
that of the mutex. For example, given the previous example the
priority associated with the mutex would be that of thread A, so for
as long as it owns the mutex thread C will run in preference to thread
B. When C releases the mutex its priority drops to the normal value
again, allowing A to run and claim the mutex. Setting the
priority for a mutex involves a call to
<TT
CLASS="FUNCTION"
>cyg_mutex_set_ceiling</TT
>, which is typically called
during initialization. It is possible to change the ceiling
dynamically but this will only affect subsequent lock operations, not
the current owner of the mutex.
      </P
><P
>Priority ceilings are very suitable for simple applications, where for
every thread in the system it is possible to work out which mutexes
will be accessed. For more complicated applications this may prove
difficult, especially if thread priorities change at run-time. An
additional problem occurs for any mutexes outside the application, for
example used internally within eCos packages. A typical eCos package
will be unaware of the details of the various threads in the system,
so it will have no way of setting suitable ceilings for its internal
mutexes. If those mutexes are not exported to application code then 
using priority ceilings may not be viable. The kernel does provide a
configuration option
<TT
CLASS="VARNAME"
>CYGSEM_KERNEL_SYNCH_MUTEX_PRIORITY_INVERSION_PROTOCOL_DEFAULT_PRIORITY</TT
>
that can be used to set the default priority ceiling for all mutexes,
which may prove sufficient.
      </P
><P
>The alternative approach is to use priority inheritance: if a thread
calls <TT
CLASS="FUNCTION"
>cyg_mutex_lock</TT
> for a mutex that it
currently owned by a lower-priority thread, then the owner will have
its priority raised to that of the current thread. Often this is more
efficient than priority ceilings because priority boosting only
happens when necessary, not for every lock operation, and the required
priority is determined at run-time rather than by static analysis.
However there are complications when multiple threads running at
different priorities try to lock a single mutex, or when the current
owner of a mutex then tries to lock additional mutexes, and this makes
the implementation significantly more complicated than priority
ceilings. 
      </P
><P
>There are a number of configuration options associated with priority
inversion. First, if after careful analysis it is known that priority
inversion cannot arise then the component
<TT
CLASS="FUNCTION"
>CYGSEM_KERNEL_SYNCH_MUTEX_PRIORITY_INVERSION_PROTOCOL</TT
>
can be disabled. More commonly this component will be enabled, and one
of either
<TT
CLASS="VARNAME"
>CYGSEM_KERNEL_SYNCH_MUTEX_PRIORITY_INVERSION_PROTOCOL_INHERIT</TT
>
or
<TT
CLASS="VARNAME"
>CYGSEM_KERNEL_SYNCH_MUTEX_PRIORITY_INVERSION_PROTOCOL_CEILING</TT
>
will be selected, so that one of the two protocols is available for
all mutexes. It is possible to select multiple protocols, so that some
mutexes can have priority ceilings while others use priority
inheritance or no priority inversion protection at all. Obviously this
flexibility will add to the code size and to the cost of mutex
operations. The default for all mutexes will be controlled by
<TT
CLASS="VARNAME"
>CYGSEM_KERNEL_SYNCH_MUTEX_PRIORITY_INVERSION_PROTOCOL_DEFAULT</TT
>,
and can be changed at run-time using
<TT
CLASS="FUNCTION"
>cyg_mutex_set_protocol</TT
>. 
      </P
><P
>Priority inversion problems can also occur with other synchronization
primitives such as semaphores. For example there could be a situation
where a high-priority thread A is waiting on a semaphore, a
low-priority thread C needs to do just a little bit more work before
posting the semaphore, but a medium priority thread B is running and
preventing C from making progress. However a semaphore does not have
the concept of an owner, so there is no way for the system to know
that it is thread C which would next post to the semaphore. Hence
there is no way for the system to boost the priority of C
automatically and prevent the priority inversion. Instead situations
like this have to be detected by application developers and
appropriate precautions have to be taken, for example making sure that
all the threads run at suitable priorities at all times.
      </P
><DIV
CLASS="WARNING"
><P
></P
><TABLE
CLASS="WARNING"
BORDER="1"
WIDTH="100%"
><TR
><TD
ALIGN="CENTER"
><B
>Warning</B
></TD
></TR
><TR
><TD
ALIGN="LEFT"
><P
>The current implementation of priority inheritance within the eCos
kernel does not handle certain exceptional circumstances completely
correctly. Problems will only arise if a thread owns one mutex,
then attempts to claim another mutex, and there are other threads
attempting to lock these same mutexes. Although the system will
continue running, the current owners of the various mutexes involved
may not run at the priority they should. This situation never arises
in typical code because a mutex will only be locked for a small
critical region, and there is no need to manipulate other shared resources
inside this region. A more complicated implementation of priority
inheritance is possible but would add significant overhead and certain
operations would no longer be deterministic.
      </P
></TD
></TR
></TABLE
></DIV
><DIV
CLASS="WARNING"
><P
></P
><TABLE
CLASS="WARNING"
BORDER="1"
WIDTH="100%"
><TR
><TD
ALIGN="CENTER"
><B
>Warning</B
></TD
></TR
><TR
><TD
ALIGN="LEFT"
><P
>Support for priority ceilings and priority inheritance is not
implemented for all schedulers. In particular neither priority
ceilings nor priority inheritance are currently available for the
bitmap scheduler.
      </P
></TD
></TR
></TABLE
></DIV
></DIV
><DIV
CLASS="REFSECT1"
><A
NAME="KERNEL-MUTEXES-ALTERNATIVES"
></A
><H2
>Alternatives</H2
><P
>In nearly all circumstances, if two or more threads need to share some
data then protecting this data with a mutex is the correct thing to
do. Mutexes are the only primitive that combine a locking mechanism
and protection against priority inversion problems. However this
functionality is achieved at a cost, and in exceptional circumstances
such as an application's most critical inner loop it may be desirable
to use some other means of locking.
      </P
><P
>When a critical region is very very small it is possible to lock the
scheduler, thus ensuring that no other thread can run until the
scheduler is unlocked again. This is achieved with calls to <A
HREF="kernel-schedcontrol.html"
><TT
CLASS="FUNCTION"
>cyg_scheduler_lock</TT
></A
>
and <TT
CLASS="FUNCTION"
>cyg_scheduler_unlock</TT
>. If the critical region
is sufficiently small then this can actually improve both performance
and dispatch latency because <TT
CLASS="FUNCTION"
>cyg_mutex_lock</TT
> also
locks the scheduler for a brief period of time. This approach will not
work on SMP systems because another thread may already be running on a
different processor and accessing the critical region.
      </P
><P
>Another way of avoiding the use of mutexes is to make sure that all
threads that access a particular critical region run at the same
priority and configure the system with timeslicing disabled
(<TT
CLASS="VARNAME"
>CYGSEM_KERNEL_SCHED_TIMESLICE</TT
>). Without
timeslicing a thread can only be preempted by a higher-priority one,
or if it performs some operation that can block. This approach
requires that none of the operations in the critical region can block,
so for example it is not legal to call
<TT
CLASS="FUNCTION"
>cyg_semaphore_wait</TT
>. It is also vulnerable to
any changes in the configuration or to the various thread priorities:
any such changes may now have unexpected side effects. It will not
work on SMP systems.
      </P
></DIV
><DIV
CLASS="REFSECT1"
><A
NAME="KERNEL-MUTEXES-RECURSIVE"
></A
><H2
>Recursive Mutexes</H2
><P
>The implementation of mutexes within the eCos kernel does not support
recursive locks. If a thread has locked a mutex and then attempts to
lock the mutex again, typically as a result of some recursive call in
a complicated call graph, then either an assertion failure will be
reported or the thread will deadlock. This behaviour is deliberate.
When a thread has just locked a mutex associated with some data
structure, it can assume that that data structure is in a consistent
state. Before unlocking the mutex again it must ensure that the data
structure is again in a consistent state. Recursive mutexes allow a
thread to make arbitrary changes to a data structure, then in a
recursive call lock the mutex again while the data structure is still
inconsistent. The net result is that code can no longer make any
assumptions about data structure consistency, which defeats the
purpose of using mutexes.
      </P
></DIV
><DIV
CLASS="REFSECT1"
><A
NAME="KERNEL-MUTEXES-CONTEXT"
></A
><H2
>Valid contexts</H2
><P
><TT
CLASS="FUNCTION"
>cyg_mutex_init</TT
>,
<TT
CLASS="FUNCTION"
>cyg_mutex_set_ceiling</TT
> and
<TT
CLASS="FUNCTION"
>cyg_mutex_set_protocol</TT
> are normally called during
initialization but may also be called from thread context. The
remaining functions should only be called from thread context. Mutexes
serve as a mutual exclusion mechanism between threads, and cannot be
used to synchronize between threads and the interrupt handling
subsystem. If a critical region is shared between a thread and a DSR
then it must be protected using <A
HREF="kernel-schedcontrol.html"
><TT
CLASS="FUNCTION"
>cyg_scheduler_lock</TT
></A
>
and <TT
CLASS="FUNCTION"
>cyg_scheduler_unlock</TT
>. If a critical region is
shared between a thread and an ISR, it must be protected by disabling
or masking interrupts. Obviously these operations must be used with
care because they can affect dispatch and interrupt latencies.
      </P
></DIV
><DIV
CLASS="NAVFOOTER"
><HR
ALIGN="LEFT"
WIDTH="100%"><TABLE
SUMMARY="Footer navigation table"
WIDTH="100%"
BORDER="0"
CELLPADDING="0"
CELLSPACING="0"
><TR
><TD
WIDTH="33%"
ALIGN="left"
VALIGN="top"
><A
HREF="kernel-alarms.html"
ACCESSKEY="P"
>Prev</A
></TD
><TD
WIDTH="34%"
ALIGN="center"
VALIGN="top"
><A
HREF="ecos-ref.html"
ACCESSKEY="H"
>Home</A
></TD
><TD
WIDTH="33%"
ALIGN="right"
VALIGN="top"
><A
HREF="kernel-condition-variables.html"
ACCESSKEY="N"
>Next</A
></TD
></TR
><TR
><TD
WIDTH="33%"
ALIGN="left"
VALIGN="top"
>Alarms</TD
><TD
WIDTH="34%"
ALIGN="center"
VALIGN="top"
><A
HREF="kernel.html"
ACCESSKEY="U"
>Up</A
></TD
><TD
WIDTH="33%"
ALIGN="right"
VALIGN="top"
>Condition Variables</TD
></TR
></TABLE
></DIV
></BODY
></HTML
>

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -