OpenCores
URL https://opencores.org/ocsvn/openrisc/openrisc/trunk

Subversion Repositories openrisc

[/] [openrisc/] [trunk/] [rtos/] [ecos-2.0/] [doc/] [html/] [ref/] [kernel-overview.html] - Blame information for rev 199

Go to most recent revision | Details | Compare with Previous | View Log

Line No. Rev Author Line
1 28 unneback
<!-- Copyright (C) 2003 Red Hat, Inc.                                -->
2
<!-- This material may be distributed only subject to the terms      -->
3
<!-- and conditions set forth in the Open Publication License, v1.0  -->
4
<!-- or later (the latest version is presently available at          -->
5
<!-- http://www.opencontent.org/openpub/).                           -->
6
<!-- Distribution of the work or derivative of the work in any       -->
7
<!-- standard (paper) book form is prohibited unless prior           -->
8
<!-- permission is obtained from the copyright holder.               -->
9
<HTML
10
><HEAD
11
><TITLE
12
>Kernel Overview</TITLE
13
><meta name="MSSmartTagsPreventParsing" content="TRUE">
14
<META
15
NAME="GENERATOR"
16
CONTENT="Modular DocBook HTML Stylesheet Version 1.76b+
17
"><LINK
18
REL="HOME"
19
TITLE="eCos Reference Manual"
20
HREF="ecos-ref.html"><LINK
21
REL="UP"
22
TITLE="The eCos Kernel"
23
HREF="kernel.html"><LINK
24
REL="PREVIOUS"
25
TITLE="The eCos Kernel"
26
HREF="kernel.html"><LINK
27
REL="NEXT"
28
TITLE="SMP Support"
29
HREF="kernel-smp.html"></HEAD
30
><BODY
31
CLASS="REFENTRY"
32
BGCOLOR="#FFFFFF"
33
TEXT="#000000"
34
LINK="#0000FF"
35
VLINK="#840084"
36
ALINK="#0000FF"
37
><DIV
38
CLASS="NAVHEADER"
39
><TABLE
40
SUMMARY="Header navigation table"
41
WIDTH="100%"
42
BORDER="0"
43
CELLPADDING="0"
44
CELLSPACING="0"
45
><TR
46
><TH
47
COLSPAN="3"
48
ALIGN="center"
49
>eCos Reference Manual</TH
50
></TR
51
><TR
52
><TD
53
WIDTH="10%"
54
ALIGN="left"
55
VALIGN="bottom"
56
><A
57
HREF="kernel.html"
58
ACCESSKEY="P"
59
>Prev</A
60
></TD
61
><TD
62
WIDTH="80%"
63
ALIGN="center"
64
VALIGN="bottom"
65
></TD
66
><TD
67
WIDTH="10%"
68
ALIGN="right"
69
VALIGN="bottom"
70
><A
71
HREF="kernel-smp.html"
72
ACCESSKEY="N"
73
>Next</A
74
></TD
75
></TR
76
></TABLE
77
><HR
78
ALIGN="LEFT"
79
WIDTH="100%"></DIV
80
><H1
81
><A
82
NAME="KERNEL-OVERVIEW">Kernel Overview</H1
83
><DIV
84
CLASS="REFNAMEDIV"
85
><A
86
NAME="AEN56"
87
></A
88
><H2
89
>Name</H2
90
>Kernel&nbsp;--&nbsp;Overview of the eCos Kernel</DIV
91
><DIV
92
CLASS="REFSECT1"
93
><A
94
NAME="KERNEL-OVERVIEW-DESCRIPTION"
95
></A
96
><H2
97
>Description</H2
98
><P
99
>The kernel is one of the key packages in all of eCos. It provides the
100
core functionality needed for developing multi-threaded applications:
101
      </P
102
><P
103
></P
104
><OL
105
TYPE="1"
106
><LI
107
><P
108
>The ability to create new threads in the system, either during startup
109
or when the system is already running.
110
        </P
111
></LI
112
><LI
113
><P
114
>Control over the various threads in the system, for example
115
manipulating their priorities.
116
        </P
117
></LI
118
><LI
119
><P
120
>A choice of schedulers, determining which thread should currently be
121
running.
122
        </P
123
></LI
124
><LI
125
><P
126
>A range of synchronization primitives, allowing threads to interact
127
and share data safely.
128
        </P
129
></LI
130
><LI
131
><P
132
>Integration with the system's support for interrupts and exceptions.
133
        </P
134
></LI
135
></OL
136
><P
137
>In some other operating systems the kernel provides additional
138
functionality. For example the kernel may also provide memory
139
allocation functionality, and device drivers may be part of the kernel
140
as well. This is not the case for eCos. Memory allocation is handled
141
by a separate package. Similary each device driver will typically be a
142
separate package. Various packages are combined and configured using
143
the eCos configuration technology to meet the requirements of the
144
application.
145
      </P
146
><P
147
>The eCos kernel package is optional. It is possible to write
148
single-threaded applications which do not use any kernel
149
functionality, for example RedBoot. Typically such applications are
150
based around a central polling loop, continually checking all devices
151
and taking appropriate action when I/O occurs. A small amount of
152
calculation is possible every iteration, at the cost of an increased
153
delay between an I/O event occurring and the polling loop detecting
154
the event. When the requirements are straightforward it may well be
155
easier to develop the application using a polling loop, avoiding the
156
complexities of multiple threads and synchronization between threads.
157
As requirements get more complicated a multi-threaded solution becomes
158
more appropriate, requiring the use of the kernel. In fact some of the
159
more advanced packages in eCos, for example the TCP/IP stack, use
160
multi-threading internally. Therefore if the application uses any of
161
those packages then the kernel becomes a required package, not an
162
optional one.
163
      </P
164
><P
165
>The kernel functionality can be used in one of two ways. The kernel
166
provides its own C API, with functions like
167
<TT
168
CLASS="FUNCTION"
169
>cyg_thread_create</TT
170
> and
171
<TT
172
CLASS="FUNCTION"
173
>cyg_mutex_lock</TT
174
>. These can be called directly from
175
application code or from other packages. Alternatively there are a
176
number of packages which provide compatibility with existing API's,
177
for example POSIX threads or &micro;ITRON. These allow application
178
code to call standard functions such as
179
<TT
180
CLASS="FUNCTION"
181
>pthread_create</TT
182
>, and those functions are
183
implemented using the basic functionality provided by the eCos kernel.
184
Using compatibility packages in an eCos application can make it much
185
easier to reuse code developed in other environments, and to share
186
code.
187
      </P
188
><P
189
>Although the different compatibility packages have similar
190
requirements on the underlying kernel, for example the ability to
191
create a new thread, there are differences in the exact semantics. For
192
example, strict &micro;ITRON compliance requires that kernel
193
timeslicing is disabled. This is achieved largely through the
194
configuration technology. The kernel provides a number of
195
configuration options that control the exact semantics that are
196
provided, and the various compatibility packages require particular
197
settings for those options. This has two important consequences.
198
First, it is not usually possible to have two different compatibility
199
packages in one eCos configuration because they will have conflicting
200
requirements on the underlying kernel. Second, the semantics of the
201
kernel's own API are only loosely defined because of the many
202
configuration options. For example <TT
203
CLASS="FUNCTION"
204
>cyg_mutex_lock</TT
205
>
206
will always attempt to lock a mutex, but various configuration options
207
determine the behaviour when the mutex is already locked and there is
208
a possibility of priority inversion.
209
      </P
210
><P
211
>The optional nature of the kernel package presents some complications
212
for other code, especially device drivers. Wherever possible a device
213
driver should work whether or not the kernel is present. However there
214
are some parts of the system, especially those related to interrupt
215
handling, which should be implemented differently in multi-threaded
216
environments containing the eCos kernel and in single-threaded
217
environments without the kernel. To cope with both scenarios the
218
common HAL package provides a driver API, with functions such as
219
<TT
220
CLASS="FUNCTION"
221
>cyg_drv_interrupt_attach</TT
222
>. When the kernel package
223
is present these driver API functions map directly on to the
224
equivalent kernel functions such as
225
<TT
226
CLASS="FUNCTION"
227
>cyg_interrupt_attach</TT
228
>, using macros to avoid any
229
overheads. When the kernel is absent the common HAL package implements
230
the driver API directly, but this implementation is simpler than the
231
one in the kernel because it can assume a single-threaded environment.
232
      </P
233
></DIV
234
><DIV
235
CLASS="REFSECT1"
236
><A
237
NAME="KERNEL-OVERVIEW-SCHEDULERS"
238
></A
239
><H2
240
>Schedulers</H2
241
><P
242
>When a system involves multiple threads, a scheduler is needed to
243
determine which thread should currently be running. The eCos kernel
244
can be configured with one of two schedulers, the bitmap scheduler and
245
the multi-level queue (MLQ) scheduler. The bitmap scheduler is
246
somewhat more efficient, but has a number of limitations. Most systems
247
will instead use the MLQ scheduler. Other schedulers may be added in
248
the future, either as extensions to the kernel package or in separate
249
packages.
250
      </P
251
><P
252
>Both the bitmap and the MLQ scheduler use a simple numerical priority
253
to determine which thread should be running. The number of priority
254
levels is configurable via the option
255
<TT
256
CLASS="VARNAME"
257
>CYGNUM_KERNEL_SCHED_PRIORITIES</TT
258
>, but a typical
259
system will have up to 32 priority levels. Therefore thread priorities
260
will be in the range 0 to 31, with 0 being the highest priority and 31
261
the lowest. Usually only the system's idle thread will run at the
262
lowest priority. Thread priorities are absolute, so the kernel will
263
only run a lower-priority thread if all higher-priority threads are
264
currently blocked.
265
      </P
266
><P
267
>The bitmap scheduler only allows one thread per priority level, so if
268
the system is configured with 32 priority levels then it is limited to
269
only 32 threads &#8212; still enough for many applications. A simple
270
bitmap can be used to keep track of which threads are currently
271
runnable. Bitmaps can also be used to keep track of threads waiting on
272
a mutex or other synchronization primitive. Identifying the
273
highest-priority runnable or waiting thread involves a simple
274
operation on the bitmap, and an array index operation can then be used
275
to get hold of the thread data structure itself. This makes the
276
bitmap scheduler fast and totally deterministic.
277
      </P
278
><P
279
>The MLQ scheduler allows multiple threads to run at the same priority.
280
This means that there is no limit on the number of threads in the
281
system, other than the amount of memory available. However operations
282
such as finding the highest priority runnable thread are a little bit
283
more expensive than for the bitmap scheduler.
284
      </P
285
><P
286
>Optionally the MLQ scheduler supports timeslicing, where the scheduler
287
automatically switches from one runnable thread to another when some
288
number of clock ticks have occurred. Timeslicing only comes into play
289
when there are two runnable threads at the same priority and no higher
290
priority runnable threads. If timeslicing is disabled then a thread
291
will not be preempted by another thread of the same priority, and will
292
continue running until either it explicitly yields the processor or
293
until it blocks by, for example, waiting on a synchronization
294
primitive. The configuration options
295
<TT
296
CLASS="VARNAME"
297
>CYGSEM_KERNEL_SCHED_TIMESLICE</TT
298
> and
299
<TT
300
CLASS="VARNAME"
301
>CYGNUM_KERNEL_SCHED_TIMESLICE_TICKS</TT
302
> control
303
timeslicing. The bitmap scheduler does not provide timeslicing
304
support. It only allows one thread per priority level, so it is not
305
possible to preempt the current thread in favour of another one with
306
the same priority.
307
      </P
308
><P
309
>Another important configuration option that affects the MLQ scheduler
310
is <TT
311
CLASS="VARNAME"
312
>CYGIMP_KERNEL_SCHED_SORTED_QUEUES</TT
313
>. This
314
determines what happens when a thread blocks, for example by waiting
315
on a semaphore which has no pending events. The default behaviour of
316
the system is last-in-first-out queuing. For example if several
317
threads are waiting on a semaphore and an event is posted, the thread
318
that gets woken up is the last one that called
319
<TT
320
CLASS="FUNCTION"
321
>cyg_semaphore_wait</TT
322
>. This allows for a simple and
323
fast implementation of both the queue and dequeue operations. However
324
if there are several queued threads with different priorities, it may
325
not be the highest priority one that gets woken up. In practice this
326
is rarely a problem: usually there will be at most one thread waiting
327
on a queue, or when there are several threads they will be of the same
328
priority. However if the application does require strict priority
329
queueing then the option
330
<TT
331
CLASS="VARNAME"
332
>CYGIMP_KERNEL_SCHED_SORTED_QUEUES</TT
333
> should be
334
enabled. There are disadvantages: more work is needed whenever a
335
thread is queued, and the scheduler needs to be locked for this
336
operation so the system's dispatch latency is worse. If the bitmap
337
scheduler is used then priority queueing is automatic and does not
338
involve any penalties.
339
      </P
340
><P
341
>Some kernel functionality is currently only supported with the MLQ
342
scheduler, not the bitmap scheduler. This includes support for SMP
343
systems, and protection against priority inversion using either mutex
344
priority ceilings or priority inheritance.
345
      </P
346
></DIV
347
><DIV
348
CLASS="REFSECT1"
349
><A
350
NAME="KERNEL-OVERVIEW-SYNCH-PRIMITIVES"
351
></A
352
><H2
353
>Synchronization Primitives</H2
354
><P
355
>The eCos kernel provides a number of different synchronization
356
primitives: <A
357
HREF="kernel-mutexes.html"
358
>mutexes</A
359
>,
360
<A
361
HREF="kernel-condition-variables.html"
362
>condition variables</A
363
>,
364
<A
365
HREF="kernel-semaphores.html"
366
>counting semaphores</A
367
>,
368
<A
369
HREF="kernel-mail-boxes.html"
370
>mail boxes</A
371
> and
372
<A
373
HREF="kernel-flags.html"
374
>event flags</A
375
>.
376
      </P
377
><P
378
>Mutexes serve a very different purpose from the other primitives. A
379
mutex allows multiple threads to share a resource safely: a thread
380
locks a mutex, manipulates the shared resource, and then unlocks the
381
mutex again. The other primitives are used to communicate information
382
between threads, or alternatively from a DSR associated with an
383
interrupt handler to a thread.
384
      </P
385
><P
386
>When a thread that has locked a mutex needs to wait for some condition
387
to become true, it should use a condition variable. A condition
388
variable is essentially just a place for a thread to wait, and which
389
another thread, or DSR, can use to wake it up. When a thread waits on
390
a condition variable it releases the mutex before waiting, and when it
391
wakes up it reacquires it before proceeding. These operations are
392
atomic so that synchronization race conditions cannot be introduced.
393
      </P
394
><P
395
>A counting semaphore is used to indicate that a particular event has
396
occurred. A consumer thread can wait for this event to occur, and a
397
producer thread or a DSR can post the event. There is a count
398
associated with the semaphore so if the event occurs multiple times in
399
quick succession this information is not lost, and the appropriate
400
number of semaphore wait operations will succeed.
401
      </P
402
><P
403
>Mail boxes are also used to indicate that a particular event has
404
occurred, and allows for one item of data to be exchanged per event.
405
Typically this item of data would be a pointer to some data structure.
406
Because of the need to store this extra data, mail boxes have a
407
finite capacity. If a producer thread generates mail box events
408
faster than they can be consumed then, to avoid overflow, it will be
409
blocked until space is again available in the mail box. This means
410
that mail boxes usually cannot be used by a DSR to wake up a
411
thread. Instead mail boxes are typically only used between threads.
412
      </P
413
><P
414
>Event flags can be used to wait on some number of different events,
415
and to signal that one or several of these events have occurred. This
416
is achieved by associating bits in a bit mask with the different
417
events. Unlike a counting semaphore no attempt is made to keep track
418
of the number of events that have occurred, only the fact that an
419
event has occurred at least once. Unlike a mail box it is not
420
possible to send additional data with the event, but this does mean
421
that there is no possibility of an overflow and hence event flags can
422
be used between a DSR and a thread as well as between threads.
423
      </P
424
><P
425
>The eCos common HAL package provides its own device driver API which
426
contains some of the above synchronization primitives. These allow
427
the DSR for an interrupt handler to signal events to higher-level
428
code. If the configuration includes the eCos kernel package then
429
the driver API routines map directly on to the equivalent kernel
430
routines, allowing interrupt handlers to interact with threads. If the
431
kernel package is not included and the application consists of just a
432
single thread running in polled mode then the driver API is
433
implemented entirely within the common HAL, and with no need to worry
434
about multiple threads the implementation can obviously be rather
435
simpler.
436
      </P
437
></DIV
438
><DIV
439
CLASS="REFSECT1"
440
><A
441
NAME="KERNEL-OVERVIEW-THREADS-INTERRUPTS"
442
></A
443
><H2
444
>Threads and Interrupt Handling</H2
445
><P
446
>During normal operation the processor will be running one of the
447
threads in the system. This may be an application thread, a system
448
thread running inside say the TCP/IP stack, or the idle thread. From
449
time to time a hardware interrupt will occur, causing control to be
450
transferred briefly to an interrupt handler. When the interrupt has
451
been completed the system's scheduler will decide whether to return
452
control to the interrupted thread or to some other runnable thread.
453
      </P
454
><P
455
>Threads and interrupt handlers must be able to interact. If a thread
456
is waiting for some I/O operation to complete, the interrupt handler
457
associated with that I/O must be able to inform the thread that the
458
operation has completed. This can be achieved in a number of ways. One
459
very simple approach is for the interrupt handler to set a volatile
460
variable. A thread can then poll continuously until this flag is set,
461
possibly sleeping for a clock tick in between. Polling continuously
462
means that the cpu time is not available for other activities, which
463
may be acceptable for some but not all applications. Polling once
464
every clock tick imposes much less overhead, but means that the thread
465
may not detect that the I/O event has occurred until an entire clock
466
tick has elapsed. In typical systems this could be as long as 10
467
milliseconds. Such a delay might be acceptable for some applications,
468
but not all.
469
      </P
470
><P
471
>A better solution would be to use one of the synchronization
472
primitives. The interrupt handler could signal a condition variable,
473
post to a semaphore, or use one of the other primitives. The thread
474
would perform a wait operation on the same primitive. It would not
475
consume any cpu cycles until the I/O event had occurred, and when the
476
event does occur the thread can start running again immediately
477
(subject to any higher priority threads that might also be runnable).
478
      </P
479
><P
480
>Synchronization primitives constitute shared data, so care must be
481
taken to avoid problems with concurrent access. If the thread that was
482
interrupted was just performing some calculations then the interrupt
483
handler could manipulate the synchronization primitive quite safely.
484
However if the interrupted thread happened to be inside some kernel
485
call then there is a real possibility that some kernel data structure
486
will be corrupted.
487
      </P
488
><P
489
>One way of avoiding such problems would be for the kernel functions to
490
disable interrupts when executing any critical region. On most
491
architectures this would be simple to implement and very fast, but it
492
would mean that interrupts would be disabled often and for quite a
493
long time. For some applications that might not matter, but many
494
embedded applications require that the interrupt handler run as soon
495
as possible after the hardware interrupt has occurred. If the kernel
496
relied on disabling interrupts then it would not be able to support
497
such applications.
498
      </P
499
><P
500
>Instead the kernel uses a two-level approach to interrupt handling.
501
Associated with every interrupt vector is an Interrupt Service Routine
502
or ISR, which will run as quickly as possible so that it can service
503
the hardware. However an ISR can make only a small number of kernel
504
calls, mostly related to the interrupt subsystem, and it cannot make
505
any call that would cause a thread to wake up. If an ISR detects that
506
an I/O operation has completed and hence that a thread should be woken
507
up, it can cause the associated Deferred Service Routine or DSR to
508
run. A DSR is allowed to make more kernel calls, for example it can
509
signal a condition variable or post to a semaphore.
510
      </P
511
><P
512
>Disabling interrupts prevents ISRs from running, but very few parts of
513
the system disable interrupts and then only for short periods of time.
514
The main reason for a thread to disable interrupts is to manipulate
515
some state that is shared with an ISR. For example if a thread needs
516
to add another buffer to a linked list of free buffers and the ISR may
517
remove a buffer from this list at any time, the thread would need to
518
disable interrupts for the few instructions needed to manipulate the
519
list. If the hardware raises an interrupt at this time, it remains
520
pending until interrupts are reenabled.
521
      </P
522
><P
523
>Analogous to interrupts being disabled or enabled, the kernel has a
524
scheduler lock. The various kernel functions such as
525
<TT
526
CLASS="FUNCTION"
527
>cyg_mutex_lock</TT
528
> and
529
<TT
530
CLASS="FUNCTION"
531
>cyg_semaphore_post</TT
532
> will claim the scheduler lock,
533
manipulate the kernel data structures, and then release the scheduler
534
lock. If an interrupt results in a DSR being requested and the
535
scheduler is currently locked, the DSR remains pending. When the
536
scheduler lock is released any pending DSRs will run. These may post
537
events to synchronization primitives, causing other higher priority
538
threads to be woken up.
539
      </P
540
><P
541
>For an example, consider the following scenario. The system has a high
542
priority thread A, responsible for processing some data coming from an
543
external device. This device will raise an interrupt when data is
544
available. There are two other threads B and C which spend their time
545
performing calculations and occasionally writing results to a display
546
of some sort. This display is a shared resource so a mutex is used to
547
control access.
548
      </P
549
><P
550
>At a particular moment in time thread A is likely to be blocked,
551
waiting on a semaphore or another synchronization primitive until data
552
is available. Thread B might be running performing some calculations,
553
and thread C is runnable waiting for its next timeslice. Interrupts
554
are enabled, and the scheduler is unlocked because none of the threads
555
are in the middle of a kernel operation. At this point the device
556
raises an interrupt. The hardware transfers control to a low-level
557
interrupt handler provided by eCos which works out exactly which
558
interrupt occurs, and then the corresponding ISR is run. This ISR
559
manipulates the hardware as appropriate, determines that there is now
560
data available, and wants to wake up thread A by posting to the
561
semaphore. However ISR's are not allowed to call
562
<TT
563
CLASS="FUNCTION"
564
>cyg_semaphore_post</TT
565
> directly, so instead the ISR
566
requests that its associated DSR be run and returns. There are no more
567
interrupts to be processed, so the kernel next checks for DSR's. One
568
DSR is pending and the scheduler is currently unlocked, so the DSR can
569
run immediately and post the semaphore. This will have the effect of
570
making thread A runnable again, so the scheduler's data structures are
571
adjusted accordingly. When the DSR returns thread B is no longer the
572
highest priority runnable thread so it will be suspended, and instead
573
thread A gains control over the cpu.
574
      </P
575
><P
576
>In the above example no kernel data structures were being manipulated
577
at the exact moment that the interrupt happened. However that cannot
578
be assumed. Suppose that thread B had finished its current set of
579
calculations and wanted to write the results to the display. It would
580
claim the appropriate mutex and manipulate the display. Now suppose
581
that thread B was timesliced in favour of thread C, and that thread C
582
also finished its calculations and wanted to write the results to the
583
display. It would call <TT
584
CLASS="FUNCTION"
585
>cyg_mutex_lock</TT
586
>. This
587
kernel call locks the scheduler, examines the current state of the
588
mutex, discovers that the mutex is already owned by another thread,
589
suspends the current thread, and switches control to another runnable
590
thread. Another interrupt happens in the middle of this
591
<TT
592
CLASS="FUNCTION"
593
>cyg_mutex_lock</TT
594
> call, causing the ISR to run
595
immediately. The ISR decides that thread A should be woken up so it
596
requests that its DSR be run and returns back to the kernel. At this
597
point there is a pending DSR, but the scheduler is still locked by the
598
call to <TT
599
CLASS="FUNCTION"
600
>cyg_mutex_lock</TT
601
> so the DSR cannot run
602
immediately. Instead the call to <TT
603
CLASS="FUNCTION"
604
>cyg_mutex_lock</TT
605
>
606
is allowed to continue, which at some point involves unlocking the
607
scheduler. The pending DSR can now run, safely post the semaphore, and
608
thus wake up thread A.
609
      </P
610
><P
611
>If the ISR had called <TT
612
CLASS="FUNCTION"
613
>cyg_semaphore_post</TT
614
> directly
615
rather than leaving it to a DSR, it is likely that there would have
616
been some sort of corruption of a kernel data structure. For example
617
the kernel might have completely lost track of one of the threads, and
618
that thread would never have run again. The two-level approach to
619
interrupt handling, ISR's and DSR's, prevents such problems with no
620
need to disable interrupts.
621
      </P
622
></DIV
623
><DIV
624
CLASS="REFSECT1"
625
><A
626
NAME="KERNEL-OVERVIEW-CONTEXTS"
627
></A
628
><H2
629
>Calling Contexts</H2
630
><P
631
>eCos defines a number of contexts. Only certain calls are allowed from
632
inside each context, for example most operations on threads or
633
synchronization primitives are not allowed from ISR context. The
634
different contexts are initialization, thread, ISR and DSR.
635
      </P
636
><P
637
>When eCos starts up it goes through a number of phases, including
638
setting up the hardware and invoking C++ static constructors. During
639
this time interrupts are disabled and the scheduler is locked. When a
640
configuration includes the kernel package the final operation is a
641
call to <A
642
HREF="kernel-schedcontrol.html"
643
><TT
644
CLASS="FUNCTION"
645
>cyg_scheduler_start</TT
646
></A
647
>.
648
At this point interrupts are enabled, the scheduler is unlocked, and
649
control is transferred to the highest priority runnable thread. If the
650
configuration also includes the C library package then usually the C
651
library startup package will have created a thread which will call the
652
application's <TT
653
CLASS="FUNCTION"
654
>main</TT
655
> entry point.
656
      </P
657
><P
658
>Some application code can also run before the scheduler is started,
659
and this code runs in initialization context. If the application is
660
written partly or completely in C++ then the constructors for any
661
static objects will be run. Alternatively application code can define
662
a function <TT
663
CLASS="FUNCTION"
664
>cyg_user_start</TT
665
> which gets called after
666
any C++ static constructors. This allows applications to be written
667
entirely in C.
668
      </P
669
><TABLE
670
BORDER="5"
671
BGCOLOR="#E0E0F0"
672
WIDTH="70%"
673
><TR
674
><TD
675
><PRE
676
CLASS="PROGRAMLISTING"
677
>void
678
cyg_user_start(void)
679
{
680
    /* Perform application-specific initialization here */
681
}
682
      </PRE
683
></TD
684
></TR
685
></TABLE
686
><P
687
>It is not necessary for applications to provide a
688
<TT
689
CLASS="FUNCTION"
690
>cyg_user_start</TT
691
> function since the system will
692
provide a default implementation which does nothing.
693
      </P
694
><P
695
>Typical operations that are performed from inside static constructors
696
or <TT
697
CLASS="FUNCTION"
698
>cyg_user_start</TT
699
> include creating threads,
700
synchronization primitives, setting up alarms, and registering
701
application-specific interrupt handlers. In fact for many applications
702
all such creation operations happen at this time, using statically
703
allocated data, avoiding any need for dynamic memory allocation or
704
other overheads.
705
      </P
706
><P
707
>Code running in initialization context runs with interrupts disabled
708
and the scheduler locked. It is not permitted to reenable interrupts
709
or unlock the scheduler because the system is not guaranteed to be in
710
a totally consistent state at this point. A consequence is that
711
initialization code cannot use synchronization primitives such as
712
<TT
713
CLASS="FUNCTION"
714
>cyg_semaphore_wait</TT
715
> to wait for an external event.
716
It is permitted to lock and unlock a mutex: there are no other threads
717
running so it is guaranteed that the mutex is not yet locked, and
718
therefore the lock operation will never block; this is useful when
719
making library calls that may use a mutex internally.
720
      </P
721
><P
722
>At the end of the startup sequence the system will call
723
<TT
724
CLASS="FUNCTION"
725
>cyg_scheduler_start</TT
726
> and the various threads will
727
start running. In thread context nearly all of the kernel functions
728
are available. There may be some restrictions on interrupt-related
729
operations, depending on the target hardware. For example the hardware
730
may require that interrupts be acknowledged in the ISR or DSR before
731
control returns to thread context, in which case
732
<TT
733
CLASS="FILENAME"
734
>cyg_interrupt_acknowledge</TT
735
> should not be called
736
by a thread.
737
      </P
738
><P
739
>At any time the processor may receive an external interrupt, causing
740
control to be transferred from the current thread. Typically a VSR
741
provided by eCos will run and determine exactly which interrupt
742
occurred. Then the VSR will switch to the appropriate ISR, which can
743
be provided by a HAL package, a device driver, or by the application.
744
During this time the system is running at ISR context, and most of the
745
kernel function calls are disallowed. This includes the various
746
synchronization primitives, so for example an ISR is not allowed to
747
post to a semaphore to indicate that an event has happened. Usually
748
the only operations that should be performed from inside an ISR are
749
ones related to the interrupt subsystem itself, for example masking an
750
interrupt or acknowledging that an interrupt has been processed. On
751
SMP systems it is also possible to use spinlocks from ISR context.
752
      </P
753
><P
754
>When an ISR returns it can request that the corresponding DSR be run
755
as soon as it is safe to do so, and that will run in DSR context. This
756
context is also used for running alarm functions, and threads can
757
switch temporarily to DSR context by locking the scheduler. Only
758
certain kernel functions can be called from DSR context, although more
759
than in ISR context. In particular it is possible to use any
760
synchronization primitives which cannot block.  These include
761
<TT
762
CLASS="FUNCTION"
763
>cyg_semaphore_post</TT
764
>,
765
<TT
766
CLASS="FILENAME"
767
>cyg_cond_signal</TT
768
>,
769
<TT
770
CLASS="FUNCTION"
771
>cyg_cond_broadcast</TT
772
>,
773
<TT
774
CLASS="FUNCTION"
775
>cyg_flag_setbits</TT
776
>, and
777
<TT
778
CLASS="FUNCTION"
779
>cyg_mbox_tryput</TT
780
>. It is not possible to use any
781
primitives that may block such as
782
<TT
783
CLASS="FUNCTION"
784
>cyg_semaphore_wait</TT
785
>,
786
<TT
787
CLASS="FUNCTION"
788
>cyg_mutex_lock</TT
789
>, or
790
<TT
791
CLASS="FUNCTION"
792
>cyg_mbox_put</TT
793
>. Calling such functions from inside
794
a DSR may cause the system to hang.
795
      </P
796
><P
797
>The specific documentation for the various kernel functions gives more
798
details about valid contexts.
799
      </P
800
></DIV
801
><DIV
802
CLASS="REFSECT1"
803
><A
804
NAME="KERNEL-OVERVIEW-ERRORS"
805
></A
806
><H2
807
>Error Handling and Assertions</H2
808
><P
809
>In many APIs each function is expected to perform some validation of
810
its parameters and possibly of the current state of the system. This
811
is supposed to ensure that each function is used correctly, and that
812
application code is not attempting to perform a semaphore operation on
813
a mutex or anything like that. If an error is detected then a suitable
814
error code is returned, for example the POSIX function
815
<TT
816
CLASS="FUNCTION"
817
>pthread_mutex_lock</TT
818
> can return various error codes
819
including <TT
820
CLASS="LITERAL"
821
>EINVAL</TT
822
> and <TT
823
CLASS="LITERAL"
824
>EDEADLK</TT
825
>.
826
There are a number of problems with this approach, especially in the
827
context of deeply embedded systems:
828
      </P
829
><P
830
></P
831
><OL
832
TYPE="1"
833
><LI
834
><P
835
>Performing these checks inside the mutex lock and all the other
836
functions requires extra cpu cycles and adds significantly to the code
837
size. Even if the application is written correctly and only makes
838
system function calls with sensible arguments and under the right
839
conditions, these overheads still exist.
840
        </P
841
></LI
842
><LI
843
><P
844
>Returning an error code is only useful if the calling code detects
845
these error codes and takes appropriate action. In practice the
846
calling code will often ignore any errors because the programmer
847
<SPAN
848
CLASS="emphasis"
849
><I
850
CLASS="EMPHASIS"
851
>&#8220;knows&#8221;</I
852
></SPAN
853
> that the function is being
854
used correctly. If the programmer is mistaken then an error condition
855
may be detected and reported, but the application continues running
856
anyway and is likely to fail some time later in mysterious ways.
857
        </P
858
></LI
859
><LI
860
><P
861
>If the calling code does always check for error codes, that adds yet
862
more cpu cycles and code size overhead.
863
        </P
864
></LI
865
><LI
866
><P
867
>Usually there will be no way to recover from certain errors, so if the
868
application code detected an error such as <TT
869
CLASS="LITERAL"
870
>EINVAL</TT
871
>
872
then all it could do is abort the application somehow.
873
        </P
874
></LI
875
></OL
876
><P
877
>The approach taken within the eCos kernel is different. Functions such
878
as <TT
879
CLASS="FUNCTION"
880
>cyg_mutex_lock</TT
881
> will not return an error code.
882
Instead they contain various assertions, which can be enabled or
883
disabled. During the development process assertions are normally left
884
enabled, and the various kernel functions will perform parameter
885
checks and other system consistency checks. If a problem is detected
886
then an assertion failure will be reported and the application will be
887
terminated. In a typical debug session a suitable breakpoint will have
888
been installed and the developer can now examine the state of the
889
system and work out exactly what is going on. Towards the end of the
890
development cycle assertions will be disabled by manipulating
891
configuration options within the eCos infrastructure package, and all
892
assertions will be eliminated at compile-time. The assumption is that
893
by this time the application code has been mostly debugged: the
894
initial version of the code might have tried to perform a semaphore
895
operation on a mutex, but any problems like that will have been fixed
896
some time ago. This approach has a number of advantages:
897
      </P
898
><P
899
></P
900
><OL
901
TYPE="1"
902
><LI
903
><P
904
>In the final application there will be no overheads for checking
905
parameters and other conditions. All that code will have been
906
eliminated at compile-time.
907
        </P
908
></LI
909
><LI
910
><P
911
>Because the final application will not suffer any overheads, it is
912
reasonable for the system to do more work during the development
913
process. In particular the various assertions can test for more error
914
conditions and more complicated errors. When an error is detected
915
it is possible to give a text message describing the error rather than
916
just return an error code.
917
        </P
918
></LI
919
><LI
920
><P
921
>There is no need for application programmers to handle error codes
922
returned by various kernel function calls. This simplifies the
923
application code.
924
        </P
925
></LI
926
><LI
927
><P
928
>If an error is detected then an assertion failure will be reported
929
immediately and the application will be halted. There is no
930
possibility of an error condition being ignored because application
931
code did not check for an error code.
932
        </P
933
></LI
934
></OL
935
><P
936
>Although none of the kernel functions return an error code, many of
937
them do return a status condition. For example the function
938
<TT
939
CLASS="FUNCTION"
940
>cyg_semaphore_timed_wait</TT
941
> waits until either an
942
event has been posted to a semaphore, or until a certain number of
943
clock ticks have occurred. Usually the calling code will need to know
944
whether the wait operation succeeded or whether a timeout occurred.
945
<TT
946
CLASS="FUNCTION"
947
>cyg_semaphore_timed_wait</TT
948
> returns a boolean: a
949
return value of zero or false indicates a timeout, a non-zero return
950
value indicates that the wait succeeded.
951
      </P
952
><P
953
>In conventional APIs one common error conditions is lack of memory.
954
For example the POSIX function <TT
955
CLASS="FUNCTION"
956
>pthread_create</TT
957
>
958
usually has to allocate some memory dynamically for the thread stack
959
and other per-thread data. If the target hardware does not have enough
960
memory to meet all demands, or more commonly if the application
961
contains a memory leak, then there may not be enough memory available
962
and the function call would fail. The eCos kernel avoids such problems
963
by never performing any dynamic memory allocation. Instead it is the
964
responsibility of the application code to provide all the memory
965
required for kernel data structures and other needs. In the case of
966
<TT
967
CLASS="FUNCTION"
968
>cyg_thread_create</TT
969
> this means a
970
<SPAN
971
CLASS="STRUCTNAME"
972
>cyg_thread</SPAN
973
> data structure to hold the thread
974
details, and a <SPAN
975
CLASS="TYPE"
976
>char</SPAN
977
> array for the thread stack.
978
      </P
979
><P
980
>In many applications this approach results in all data structures
981
being allocated statically rather than dynamically. This has several
982
advantages. If the application is in fact too large for the target
983
hardware's memory then there will be an error at link-time rather than
984
at run-time, making the problem much easier to diagnose. Static
985
allocation does not involve any of the usual overheads associated with
986
dynamic allocation, for example there is no need to keep track of the
987
various free blocks in the system, and it may be possible to eliminate
988
<TT
989
CLASS="FUNCTION"
990
>malloc</TT
991
> from the system completely. Problems such
992
as fragmentation and memory leaks cannot occur if all data is
993
allocated statically. However, some applications are sufficiently
994
complicated that dynamic memory allocation is required, and the
995
various kernel functions do not distinguish between statically and
996
dynamically allocated memory. It still remains the responsibility of
997
the calling code to ensure that sufficient memory is available, and
998
passing null pointers to the kernel will result in assertions or
999
system failure.
1000
      </P
1001
></DIV
1002
><DIV
1003
CLASS="NAVFOOTER"
1004
><HR
1005
ALIGN="LEFT"
1006
WIDTH="100%"><TABLE
1007
SUMMARY="Footer navigation table"
1008
WIDTH="100%"
1009
BORDER="0"
1010
CELLPADDING="0"
1011
CELLSPACING="0"
1012
><TR
1013
><TD
1014
WIDTH="33%"
1015
ALIGN="left"
1016
VALIGN="top"
1017
><A
1018
HREF="kernel.html"
1019
ACCESSKEY="P"
1020
>Prev</A
1021
></TD
1022
><TD
1023
WIDTH="34%"
1024
ALIGN="center"
1025
VALIGN="top"
1026
><A
1027
HREF="ecos-ref.html"
1028
ACCESSKEY="H"
1029
>Home</A
1030
></TD
1031
><TD
1032
WIDTH="33%"
1033
ALIGN="right"
1034
VALIGN="top"
1035
><A
1036
HREF="kernel-smp.html"
1037
ACCESSKEY="N"
1038
>Next</A
1039
></TD
1040
></TR
1041
><TR
1042
><TD
1043
WIDTH="33%"
1044
ALIGN="left"
1045
VALIGN="top"
1046
>The eCos Kernel</TD
1047
><TD
1048
WIDTH="34%"
1049
ALIGN="center"
1050
VALIGN="top"
1051
><A
1052
HREF="kernel.html"
1053
ACCESSKEY="U"
1054
>Up</A
1055
></TD
1056
><TD
1057
WIDTH="33%"
1058
ALIGN="right"
1059
VALIGN="top"
1060
>SMP Support</TD
1061
></TR
1062
></TABLE
1063
></DIV
1064
></BODY
1065
></HTML
1066
>

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.