have limited protection mechanisms. For example, I/O instructions need not be privileged instructions that trap to the OS; tasks can directly perform their own I/O. Similarly, memory protection mechanisms can be minimized.
• Direct use of interrupts: General-purpose operating systems typically do not permit any user process to use interrupts directly.
13.5 An advantage of an embedded OS based on an existing commercial OS compared to a purpose-built embedded OS is that the embedded OS derived from a commercial general-purpose OS is based on a set of familiar interfaces, which facilitates portability. The disadvantage of using a general-purpose OS is that it is not optimized for real-time and embedded applications. Thus,
considerable modification may be required to achieve adequate performance.
13.6 • Low interrupt latency: the time it takes to respond to an interrupt and begin executing an ISR.
• Low task switching latency: the time it takes from when a thread becomes available to when actual execution begins.
• Small memory footprint: memory resources for both program and data are kept to a minimum by allowing all components to configure memory as needed.
• Deterministic behavior: throughout all aspect of execution, the kernels performance must be predictable and bounded to meet real-time application requirements.
13.7 An ISR is invoked in response to a hardware interrupt. Hardware interrupts are delivered with minimal intervention to an ISR. The HAL decodes the hardware source of the interrupt and calls the ISR of the attached interrupt object. This ISR may manipulate the hardware but is only allowed to make a restricted set of calls on the driver API. When it returns, an ISR may request that its DSR should be scheduled to run. A DSR is invoked in response to a request by an ISR. A DSR will be run when it is safe to do so without interfering with the scheduler. Most of the time the DSR will run immediately after the ISR, but if the current thread is in the scheduler, it will be delayed until the thread is finished. A DSR is allowed to make a larger set of driver API calls, including, in particular, being able to call cyg_drv_cond_signal() to wake up waiting threads.
13.8 The eCos kernel can be configured to include one or more of six different thread synchronization mechanisms. These include the classic synchronization
mechanisms: mutexes, semaphores, and condition variables. In addition, eCos supports two synchronization/communication mechanisms that are common in real-time systems, namely event flags and mailboxes. Finally, the eCos kernel supports spinlocks, which are useful in SMP (symmetric multiprocessing) systems.
13.9 A wireless sensor network.
13.10 •Allow high concurrency: In a typical wireless sensor network application, the devices are concurrency intensive. Several different flows of data must be kept moving simultaneously. While sensor data is input in a steady stream, processed results must be transmitted in a steady stream. In addition, external controls from remote sensors or base stations must be managed.
• Operate with limited resources: The target platform for TinyOS will have limited memory and computational resources and run of batteries or solar power. A single platform may offer only kilobytes of program memory and hundreds of bytes of RAM. The software must make efficient use of the available processor and memory resources while enabling low power communication.
• Adapt to hardware evolution: Mote hardware is in constant evolution;
applications and most system services must be portable across hardware
generations. Thus, should be possible to upgrade the hardware with little or no software change, if the functionality is the same.
• Support a wide range of applications: Applications exhibit a wide range of requirements in terms of lifetime, communication, sensing, and so on. A modular, general-purpose embedded OS is desired so that a standardized approach leads to economies of scale in developing applications and support software.
• Support a diverse set of platforms: As with the preceding point, a general- purpose embedded OS is desirable.
• Be robust: Once deployed, a sensor network must run unattended for months or years. Ideally, there should be redundancy both within a single system and across the network of sensors. However, both types of redundancy require additional resources. One software characteristic that can improve robustness is to use highly modular, standardized software components.
13.11 An embedded software system built using TinyOS consists of a set of small modules, called components, each of which performs a simple task or set of tasks, and which interface with each other and with hardware in limited and well-defined ways.
13.12 The scheduler plus a number of components.
13.13 The default scheduler in TinyOS is a simple FIFO (first-in-first-out) queue.
A A N S W E R S T O N S W E R S T O P P R O B L E M S R O B L E M S
13.1 This ensures proper exclusion with code running on both other processors and this processor.
13.2 Wherever this is called from, this operation effectively pauses the processor until it succeeds. This operations should therefore be used sparingly, and in situations where deadlocks cannot occur.
13.3 There should be no processors attempting to claim the lock at the time this function is called, otherwise the behavior is undefined.
13.4 The mutex should be unlocked and there should be no threads waiting to lock it when this call in made.
13.5 Timeslicing within a given priority level is irrelevant since there can be only one thread at each priority level.
13.6 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 behavior 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.
13.7 a. This listing is an example using a condition variable. Thread A is acquiring data that are processed by Thread B. First, B executes. On line 24, B acquires the mutex associated with the condition variable. There is no data in the buffer and buffer_empty is initialized so true, so B calls cyg_cond_wait on line 26.
This call suspends B waiting for the condition variable to be set and it unlocks the mutex mut_cond_var. Once A has acquired data, it sets buffer_empty to false (line 11). A then locks the mutex (line 13), signals the condition variable (line 15), and then unlocks the mutex (line 17). B can now run. Before returning from cyg_cond_wait, the mutex mut_cond_var is locked and owned by B.
B can now get the data buffer (line 28) and set buffer_empty to true (line 31). Finally, the mutex is released by B (line 33) and the data in the buffer is processed (line 35)
b. If this code were not atomic, it would be possible for B to miss the signal call from A even though the buffer contained data. This is because
cyg_cond_wait first checks to see if the condition variable is set and, in this case, it is not. Next, the mutex is released in the cyg_cond_wait call. Now, A executes, putting data into the buffer and then signals the condition variable (line 15). Then, B returns to waiting. However, the condition variable has been set.
c. This ensures that the condition that B is waiting on is still true after returning from the condition wait call. This is needed in case there are other threads waiting on the same condition. Source: [MASS03]
13.8 Even if the two threads were running at the same priority, the one attempting to claim the spinlock would spin until it was timesliced and a lot of processor time would be wasted. If an interrupt handler tried to claim a spinlock owned by a thread, the interrupt handler would loop forever.
13.9 The basic reason is that the system is almost completely idle. You only need intelligent scheduling when a resource is under contention.
13.10 a. Clients are not able to monopolize the resource queue by making multiple requests.
b. The basic reason is that a component may still hold a lock after it calls release.
It may still hold the lock because transferring control to the next holder requires reconfiguring the hardware in a split-phase operation. The lock can be granted to the next holder only after this reconfiguration occurs. This means that technically, a snippet of code such as
release() request()
can be seen as the lock as re-request. There are other solutions to the problem (e.g., give the lock a special "owner" who holds it while reconfiguring), but this raises issue when accounting for CPU cycles or energy.