If RPC has to be a viable mechanism for structuring operating
systems services above the kernel, using the client server paradigm.
Then it is important to reduce this overhead.
Now let's see how we can reduce the overheads.
And make RPC cheap enough that
you want to use it in building client service systems.
How do we remove these overheads?
The trick is to optimize the common keys.
Now what are the common keys?
Well, the common keys is the actual calls that are being made by the client
to the server.
We expect that those calls are going to be made several times
during the lifetime of the server and the client.
And so that's the key thing.
That you want to make sure that during the actual calls,
the copying overhead that I talked about.
And the locality of the arguments and the results, in terms of stuff being in
the caches that are accessible to the client and the server, that's the key.
That's the common key.
That's what we want to make as efficient as possible.
Now, setting up the relationship between the client and the server itself,
on the very first call by the client, that needs to be done exactly once.
And that process is what is called binding.
Binding the client and server.
That is done once, the first call is when the binding happens, and
that's done once.
Now, since the setup for the binding is done only once, it's a one-time cost.
It's okay if it is more expensive than the actual costs.
So, the binding, we can afford to make it more time consuming,
it's okay to do that.
And these ideas should sound very familiar to you from exokernal,
that we've discussed before, that we want to make the one time costs,
not focus on the one time cost of setting up.
Which is a one time cost, but focus on it is a recurring cost,
which is the actual calls that are being made.
So now, let's discuss in more detail how this binding works.
The server has an entry point procedure called
foo that it wants to make it available for clients to call.
And in order to make it available for everybody,
it publishes this entry point procedure in a name server.
And let's the kernel know that there's an entry point procedure called
foo that's available for it.
And the name server is a vehicle by which anyone in the system can find on.
It's sort of like the yellow pages, right?
So, you want to look up somebody's name or
phone number, you look it up in the yellow pages.
Similarly, this name server serves the same purpose
that if I want to know what services are offered as a client.
If I want to know what services are offered by a particular server,
I can find out from the nameserver what are the entry point
services available from S.
So foo is an entry point service that's available in the server,
registers the name server, and
let's the kernel know that it has this particular entry point.
And at this point the server is basically waiting for
bind requests to come from the colonel.
Now the client looks up the name server and
finds that S is an entry point called foo.
So this is an entry point that's available for
this client to make a call on the server.
So the client issues this call s.foo,
meaning that it wants to execute this procedure foo onto server S.
And so that's RPC call, the first time, C is making.
And this of course results in a trap into the kernel.
The kernel doesn't know whether the server is willing to
accept calls from the client or not.
And therefore what it has to do is,
it has to check with the server whether there's a legitimate bona-fide client
that can make calls on those entry point procedure foo.
And so the kernel basically makes an up-call into the server saying that hey,
you know what?
There is this client that wants to make something with this identity.
Wants to make a call on your entry point procedure foo.
And that's the up call that goes into the server.
The server, if it recognizes that this client is a bonafide
client that can make this call, it grants permission via the kernel that
this client can make this call on its entry point procedure foo.
So once this validation has been done, what the kernel does
is to set up a descriptor called the procedure descriptor.
And the procedure descriptor is a data structure that is in the kernel.
And it is for this particular entry point procedure foo.
And it's part of granting access to the client
to make this call into its entry point procedure, foo.
What the server is going to do is tell the kernel
that these are the characteristics of this particular entry point procedure.
In particular, it's going to say,
this is the entry point address where you have to call me if there is call.
This is the address of the entry point procedure in my address space
where code exists for this particular procedure foo.
And this is indicating the size of an argument stack, and
I'm going to talk to you a little bit more about this in a minute.
And this argument stack is going to be the communication area between
the client and the server, and this entry in the procedure descriptor is
just seeing, what are the sizes of this argument stack?
So this communication vehicle that is going to be established
between the client and the server is going to be dependent at
the formal parameters that are being passed the client and the server.
And the results that are being passed from the server back to the client.
Based on that, the server is going to indicate to the kernel that
communication area that I want is this size.
So, that's the size of this A-stack.
I'm going to talk more about that in a minute.
And it is also going to say how many simultaneous calls
S is willing to accept for this particular procedure foo.
And the purpose of this is, if this is a multi-processor and
there are multiple cores and multiple processes available.
Then it may be possible for
S to farm out multiple threads to execute simultaneous calls that
are coming in from multiple clients distributed in the system.
And so they're saying how many concurrent calls
the server is willing to accept on behalf of this particular procedure.
So, this procedure descriptor is specific.
Do this procedure foo, and it is saying,
where is the entry point in the server's domain for this particular procedure?
What are the size of the communication buffer that is needed to be established
by the kernel for communication between the client and the server?
And the third thing is, how many simultaneous calls
the server is willing to accept for this particular procedure, foo.