CS452 - Real-Time Programming - Fall 2010
Lecture 11 - Name Server
Pubilic Service Announcements
- Kernel 1?
- Hardware interrupts to come
- Send/Receive/Reply
- performance
- no caches, no optimization
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
Receives
- 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 #5 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
Received 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
If sendQ is not empty
- Extract sender from 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.
Servers
What is a server?
- a task that provides service to any task that knows its task id.
- a task that owns a resource and provides synchronized access to it.
- above,
- `a task' owns the interface
- other tasks may do the work
How are servers implemented?
- Receive is the key
- Receive a request
- Reply the response
- Sender (client, task that is making the request) blocks until the
response is available. That is, sender, in effect, is running at the
priority of the server between the request and its reponse
- Server priority should be set according to the importance of the
service it supplies.
- But client priority should be considered by the server. For example,
- One set of instructions for higher priority client
- One set of instructions for lower priority client
Name Server
What is a name server?
- There is a set of global execution-independent names
- There is a set of execution-dependent resources that provide services
associated with the names
- Name server maintains an up-to-date table mapping names to resources
- Accepts requests to update the table
- Accepts queries concerning the table
Why Do We Need a Name Server
| Names |
constant across applications & executions |
interface |
Associated with a set of services (an API) |
| Task Ids |
vary across applications & executions |
implementation |
Associated with a particular set of instructions and data (an
execution) |
How do You Get the Task Id of the Name Server?
- Make it a constant across executions
Name Server API
int RegisterAs( char *name );
- One task can be registered under two names.
- Each name is associated with a single task.
- Name is
\000 terminated.
int WhoIs( char *name );
Name Server Semantics
RegisterAs
- Errors
- Not a legal name.
- tid is not a task
- tid is not the Name Server
- Already somebody registered with that name
WhoIs
- Errors
- Not a legal name.
- tid is not a task
- tid is not the Name Server
- No task registered under that name
Comments
- Are we likely to want an
Unregister primitive?
- Perhaps
RegisterAs should just overwrite.
Name Server Implementation
User Code
E.g., RegisterAs
typedef struct {
int type;
char *name;
} NSstruct;
int RegisterAs( char *name ) {
int result;
NSstruct req;
req.type = REGISTERAS;
req.name = name;
bytes = Send( NSTid, &req, sizeof(NSstruct), (char *) result, sizeof(int) );
if ( bytes != sizeof(int) ) {
// Do something error-like
} else return result;
}
There are lots of possible variations, and this one is by no means the
best.
Server Code
typedef struct {
int type;
char *name;
} NSstruct;
int bytes, tid;
NSstruct req;
// initialize tables
FOREVER {
bytes = Receive( &tid, &req, sizeof(NSstruct) );
if ( bytes != sizeof(NSstruct) ) {
// Reply with error
} else {
switch( req.type ) {
case REGISTERAS:
insert( name, tid );
Reply( tid, SUCCESS, sizeof(int) );
break;
case WHOIS:
result = lookup( name );
Reply( tid, result, sizeof(int) );
break;
default
Reply( tid, ERROR, sizeof(int) );
break;
}
}
}
Comments
- This code wouldn't work. Why?
- How much will this code run?
- How would you implement insert & lookup?
- Figure out
- What deadlines does Nameserver have?
- How many names will be in NameServer?
- How many ReisterAs? and when?
- How many WhoIs? and when?
- What should be allowable as a name?
Return to: