Details | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
1676 | tullio | 1 | %---------------------------------------------------------------------------- |
2 | \chapter{System Start-up and Termination} |
||
3 | %---------------------------------------------------------------------------- |
||
4 | |||
5 | Each S.Ha.R.K. application starts as a sequential C program, with |
||
6 | the classical main funcion. The multitasking environment \index{system initialization} |
||
7 | is already initialized when the application starts. From the main |
||
8 | task you can call any system primitive. |
||
9 | |||
10 | The system finishes when a sys\_end or sys\_abort function is called, |
||
11 | or when the last user task is terminated. For more information, see |
||
12 | \emph{The Generic Kernel Internals} chapter of the S.Ha.R.K. Kernel |
||
13 | Architecture Manual. |
||
14 | |||
15 | The \texttt{sys\_atrunlevel()} primitive\index{sys\_atexit()} allows |
||
16 | to post some handlers, which are automatically executed by the kernel |
||
17 | when it changes runlevel. Such functions can be issued either in the |
||
18 | target execution environment (generally MS-DOS) or just before terminating |
||
19 | the S.Ha.R.K. kernel, depending on the third argument of the primitive. |
||
20 | The handlers posted through \texttt{sys\_atrunlevel()} may also be |
||
21 | called on a kernel abort due to fatal errors. |
||
22 | |||
23 | %---------------------------------------------------------------------------- |
||
24 | \section{Initialization File\label{sec:InitFile}} |
||
25 | %---------------------------------------------------------------------------- |
||
26 | |||
27 | When the system starts, one of the things to be done before going |
||
28 | in multitasking mode is to initialize the devices, the resources and |
||
29 | the schedulers which will be used by the application. To do that, |
||
30 | the Kernel calls the \_\_kernel\_register\_levels\_\_ function, that |
||
31 | usually registers the following modules (see the S.Ha.R.K. Kernel |
||
32 | architecture Manual for more details): |
||
33 | |||
34 | \begin{description} |
||
35 | \item [Scheduling~Modules]A scheduling module implements a particular |
||
36 | Scheduling Algorithm (for example EDF, RM, Round Robin, and so on). |
||
37 | \item [Resource~Modules]A resource module implements a shared resource |
||
38 | access protocol (for example the semaphores, the mutexes, and so on). |
||
39 | \item [Other~devices]Such for example the File System, and other devices |
||
40 | that need to be initialized when the Multitasking Mode is not started |
||
41 | yet. |
||
42 | \end{description} |
||
43 | |||
44 | The function returns a TICK value (in microseconds) that is the time |
||
45 | that will be used for programming the periodic timer interrupt of |
||
46 | the PC. If a value of 0 is returned, the one-shot timer is used instead |
||
47 | (see the OSLib documentation for more informations). Typical return |
||
48 | values range from 250 to 2000 microseconds. |
||
49 | |||
50 | Here is a typical initialization function: |
||
51 | |||
52 | \begin{verbatim} |
||
53 | TIME __kernel_register_levels__(void *arg) { |
||
54 | struct multiboot_info *mb = (struct multiboot_info *)arg; |
||
55 | LEVEL EDF_level; |
||
56 | |||
57 | EDF_level = EDF_register_level(EDF_ENABLE_ALL); |
||
58 | RR_register_level(RRTICK, RR_MAIN_YES, mb); |
||
59 | CBS_register_level(CBS_ENABLE_ALL, EDF_level); |
||
60 | dummy_register_level(); |
||
61 | |||
62 | SEM_register_module(); |
||
63 | CABS_register_module(); |
||
64 | |||
65 | return 1000; |
||
66 | } |
||
67 | \end{verbatim} |
||
68 | |||
69 | As you can see, the system initialization function registers an EDF, |
||
70 | a Round Robin and a CBS module. Then, It register a dummy Module (that |
||
71 | usually is the last of the Scheduling Modules). For more informations |
||
72 | about the Scheduling policies, see Section \ref{sec:sched}. Finally, |
||
73 | Semaphores and CABS are registered, and a value of 1 ms Tick time |
||
74 | is returned to initialize the PC's real-time clock. |
||
75 | |||
76 | For a survey of the architecture of the Scheduling Modules and the |
||
77 | Resource Modules see theKernel Overview Chapter of the S.Ha.R.K. Kernel |
||
78 | Architecture Manual. A set of Initialization functions can be found |
||
79 | on the kernel/init directory of the S.Ha.R.K. source tree. An explanation |
||
80 | of each registration function for each Module can be found in the |
||
81 | S.Ha.R.K. Module Repository Manual. |
||
82 | |||
83 | After the registration of the modules in the system, the Kernel switch |
||
84 | in Multitasking mode, and starts the execution of the handlers that |
||
85 | the modules have posted with the \texttt{sys\_atrunlevel} primitive. |
||
86 | Usually at least one Module will create and activate a task (for example, |
||
87 | the Round Robin Scheduling Module does that) that will start when |
||
88 | all the handlers will be processed. The body of that task is usually |
||
89 | called \_\_init\_\_() and provides an initialization for the most |
||
90 | commonly used devices (such the keyboard, and so on) and modules. |
||
91 | As the last thing, the function simply call the main() function, that |
||
92 | is, the user application starts. A sample of a typical \_\_init\_\_() |
||
93 | function is showed below: |
||
94 | |||
95 | \begin{verbatim} |
||
96 | TASK __init__(void *arg) { |
||
97 | struct multiboot_info *mb = (struct multiboot_info *)arg; |
||
98 | |||
99 | HARTPORT_init(); |
||
100 | __call_main__(mb); |
||
101 | return (void *)0; |
||
102 | } |
||
103 | \end{verbatim} |
||
104 | |||
105 | The source code of the \_\_init\_\_() function is usually inserted |
||
106 | in the initialization file after the \_\_kernel\_register\_levels\_\_ |
||
107 | function. For more information on \_\_call\_main\_\_ see the \texttt{include/kernel/func.h} |
||
108 | include file. |
||
109 | |||
110 | Using the new driver layer the \_\_init()\_\_ function slightly change. |
||
111 | First is executed the HARTPORT\_init function that initialize the |
||
112 | Hartik Port layer (if required), then a task that close all drivers |
||
113 | is created. The next step is the initialization of all used drivers, |
||
114 | followed by the registration of the shutdown task that will be executed |
||
115 | during the system shutdown procedure. At the end the function 'main' |
||
116 | is executed. A tipical example with the new \_\_init()\_\_ function |
||
117 | is: |
||
118 | |||
119 | \begin{verbatim} |
||
120 | TASK __init__(void *arg) { |
||
121 | struct multiboot_info *mb = (struct multiboot_info *)arg; |
||
122 | |||
123 | HARTPORT_init(); |
||
124 | |||
125 | /* Create the shutdown task. */ |
||
126 | /* It will be activated at RUNLEVEL SHUTDOWN */ |
||
127 | set_shutdown_task(); |
||
128 | |||
129 | /* Init the drivers */ |
||
130 | device_drivers_init(); |
||
131 | |||
132 | /* Set the shutdown task activation */ |
||
133 | sys_atrunlevel(call_shutdown_task, NULL, RUNLEVEL_SHUTDOWN); |
||
134 | __call_main__(mb); |
||
135 | |||
136 | return (void *)0; |
||
137 | } |
||
138 | \end{verbatim} |
||
139 | |||
140 | ATTENTION! In some initialization files the function that activate |
||
141 | the shutdown task is in the form: |
||
142 | |||
143 | \begin{verbatim} |
||
144 | #define SHUTDOWN_TIMEOUT_SEC 3 |
||
145 | |||
146 | void call_shutdown_task(void *arg) { |
||
147 | struct timespec t; |
||
148 | |||
149 | sys_gettime(&t); |
||
150 | t.tv_sec += SHUTDOWN_TIMEOUT_SEC; |
||
151 | |||
152 | /* Emergency timeout to exit from RUNLEVEL_SHUTDOWN |
||
153 | kern_event_post(&t,(void *)((void *)sys_abort_shutdown), (void *)0); |
||
154 | |||
155 | task_activate(shutdown_task_PID); |
||
156 | } |
||
157 | \end{verbatim} |
||
158 | |||
159 | This implementation say that the task has 3 seconds to perform drivers |
||
160 | stop. After that interval the system is forced to close even is some |
||
161 | drivers are not closed. If a longer time is needed a greater value |
||
162 | for \texttt{SHUTDOWN\_TIMEOUT\_SEC} constant must be used. If a shutdown |
||
163 | without the timer is preferred the function could be in the simpler |
||
164 | form: |
||
165 | |||
166 | \begin{verbatim} |
||
167 | void call_shutdown_task(void *arg) { |
||
168 | task_activate(shutdown_task_PID); |
||
169 | } |
||
170 | \end{verbatim} |
||
171 | |||
172 | %---------------------------------------------------------------------------- |
||
173 | \subsection{Information about the dependencies among modules} |
||
174 | %---------------------------------------------------------------------------- |
||
175 | |||
176 | Some modules, drivers and ports need to use the semaphores managed |
||
177 | by specific resource sharing modules. In that case, the modules, drivers |
||
178 | and ports expect the semaphores to be initialized by the application |
||
179 | into the initialization file. Here there is a brief list of the modules |
||
180 | which require other module initialization \footnote{All those dependancies wish to be removed.}. |
||
181 | |||
182 | The HARTIK ports (module HARTPORT) use the semaphores, so you must |
||
183 | include and initialize the semaphores if your application or a module |
||
184 | used by your application uses the HARTIK ports. |
||
185 | |||
186 | The HARTIK ports are used by the following modules: |
||
187 | |||
188 | \begin{itemize} |
||
189 | \item ports/first |
||
190 | \item drivers/net |
||
191 | \item drivers/input |
||
192 | \item drivers/oldchar |
||
193 | \end{itemize} |
||
194 | The semaphores are also used by: |
||
195 | |||
196 | \begin{itemize} |
||
197 | \item ports/tftp |
||
198 | \item drivers/oldsnd. |
||
199 | \end{itemize} |
||
200 | |||
201 | %---------------------------------------------------------------------------- |
||
202 | \section{System primitives} |
||
203 | %---------------------------------------------------------------------------- |
||
204 | |||
205 | Here is a list of primitives whose use is related to the system initialization. |
||
206 | |||
207 | %---------------------------------------------------------------------------- |
||
208 | \begin{intest} |
||
209 | SYS\_ATRUNLEVEL\index{SYS\_ATRUNLEVEL} |
||
210 | \end{intest} |
||
211 | |||
212 | \begin{description} |
||
213 | \item [\texttt{int}]\texttt{sys\_atrunlevel(void (*func\_code)(void *),void |
||
214 | *parm, BYTE when);} |
||
215 | \item [Description:]The Generic Kernel supports the specification of the |
||
216 | functions to be called at system initialization and termination. These |
||
217 | functions can be registered through this system primitive; the parameters |
||
218 | for that function are: |
||
219 | \item [\texttt{f}]the function to be registered; |
||
220 | \item [\texttt{p}]the parameter to be passed to function \texttt{f} when |
||
221 | the function will be called; |
||
222 | \item [\texttt{when}]is the situation in witch that function will be called. |
||
223 | The correct values are the following: |
||
224 | |||
225 | \begin{description} |
||
226 | \item [\texttt{RUNLEVEL\_INIT}]Used when programming Modules; |
||
227 | \item [\texttt{RUNLEVEL\_SHUTDOWN}]The function will be called after a |
||
228 | call to \texttt{sys\_abort} or \texttt{sys\_end}; The system is still |
||
229 | in multitasking mode; |
||
230 | \item [\texttt{RUNLEVEL\_BEFORE\_EXIT}]The function will be called when |
||
231 | the Kernel exits from multitasking mode; |
||
232 | \item [\texttt{RUNLEVEL\_AFTER\_EXIT}]The function is called before the |
||
233 | system hangs (or returns to the host OS, if the proprietary extender |
||
234 | is used). |
||
235 | |||
236 | \end{description} |
||
237 | |||
238 | It is also possible to specify with an OR operator a flag \texttt{NO\_AT\_ABORT} |
||
239 | that disables the call to the functions if the system is exiting with |
||
240 | a \texttt{sys\_abort} function. |
||
241 | |||
242 | \end{description} |
||
243 | |||
244 | You can post at most \texttt{MAX\_RUNLEVEL\_FUNC} functions. See the |
||
245 | S.Ha.R.K. Kernel Architecture Manual for more details. |
||
246 | |||
247 | \begin{description} |
||
248 | \item [See]\textbf{also}: \texttt{sys\_init()}, \texttt{sys\_end()}. |
||
249 | \end{description} |
||
250 | |||
251 | %---------------------------------------------------------------------------- |
||
252 | \begin{intest} |
||
253 | EXIT\index{exit} |
||
254 | \end{intest} |
||
255 | |||
256 | \begin{description} |
||
257 | \item [\texttt{void exit(int status);}] |
||
258 | \item [Description:]This function call terminates the Kernel. In this phase, |
||
259 | the Kernel tries to correctly close all the initialized modules and |
||
260 | drivers. The functions eventually posted with the \texttt{sys\_at\_runlevel} |
||
261 | call are also executed. |
||
262 | |||
263 | If called inside an event or inside an ISR, it does return to the |
||
264 | caller. The system shutdown will start when all the current interrupts |
||
265 | have been serviced, and the system has been rescheduled. Otherwise, |
||
266 | this function follows the POSIX specification. |
||
267 | \item [See]\textbf{also}: \texttt{\_exit()}, \texttt{sys\_panic()}, \texttt{sys\_atrunlevel()}. |
||
268 | \end{description} |
||
269 | |||
270 | %---------------------------------------------------------------------------- |
||
271 | \begin{intest} |
||
272 | \_EXIT\index{\_exit} |
||
273 | \end{intest} |
||
274 | |||
275 | \begin{description} |
||
276 | \item [\texttt{void \_exit(int status);}] |
||
277 | \item [Description:]Same as \texttt{exit()}. functions posted through \texttt{sys\_at\_runlevel} |
||
278 | with \texttt{NO\_AT\_ABORT} set or functions posted with \texttt{atexit()} |
||
279 | are not executed. |
||
280 | \item [See]\textbf{also}: \texttt{exit()}, \texttt{atexit()}, \texttt{sys\_panic()}, |
||
281 | \texttt{sys\_atrunlevel()}. |
||
282 | \end{description} |
||
283 | |||
284 | %---------------------------------------------------------------------------- |
||
285 | \begin{intest} |
||
286 | SYS\_PANIC\index{sys\_end} |
||
287 | \end{intest} |
||
288 | |||
289 | \begin{description} |
||
290 | \item [\texttt{void sys\_panic(const char * fmt, ...);}] |
||
291 | \item [Description:]This function call print a message then call sys\_abort(333). |
||
292 | \item [See]\textbf{also}: \texttt{sys\_abort()}, \texttt{sys\_end()}, \texttt{sys\_atrunlevel()}. |
||
293 | \end{description} |
||
294 | |||
295 | %---------------------------------------------------------------------------- |
||
296 | \begin{intest} |
||
297 | SYS\_SHUTDOWN\_MESSAGE\index{sys\_\_shutdown\_message} |
||
298 | \end{intest} |
||
299 | |||
300 | \begin{description} |
||
301 | \item [\texttt{int sys\_shutdown\_message(char *fmt,...);}] |
||
302 | \item [Description:]This function call saves a message in a reserved area, |
||
303 | that will be printed at system shutdown. It does not end the system. |
||
304 | \item [See]\textbf{also}: \texttt{sys\_panic()}. |
||
305 | \end{description} |
||
306 | |||
307 | %---------------------------------------------------------------------------- |
||
308 | \begin{intest} |
||
309 | SYS\_ABORT\_SHUTDOWN\index{sys\_abort\_shutdown} |
||
310 | \end{intest} |
||
311 | |||
312 | \begin{description} |
||
313 | \item [\texttt{int sys\_abort\_shutdown(int err);}] |
||
314 | \item [Description:]This function will force the system to end the \texttt{SHUTDOWN} |
||
315 | runlevel if there are system tasks which cannot be stopped. If called |
||
316 | when the system is still in the \texttt{RUNLEVEL\_RUNNING} runlevel, |
||
317 | the function behaves like \texttt{exit()}. If called inside an OSLib |
||
318 | event or inside an IRQ,it does return to the caller. The system shutdown |
||
319 | will start when all the current interrupts have been serviced, and |
||
320 | the system has been rescheduled. Otherwise, this function does not |
||
321 | return. |
||
322 | \end{description} |
||
323 | |||
324 | %---------------------------------------------------------------------------- |
||
325 | \begin{intest} |
||
326 | SYS\_SET\_REBOOT\index{sys\_set\_reboot} |
||
327 | \end{intest} |
||
328 | |||
329 | \begin{description} |
||
330 | \item [\texttt{int sys\_set\_reboot(int mode);}] |
||
331 | \item [Description:]This function sets the reboot \texttt{mode}, which |
||
332 | specifies what will happen after the system end. \texttt{mode} options |
||
333 | are: |
||
334 | \item [EXIT\_MODE\_HALT:]the system will call the halt (\texttt{HLT}) instruction. |
||
335 | \item [EXIT\_MODE\_COLD:]the system will perform the cold reboot (slow |
||
336 | reboot). |
||
337 | \item [EXIT\_MODE\_WARM:]the system will perform the warm reboot (fast |
||
338 | reboot). |
||
339 | \item [EXIT\_MODE\_REAL:]the system will return to the real mode (\textbf{default |
||
340 | selection}). |
||
341 | \end{description} |
||
342 | |||
343 | %---------------------------------------------------------------------------- |
||
344 | \chapter{Task Management} |
||
345 | %---------------------------------------------------------------------------- |
||
346 | |||
347 | %---------------------------------------------------------------------------- |
||
348 | \section{Task Model} |
||
349 | %---------------------------------------------------------------------------- |
||
350 | |||
351 | S.Ha.R.K. tasks are defined using standard C functions which return |
||
352 | a \texttt{void *} type \footnote{for readability, that type has been called TASK.} and can have one void * argument, which is passed when the task |
||
353 | is created. A task is identified by a system-wide unique process identifier |
||
354 | (\texttt{PID}) and a consecutive number \footnote{a task with PID p has a consecutive number that is proc\_table{[}p{]}.task\_ID.}. |
||
355 | |||
356 | A task has tipically a set of Quality of Service requirements that |
||
357 | need to be fullfilled by the Kernel. The kernel uses its registered |
||
358 | Scheduling Modules to meet the QoS required by a specified task. The |
||
359 | QoS reuired is specified at creation time through a Task Model, that |
||
360 | is passed to the task creation primitives. |
||
361 | |||
362 | A Task, can be \textit{Periodic} or \textit{Aperiodic}. Periodic tasks |
||
363 | are automatically activated by the kernel with a desired period, whereas |
||
364 | aperiodic tasks can either be activated by an explicit system call |
||
365 | or upon the occurrence of a desired event. |
||
366 | |||
367 | The typical task code consists of an optional initialization of local |
||
368 | variables and resources, followed by a (finite or infinite) loop, |
||
369 | representing the task's body. The last instruction of such a loop |
||
370 | must be the primitive \texttt{task\_endcycle()}\index{task\_endcycle()} |
||
371 | or the primitive \texttt{task\_sleep()} \index{task\_sleep()} which |
||
372 | signals the end of a generic job. |
||
373 | |||
374 | The task can access a local and a global context by following the |
||
375 | C scoping rules; the local context is defined by the local variables |
||
376 | and the single optional input argument. The following example shows |
||
377 | a typical task code fragment: |
||
378 | |||
379 | \begin{verbatim} |
||
380 | void *my_task(void *par) { |
||
381 | |||
382 | /* Local Context*/ |
||
383 | int a, b, c; |
||
384 | |||
385 | /* Initialization */ |
||
386 | b = c = (int)par + 1; |
||
387 | a = (int)par / 2; |
||
388 | ... |
||
389 | |||
390 | while (1) { |
||
391 | |||
392 | /* Body here!*/ |
||
393 | ... |
||
394 | |||
395 | task_endcycle(); |
||
396 | } |
||
397 | } |
||
398 | \end{verbatim} |
||
399 | |||
400 | \noindent \texttt{my\_task()} has just one integer input argument |
||
401 | (passed through the void * parameter) and three local variables\index{local task context}. |
||
402 | The life-cycle of the local variables is the same as the task one, |
||
403 | since they are allocated on the task's stack. Obviously they retain |
||
404 | their values between two consecutive jobs. |
||
405 | |||
406 | \noindent One of the most important parameters for a real-time task |
||
407 | $\tau_{i}$ is the \emph{deadline}, defined as the maximum time allowed |
||
408 | for a task job to terminate. More precisely, we distinguish between |
||
409 | the \emph{absolute deadline} (denoted by $d_{i}$) specified with |
||
410 | respect to time 0, and the \emph{Relative Deadline} (denoted by $D_{i}$) |
||
411 | specified with respect to the activation time $r_{i,k}$ of the $k$-th |
||
412 | job of task $\tau_{i}$. We have that: |
||
413 | \[ |
||
414 | d_{i}=r_{i,k}+D_{i}. |
||
415 | \] |
||
416 | |||
417 | \noindent Tasks can also have different level of criticality, for |
||
418 | example: |
||
419 | |||
420 | \begin{itemize} |
||
421 | \item \texttt{HARD}\index{HARD} tasks are the most critical in the system. |
||
422 | For this reason, they are subjected to a guarantee algorithm at creation |
||
423 | time. The system enforces a strict compliance to the deadline constraint |
||
424 | for this kind of tasks% |
||
425 | \footnote{The guarantee algorithm tries to verify that both the newly activated |
||
426 | hard task and the previously existing ones will finish within their |
||
427 | deadlines% |
||
428 | }. If a hard deadline is missed, the system raises an exception which, |
||
429 | by default, results in the program termination. Recovery actions can |
||
430 | be programmed for this kind of exception (as shown below). |
||
431 | \item \texttt{SOFT}\index{SOFT} tasks can miss some deadline, and are scheduled |
||
432 | in order not to jeopardize HARD tasks' schedulability. This is done |
||
433 | through a service mechanism (see \ref{sec:sched}) which guarantees |
||
434 | each soft task a predefined bandwidth (i.e., a fraction of processor |
||
435 | utilization) while preserving the guarantee performed on hard tasks. |
||
436 | \item \texttt{NRT}\index{NRT} (Non Real-Time) tasks are scheduled in background |
||
437 | according to their relative fixed priority. Typically, they are used |
||
438 | for monitoring or debugging purposes. |
||
439 | \end{itemize} |
||
440 | The Task criticality, periodicity and the deadlines are coded into |
||
441 | the Task Model that is passed to the creation primitive. Each new |
||
442 | Scheduling Module can use its own Task Model to include the specific |
||
443 | task QoS requirements. |
||
444 | |||
445 | Each task can be in one of a set of states; the states that a task |
||
446 | can be in depend on each particular Module. For example, typical Task |
||
447 | states can be: |
||
448 | |||
449 | \begin{itemize} |
||
450 | \item \texttt{\textbf{EXE}}\index{EXE}: at any time, in the system there |
||
451 | is only one task in the EXE state, and it is the task actually executing. |
||
452 | \item \texttt{\textbf{READY}}\index{READY}: it includes all active tasks |
||
453 | ready to execute, except for the currently running task. |
||
454 | \item \texttt{\textbf{SLEEP}}\index{SLEEP}: it includes all aperiodic tasks |
||
455 | which terminated a job and are waiting for the next activation. Moreover, |
||
456 | each created task (periodic or aperiodic) that has not been activated |
||
457 | is put in the SLEEP state. |
||
458 | \item \texttt{\textbf{IDLE}}\index{IDLE}: is the state of those periodic |
||
459 | tasks which terminated a job and are waiting for the next activation. |
||
460 | \item \texttt{\textbf{BLOCKED}}\index{BLOCKED}: it includes all the tasks |
||
461 | blocked on a semaphore. |
||
462 | \end{itemize} |
||
463 | |||
464 | %---------------------------------------------------------------------------- |
||
465 | \section{The scheduling policy% |
||
466 | \footnote{This section is derived from the Kernel overview chapter of the S.Ha.R.K. |
||
467 | Architecture Manual.% |
||
468 | }\label{sec:sched} } |
||
469 | %---------------------------------------------------------------------------- |
||
470 | |||
471 | The S.Ha.R.K. scheduling architecture is based on a \emph{Generic |
||
472 | Kernel}, which does not implement any particular scheduling algorithm, |
||
473 | but postpones scheduling decisions to external entities, the \emph{scheduling |
||
474 | modules}. External modules can implement periodic scheduling algorithms, |
||
475 | soft task management through real-time servers, semaphore protocols, |
||
476 | and resource management policies. |
||
477 | |||
478 | The Generic Kernel provides the mechanisms used by the modules to |
||
479 | perform scheduling and resource management thus allowing the system |
||
480 | to abstract from the specific algorithms that can be implemented. |
||
481 | The Generic Kernel simply provides the primitives without specifying |
||
482 | any algorithm, whose implementation resides in external modules, configured |
||
483 | at run-time with the Initialization function. |
||
484 | |||
485 | Scheduling Modules are used by the Generic Kernel to schedule tasks, |
||
486 | or serve aperiodic requests using an aperiodic server. The Scheduling |
||
487 | Modules are organized into levels, one Module for each level. These |
||
488 | levels can be thought as priority scheduling levels (their priority |
||
489 | correspond to the order which they appear in the Initialization function). |
||
490 | When the Generic Kernel has to perform a scheduling decision, it asks |
||
491 | the modules for the task to schedule, according to fixed priorities: |
||
492 | first, it invokes a scheduling decision to the highest priority module, |
||
493 | then (if the module does not manage any task ready to run), it asks |
||
494 | the next high priority module, and so on. The Generic Kernel schedules |
||
495 | the first task of the highest priority non empty module's queue. |
||
496 | |||
497 | In this way, the Scheduling policy can be tuned simply modifying the |
||
498 | Initialization function. The standard distribution of the S.Ha.R.K. |
||
499 | Kernel includes a set of predefined Initialization functions that |
||
500 | can be used when developing a new application. For more informations |
||
501 | see the S.Ha.R.K. Module Manual, where each Scheduling Modules and |
||
502 | each predefined initialization functioon are described in detail. |
||
503 | |||
504 | %---------------------------------------------------------------------------- |
||
505 | \section{Task Creation\index{task creation}} |
||
506 | %---------------------------------------------------------------------------- |
||
507 | |||
508 | In order to run a S.Ha.R.K. task, three steps have to be performed: |
||
509 | parameters definition, creation, and activation. To improve the system |
||
510 | flexibility, each task can be characterized by a large number of parameters, |
||
511 | most of which are optional. For this reason, a set of structures derived |
||
512 | from \texttt{TASK\_MODEL}\index{MODEL} structure have been introduced |
||
513 | to simplify the parameters' definition phase. The first thing to do |
||
514 | in order to define a task is to declare a Model variable and initialize |
||
515 | it using the pmacro provided (see the S.Ha.R.K. Module Manual for |
||
516 | more informations). |
||
517 | |||
518 | \noindent Once the task's parameters have been set, the task can be |
||
519 | created using the \texttt{task\_create} or the \texttt{task\_createn} |
||
520 | system call. |
||
521 | |||
522 | %---------------------------------------------------------------------------- |
||
523 | \begin{intest} |
||
524 | TASK\_CREATEN\index{task\_createn()} and TASK\_CREATE\index{task\_create()} |
||
525 | \end{intest} |
||
526 | |||
527 | \begin{description} |
||
528 | \item [\texttt{PID task\_createn(char *name, TASK (*body)(), |
||
529 | TASK\_MODEL *m, ...);}] |
||
530 | \item [\texttt{PID task\_create(char *name, TASK (*body)(), |
||
531 | TASK\_MODEL *m, RES\_MODEL *r);}] |
||
532 | \item [Description:]\texttt{task\_createn} creates a new task. \texttt{name} |
||
533 | is a pointer to a string representing the task name; \texttt{body()} |
||
534 | is a pointer to the task body (i.e. the name of the C function containing |
||
535 | the task code); \texttt{m} specifies the Model that contain the QoS |
||
536 | specification of the task (the value can not be equal to NULL). Then, |
||
537 | follow a list of Resource Models terminated with NULL (see the S.Ha.R.K. |
||
538 | Module Manual for the available Task Models and Resource Models). |
||
539 | \texttt{task\_create} is a redefinition of \texttt{task\_createn} |
||
540 | that can be used when there is at least one Resource Model to be passed |
||
541 | to the creation primitive. |
||
542 | \item [Return]\textbf{value}: The function returns the identifier of the |
||
543 | newly created task, or -1 if the task creation fails (in this case |
||
544 | the \texttt{errno()} system call can be used to determine the error's |
||
545 | cause). |
||
546 | \item [See]\textbf{also}: \texttt{task\_activate()}, \texttt{task\_kill()}. |
||
547 | \item [Example]~ |
||
548 | \end{description} |
||
549 | |||
550 | \begin{verbatim} |
||
551 | int main(int argc, char **argv) { |
||
552 | HARD_TASK_MODEL m; |
||
553 | hard_task_default_model(m); |
||
554 | hard_task_def_wcet(m,ASTER_WCET); |
||
555 | hard_task_def_mit(m,10000); |
||
556 | hard_task_def_group(m,1); |
||
557 | hard_task_def_ctrl_jet(m); |
||
558 | |||
559 | p1 = task_create("Aster", aster, &m, NULL); |
||
560 | if (p1 == -1) { |
||
561 | perror("Error: Could not create task <aster> ..."); |
||
562 | exit(1); |
||
563 | } |
||
564 | } |
||
565 | \end{verbatim} |
||
566 | |||
567 | |||
568 | %---------------------------------------------------------------------------- |
||
569 | \section{Group Creation} |
||
570 | %---------------------------------------------------------------------------- |
||
571 | |||
572 | Group creation is a feature provided by S.Ha.R.K. that allows a user |
||
573 | to create a set of tasks. The group creation differs from the creation |
||
574 | made by the task\_create and task\_createn primitives because in group |
||
575 | creation the acceptance test is done for the whole set of task (and |
||
576 | not for every task in sequence) only when every task which belong |
||
577 | to the set has been initialized in the system. After the acceptance |
||
578 | test, the user have to inquire the Scheduling Module to see the tasks |
||
579 | that have been accepted and successfully created in the system. |
||
580 | |||
581 | The primitives provided by S.Ha.R.K. to support group creation are: |
||
582 | |||
583 | \begin{itemize} |
||
584 | \item group\_create |
||
585 | \item group\_create\_accept |
||
586 | \item group\_create\_reject |
||
587 | \end{itemize} |
||
588 | The documentation about group creation can be found in the \emph{Group |
||
589 | creation HOWTO} available on the S.Ha.R.K. website. |
||
590 | |||
591 | %---------------------------------------------------------------------------- |
||
592 | \section{Task Activation and Termination} |
||
593 | %---------------------------------------------------------------------------- |
||
594 | |||
595 | When a task is created, it is put in the \texttt{SLEEP} state, where |
||
596 | it is kept until activation, which can be triggered by an external |
||
597 | interrupt or by an explicit \texttt{task\_activate()} primitive). |
||
598 | Periodic jobs that complete execution are handled by the registered |
||
599 | Scheduling Modules (usually they are put in an \texttt{IDLE} state |
||
600 | or similar), from which they will be automatically re-activated by |
||
601 | the system timer. Aperiodic jobs that complete execution return to |
||
602 | the \texttt{SLEEP} state, where they wait for an explicit re-activation. |
||
603 | |||
604 | Some scheduling models (such as the EDF and RM modules) support release |
||
605 | offsets. In an offset is given in the task model, the \texttt{task\_activate()} |
||
606 | will put the task in the \texttt{IDLE} state, waiting for the first |
||
607 | release to occur. |
||
608 | |||
609 | A task can be destroyed using the \texttt{task\_kill()} system call, |
||
610 | that frees its descriptor. A task can kill itself using the \texttt{task\_abort()} |
||
611 | system call. |
||
612 | |||
613 | In S.Ha.R.K., tasks can be members of groups to allow simultaneous |
||
614 | activation or termination. A task can be put in a group through a |
||
615 | macro that works on the task model passed at task creation. The name |
||
616 | of the macro depends on the name of the Task Model used; usually its |
||
617 | name is like \texttt{XXX\_task\_def\_group(group\_number)}, where |
||
618 | XXX is the name of the task Model; the group\_number 0 indicates that |
||
619 | a task belongs to no groups. |
||
620 | |||
621 | Task cancellation, join, the cleanup handlers and the task specific |
||
622 | data works as the POSIX standard; only the name of the primitives |
||
623 | are changed from \texttt{pthread\_XXX} to \texttt{task\_XXX}. In any |
||
624 | case, the \texttt{pthread\_XXX} versions are available for POSIX compatibility. |
||
625 | |||
626 | \textbf{Warning}: \texttt{task\_kill()} kills a task only if the cancellation |
||
627 | type of the task is set to asynchronous. If the cancellation type |
||
628 | is set to deferred, the task will terminate only when it reach a cancellation |
||
629 | point. |
||
630 | |||
631 | %---------------------------------------------------------------------------- |
||
632 | \begin{intest} |
||
633 | TASK\_ACTIVATE\index{task\_activate()} |
||
634 | \end{intest} |
||
635 | |||
636 | \begin{description} |
||
637 | \item [\texttt{int task\_activate(PID p);}] |
||
638 | \item [Description:]It activates task \texttt{p}. Usually the activation |
||
639 | will insert the task into the ready queue. (If the task has an offset, |
||
640 | the task will be put in the ready queue after the offset.) Returns |
||
641 | |||
642 | is set to \texttt{EUNVALID\_TASK\_ID}. |
||
643 | \item [See]\textbf{also}: \texttt{task\_create(), task\_kill(), group\_activate()}. |
||
644 | \end{description} |
||
645 | |||
646 | %---------------------------------------------------------------------------- |
||
647 | \begin{intest} |
||
648 | TASK\_KILL\index{task\_kill()} |
||
649 | \end{intest} |
||
650 | |||
651 | \begin{description} |
||
652 | \item [\texttt{int task\_kill(PID p);}] |
||
653 | \item [Description:]It asks for a cancellation of the task p. It returns |
||
654 | -1 in case of error, 0 otherwise. If an error occurred, the errno |
||
655 | variable is set to \texttt{EINVALID\_KILL}. A task which has the NO\_KILL |
||
656 | flag set can not be killed. If the task has already been killed but |
||
657 | it is not died yet, the primitive does nothing. A task that has the |
||
658 | cancellation type set to asynchronous will die just when it will be |
||
659 | scheduled again by the system; instead, if the cancellation type is |
||
660 | set to deferred, the task will die only at the reaching of a cancellation |
||
661 | point. This function is the correspondent of the \texttt{pthread\_cancel()} |
||
662 | primitive. |
||
663 | \item [See]\textbf{also}: \texttt{task\_create(), task\_activate(), group\_kill()}. |
||
664 | \end{description} |
||
665 | \pagebreak |
||
666 | |||
667 | %---------------------------------------------------------------------------- |
||
668 | \begin{intest} |
||
669 | TASK\_ABORT\index{task\_abort()} |
||
670 | \end{intest} |
||
671 | |||
672 | \begin{description} |
||
673 | \item [\texttt{void task\_abort(void *returnvalue);}] |
||
674 | \item [Description:]It aborts the calling task, removing it from the system. |
||
675 | If the task is joinable, the return value will be stored by the kernel |
||
676 | and given to any task that calls a task\_join primitive on the died |
||
677 | task. |
||
678 | \item [See]\textbf{also}: \texttt{task\_create(), task\_activate(), task\_kill()}. |
||
679 | \end{description} |
||
680 | |||
681 | %---------------------------------------------------------------------------- |
||
682 | \begin{intest} |
||
683 | TASK\_BLOCK\_ACTIVATION\index{task\_block\_activation()} |
||
684 | \end{intest} |
||
685 | |||
686 | \begin{description} |
||
687 | \item [\texttt{int task\_block\_activation(PID p);}] |
||
688 | \item [Description:]It blocks all explicit activation of a task made with |
||
689 | \texttt{task\_activate} and \texttt{group\_activate}. The activations |
||
690 | made after this call are buffered (counted) in an internal counter. |
||
691 | It returns 0 in case of success or -1 in case of error. In the latter |
||
692 | case, \texttt{errno} is set to \texttt{EUNVALID\_TASK\_ID}. If the |
||
693 | activations were already blocked, it does nothing. |
||
694 | \item [See]\textbf{also}: \texttt{task\_unblock\_activation(), task\_activate()}. |
||
695 | \end{description} |
||
696 | |||
697 | %---------------------------------------------------------------------------- |
||
698 | \begin{intest} |
||
699 | TASK\_UNBLOCK\_ACTIVATION\index{task\_unblock\_activation()} |
||
700 | \end{intest} |
||
701 | |||
702 | \begin{description} |
||
703 | \item [\texttt{int task\_unblock\_activation(PID p);}] |
||
704 | \item [Description:]It unblocks the activations of a task after a call |
||
705 | to task\_block\_activation. After this call, the task can be explicitly |
||
706 | activated. It returns the number of buffered activations, or -1 if |
||
707 | an error occurred. If an error occurred, the errno variable is set |
||
708 | to EUNVALID\_TASK\_ID. If the activations were not blocked, it simply |
||
709 | returns 0. Note that the primitive simply returns the number of buffered |
||
710 | activations, \emph{without} activating the task. |
||
711 | \item [See]\textbf{also}: \texttt{task\_block\_activation(), task\_activate()}. |
||
712 | \end{description} |
||
713 | |||
714 | %---------------------------------------------------------------------------- |
||
715 | \begin{intest} |
||
716 | GROUP\_ACTIVATE\index{group\_activate()} |
||
717 | \end{intest} |
||
718 | |||
719 | \begin{description} |
||
720 | \item [\texttt{int group\_activate(WORD g);}] |
||
721 | \item [Description:]It activates all tasks belonging to group \texttt{g}. |
||
722 | Returns 0 in case of success or -1 in case of error; the \texttt{errno} |
||
723 | variable is set to \texttt{EUNVALID\_GROUP}. |
||
724 | \item [See]\textbf{also}: \texttt{task\_create(), task\_activate(), group\_kill()}. |
||
725 | \end{description} |
||
726 | |||
727 | %---------------------------------------------------------------------------- |
||
728 | \begin{intest} |
||
729 | GROUP\_KILL\index{group\_kill()} |
||
730 | \end{intest} |
||
731 | |||
732 | \begin{description} |
||
733 | \item [\texttt{void group\_kill(WORD g);}] |
||
734 | \item [Descrizione:]It kills all tasks belonging to group \texttt{g}. It |
||
735 | returns -1 in case of error, 0 otherwise. If an error occurred, the |
||
736 | errno variable is set to \texttt{EUNVALID\_GROUP}. The kill request |
||
737 | to a single task that belong to a group is done in a way similar to |
||
738 | that done in the primitive \texttt{task\_kill()}. |
||
739 | \item [See]\textbf{also}: \texttt{task\_create(), task\_activate(), group\_activate()}, |
||
740 | \texttt{task\_kill()}. |
||
741 | \end{description} |
||
742 | |||
743 | %---------------------------------------------------------------------------- |
||
744 | \section{Task Instances} |
||
745 | %---------------------------------------------------------------------------- |
||
746 | |||
747 | S.Ha.R.K. supports the concept of instance for its task. A typical |
||
748 | task function is composed by an initialization part and a body part |
||
749 | that does the work for that the task was created; for example: |
||
750 | |||
751 | \begin{verbatim} |
||
752 | void *mytask(void *arg) { |
||
753 | /* initialization part */ |
||
754 | |||
755 | for (;;) { |
||
756 | /* body */ |
||
757 | ... |
||
758 | |||
759 | task_endcycle(); |
||
760 | } |
||
761 | } |
||
762 | \end{verbatim} |
||
763 | |||
764 | In the example, the task will never terminate, and it also calls the |
||
765 | \texttt{task\_endcycle} primitive to say to the Kernel that the current instance |
||
766 | is terminated \footnote{The concept of instance is introduced into S.Ha.R.K. |
||
767 | because in that way the Kernel can directly support task Quality Of Service |
||
768 | parameters like deadlines, periods and so on in a native way. Note that the |
||
769 | concept of instance is not covered by the POSIX standard, that only support a |
||
770 | fixed priority scheduler. In POSIX, a periodic task can only be implemented |
||
771 | using the Real-Time extensions and in particular using the Timer feature. |
||
772 | S.Ha.R.K. implements also that approach, however the native primitives are |
||
773 | better in terms of efficiency.}. |
||
774 | |||
775 | %---------------------------------------------------------------------------- |
||
776 | \begin{intest} |
||
777 | TASK\_ENDCYCLE\index{task\_endcycle()} |
||
778 | \end{intest} |
||
779 | |||
780 | \begin{description} |
||
781 | \item [\texttt{void task\_endcycle(void);}] |
||
782 | \item [Description:]It terminates the currently executing job of the calling |
||
783 | task. The behaviour of this primitive may sligtly change depending |
||
784 | on the Scheduling Module registered at initialization time. Tipically, |
||
785 | the \texttt{task\_endcycle} primitive suspends the task until an automatic |
||
786 | reactivation that is made internally by the Kernel. Moreover, the |
||
787 | \texttt{task\_endcycle()} primitive usually keeps track of pending |
||
788 | activations \footnote{There is a pending activation when a task is activated before the |
||
789 | current instance has finished. In this case, if the \texttt{task\_endcycle()} |
||
790 | primitive is called and there is a pending activation, it simply does |
||
791 | nothing.}. Previous versions of the kernel supported a \texttt{task\_sleep()}\index{task\_sleep()} |
||
792 | primitive with similar behavior. That function is currently unsupported. |
||
793 | The primitive is a cancellation point. |
||
794 | \item [Implementation:]This primitive is implemented as task\_message(NULL, 1); |
||
795 | \footnote{Note on the implementation: this primitive is implemented as |
||
796 | \texttt{task\_message(NULL, 1);}} |
||
797 | \item [See]\textbf{also:} \texttt{task\_activate}. |
||
798 | \end{description} |
||
799 | |||
800 | %---------------------------------------------------------------------------- |
||
801 | \section{Task (thread) specific data} |
||
802 | %---------------------------------------------------------------------------- |
||
803 | |||
804 | These functions works in a way equal to their POSIX counterparts. |
||
805 | These primitives are used for managing task specific data, that are |
||
806 | a few data variables that can be referred in a common way independently |
||
807 | from the task that asks for it. The system also ensures a proper cleanup |
||
808 | when the task is killed. As an example, the \texttt{errno} variable |
||
809 | can be thought as a task specific data. In this manual only their |
||
810 | interfaces are described; for more informations, see the POSIX standard. |
||
811 | |||
812 | %---------------------------------------------------------------------------- |
||
813 | \begin{intest} |
||
814 | TASK\_KEY\_CREATE\index{task\_key\_create()} |
||
815 | \end{intest} |
||
816 | |||
817 | \begin{description} |
||
818 | \item [\texttt{int task\_key\_create(task\_key\_t *key, void |
||
819 | (*destructor)(void *));}] |
||
820 | \item [Description:]It creates a task key that can be used to refer a task\_specific |
||
821 | data. The name of the POSIX counterpart is \texttt{pthread\_key\_create}. |
||
822 | \end{description} |
||
823 | \pagebreak |
||
824 | |||
825 | %---------------------------------------------------------------------------- |
||
826 | \begin{intest} |
||
827 | TASK\_GETSPECIFIC\index{task\_getspecific()} |
||
828 | \end{intest} |
||
829 | |||
830 | \begin{description} |
||
831 | \item [\texttt{void *task\_getspecific(task\_key\_t key);}] |
||
832 | \item [Description:]It gets the current value for the key (note that the |
||
833 | value of the key vary from task to task). The name of the POSIX counterpart |
||
834 | is \texttt{pthread\_getspecific}. |
||
835 | \end{description} |
||
836 | |||
837 | %---------------------------------------------------------------------------- |
||
838 | \begin{intest} |
||
839 | TASK\_SETSPECIFIC\index{task\_setspecific()} |
||
840 | \end{intest} |
||
841 | |||
842 | \begin{description} |
||
843 | \item [\texttt{int task\_setspecific(task\_key\_t key, const void |
||
844 | *value); }] |
||
845 | \item [Description:]It sets the current value for the key. The name of |
||
846 | the POSIX counterpart is \texttt{pthread\_setspecific}. |
||
847 | \end{description} |
||
848 | |||
849 | %---------------------------------------------------------------------------- |
||
850 | \begin{intest} |
||
851 | TASK\_KEY\_DELETE\index{task\_key\_delete()} |
||
852 | \end{intest} |
||
853 | |||
854 | \begin{description} |
||
855 | \item [\texttt{int task\_key\_delete(task\_key\_t key);}] |
||
856 | \item [Description:]It deletes the current key. The name of the POSIX counterpart |
||
857 | is \texttt{pthread\_key\_delete}. |
||
858 | \end{description} |
||
859 | |||
860 | %---------------------------------------------------------------------------- |
||
861 | \section{Task cancellation} |
||
862 | %---------------------------------------------------------------------------- |
||
863 | |||
864 | These primitives are used when managing task cancellation. They are |
||
865 | directly derived from the POSIX standard. Nothe that the POSIX interface |
||
866 | is also available. |
||
867 | |||
868 | %---------------------------------------------------------------------------- |
||
869 | \begin{intest} |
||
870 | TASK\_CLEANUP\_PUSH\index{task\_cleanup\_push()} |
||
871 | \end{intest} |
||
872 | |||
873 | \begin{description} |
||
874 | \item [\texttt{void task\_cleanup\_push(void (*routine)(void |
||
875 | *), void *arg);}] |
||
876 | \item [Description:]It pushes the specified cancellation cleanup handler |
||
877 | routine onto the cancellation cleanup stack. The name of the POSIX |
||
878 | counterpart is \texttt{pthread\_cleanup\_push}. |
||
879 | \end{description} |
||
880 | |||
881 | %---------------------------------------------------------------------------- |
||
882 | \begin{intest} |
||
883 | TASK\_CLEANUP\_POP\index{task\_cleanup\_pop()} |
||
884 | \end{intest} |
||
885 | |||
886 | \begin{description} |
||
887 | \item [\texttt{void task\_cleanup\_pop(int execute);}] |
||
888 | \item [Description:]It removes the routine at the top of the cancellation |
||
889 | cleanup stack of the calling thread. If \texttt{execute} is not equal |
||
890 | 0, the routine previously pushed is called. The name of the POSIX |
||
891 | counterpart is \texttt{pthread\_cleanup\_pop}. |
||
892 | \end{description} |
||
893 | |||
894 | %---------------------------------------------------------------------------- |
||
895 | \begin{intest} |
||
896 | TASK\_TESTCANCEL\index{task\_testcancel()} |
||
897 | \end{intest} |
||
898 | |||
899 | \begin{description} |
||
900 | \item [\texttt{void task\_testcancel(void);}] |
||
901 | \item [Description:]creates a cancellation point in the calling task. The |
||
902 | primitive has no effect if cancelability is disabled. The name of |
||
903 | the POSIX counterpart is \texttt{pthread\_testcancel}. |
||
904 | \end{description} |
||
905 | |||
906 | \pagebreak |
||
907 | |||
908 | %---------------------------------------------------------------------------- |
||
909 | \begin{intest} |
||
910 | TASK\_SETCANCELSTATE\index{task\_setcancelstate()} |
||
911 | \end{intest} |
||
912 | |||
913 | \begin{description} |
||
914 | \item [\texttt{int task\_setcancelstate(int state, int *oldstate);}] |
||
915 | \item [Description:]This primitive sets the cancelability state of the |
||
916 | calling thread to the indicate \emph{state} \emph{and} returns the |
||
917 | previous cancelability state at the location referenced by \emph{oldstate}. |
||
918 | Legal values for state are \texttt{TASK\_CANCEL\_ENABLE} and \texttt{TASK\_CANCEL\_DISABLE}. |
||
919 | \texttt{pthread\_setcancelstate} is the name of the POSIX counterpart. |
||
920 | \end{description} |
||
921 | |||
922 | %---------------------------------------------------------------------------- |
||
923 | \begin{intest} |
||
924 | TASK\_SETCANCELTYPE\index{task\_setcanceltype()} |
||
925 | \end{intest} |
||
926 | |||
927 | \begin{description} |
||
928 | \item [int task\_setcanceltype(int type, int *oldtype);] |
||
929 | \item [Description:]This primitive sets the cancelability type of the calling |
||
930 | thread to the indicate \emph{type} and returns the previous cancelability |
||
931 | type at the location referenced by \emph{oldtype}. Legal values for |
||
932 | state are \texttt{TASK\_CANCEL\_DEFERRED} and \texttt{TASK\_CANCEL\_ASINCHRONOUS}. |
||
933 | The name of the POSIX counterpart is \texttt{pthread\_setcanceltype}. |
||
934 | \end{description} |
||
935 | |||
936 | %---------------------------------------------------------------------------- |
||
937 | \section{Join} |
||
938 | %---------------------------------------------------------------------------- |
||
939 | |||
940 | The join primitives allow a task to wait for the termination of another task |
||
941 | \footnote{not of an instance of a task!}. The return value of the terminated |
||
942 | task is passed to the join primitive and the caller can use the value. These |
||
943 | primitives are directly derived from the POSIX standard. It means that a join |
||
944 | can be done only on a joinable task. But note that when a task created with the |
||
945 | creation primitives \footnote{\texttt{task\_create}, \texttt{task\_createn}, |
||
946 | \texttt{group\_create}} starts it is \emph{not} in the joinable state |
||
947 | \footnote{this is done to remain similar to the previous versions of the Hartik |
||
948 | Kernel...}. This behaviour differs from the standard behavior of the POSIX |
||
949 | standard, which specifies that every new task shall be in the joinable state. |
||
950 | However, S.Ha.R.K. provides also the \texttt{pthread\_create} primitive that is |
||
951 | \emph{fully compliant} with the standard. Finally, a S.Ha.R.K. task can switch |
||
952 | between the jonable and non-joinable state using the primitives |
||
953 | \texttt{task\_joinable} and \texttt{task\_unjoinable} \footnote{In the POSIX |
||
954 | standard, only the \texttt{pthread\_detach} primitive is available.}. |
||
955 | |||
956 | %---------------------------------------------------------------------------- |
||
957 | \begin{intest} |
||
958 | TASK\_JOIN\index{task\_join()} |
||
959 | \end{intest} |
||
960 | |||
961 | \begin{description} |
||
962 | \item [\texttt{int task\_join(PID p, void **value);}] |
||
963 | \item [Description:]The primitive suspends the execution of the calling |
||
964 | task until the task p terminates, unless the task p has already terminated. |
||
965 | On return from a successful \texttt{task\_join} call with a non-\texttt{NULL} |
||
966 | \emph{value} argument, the value returned by the thread through a |
||
967 | task\_abort shall be made available in the location referenced by |
||
968 | \emph{value}. When the primitive returns successfully the target task |
||
969 | has been terminated. The primitive returns 0 in case of success, otherwise |
||
970 | it returns \texttt{EINVAL} if the value \texttt{p} does not refer |
||
971 | to a task that can be joined, \texttt{ESRCH} if the value \texttt{p} |
||
972 | does not refer to a valid task, and \texttt{EDEADLK} if a deadlock |
||
973 | was detected. The name of the POSIX counterpart is \texttt{pthread\_join}. |
||
974 | \end{description} |
||
975 | |||
976 | %---------------------------------------------------------------------------- |
||
977 | \begin{intest} |
||
978 | TASK\_JOINABLE\index{task\_joinable()} |
||
979 | \end{intest} |
||
980 | |||
981 | \begin{description} |
||
982 | \item [\texttt{int task\_joinable(PID p);}] |
||
983 | \item [Description:]This function set the detach state of a task p to joinable. |
||
984 | This function is not present in Posix standard. It returns ESRCH if |
||
985 | p is non a valid task. |
||
986 | \end{description} |
||
987 | \pagebreak |
||
988 | |||
989 | %---------------------------------------------------------------------------- |
||
990 | \begin{intest} |
||
991 | TASK\_UNJOINABLE\index{task\_unjoinable()} |
||
992 | \end{intest} |
||
993 | |||
994 | \begin{description} |
||
995 | \item [\texttt{int task\_unjoinable(PID p);}] |
||
996 | \item [Description:]This function sets the detach state of a task to detached. |
||
997 | The name of the POSIX counterpart is \texttt{pthread\_detach}. The |
||
998 | function returns \texttt{EINVAL} if \texttt{p} can not be joined (or |
||
999 | currently a task has done a join on it), or \texttt{ESRCH} if \texttt{p} |
||
1000 | is not correct. |
||
1001 | \end{description} |
||
1002 | |||
1003 | %---------------------------------------------------------------------------- |
||
1004 | \section{Preemption control} |
||
1005 | %---------------------------------------------------------------------------- |
||
1006 | |||
1007 | S.Ha.R.K. provides two primitives that set the preemptability of a |
||
1008 | task. A non-preemptive task can not be preempted by another task; |
||
1009 | interrupts are handled in the usual way. These primitives can be used |
||
1010 | to implement short critical sections. Note the difference between |
||
1011 | this kind of non -preemption and the interrupt disabling done using |
||
1012 | kern\_cli and kern\_sti: in the latter case, interrupt can not preempt |
||
1013 | the critical sections. A new task usually starts in a preemptive state. |
||
1014 | |||
1015 | %---------------------------------------------------------------------------- |
||
1016 | \begin{intest} |
||
1017 | TASK\_NOPREEMPT\index{task\_nopreempt()} |
||
1018 | \end{intest} |
||
1019 | |||
1020 | \begin{description} |
||
1021 | \item [void task\_nopreempt(void);] |
||
1022 | \item [Description:]After the call of this primitive, the task is non-preemptive. |
||
1023 | \end{description} |
||
1024 | |||
1025 | %---------------------------------------------------------------------------- |
||
1026 | \begin{intest} |
||
1027 | TASK\_PREEMPT\index{task\_preempt()} |
||
1028 | \end{intest} |
||
1029 | |||
1030 | \begin{description} |
||
1031 | \item [void task\_nopreempt(void);] |
||
1032 | \item [Description:]After the call of this primitive, the task become again |
||
1033 | preemptive. |
||
1034 | \end{description} |
||
1035 | |||
1036 | %---------------------------------------------------------------------------- |
||
1037 | \section{Suspending a task} |
||
1038 | %---------------------------------------------------------------------------- |
||
1039 | |||
1040 | The following system calls can be used by a task to suspend itself |
||
1041 | for a known or unknown time. (Note: it is dangerous to use these system |
||
1042 | calls in a hard real-time task.) |
||
1043 | |||
1044 | %---------------------------------------------------------------------------- |
||
1045 | \begin{intest} |
||
1046 | TASK\_DELAY\index{task\_delay()} |
||
1047 | \end{intest} |
||
1048 | |||
1049 | \begin{description} |
||
1050 | \item [\texttt{void task\_delay(DWORD t);}] |
||
1051 | \item [Description:]It causes the calling task to be blocked for at least |
||
1052 | \texttt{t} microseconds. Note that \texttt{t} is the \emph{minimum} |
||
1053 | delay time. In facts, after \texttt{t} microseconds the task is inserted |
||
1054 | in the ready queue and can be delayed by higher priority tasks. This |
||
1055 | function was inherited from the previous versions of Hartik. Please, |
||
1056 | use the POSIX counterpart \texttt{nanosleep} instead! |
||
1057 | \end{description} |
||
1058 | |||
1059 | %---------------------------------------------------------------------------- |
||
1060 | \section{Job ExecutionTime (JET) estimation} |
||
1061 | %---------------------------------------------------------------------------- |
||
1062 | |||
1063 | S.Ha.R.K. provides a set of primitives that allows to precisely estimate |
||
1064 | the system load. These primitives can be used to tune the parameters |
||
1065 | that are given at task creation, and to get statistics about the system |
||
1066 | load. |
||
1067 | |||
1068 | The execution time estimation is done on a task basis. That is, S.Ha.R.K. |
||
1069 | provides three primitives that allows the user to estimate the JET |
||
1070 | of every task. For every task, it is possible to know the mean execution |
||
1071 | time, the maximum execution time, the time consumed by the current |
||
1072 | instance and the time consumed by the last \texttt{JET\_TABLE\_DIM} |
||
1073 | instances. |
||
1074 | |||
1075 | The user have to explicitly enable the Kernel to record the JET informations |
||
1076 | for a specific task. This is done at task creation time; usually a |
||
1077 | macro that enable the JET is provided in the definition of every task |
||
1078 | model (see the S.Ha.R.K. Module Manual). |
||
1079 | |||
1080 | Here is an example of the use of the JET functions: |
||
1081 | |||
1082 | \begin{verbatim} |
||
1083 | /* The Goofy Task */ |
||
1084 | void *goofy(void *arg) { |
||
1085 | int i; |
||
1086 | for (;;) { |
||
1087 | for (i = 0; i < 100; i++) |
||
1088 | kern_printf("Yuk!"); |
||
1089 | task_endcycle(); |
||
1090 | } |
||
1091 | } |
||
1092 | |||
1093 | PID goofy_PID; |
||
1094 | |||
1095 | /* a NRT task that never finish */ |
||
1096 | |||
1097 | void *jetcontrol(void *arg) { |
||
1098 | TIME sum, max, curr, last[5]; |
||
1099 | int nact; |
||
1100 | |||
1101 | for (;;) { |
||
1102 | if (jet_getstat(p, &sum, &max, &nact, &curr) == -1) |
||
1103 | continue; |
||
1104 | for (j = 0; j < 5; j++) last[j] = 0; |
||
1105 | jet_gettable(p, &last[0], 5); |
||
1106 | printf_xy(1, 20, WHITE,"goofy_PID=%d mean=%d max=%d nact=%d", |
||
1107 | goofy_PID, sum / (nact == 0 ? 1 : nact), max, nact); |
||
1108 | printf_xy(1, 21, WHITE, "L1=%d L2=%d L3=%d L4=%d L5=%d", |
||
1109 | last[0], last[1], last[2], last[3], last[4]); |
||
1110 | } |
||
1111 | } |
||
1112 | |||
1113 | *int main(int argc, char **argv) { |
||
1114 | ... |
||
1115 | |||
1116 | /* The task goofy is created, specifying that the Kernel |
||
1117 | should take care of the JET data */ |
||
1118 | |||
1119 | HARD_TASK_MODEL m; |
||
1120 | |||
1121 | hard_task_default_model(m); |
||
1122 | |||
1123 | /* ... other hard_task_XXX macros */ |
||
1124 | hard_task_def_ctrl_jet(m); /* JET enabling */ |
||
1125 | goofy_PID = task_create("Goofy", goofy, &m, NULL); |
||
1126 | |||
1127 | /* ... creation of the JET control task, and so on */ |
||
1128 | } |
||
1129 | \end{verbatim} |
||
1130 | |||
1131 | %---------------------------------------------------------------------------- |
||
1132 | \begin{intest} |
||
1133 | JET\_GETSTAT\index{jet\_getstat()} |
||
1134 | \end{intest} |
||
1135 | |||
1136 | \begin{description} |
||
1137 | \item [int jet\_getstat(PID p, TIME *sum, TIME *max, int *n, TIME |
||
1138 | *curr);] |
||
1139 | \item [Description:]This primitive returns some JET informations about |
||
1140 | the task p. The informations retrieved are stored into the following |
||
1141 | parameters: |
||
1142 | |||
1143 | \begin{description} |
||
1144 | \item [\texttt{sum}]is the task total execution time since it was created |
||
1145 | or since the last call to the \texttt{jet\_delstat} function; |
||
1146 | \item [\texttt{max}]is the maximum time used by a task instance since it |
||
1147 | was created or since the last call to the \texttt{jet\_delstat} function; |
||
1148 | \item [\texttt{n}]is the number of terminated instances which sum and max |
||
1149 | refers to; |
||
1150 | \item [\texttt{curr}]is the total execution time of the current instance. |
||
1151 | \end{description} |
||
1152 | |||
1153 | If a parameter is passed as \texttt{NULL} the information is not returned. |
||
1154 | The function returns 0 if the \texttt{PID} passed is correct, \texttt{-1} |
||
1155 | if the PID passed does not correspond to a valid PID or the task does |
||
1156 | not have the \texttt{JET\_ENABLE} bit set. |
||
1157 | |||
1158 | \end{description} |
||
1159 | |||
1160 | %---------------------------------------------------------------------------- |
||
1161 | \begin{intest} |
||
1162 | JET\_DELSTAT\index{jet\_delstat()} |
||
1163 | \end{intest} |
||
1164 | |||
1165 | \begin{description} |
||
1166 | \item [int jet\_delstat(PID p);] |
||
1167 | \item [Description:]The primitive voids the actual task execution time |
||
1168 | data mantained by the Generic Kernel. The function returns 0 if the |
||
1169 | PID passed is correct, \texttt{-1} if the PID passed does not correspond |
||
1170 | to a valid PID or the task does not have the \texttt{JET\_ENABLE} |
||
1171 | bit set. |
||
1172 | \end{description} |
||
1173 | |||
1174 | %---------------------------------------------------------------------------- |
||
1175 | \begin{intest} |
||
1176 | JET\_GETTABLE\index{jet\_gettable()} |
||
1177 | \end{intest} |
||
1178 | |||
1179 | \begin{description} |
||
1180 | \item [int jet\_gettable(PID p, TIME *table, int n);] |
||
1181 | \item [Description:]The primitive returns the last \texttt{n} execution |
||
1182 | times of the task \texttt{p}. If the parameter n is less than 0, it |
||
1183 | returns only the last values stored since the last call to \texttt{jet\_gettable} |
||
1184 | (up to a maximum of \texttt{JET\_TABLE\_DIM} values). If the value |
||
1185 | is greater than 0, the function returns the last \texttt{min(n,~JET\_TABLE\_DIM)} |
||
1186 | values registered. The return value is \texttt{-1} if the task passed |
||
1187 | as parameter does not exist or the task does not have the \texttt{JET\_ENABLE} |
||
1188 | bit set, otherwise the number of values stored into the array is returned. |
||
1189 | The table passed as parameter should store at least \texttt{JET\_TABLE\_DIM} |
||
1190 | elements. |
||
1191 | \end{description} |
||
1192 |