CS452 - Real-Time Programming - Fall 2010
Lecture 10 - Message Passing
Pubilic Service Announcements
- Finished kernel 1 material
- TS7200, how it is fragile
Overview
Message passing combines synchronization and communication in one set of
primitives.
int Send( Tid tid, char *message, int mslen, char *reply, int rplen )
- blocks until Reply occurs
int Receive( Tid *tid, char *message, int mslen )
- blocks until message is available
- Only one waiting sender is processed per Receive
- Why?
- Hint. There might be tasks that are higher priority than the
Receiver.
int Reply( Tid tid, char *reply, int rplen )
- does not block
- unblocks task that called Send
- Send and Reply become READY at the same time.
Sequence of States
Sender
- Active -> Receive_Blocked
- Receive_Blocked -> Reply_Blocked
- May happen right away
- When Receive was called first AND
- the Receiver's SendQ is empty
- Otherwise, when Receive is called
- Reply_Blocked -> Ready
Receiver
- Active -> Send_Blocked
- Send_Blocked -> Ready
- May happen right away
- if the sendQ is not empty
- Ready -> Active
- ...
- Active -> Ready
There are two cases
Send before Receive
Send
...
Receive
...
Reply
... ...
Message copying occurs inside Receive and Reply.
Receive needs to have a list of current senders, the ReceiveQ
Receive before Send
Receive
...
Send
...
Reply
... ...
Message copying occurs inside Send and Reply.
Practical Details
- Need to keep around request
- For Send_Blocked receivers in the SendQ
- The same as Receive_Blocked senders
- For Reply_Blocked senders.
- Messages
- Typed as strings
- which require formatting and parsing
- Normally cast from pointers to structs
- sizeof( ) is useful
- struct expected by receiver/sender must agree with struct
provided by sender/receiver
- Type-checking by compiler not possible
- Dynamic type-checking possible
- First element of struct is its type
- Even polymorphism is possible
- Message types should be handled in a more modern way
- type extension
- would provide a more uniform handling of error returns
- Task states
- DEFUNCT is terminal
- READY -> ACTIVE
- ACTIVE -> SEND_BLOCKED, RECEIVE_BLOCKED, READY, DEFUNCT
- SEND_BLOCKED -> READY
- RECEIVE_BLOCKED -> REPLY_BLOCKED
- REPLY_BLOCKED -> READY
- You can add extra return values beyond those specified
int Send( Tid tid, char *message, int mslen, char *reply, int rplen )
These are pretty self explanatory, except
- The return value is the number of characters actually placed in the
reply-buffer
- including the terminal character ( \000 ) if the contents of the
reply buffer is a string
- If something goes wrong, the return value is negative, coded to
indicate what went wrong
What can go wrong
- Illegal
tid
tid
not an existing task
It's up to Send
to check that the reply-buffer was big
enough by looking at its return value
It's not an error if the task to which we Send
never
Receive
s
- Should it be?
- Hint. Finding out if a task "never Receives" is equivalent to what
problem?
- Parsing
argument
and reply-buffer
is
potentially costly and error-prone
- A type system might be nice
- But then you would feel compelled to implement run-time type
checking
Implementing Send
What's in user space is just stubs.
- checking arguments
- putting arguments in the right place
- Note that there are five arguments
What the kernel must do
- Check arguments
- Change state of sender to RECEIVE_BLOCKED
- Put sender on the end of the receiver's sendQ
- If receiver is SEND_BLOCKED, do #3 in Receive, immediately below.
int Receive( Tid *tid, char *message, int msglen )
These are pretty self explanatory, except
- How is the task id copied form kernel to receiver?
- That is, where does the pointer point to?
- What if the buffer wasn't big enough?
- If several tasks have done
Send
, which one gets
Receive
d first?
- return value is number of bytes in message, including terminal
character (\000) if the message is really a string..
- It seems as though the return value should be the tid. Something is
not right.
- If something goes wrong, the return value is negative, coded to
indicate what went wrong
What can go wrong?
- Only part of the message was copied
It's up to Receive
to check that the message-buffer was
big enough by looking at its return value
Implementing Receive
What the kernel must do
- Check arguments
- Change receiver's state to SEND_BLOCKED
- Check the
sendQ
- If SENDQ_EMPTY
- Exit from kernel after scheduling
sendQ
is not empty
sender = head( sendQ )
sendQ = next( sendQ )
- copy message from sender to receiver, after checking buffer
sizes
- change sender's state to REPLY_BLOCKED
- change receiver's state to READY
- put sender's tid into receiver's argument
- put receiver on its
readyQ
- set up receiver's return value
int Reply( Tid tid, char *reply, int rplen )
These are pretty self explanatory, except
- The Replyer need not be the Receiver, but must be in contact with the
Receiver
- When all goes well Reply leaves two tasks READY when it completes
Implementing Reply
- Check arguments
- sender (tid) must be REPLY_BLOCKED
- Copy message from replier to sender, checking buffer sizes
- Put sender on readyQ
- Set up sender's return value
- Change sender's state to READY
- Put replier on readyQ
- Set up replier's return value
- Change replier's state to READY.
Return to: