Title: Realtime and multitasking systems
1Real-time and multitasking systems
- INGE4
- Majeure  Systèmes EmbarquésÂ
- 2006-2007
2CM4 Communication and synchronization between
tasks
Asynchronous messages Pipes, Mailboxes
Synchronous messages Rendezvous Different
inter-task synchronization mechanisms
3The needs
- In a real-time application, the tasks are not
independent, they must collaborate, therefore
they have the need to - synchronize,
- communicate,
- share information
4Example of inter-task cooperation
synchronization, communication and mutual
exclusion
5Communication between tasks
- By pipes in FIFO and urgent modes
- By mailboxes
- By message queue
6Communication
- Information exchange
- from a task to another
- from an ISR to a task
- The communication services are
- Pipes
- Files de messages
- Shared memory
7Pipes PIPE or FIFO
- Ordinary pipes
- Named pipes
8Communication with pipes
SEND MESSAGE
RECEIVING THE MESSAGE
MESS. 2
MESS. 1
MESS. 3
9Pipe
- There are two types of pipes
- The ordinary pipe also called  unnamed
- In order to use this tube, it is necessary to
know a descriptor associated to its entry in the
allocation table, corresponding to its mode (read
or write). - The descriptor is acquired in two manners
- by calling the pipe creation primitive pipe()
- by inheriting a process inherits while it is
created the descriptors of its parent process,
and in particular it inherits the pipe
descriptors. - Conclusion an unnamed pipe is adapted between
the communication of two parent processes it is
not very adapted for communication between two
independent processes.
10Named pipes
- Named pipes (FIFO)
- the named pipes allow to transmit data between
two processes which are not attached by
parent-child ties (no inheritance thus). - they posses a reference in the file system in
order to allow the unrelated processes to
communicate in FIFO mode
11Pipes Readers and writers
- The number of readers
- It's the number of descriptors associated as
reading at the entry of the pipe in the opened
files' table. - If this number is zero, any reading of the pipe
is forbidden. - The number of writers
- It is a number of descriptors associated at the
entry of the pipe.
12Creating an unnamed pipe
pipe1
pipe0
- The creation of an unnamed pipe is done in the
following way - include ltstdio.hgt
- include ltstring.hgt
- include ltunistd.hgt
- Â
- int my_pipe2 /descripteurs
- if(pipe(my_pipe) ! 0)
-
- printf("Pipe creation error \n")
- exit(-1)
-
13Reading and writing in an unnamed pipe
- The read/write in an unnamed pipe are done with
the help of the functions read() and write() as
for a classical file. The syntax is the
following - define MAXCAR 256
- main
- int ai 5
- char chainei MAXCAR, chaineo MAXCAR
- sprintf(chainei,"Hello all d \n", ai)
- write(pipe1,chainei, MAXCAR)//Ecriture dans
l'entrée du tube - read(pipe0,chaineo,MAXCAR) //lecture dans la
sortie du tube
14Reading and writing in an unnamed pipe
- Reading an empty pipe blocks the process who
attempted the read. - Writing in a full pipe (4096 byres are occupied)
blocks the process who did the write. - Using a pipe to send data inside the same process
is useless. - However, a pipe can be used to send data between
a child and a parent process (unnamed pipes). If
it is created before fork(), as any opened file
is shared between parent and child.
15Reading and writing in an unnamed pipe
- Since a pipe is unidirectional, each process must
close one entry of the pipe. A pipe end is closed
with the function close () - Example
- close(tube1) //closing the entry of the pipe
- For a bidirectional communication between parent
and child, two pipes must be used.
16Synchronization of read/writes in a pipe
- The mechanisms of synchronization of readers and
writers in a pipe are the following - read if the process requests the lecture of n
characters in a pipe containing m gt 1 characters,
there is a read of min(n, m) characters (and the
immediate return of read). - void pipe if the process requests a read in a
void pipe which has at least one writer, it is
(by default) put to sleep until a write takes
place. - end of pipe if a process requests a read in a
pipe which has no writer, the read returns 0 is
order to show the end of the pipe.Â
17Synchronization of read/writes in a pipe
- Write if a process requests the write of n
characters (n lt PIPE_BUF) in a pipe which has at
least one reader, the system guarantees that the
n characters will be written in a consecutive
manner in the pipe, even if the writing takes
place with between sleeps of the write process.
Any other write request is blocked until the
first one is completed. - Pipe full if a process requests a write in a
full pipe which has at least one reader, it is
put to sleep until at least one read takes
place. - Pipe without reader if a process requests a
write in a pipe which has no reader, the kernel
sends the signal SIGPIPE, which, by default,
causes the writer process to terminate (with the
message Broken pipe).Â
18Example 1 (1/3)
- In the program pipes.c, the process starts by
creating a pipe and then a child process. - The child (respectively the parent) redirects
(with dup) its standard exit (respectively
standard input) in the pipe. - Then the child executes the ps command and the
parent executes the wc -l command which counts
the number of lines on its standard input and
prints it on its standard output. - We obtain the display of the number of processes
as given by ps. This program realizes
approximatively the Shell command ps wc -l
19Example 1 (2/3)
- include ltstdio.hgt
- include ltstdlib.hgt //déclaration de system
- include ltunistd.hgt //déclaration de fork,
pipe - include ltsys/wait.hgt
- int tube2
- main ()
- int i
- if (pipe(tube) -1) //creation du tube
- printf( "Pipe opening impossible\n")
- exit(l)
- i fork( ) // naissance d'un fils
- if(i 0) // processus fils
- close(l) dup(tubel)// redirection de la
sortie standard - close(tubeo) close(tubel)
- execlp("ps", "ps", 0) // exécution de
la commande ps - else // processus pere
- close(0) dup(tube0) // redirection de
l'entree standard - close(tubeo) close(tubel)
- execlp("wc", "wc","-l", 0) / exécution de wc
l
20Example 1 (3/3)
- Execution
- pipes
- 12
- Remarks
- The reads/writes being made in a blocking mode,
it is important that the parent process closes
the pipe in write otherwise, this process being
the reader of the pipe, if it is also writer, it
will never end and will be put to sleep forever
(moreover, the child process will remain a
zombie).
21Unnamed Pipes
- Unnamed pipe example (Shell)
- cat myfile grep key sort lpr
- The parent process (the shell or shell script
that creates the pipes) also spawns the child
processes that access the pipe - cat, grep, sort, and lpr in this case
- Note the shell or script process that sets up
the pipes CANNOT access the pipes itself!
22Named pipe characteristics
- Introduced by POSIX under the name fifo
- Allows to communicate between processes not
having the same parent process - allows to communicate even two processes not
belonging to the same user - It has the same characteristics as the unnamed
pipe but possesses the references in the file
handling system - it's this reference which helps the processes to
make the pipe - uses the function open in order to abtain the
descriptor for read/write.
23Named pipe creation
- System primitive mkfifo
- include ltsys/stat.hgt
- int mkfifo (const char path, mode mode_t)
- path denotes the path to access the pipe
- mode
- denotes the different access right of the users.
- is limited to read and write for the
three classes (u, g, o) - mkfifo returns 0 if the creation succeeded and -1
is case of failure, for example is the node
exists already.
24Named pipe creation
- // rw for user // r for the group // r for the
others - includeltsys/stat.hgt
- includeltsys/types.hgt
- includeltstdio.hgt
- void main (void)
- mode_t mode
- char chemin "fifo_ING4"
- fprintf(stderr, "Creating a named pipe\n")
- mode S_IRUSR S_IWUSR S_IRGRP S_IROTH
- if ((mkfifo (chemin, mode))-1)
- fprintf(stderr, "Error creating a named
pipe\n") - exit(-1)
- else
- fprintf(stderr, "Creation of named pipe
succeeded\n")
25Named pipe opening deadlock danger
deadlock
- includeltfcntl.hgt
- includeltstdio.hgt
- void main(void)
- // code process 2
- int descR, descW
- ...
- //pipe1 et pipe2 already exist
- descW open("pipe2",O_WRONLY)
- fprintf(stderr,"Opening pipe2 in write")
- descR open("pipe1",O_RDONLY)
- fprintf(stderr,"Opening pipe1 in read")
- ...
-
- includeltfcntl.hgt
- includeltstdio.hgt
- void main(void)
- // code process
- int descR, descW
- ...
- //pipe1 et pipe2 already exist
- descW open("pipe1",O_WRONLY)
- fprintf(stderr,"Opening pipe1 in write")
- descR open("pipe2",O_RDONLY)
- fprintf(stderr,"Opening pipe2 in read")
- ...
26Named pipe Re ad
- System primitive read
- include ltunistd.hgt
- ssize_t int read(int descR, void zone, size_t
taille) - Read principle
- if the pipe is empty
- no writers the end of file is reached no
character is read and 0 is returned - at least one writer
- if read is blocking (default behavior), the
process is put to sleep while the pipe is empty - if read is non-blocking, (O_NONBLOCK is set),
there is no wait and the return is -1 (errno is
EAGAIN). - if the pipe isn't empty (contains x bytes) read
of min(size, x) and put into memory address
zone
27Named pipe Write
- System primitive write
- include ltunistd.hgt
- ssize_t int write(int descW, void zone, size_t
taille) - Write principle
- No readers the signal SIGPIPE is sent to the
write process (while attempting to write) ? by
default destruction of the process (broken pipe
message). - At least a reader
- identical to the unnamed pipes
28Named pipe Write
- At least one reader
- if the write is blocking, the return of this
writing takes place only if n bytes were
written (sleep possible). - if the write is non-blocking
- if n ? PIPE_BUF and there are n free bytes in the
pipe, atomic write - if n ? PIPE_BUF and there are not n free bytes o,
the pipe, immediate return with -1 (errno
EAGAIN) - if n ? PIPE_BUF, the return is
- a number lt n, if at least one byte can pe written
- -1 (errno is EAGAIN), if no byte was written
29Named pipe Closing
- The system primitive close
- include ltunistd.hgt
- int close (int desc)
- desc denotes the descriptor of the pipe to be
closed (read or write) - Beware of the consequences
- No readers
- No writers
- limit case physical suppression of the named
pipe - Return
- 0 in the case of success
- -1 in the case of failure
30Named pipe Termination
- The system primitive unlink
- include ltunistd.hgt
- int unlink (const char path)
- path denotes the path to access the pipe to be
suppressed - Conditions on suppression (freeing the space)
- Number of physical links zero
- Number of internals links zero (no
readers/writers) - Thus, if one or more processes have an open named
pipe while it is suppressed (with thr shell
command rm for example), only the directory entry
is suppressed ? no access possible for a new
process - Returns
- 0 success
- -1 failure
31Named pipe opening
- The system primitive open
- includeltfcntl.hgt
- int open(const char path, int option)
- path denotes the path to access the pipe
- option defines the opening mode (O_RDONLY,
O_WRONLY, O_NONBLOCK) - The opening is blocking by default
- An opening in read-only is blocking if there are
no writers. - An opening in in write only is blocking if there
are no readers. - The synchronization of openings is assured by the
system - Beware of deadlocks!
- The opening of a pipe may be non-blocking
(changing is possible with the system call fcntl) - Returns The descriptor in the case of a success
- -1 in case of an error
32Named Pipes (UNIX Shell)
- Named pipes can be accessed by any process that
knows the name - Named pipes appear in the users directory list
- ls -l
- pwr_r__r__ 1 krf 0 Mar 27 1933 mypipe
- Like any other file, umask determines the initial
permissions. To prevent the potential for
disturbances by other users, its a good idea to
remove the read/write permissions for Group and
Other if they are normally provided by your umask
33Named Pipe Creation
- Named pipes are created using the mknod or the
mkfifo commands - mkfifo name
- or mkfifo m mode name
- mknod name p
- Make sure you remove (rm) your pipes after use!
34Using Named Pipes (UNIX Shell)
- First, create your pipes
- mknod pipe1 p
- mknod pipe2 p
- mknod pipe3 p
- Then, attach a data source to your pipes
- ls -l gtgt pipe1
- cat myfile gtgt pipe2
- who gtgt pipe3
- Then, read from the pipes with your reader
process - cat lt pipe1 lpr
- spell lt pipe2
- sort lt pipe3
- Finally, delete your pipes
- rm pipe1-3
35Example client/server communication
- A server waits for questions from clients in a
pipe fifo_pipe1 . A question corresponds to
the send request of n numbers chosen by the
server (n is a random number between 1 and NMAX
chosen randomly by the client). - In its question, the client sends equally its PID
number, so that the server can make it ready
through the signal SIGUSR1 when it writes the
answer. - Indeed, since more clients could wait for an
answer in the same pipe, it is necessary to
define a protocol which assures that each client
reads the answers destined to him. - The client acknowledges the server through the
same signal when it reads the answer.
36Example client/server communication
fifo_pipe1
question
answer
fifo_pipe2
37Message queue
- Creation
- Write
- Read
- Destruction control
38Message queue
- A message queue must be created, starting from a
key function, with the function msgget() of
prototype - int msgget (key_t key, int attributes)
- key is the key, its attributes allow to
define the access rights of the process to the
message queue. - The return of the function msgget is the
identifier of the message queue.
39Message queue
- The parameter IPC_PRIVATE passes as key to the
process and also to its children in order to use
the message queue. - Attributes binary OR on the attributes
- IPC_CREAT creates a new message queue, if there
is not one associated with the key sent as first
argument - IPC_EXCL always creates a new message queue.
The function msgget() fails if a message queue
already exists with the given key. - as usual access rights are also available
40Message queue Example
- include ltstdio.hgt
- include ltsys/types.hgt
- include ltsys/ipc.hgt
- include ltsys/msg.hgt
- int msgid // Message queue identifier
- /--- Creating Message queue --- /
- if(msgid msgget (IPC_PRIVATE,IPC_CREATIPC_EXCL
0x666) -1) - printf("Creation error on message queue\n")
- exit (-1)
-
- printf ("Message queue identifier d \n",msgid)
41Message queue Examples
- Queue reserved to the caller process and its
children - file msgget(IPC_PRIVATE, Ox600)
- In order to access a queue allowing the dialog
with other processes of the same application - ma_cle (key_t) NOMBRE_MAGIQUE
- file msgget(ma_cle, IPC_CREAT 0x660)
- The other processes will use the same magical
number in order to access the queue! - Beware of conflicts!
42Message queue Examples
- In order to assure the creation of a new message
queue, the case of a server, or demon process for
example - ma_cle ftok(argv0, 0)
- file msgget(ma_cle, IPC_CREAT IPC_EXCL
0x622) - ftok created by a key made by the name of the
application - a process who must use only an existing message
queue - ma_cle ftok(fichier_executable_serveur, 0)
- file msgget(ma_cle, 0)
43Structure of the messages exchanged via e queue
- A generic structure struct msgbuf is defined in
the in the header file sys/msg.h. - struct msgbuf
- long mtype
// type du message - char mtextelongueur
//texte du message -
- The programmer defines his own type of message by
modifying upon wish the existing definition. His
structure struct mymsg must only verify the
properties - the first field must be long.
- the other fields, in arbitrary numbers, may have
any type, except pointers.
44Example of message structure
- typedef struct
- long type
- int tab10
- float x, y
- mymsg
- The message type is a positive integer. In has
meaning only for the application, not for the
system. It is possible to extract a message of a
given type from a queue and to realize a
multiplexing of the messages, that is a selection
inside the queue in the moment of extraction. - Attention Addresses cannot be changed, the
memory zones of two messages are disjoint.
45Writing a message in a queue msgsnd
- The writing ("sent") of a message in a queue is
done with the system call msgsnd declared by - int msgsnd(int msgid, void p_msg, int lg, int
option) - msgid is the internal message queue identifier
- p_msg is a pointer to the first character of the
text message. - lg is the message length.
46Writing a message in a queue msgsnd
- option can take two values
- 0 is the default value, which corresponds to a
clocking write is the queue is full (for a queue,
there is a maximum number of messages and a
maximum number of bytes), the process is put to
sleep until there is enough space to memorize the
sent messge. - IPC_NOWAIT corresponds to a non-blocking
write if the message queue is full, immediate
return -1 (errno is put to EAGAIN). - The return of msgsnd is the length of the sent
message and -1 in the case of failure.
47Receiving a message from a file msgrcv
- The extraction ("reception") of a message from a
file is done by making the system cal msgrcv - int msgrcv(int msgid, void p-msg, int lg, long
type, int option) - msgid is the internal identifier of the file, in
which a demand is formulated. - p_msg is a pointer on a memory zone prone to
receive a message and the text zone has a length
inferior or equal to lg. - type allows to choose the message to be
extracted - type 0 the oldest message
- type gt 0 the oldest message of a given type
- type lt 0 the oldest message but with the
smallest type (the type of a message is always
positive) and inferior or equal to type. This
allows to define priorities in messages.
48Receiving a message from a file msgrcv
- Option can take three values
- 0 is the default value and corresponds to a
blocked read if the queue does not contain
messages of the specified type the msgrcv
process is put to sleep until a message of that
type arrives. - IPC_NOWAIT corresponds to a non,-blocking read
if the message queue does not contain messages of
the specified type, immediate return -1, errno is
set to EAGAIN. - MSG_NOERROR if the text to be extracted is
longer than ig, (this option forces the
extraction with truncation), then by default
there is an error. - The returs of msgrcv is the message length
written at the address p_msg and -1 in the case
of failure.
49 Control primitive for the message queue
- The function msgctl allows to work on the
structure pointed by pt - includeltsys/msg.hgt
- int msgctl(int msgid, int option, struct msqid_ds
pt) - msgid identifier of the created message queue
- Options
- IPC_Stat write at pt the information on the
message queue read the structure - pt value memory address containing the
information - IPC_SET modification of the values of uid, gid
or mode. - pt value memory address containing the
information - IPC_RMID removal of the message queue
- pt value NULL
- Return
- 0 no error
- -1 if error
50Example. (1/4)
- The program bourse.c must be called with an
argument which is, the client's message queue or
the agent message queue. The process stars by
opening a message queue with the key 1515. - If the argument is client the process sends
(msgsnd) in a queue a sell order of the
Eurotunnel shares (message of type 1). - If the argument is agent the process receives
(msgrcv) from the queue a type 1 message and
prints the received order. - include ltstdio.hgt
- include ltsys/types.hgt
- include ltsys/ipc.hgt
- include ltsys/msg.hgt
- include lterrno.hgt
- include ltstring.hgt
- struct ordre // sell shares
- long type
- char texte50 // nom de l'action
- int nombre // nombre a vendre
- message
- int longueur sizeof(message)-4
51Example (2/4)
- main(int argc, char argv)
- key-t cle 1515
- int ident // creation ou ouverture d'une file
de cle 1515 - if((ident msgget(cle, 0666IPC_CREAT)) -1)
- perror("msgget")
- exit(-1)
- if(strcmp(argv1, "client") 0) // code du
client - message.type 1 // envoi d'un message de type
1 (ordre de vente) - message.nombre 250000
- sprintf(message.texte, "Sell d Eurotunnel",
message.nombre) - if(msgsnd(ident, message, longueur, 0) -1)
- perror("msgsnd")
- exit(-1)
-
52Example. (3/4)
- // code pour l'agent de change
- if(strcmp(argvl, "agent") 0)
-
- // réception d'un message de type 1
- if(msgrcv(ident, message, longueur, 1, 0) -1)
- perror("msgrcv")
- exit(-l)
-
- printf("Received message type ld\n",
message.type) - printf("Ordre recu s\n", message.texte)
53Example Execution (4/4)
- bourse client (the client sends an order)
- ipcs -q (check)
- ------ Message Queues --------
- msqid owner perms used-bytes messages
- 0 root 666 0 0
- 1 jmc 666 56 0
- Â bourse agent (the agent gets un
message) - Received message de type 1
- Order received Sell 250000 Eurotunnel
- ipcs -q
(check) - ------ Message Queues --------
- msqid owner perms used-bytes messages
- 0 root 666 0 0
- jmc 666 0 0
54Shared memory
- Creation
- Write
- Read
- Control and destruction
55Shared memory
- The shared memory allows processes to physically
share pages of data - A shared memory segment does not depend on a
specific process it is an independent object in
the system. It is characterized by its size. - Any processor knowing it by key can attach it to
its address space.
56Creating a shared memory
- The shared memory must be created, staring from a
key, by the function shmget () of prototype - include ltsys/shm.hgt
- int shmget (key_t key, int size, int
attributes) - key is the key (obviously),
- The parameter IPC_PRIVATE passes as key allows
processes and to theirs children to use the
shared memory. - size the number of the bytes of the shared
memory, - attributes binary OR between the different
options - defines the rights of read/write (example 666)
- IPC_CREAT allows the creation of a shared memory
- IPC_EXCL forces the creation of a shared memory
- Returns
- The return of the function shmget() is the memory
identifier. - -1 in case of error.
57Created shared memory Attachment primitive
- All processes using shared memory, including the
one that created it, must attach to the shared
memory, after it is created. The attachment is
done with the function shmat() with the prototype
below, and identifier returned by shmget() - include ltsys/shm.hgt
- int shmat (int shmid, const void address, int
attribut) - shmid returned identifier whiles creating the
shared memory - address desired address for the attachment . If
this argument is NULL, the kernel searches an
empty space. - The attachment can be done in read-only if the
attribute SHM_RDONLY is passed as third argument.
- Returns
- effective attachment address
- -1 if error
58Created shared memory Detaching primitive
- Allows to a process which has attached a segment
in its space to detach it. - include ltsys/shm.hgt
- int shmdt (const void address)
- address attachment address sent by shmat
- Returns
- 0 no error
- -1 if error
59Created shared memory Control primitive
- Allows a process which has already attached a
segment in its address space to receive
information on the shared memory, to modify or to
destroy a segment of shared data. - include ltsys/shm.hgt
- int shmctl (int shmid, int op, struct shmid_ds
pt) - shmid memory identifier
- op
- IPC_RMID suppression of the segment after the
last detachment - IPC_STAT information changing under pt
- IPC_SET modification of the input uid, gid,
mode - After call
- pt contains the information relative to the
segment for IPC_STAT - Returns
- 0 no error
- -1 if error
60Created shared memory Control primitive
- Struct shmid_ds
- struct ipc_perm shm_perm //droits
- int shm segsz //taille du segment en octets
- pid_t shm_Ipid //pid de la tâche ayant effectuée
la dernière op. - pid_t shm_cpid //pid de la tâche créateur de la
mémoire - u_short shm_nattch //nombre dattachement
- time_t shm_atime // dernier attachement
- time_t shm_dtime // dernier détachement
- time_t shm_ctime // dernière modification
-
- Struct ipc_perm
- uid_ uid
- gid_t gid
- ind_t cuid
- gid_t cgid
- u_short mode
- u_short seg //nombre dutilisation
- key_t key
61Shared memory an example
- The example below proposes an attachment in read
and write. - include ltstdio.hgt
- include ltsys/types.hgt
- include ltsys/ipc.hgt
- include ltsys/shm.hgt
- define MODE-MEMOIRE (SHM_RSHM_W) /utilisation
en lect. et écrit. - int shmid // identificateur de mémoire partagée
- char shmptr // pointeur sur la mémoire partagée
- // --- Création de la Mémoire Partagée 5 octets
--- - if (shmid shmget (IPC_PRIVATE, 5,MODE MEMOIRE)
lt 0) - perror("shmget")
- exit(-1)
- /--- Attachement a la Mémoire Partagée --/
- if(shmptr shmat (shmid, 0,0) (void )-1)
- printf("Erreur d'Attachement à la mémoire
partagée \n" ), - exit (-1)