We now try to write a C program that is running this periodic thread set.
For such a purpose, we must fill the
init.c
file in order to schedule this thread set according to Rate Monotonic.
The sourcecode you have to fill is shown by several stars.
Each thread T1 and T2 must run the function
periodic_task. The period is sent to each thread with
the thread argument.
Décrire les deux étapes : gestion des priorités avec les requirements et mise en oeuvre
des dates de réveil périodique
To implement this program, we need to use the nanosleep
and the clock_gettime functions to ensure the periodic
releases of T1 and T2.
Arithmetic operations on timespec structs can be made with
C functions of
files ts.h and
ts.c.
Finally, the two threads T1 and T2 must be activated for their first activation at the same time.
The
critical_instant variable stores the first release time for T1 and T2.
To reach this critical instant, the initialization thread (thread running POSIX_init
function) must not be preempted
before its completion (when it reaches its pthread_exit() statement).
When the initialization thread is completed, T1 and T2 must start their execution simultaneously.
To be sure that the initialization thread is completed without being preempted, you must correctly
assign priorities to the 3 threads.
Question 1:
During this question, we experiment the synchronization tools provided by RTEMS.
First, download and save
the following file and put it in a directory called EXO3.
This program contains two threads reading a text stored in a shared
memory. The text is read character by character and is written to a second memory area.
- Compile and test the program. What can you see? Explain this behavior.
- Modify this program to correct this wrong behavior. For such a purpose, you can
use the pthread_mutex_lock and pthread_mutex_unlock functions
or the sem_wait and sem_post functions.
Question 2:
For this second question,
download and save
this new file and put it in the EXO3 directory.
- Compile and test this program. This program implements a producer/consumer
synchronization.
The buffer stores only one character.
Producers write data in the buffer character by character.
Consumers read data from the buffer character by character.
The buffer is built with two counting semaphores
called
number_of_full_positions and number_of_empty_positions (respectivly initialized
with the values 0 and 1).
Those semaphores model/store the number of busy and empty positions into the buffer.
These semaphores allow you to block producers when no empty locations exist in the buffer and
respectively to block consumers when no full locations exist in the buffer.
The algorithm of a producer is :
while(1)
{
P(number_of_empty_positions);
Produce and put a character into the buffer;
V(number_of_full_positions);
}
The algorithm of a consumer is :
while(1)
{
P(number_of_full_positions);
Read and display a character from the buffer;
V(number_of_empty_positions);
}
- Change this program in order to define a buffer that is able to store upto
4 characters (e.g. a buffer composed of 4 positions).
The buffer must be a FIFO buffer.
- Change your program in order to allow several producers and several consumers
to handle your 4 positions buffer.
In the previous exercises, we have seen that C programs for RTEMS were different of classical
Unix C
programs : C real-time programs are usually not fully portable from one real-time operating system
to another.
In this second part of the laboratory, we will write Ada programs.
Ada provides a higher level of portability for real-time programs and also enforce
a higher level of reliability, thanks to its safety mechanisms (e.g. strong type checking).
To introduce these
Ada features, we will work with two runtimes:
- First, for exercises 4 to 10, we will compile and run programs on Linux.
Here, we use a GNAT compiler with a Linux runtime.
- Second, for exercises 11 to 13, we will compile on Linux programs that will be run on a RTEMS/Sparc Leon
processor. We always use the GNAT compiler, but with a RTEMS runtime. We will see in this second
set of exercises that we handle the same programming language, but with a different compiler.
For exercises 4 to 10, in order
to compile and run Ada programs on Linux,
in a shell, launch the following command:
source adalinux.bash
Exercise 4 : hello world
Save the file
hello.adb.
Compile this program with the following command:
gnatmake -g hello
What are the different files generated by gnatmake ?
Exercise 5 : input/output
- Download, compile and test the following file: readint.adb.
- Change readint.adb in order to read an integer from the keyboard and to print it on the
screen.
- In this changed program, what does the program do when we give it a string instead of an
integer?
- Write a package called my_io which contains the source code to
print to the screen/read from keyboard
float and integer data.
Change the program of question 3 in order to use your package
my_io.
Exercise 6 : compute average of floats
Write a program that:
- Allows you to read several floats from the keyboard.
- When you give 0.0 to your program, your program computes and displays
the average, maximum value and minimum value of all floats previously read from the keyboard.
- We do not have to handle an array in this program.
Exercise 7 : generic package
Download and save these files:
stacks.ads,
stacks.adb,
and the main procedure test_stack.adb.
These files implement a polish calculator.
- Compile and test these programs.
- Add three procedures :
- The first one implements the divide operator.
- The second one allows to reverse the order of the data stored in the stack.
- The last one allows to display to the screen the data stored in the stack.
Exercise 8 : declaring a task
Write an Ada program :
- Which defines a task type element.
- Which declares 10 tasks of type element.
- The body of the tasks type element must display a message to the screen every second.
Exercise 9 : tasks with entries : implementing a mutex
Write an Ada program composed of two anonymous tasks : task tick
and task tock.
Each task handles a common/global counter. The counter is a Integer variable
declared in the main procedure (this variable is
shared by all tasks). Each task loops on the following statements:
- Increment the counter.
- Display the value of the counter and displays either "tick" or "tock".
- Wait for one second before starting a new iteration.
The main procedure contains only one statement : the initialization of the counter to
zero.
Questions:
- Compile and test the program.
- How many tasks your program contains?
- Can you say when the tasks start to run ?
- Can you say when the main procedure is terminated?
- This program raises a very classical concurrency problem. What is this problem?
To give of solution to the problem of the question 5,
we would like to change the program. We add a new task called mutex.
This task has two entries allowing a rendez-vous with the two other tasks : these two entries, called
P and V have no parameter.
This task should allow to implement critical sections on shared resources.
- Propose a source code for this third task. Explain how it works.
- Update the previous program in order to ensure critical section of
the shared counter.
Exercise 10 : protected types: producer and consumer
In this program, we implement a producer/consumer synchronization.
These consumers and producers share integer data by a buffer, which is a simple integer table.
This table can store 4 integers.
The buffer is managed according to a FIFO policy : the first inserted/produced integer will also be the
first to be removed/consumed.
- In a first step, write an Ada program composed of only one producer and one consumer.
For such a purpose, you must use :
- The Semaphores package (both files
Semaphores.ads
and Semaphores.adb).
This protected type implements a counting semaphore in Ada.
You have
a program that shows how to use this package
in order to build a critical section between several tasks.
- In your program, you must define two counting semaphores called
number_of_full_positions et number_of_empty_positions (respectively initialized
with the values 0 and 4).
Those semaphores model/store the number of busy and empty positions into the buffer.
These semaphores allow you to block producers when no empty location exist in the buffer and
respectively to block consumers when no full locations exist in the buffer.
- The algorithm of a producer is :
loop
P(number_of_empty_positions);
Produce and put the integer into the buffer;
V(number_of_full_positions);
end loop;
- The algorithm of a consumer is :
loop
P(number_of_full_positions);
Read and display an integer from the buffer;
V(number_of_empty_positions);
end loop;
- Change your program in order to allow several producers and several consumers
to handle the buffer.
- From a practical point of view,
Ada programmers do not use semaphores to implement a semaphore.
They usually directly implement the synchronization protocol into a protected object.
Write a protected object that implements a FIFO buffer of 4 integers that can be handled
by several producers and consumers. This protected object must provide two subprograms:
- A first sub-program that allows a producer to store a new integer in the buffer.
- A second sub-program that allows a consumer to read/remove an integer from the buffer.
- Here is a possible protected object specification for this question:
Buffer_Size : constant Integer :=4;
type Buffer_Type is array (1..Buffer_Size) of Integer;
protected type Synchronized_Buffer is
entry Write(Val : in Integer);
entry Read(Val : in out Integer);
private
Buffer_Data : Buffer_Type;
Index_Cons : Integer :=1;
Index_Prod : Integer :=1;
Number_Element : Integer:=0;
end Synchronized_Buffer;
Exercise 14 : a pipe-line of a set of Ada tasks
We would like to write an Ada program to convert base 10 integers to base 2 integers.
To convert, we use the method which consists in dividing several times the base 10 integer
by 2.
To increase efficiency, we would like to implement this program with a pipe-line of Ada tasks.
The task interface is the following:
type Stage;
type Stage_Ptr is access Stage;
task type Stage is
entry Next_Stage (
A_Task : in Stage_Ptr );
entry Divide (
Value : in Integer );
end Stage;
Each stage/task contains two entries:
-
An entry called Next_Stage:
this entry allows a stage n to store a pointer
to the next stage n+1.
This entry is only used at program initialization in order to build the pipe-line.
-
An entry called Divide: this entry of a stage n is called by the stage n-1.
This entry does the following statements :
- It divides by 2 the received data and displays
to the screen the remainder (0 or 1).
- After division, if the result is greater than 0, the current stage
must send the result to the next stage to continue
the computation.
- After division, if the result is greater than 0 and if the data has reached the
last stage, the program must display an error message.
- Finally, after division, if the result is equal to 0, it means that the computation is completed.
Write the body of the Stage task and a main procedure to use it.
The main procedure must :
- Dynamically allocate
and initialize a 8 stages pipe-line.
-
Allows the user to give a base 10 integer from the keyboard and to send it to the first stage of the
pipe-line.
Exercise 11 : cross compiling of an Ada program
In this exercise, we will compare the "Linux/Intel 386" Ada runtime and the "RTEMS/Leon" Ada runtime.
Download and save
the following file. Put it in a directory called EXO11.
- Compile the hello.adb program with the command gnatmake -g hello.adb.
- Launch the command file hello : what is the meaning of the displayed information?
- Launch hello and check that the program works well on Linux.
- Now, we will run the same program on a RTEMS and Leon/Sparc processor. For such a purpose,
launch the source rtems.bash command, as we made it to compile our first C programs.
Now you can compile C or Ada programs for RTEMS.
- Compile again the hello.adb program, but now with the following command:
make clean
make
- This command has built a new executable file called o-optimize/hello.exe.
- Launch the file o-optimize/hello.exe. What is the meaning of the displayed information?
- Can we directly launch the binary o-optimize/hello.exe from your Unix Shell?
- Then, run o-optimize/hello.exe on the tsim simulator as follow:
$tsim
tsim>
tsim>load o-optimize/hello.exe
tsim>go
Hello world
Exercise 12 : fixed priority scheduling with Ada
Remind that to
do this exercise, you must use the RTEMS cross-compiler for the Sparc/Leon
processor (available from rtems.bash)
and the tsim simulator.
In this exercise, we will experiment real-time scheduling of an Ada program on a RTEMS target.
First, download
this file
and save it in a directory called EXO12.
- Edit the gnat.adc file. What is the meaning
of each pragma that is present in this file?
- Edit the scheduling.adb file to assign a 12 priority level to
task Task_A, a 11 priority level to task Task_B and a 13 priority level to
the main procedure.
- Compile scheduling.adb.
- Test your program with the simulator and see the generated scheduling.
- Change your program in order to assign an
equal level of priority to all tasks. Compile and run this new program.
- Add the statement delay 1.0 in
the loop of each task body. What does this statement do?
Compile and run this last program.
Exercise 13 : implementing periodic tasks scheduled according to Rate Monotonic
Remind that to
do this exercise, you must use the RTEMS cross-compiler for the Sparc/Leon
processor (available from rtems.bash)
and the tsim simulator
First, download
this file
and save it in a directory called EXO13.
- We assume the following task set :
- Task T1 : period=1 second, capacity=10 millisecond.
- Task T2 : period=500 millisecond, capacity=10 millisecond.
- Task T3 : period=250 millisecond, capacity=10 millisecond.
- Deadlines are equal to periods.
- Assuming we use a preemptive Rate Monotonic
scheduling, can you prove that this task set will meet its deadlines? Give the
scheduling sequence of this tasks set (compute this scheduling sequence by hand).
-
We now write an Ada program that implements the previous periodic task set.
You have to fill the files periodic.adb and generic_periodic_task.adb
for such a purpose.
The parts of the program you have to fill are shown by several stars:
-
The file periodic.adb is the main procedure and has to create T1, T2 and T3.
periodic.adb must also ensure that a critical instant will occur (e.g. T1, T2 and T3 must
start at the same time, after completion time of the main procedure).
First_Release_Time stores the first release time for T1, T2 and T3.
Subprograms
Program_T1, Program_T2 and Program_T3 are the code each task has
to run at each of its release time.
- The
generic_periodic_task.adb generic unit implements a periodic task. This generic package has to be
instantiated three times : for T1, T2 and T3.
For each task, this generic unit is parametrized by its period, its priority, its
first release time and also the subprogram the task has to run each time the task is released.
-
Fill periodic.adb : you must instantiate the 3 tasks T1, T2 and T3.
-
Fill generic_periodic_task.adb : you must provide a body for the periodic task.
In this task body, you must add the statements that allow the task to run 5 iterations.
For each iteration, the task must run its code, compute and wait for its next periodic release time.
- Compile and test your program with tsim.
The goal of this project is to implement a software to simulate the scheduling of a set of
periodic task on multiprocessors.
We give you a first release of this simulator.
This software is composed of several Ada packages
available in this directory.
To compile and run this software, you must use the linux compiler
that is available with the following command:
source adalinux.csh.
This software must be run on linux.
This software is composed of the following packages:
- Package tasks defines and implements tasks that can
be handled by the simulator.
- Package schedulers implements the simulator itself.
The provided simulator implements only one scheduler : a preemptive Rate Monotonic scheduling.
The main procedure main
is an example of use of the simulator:
This main procedure defines 3 tasks that are scheduled by
a preemptive Rate Monotonic scheduler on a uniprocessor system.
Work to do for this project:
For this project:
- You can work by groups of 2 students (no more).
- You must send your work by email no latter than the 15th of June : marks will be
delivered to the USTH the 16th of June
- You must send both the source code you wrote and also a small report
which contains the answers for the questions below.
- You are not allowed to add in your report or source
code any pieces of text or source code
from the web or from another student.
Question 1 : test of the current simulator implementation
- We assume a set of three periodic tasks defined by the following parameters S1=S2=S3=0, P1=5, C1=1, P2=10,
C2=3, P3=30 et C3=8.
Draw the scheduling sequence according to a preemptive Rate Monotonic scheduling.
Is this task set schedulable?
- Change main.adb to run the task set of the previous question, compile and test
your program.
Check that the scheduling you computed by hand is similar to the one computed
by the simulator.
- In the main procedure main.adb and in the file scheduler.adb,
can you find :
-
Where and how the tasks of the system are initialized.
- Where and how the task periodic releases are implemented.
Question 2 : implementing global preemptive Rate Monotonic for a two cores processor
We now assume that the task set is scheduled on a processor with two cores.
We want to apply a global preemptive scheduling.
Update the set of packages to implement this new scheduling method
into the simulateur.
A global preemptive Rate Monotonic works like as follow (see
the lecture on global multiprocessor real-time scheduling for further details):
- At the same time, the scheduler can run two tasks if two tasks are ready to run. One task is run
on each core.
- At any time, the two tasks running on the cores are the ready tasks which have the smallest period.
- If the two cores are busy and if a task A is released which has a smallest period than the
two tasks running on the cores, then, task A must preempt the task which has the larger period.
- A task can migrate from one core to another one. Two kinds of migrations exist : 1) when tasks can migrate at
any time 2) when tasks can migrate only at the release time of the task.
Implement the global preemptive Rate Monotonic scheduler with the two types of migration.
Test your program with at least two task sets that you will describe in your
report and say which migration policy is better for each task set example.
You must send me your program with the task sets you used to test your program.
Your solution must also be explained in your report.
Question 3 : implementing global preemptive EDF for a two cores processor
Global scheduling can be applied with any kind of scheduler.
For example, We can adapt preemptive EDF as a global preemptive scheduler as follow (again, we assume here a two
cores processor) :
- At the same time, the scheduler can run two tasks if two tasks are ready to run. One task is run
on each core.
-
At any time, the two tasks running on the cores are the ready tasks which have the smallest dynamic deadline.
- If the two cores are busy and if a task A is released which has a dynamic deadline smaller than the
two tasks running on the cores, then, task A must preempt the task which has the largest dynamic deadline.
- A task can migrate from one core to another one. Two kinds of migrations exist : 1) when tasks can migrate at
any time 2) when tasks can migrate only at the release time of the task.
Implement the global preemptive EDF scheduler with one of the two types of migrations.
Test your program with at least two task sets that you will describe in your
report.
You must send me your program with the task sets you used to test your program.
Your solution must also be explained in your report.
The goal of this project is to implement a software to simulate the scheduling of a set of
aperiodic task servers such as the one we have seen during
the lecture : the polling server.
We give you a first release of this simulator.
This software is composed of several Ada packages
available in this directory.
To compile and run this software, you must use the linux compiler
that is available with the following command:
source adalinux.csh.
This software must be run on linux.
This software is composed of the following packages:
- Package tasks defines and implements tasks that can
be handled by the simulator.
- Package schedulers implements the simulator itself.
The provided simulator implements only one scheduler : a Rate Monotonic scheduling.
The main procedure main
is an example of use of the simulator:
This main procedure defines 3 tasks that are scheduled by
Rate Monotonic.
Work to do for this project:
For this project:
- You can work by groups of 2 students (no more).
- You must send your work by email no latter than the 20th of June.
- You must send both the source code you wrote and also a small report
which contains the answers for the questions below.
- You are not allowed to add in your report or source
code any pieces of text or source code
from the web or from another student.
Question 1 : test of the current simulator implementation
- We assume a set of three periodic tasks defined by the following parameters S1=S2=S3=0, P1=5, C1=1, P2=10,
C2=3, P3=30 et C3=8.
Draw the scheduling sequence according to a preemptive Rate Monotonic scheduling.
Is this task set schedulable?
- Change main.adb to run the task set of the previous question, compile and test
your program.
Check that the scheduling you computed by hand is similar to the one computed
by the simulator.
- In the main procedure main.adb and in the file scheduler.adb,
can you find :
-
Where and how the tasks of the system are initialized.
- Where and how the task periodic releases are implemented.
Question 2 : implementing the polling server into the simulator
Update the set of packages to implement the polling server to schedule
aperiodic tasks with periodic tasks.
Remind that the polling server works like this (see page 25 of
the lecture on real-time scheduling):
- The server
run aperiodic tasks arrived before the server
activation.
- It does not use processor if no aperiodic task is present : the
server capacity is lost if no aperiodic task is present.
- The server stops aperiodic
execution when its capacity is exhausted.
Test your program with at least two task sets that you will describe in your
report.
You must send me your program with the task sets you used to test your program.
Your solution must also be explained in your report.
Question 3 : implementing a new aperiodic task server
We now request you to implement a new aperiodic server algorithm in the simulator.
This aperiodic server is
described
in this article.
You must read this article, understand this scheduler and implement it in the simulator.
This article presents several schedulers : the polling server,
the priority exchange server and the deferrable server.
You can choose to implement either the priority exchange server or the deferrable server.
For this last question, you must :
- Explain in your report how the implemented scheduler works.
- Test your program with at least two task sets.
You must send your program with the task sets you used to test your program.
Your program must also be explained in your report.
The goal of this project is to implement a software to simulate the scheduling of a set of
task with classical real-time schedulers such as EDF or Rate Monotonic.
This software is composed of several Ada packages
available in this directory.
To compile and run this software, you must use the linux compiler
that is available with the following command:
source adalinux.csh.
This software must be run on linux.
This software is composed of the following packages:
- Package user_level_tasks defines and implements tasks that can
be handled by the simulator.
These tasks are interacting with the simulator by two means:
- By rendez-vous in order to simulate the exhausted time.
The wait_for_processor
entry indicates to the simulator that a task is ready to be run and that it
waits for the processor in order to run its next unit of time of its capacity.
The
release_processor
entry is called by a task to report to the scheduler that the task has completed
execution of one unit of time.
-
By a protected object. The protected object user_level_scheduler
stores the scheduling data handled by the scheduling simulator.
These data are protected because the simulator and several tasks can
access them at the same time.
Note that with the provided example, this synchronization is not required : it is
required when several schedulers work all together (e.g. multiprocessor scheduling).
The protected data by the protected object are the list of the tasks
(variables called
tcbs and
number_of_tasks), the current time
of the simulation (variable current_time), ...
- Package user_level_schedulers implements the simulator itself.
The provided simulator implements only one scheduler : a Rate Monotonic scheduling.
The main procedure example and the package my_subprograms
is an example of use of the simulator:
- my_subprograms contains the code each task has to run for
each release.
For this example, the code only contains a display on the screen.
- The main procedure example defines 3 tasks that are scheduled by
Rate Monotonic.
Work to do for this project:
For this project:
- You can work by groups of 2 students (no more).
- You must send your work by email no latter than the 21th of June.
I will ask to Daniel Hagimont if we can have an extra time, but its not sure.
- You must send both the source code you wrote and also a small report
which contains the answers for the questions below.
- You are not allowed to add in your report or source
code any pieces of text or source code
from the web or from another student.
Question 1 : test of the current simulator implementation
- We assume a set of three periodic tasks defined by the following parameters S1=S2=S3=0, P1=5, C1=1, P2=10,
C2=3, P3=30 et C3=8.
Draw the scheduling sequence according to a preemptive Rate Monotonic scheduling.
Is this task set schedulable?
- Compile and run example.adb.
Check that the scheduling you computed by hand is similar to the one computed
by the simulator.
- In the main procedure example.adb,
can you find :
-
Where and how the tasks of the system are initialized.
- Where and how the task periodic releases are implemented.
Question 2 : implementing EDF into the simulator
Add a new procedure in the simulator (in the package user_level_schedulers) to compute an
EDF scheduling.
Your program must support the scheduling of both periodic and aperiodic tasks. Do not forget to provide task sets to show how works
your solution.
Test your program with several task set.
You must send me your program with the task sets you used to test your program.
Your solution must also be explained in your report.
Question 3 : implementing a new scheduler into the simulator
We now request you to implement a new algorithm in the simulator.
The algorithm to implement is called MUF. This scheduler is
described
in this article.
You must read this article, understand this scheduler and implement it in the scheduler.
For this last question:
- Explain in your report what is a laxity.
- Explain in your report how MUF is working.
- Test your program with several task sets.
You must send your program with the task sets you used to test your program.
Your program must also be explained in your report.