📄 kernel.sgml
字号:
To function correctly an operating system kernel must protect itsvital data structures, such as the run queues, from concurrentaccess. In a single CPU system the only concurrent activities to worryabout are asynchronous interrupts. The kernel can easily guard itsdata structures against these by disabling interrupts. However, in amulti-CPU system, this is inadequate since it does not block access byother CPUs. </para> <para>The eCos kernel protects its vital data structures using the schedulerlock. In single CPU systems this is a simple counter that isatomically incremented to acquire the lock and decremented to releaseit. If the lock is decremented to zero then the scheduler may beinvoked to choose a different thread to run. Because interrupts maycontinue to be serviced while the scheduler lock is claimed, ISRs arenot allowed to access kernel data structures, or call kernel routinesthat can. Instead all such operations are deferred to an associatedDSR routine that is run during the lock release operation, when thedata structures are in a consistent state. </para> <para>By choosing a kernel locking mechanism that does not rely on interruptmanipulation to protect data structures, it is easier to convert eCosto SMP than would otherwise be the case. The principal change needed tomake eCos SMP-safe is to convert the scheduler lock into a nestablespin lock. This is done by adding a spinlock and a CPU id to theoriginal counter. </para> <para>The algorithm for acquiring the scheduler lock is very simple. If thescheduler lock's CPU id matches the current CPU then it can just incrementthe counter and continue. If it does not match, the CPU must spin onthe spinlock, after which it may increment the counter and store itsown identity in the CPU id. </para> <para>To release the lock, the counter is decremented. If it goes to zerothe CPU id value must be set to NONE and the spinlock cleared. </para> <para>To protect these sequences against interrupts, they must be performedwith interrupts disabled. However, since these are very short codesequences, they will not have an adverse effect on the interruptlatency. </para> <para>Beyond converting the scheduler lock, further preparing the kernel forSMP is a relatively minor matter. The main changes are to convertvarious scalar housekeeping variables into arrays indexed by CPUid. These include the current thread pointer, the need_rescheduleflag and the timeslice counter. </para> <para>At present only the Multi-Level Queue (MLQ) scheduler is capable ofsupporting SMP configurations. The main change made to this scheduleris to cope with having several threads in execution at the sametime. Running threads are marked with the CPU that they are executing on.When scheduling a thread, the scheduler skips past any running threadsuntil it finds a thread that is pending. While not a constant-timealgorithm, as in the single CPU case, this is still deterministic,since the worst case time is bounded by the number of CPUs in thesystem. </para> <para>A second change to the scheduler is in the code used to decide whenthe scheduler should be called to choose a new thread. The schedulerattempts to keep the <property>n</property> CPUs running the<property>n</property> highest priority threads. Since an event orinterrupt on one CPU may require a reschedule on another CPU, theremust be a mechanism for deciding this. The algorithm currentlyimplemented is very simple. Given a thread that has just been awakened(or had its priority changed), the scheduler scans the CPUs, startingwith the one it is currently running on, for a current thread that isof lower priority than the new one. If one is found then a rescheduleinterrupt is sent to that CPU and the scan continues, but now usingthe current thread of the rescheduled CPU as the candidate thread. Inthis way the new thread gets to run as quickly as possible, hopefullyon the current CPU, and the remaining CPUs will pick up the remaininghighest priority threads as a consequence of processing the rescheduleinterrupt. </para> <para>The final change to the scheduler is in the handling oftimeslicing. Only one CPU receives timer interrupts, although all CPUsmust handle timeslicing. To make this work, the CPU that receives thetimer interrupt decrements the timeslice counter for all CPUs, notjust its own. If the counter for a CPU reaches zero, then it sends atimeslice interrupt to that CPU. On receiving the interrupt thedestination CPU enters the scheduler and looks for another thread atthe same priority to run. This is somewhat more efficient thandistributing clock ticks to all CPUs, since the interrupt is onlyneeded when a timeslice occurs. </para> <para>All existing synchronization mechanisms work as before in an SMPsystem. Additional synchronization mechanisms have been added toprovide 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 specialattention is in device drivers and especially interrupt handling. Itis quite possible for the ISR, DSR and thread components of a devicedriver to execute on different CPUs. For this reason it is much moreimportant that SMP-capable device drivers use the interrupt-relatedfunctions correctly. Typically a device driver would use the driverAPI rather than call the kernel directly, but it is unlikely thatanybody would attempt to use a multiprocessor system without thekernel package. </para> <para>Two new functions have been added to the Kernel APIto do <link linkend="kernel-interrupts-smp">interruptrouting</link>: <function>cyg_interrupt_set_cpu</function> and<function>cyg_interrupt_get_cpu</function>. Although not currentlysupported, special values for the cpu argument may be used in futureto indicate that the interrupt is being routed dynamically or isCPU-local. Once a vector has been routed to a new CPU, all otherinterrupt masking and configuration operations are relative to thatCPU, where relevant. </para> <para>There are more details of how interrupts should be handled in SMPsystems 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 <cyg/kernel/kapi.h> </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 applicationcode and eCos packages to create new threads. In many applicationsthis only happens during system initialization and all required datais allocated statically. However additional threads can be created atany time, if necessary. A newly created thread is always in suspendedstate and will not start running until it has been resumed via a callto <function>cyg_thread_resume</function>. Also, if threads arecreated during system initialization then they will not start runninguntil the eCos scheduler has been started. </para> <para>The <parameter class="function">name</parameter> argument is usedprimarily for debugging purposes, making it easier to keep track ofwhich <structname>cyg_thread</structname> structure is associated withwhich application-level thread. The kernel configuration option<varname>CYGVAR_KERNEL_THREADS_NAME</varname> controls whether or notthis name is actually used. </para> <para>On creation each thread is assigned a unique handle, and this will bestored in the location pointed at by the <parameterclass="function">handle</parameter> argument. Subsequent operations onthis thread including the required<function>cyg_thread_resume</function> should use this handle toidentify the thread. </para> <para>The kernel requires a small amount of space for each thread, in theform of a <structname>cyg_thread</structname> data structure, to holdinformation such as the current state of that thread. To avoid anyneed for dynamic memory allocation within the kernel this space has tobe provided by higher-level code, typically in the form of a staticvariable. The <parameter class="function">thread</parameter> argumentprovides 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>voidthread_entry_function(cyg_addrword_t data){ …} </programlisting> <para>The second argument to <function>cyg_thread_create</function> is apointer to such a function. The third argument <parameterclass="function">entry_data</parameter> is used to pass additionaldata to the function. Typically this takes the form of a pointer tosome static data, or a small integer, or <literal>0</literal> if thethread does not require any additional data. </para> <para>If the thread entry function ever returns then this is equivalent tothe thread calling <function>cyg_thread_exit</function>. Even thoughthe thread will no longer run again, it remains registered with thescheduler. 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> argumentprovides additional information to the scheduler. The exact detailsdepend on the scheduler being used. For the bitmap and mlqueueschedulers it is a small integer, typically in the range 0 to 31, with0 being the highest priority. The lowest priority is normally usedonly by the system's idle thread. The exact number of priorities iscontrolled by the kernel configuration option<varname>CYGNUM_KERNEL_SCHED_PRIORITIES</varname>. </para> <para>It is the responsibility of the application developer to be aware ofthe various threads in the system, including those created by eCospackages, and to ensure that all threads run at suitable priorities.For threads created by other packages the documentation provided bythose packages should indicate any requirements. </para> <para>The functions <function>cyg_thread_set_priority</function>,<function>cyg_thread_get_priority</function>, and<function>cyg_thread_get_current_priority</function> can be used tomanipulate a thread's priority. </para> </refsect1> <refsect1 id="kernel-thread-create-stack"><title>Stacks and Stack Sizes</title> <para>Each thread needs its own stack for local variables and to keep trackof function calls and returns. Again it is expected that this stack isprovided by the calling code, usually in the form of static data, sothat the kernel does not need any dynamic memory allocationfacilities. <function>cyg_thread_create</function> takes two argumentsrelated to the stack, a pointer to the base of the stack and the totalsize of this stack. On many processors stacks actually descend from thetop down, so the kernel will add the stack size to the base address todetermine the starting location. </para> <para>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -