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

📄 kernel.sgml

📁 eCos/RedBoot for勤研ARM AnywhereII(4510) 含全部源代码
💻 SGML
📖 第 1 页 / 共 5 页
字号:
install an interrupt object for this CPU's inter-CPU interrupt. From
this point on the code is the same as for the single CPU case: an
initial thread is chosen and entered.
      </para>
      <para>
From this point on the CPUs are all equal, eCos makes no further
distinction between the primary and secondary CPUs. However, the
hardware may still distinguish between them as far as interrupt
delivery is concerned.
      </para>
    </refsect1>

    <refsect1 id="kernel-smp-scheduling">
      <title>Scheduling</title>
      <para>
To function correctly an operating system kernel must protect its
vital data structures, such as the run queues, from concurrent
access. In a single CPU system the only concurrent activities to worry
about are asynchronous interrupts. The kernel can easily guard its
data structures against these by disabling interrupts. However, in a
multi-CPU system, this is inadequate since it does not block access by
other CPUs.
      </para>
      <para>
The eCos kernel protects its vital data structures using the scheduler
lock. In single CPU systems this is a simple counter that is
atomically incremented to acquire the lock and decremented to release
it. If the lock is decremented to zero then the scheduler may be
invoked to choose a different thread to run. Because interrupts may
continue to be serviced while the scheduler lock is claimed, ISRs are
not allowed to access kernel data structures, or call kernel routines
that can. Instead all such operations are deferred to an associated
DSR routine that is run during the lock release operation, when the
data structures are in a consistent state.
      </para>
      <para>
By choosing a kernel locking mechanism that does not rely on interrupt
manipulation to protect data structures, it is easier to convert eCos
to SMP than would otherwise be the case. The principal change needed to
make eCos SMP-safe is to convert the scheduler lock into a nestable
spin lock. This is done by adding a spinlock and a CPU id to the
original counter.
      </para>
      <para>
The algorithm for acquiring the scheduler lock is very simple. If the
scheduler lock's CPU id matches the current CPU then it can just increment
the counter and continue. If it does not match, the CPU must spin on
the spinlock, after which it may increment the counter and store its
own identity in the CPU id.
      </para>
      <para>
To release the lock, the counter is decremented. If it goes to zero
the CPU id value must be set to NONE and the spinlock cleared.
      </para>
      <para>
To protect these sequences against interrupts, they must be performed
with interrupts disabled. However, since these are very short code
sequences, they will not have an adverse effect on the interrupt
latency.
      </para>
      <para>
Beyond converting the scheduler lock, further preparing the kernel for
SMP is a relatively minor matter. The main changes are to convert
various scalar housekeeping variables into arrays indexed by CPU
id. These include the current thread pointer, the need_reschedule
flag and the timeslice counter.
      </para>
      <para>
At present only the Multi-Level Queue (MLQ) scheduler is capable of
supporting SMP configurations. The main change made to this scheduler
is to cope with having several threads in execution at the same
time. Running threads are marked with the CPU that they are executing on.
When scheduling a thread, the scheduler skips past any running threads
until it finds a thread that is pending. While not a constant-time
algorithm, as in the single CPU case, this is still deterministic,
since the worst case time is bounded by the number of CPUs in the
system.
      </para>
      <para>
A second change to the scheduler is in the code used to decide when
the scheduler should be called to choose a new thread. The scheduler
attempts to keep the <property>n</property> CPUs running the
<property>n</property> highest priority threads. Since an event or
interrupt on one CPU may require a reschedule on another CPU, there
must be a mechanism for deciding this. The algorithm currently
implemented is very simple. Given a thread that has just been awakened
(or had its priority changed), the scheduler scans the CPUs, starting
with the one it is currently running on, for a current thread that is
of lower priority than the new one. If one is found then a reschedule
interrupt is sent to that CPU and the scan continues, but now using
the current thread of the rescheduled CPU as the candidate thread. In
this way the new thread gets to run as quickly as possible, hopefully
on the current CPU, and the remaining CPUs will pick up the remaining
highest priority threads as a consequence of processing the reschedule
interrupt.
      </para>
      <para>
The final change to the scheduler is in the handling of
timeslicing. Only one CPU receives timer interrupts, although all CPUs
must handle timeslicing. To make this work, the CPU that receives the
timer interrupt decrements the timeslice counter for all CPUs, not
just its own. If the counter for a CPU reaches zero, then it sends a
timeslice interrupt to that CPU. On receiving the interrupt the
destination CPU enters the scheduler and looks for another thread at
the same priority to run. This is somewhat more efficient than
distributing clock ticks to all CPUs, since the interrupt is only
needed when a timeslice occurs.
      </para>
      <para>
All existing synchronization mechanisms work as before in an SMP
system. Additional synchronization mechanisms have been added to
provide explicit synchronization for SMP, in the form of
<link linkend="kernel-spinlocks">spinlocks</link>.
      </para>
    </refsect1>

    <refsect1 id="kernel-smp-interrupts">
      <title>SMP Interrupt Handling</title>
      <para>
The main area where the SMP nature of a system requires special
attention is in device drivers and especially interrupt handling. It
is quite possible for the ISR, DSR and thread components of a device
driver to execute on different CPUs. For this reason it is much more
important that SMP-capable device drivers use the interrupt-related
functions correctly. Typically a device driver would use the driver
API rather than call the kernel directly, but it is unlikely that
anybody would attempt to use a multiprocessor system without the
kernel package.
      </para>
      <para>
Two new functions have been added to the Kernel API
to do <link linkend="kernel-interrupts-smp">interrupt
routing</link>: <function>cyg_interrupt_set_cpu</function> and
<function>cyg_interrupt_get_cpu</function>. Although not currently
supported, special values for the cpu argument may be used in future
to indicate that the interrupt is being routed dynamically or is
CPU-local. Once a vector has been routed to a new CPU, all other
interrupt masking and configuration operations are relative to that
CPU, where relevant.
      </para>

      <para>
There are more details of how interrupts should be handled in SMP
systems in <xref linkend="devapi-smp-support">.
      </para>
    </refsect1>

  </refentry>

<!-- }}} -->

<!-- {{{ cyg_thread_create()            -->

  <refentry id="kernel-thread-create">

    <refmeta>
    <refentrytitle>Thread creation</refentrytitle>
    </refmeta>

    <refnamediv>
      <refname>cyg_thread_create</refname>
      <refpurpose>Create a new thread</refpurpose>
    </refnamediv>

    <refsynopsisdiv>
      <funcsynopsis>
	<funcsynopsisinfo>
#include &lt;cyg/kernel/kapi.h&gt;    
	</funcsynopsisinfo>
        <funcprototype>
          <funcdef>void <function>cyg_thread_create</function></funcdef>
          <paramdef>cyg_addrword_t <parameter>sched_info</parameter></paramdef>
          <paramdef>cyg_thread_entry_t* <parameter>entry</parameter></paramdef>
          <paramdef>cyg_addrword_t <parameter>entry_data</parameter></paramdef>
          <paramdef>char* <parameter>name</parameter></paramdef>
          <paramdef>void* <parameter>stack_base</parameter></paramdef>
          <paramdef>cyg_ucount32 <parameter>stack_size</parameter></paramdef>
          <paramdef>cyg_handle_t* <parameter>handle</parameter></paramdef>
          <paramdef>cyg_thread* <parameter>thread</parameter></paramdef>
        </funcprototype>
      </funcsynopsis>
    </refsynopsisdiv>

    <refsect1 id="kernel-thread-create-description"><title>Description</title>
      <para>
The <function>cyg_thread_create</function> function allows application
code and eCos packages to create new threads. In many applications
this only happens during system initialization and all required data
is allocated statically.  However additional threads can be created at
any time, if necessary. A newly created thread is always in suspended
state and will not start running until it has been resumed via a call
to <function>cyg_thread_resume</function>. Also, if threads are
created during system initialization then they will not start running
until the eCos scheduler has been started.
      </para>
      <para>
The <parameter class="function">name</parameter> argument is used
primarily for debugging purposes, making it easier to keep track of
which <structname>cyg_thread</structname> structure is associated with
which application-level thread. The kernel configuration option
<varname>CYGVAR_KERNEL_THREADS_NAME</varname> controls whether or not
this name is actually used.
      </para>
      <para>
On creation each thread is assigned a unique handle, and this will be
stored in the location pointed at by the <parameter
class="function">handle</parameter> argument. Subsequent operations on
this thread including the required
<function>cyg_thread_resume</function> should use this handle to
identify the thread.
      </para>
      <para>
The kernel requires a small amount of space for each thread, in the
form of a <structname>cyg_thread</structname> data structure, to hold
information such as the current state of that thread. To avoid any
need for dynamic memory allocation within the kernel this space has to
be provided by higher-level code, typically in the form of a static
variable. The <parameter class="function">thread</parameter> argument
provides this space.
      </para>
    </refsect1>

    <refsect1 id="kernel-thread-create-entry"><title>Thread Entry Point</title>
      <para>
The entry point for a thread takes the form:
      </para>
      <programlisting width=72>
void
thread_entry_function(cyg_addrword_t data)
{
    &hellip;
}
      </programlisting>
      <para>
The second argument to <function>cyg_thread_create</function> is a
pointer to such a function. The third argument <parameter
class="function">entry_data</parameter> is used to pass additional
data to the function. Typically this takes the form of a pointer to
some static data, or a small integer, or <literal>0</literal> if the
thread does not require any additional data.
      </para>
      <para>
If the thread entry function ever returns then this is equivalent to
the thread calling <function>cyg_thread_exit</function>. Even though
the thread will no longer run again, it remains registered with the
scheduler. If the application needs to re-use the
<structname>cyg_thread</structname> data structure then a call to
<function>cyg_thread_delete</function> is required first.
      </para>
    </refsect1>

    <refsect1 id="kernel-thread-create-priorities"><title>Thread Priorities</title>
      <para>
The <parameter class="function">sched_info</parameter> argument
provides additional information to the scheduler. The exact details
depend on the scheduler being used. For the bitmap and mlqueue
schedulers it is a small integer, typically in the range 0 to 31, with
0 being the highest priority. The lowest priority is normally used
only by the system's idle thread. The exact number of priorities is
controlled by the kernel configuration option
<varname>CYGNUM_KERNEL_SCHED_PRIORITIES</varname>. 
      </para>
      <para>
It is the responsibility of the application developer to be aware of
the various threads in the system, including those created by eCos
packages, and to ensure that all threads run at suitable priorities.
For threads created by other packages the documentation provided by
those packages should indicate any requirements.
      </para>
      <para>

⌨️ 快捷键说明

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