Finding the server
The first thing that a client does is call open() to get a file descriptor. (Note that if the client calls the higher-level function fopen() instead, the same discussion applies—fopen() eventually calls open()).
Inside the C library implementation of open(), a message is constructed, and sent to the process manager (procnto) component. The process manager is responsible for maintaining information about the pathname space. This information consists of a tree structure that contains pathnames, process ID, channel ID, and handle associations (the first value, 0, is no longer in use):
fd = open ("/dev/ser1", O_WRONLY);
In the client's C library implementation of open(),
a message is constructed and sent to the process manager.
This message states, I want to open /dev/ser1; who
should I talk to?
The process manager receives the request and looks through its tree structure
to see if there's a match (let's assume for now that we need an exact match).
Sure enough, the pathname /dev/ser1
matches
the request, and the process manager is able to reply to the client:
I found /dev/ser1. It's
being handled by process ID 44, channel ID 1, handle 1. Send them
your request!
Remember, we're still in the client's open() code!
So, the open() function creates another message, and a
connection to the process ID (44),
channel ID (1), stuffing the handle into the message itself.
This message is really the connect
message—it's the
message that the client's open() library uses to establish a connection
to a resource manager (step 3 in the picture below).
When the resource manager gets the connect message, it looks at it and performs validation.
For example, you may have tried to open-for-write a resource manager that
implements a read-only filesystem, in which case you'd get back an error (in
this case, EROFS).
In our example, however, the serial port resource manager looks at the
request (we specified O_WRONLY; perfectly legal for a serial
port) and replies back with an EOK (step 4 in the picture below).
Finally, the client's open() returns to the client with a valid file descriptor.
Really, this file descriptor is the connection ID we just used to send
a connect message to the resource manager!
Had the resource manager not given us an EOK,
we would have passed this error back to the client (via errno and a
-1 return from open()).
(It's worthwhile to note that the process manager can return the process ID
and channel ID of more than one resource manager in response to a name resolution request.
In that case, the client will try each of them in turn until one
succeeds, returns an error that's not ENOSYS, or ENOENT,
or the client exhausts the list, in which case the open() fails.
We'll discuss this further when we look at the before
and after
flags, later on.)