ยง48.5โ€“48.6Remote Procedure Call (RPC) and Summary

Part III OSTEP pp. 613โ€“618 ยท ~7 min read

  • remote procedure call
  • stub generator
  • marshaling
  • end-to-end argument

Distributed shared memory failed; the abstraction that won comes from programming languages, not the OS.

48.5 Remote Procedure Call (RPC)

A remote procedure call (RPC) has one goal: make running code on a remote machine as simple as calling a local function. The server exports some routines; the client calls them; the RPC system handles all the messaging magic in between. That magic has two pieces: a stub generator and a run-time library.

The stub generator

The stub generator (or protocol compiler) takes an interface โ€” the calls the server wants to export โ€” and generates the glue code so nobody has to write fragile message-packing by hand:

interface (.x file)int func1(int);int func2(int,int);the calls the server exportsstubgeneratorclient stubserver stubgenerated glue: no hand-written packing code
The stub generator turns an interface into a client stub (called like a local function) and a server stub (which dispatches to the real code).

The heart of a stub is marshaling โ€” packing the function id and arguments into one contiguous message buffer (a.k.a. serialization); the far side unmarshals (deserializes) them back out:

func1(x = 7)id = 1arg = 7marshal1 | 7 | โ€ฆ | โ€ฆone contiguous message bufferthe receiver reverses this โ€” "unmarshal" โ€” to recover func1 and its argument
Marshaling flattens a call โ€” the functionโ€™s id and its arguments โ€” into a single byte buffer for the network. Unmarshaling reverses it.

Put the pieces in motion and a whole remote call looks like this:

One RPC, end to end. The client just calls func1(x); the stubs and run-time do everything else โ€” marshal, send, dispatch, and marshal the result back.
Client
Client stub
Server stub
Server
The call goes out
The reply comes back
step 1 / 10 ยท time flows downward

The client has no idea a network is involved.

Two wrinkles the stub compiler must handle: complex arguments โ€” a raw pointer (as in write(fd, buf, size)) means nothing on another machine, so RPC uses well-known types (e.g. a buffer_t of a given size) or annotations to know which bytes to serialize โ€” and server concurrency: a server that handles one request at a time wastes resources whenever a call blocks, so real servers use a thread pool, dispatching each request to a worker (paying the usual cost of locks and synchronization).

The run-time library

The run-time library does the heavy lifting โ€” performance and reliability. A few of its jobs:

  • Naming. The client must find the service โ€” commonly by hostname/IP + port number (and, above that, DNS). Full naming is a deep topic of its own.
  • Transport choice. Reliable TCP seems the obvious pick, but itโ€™s usually the wrong one:
Why most RPC packages run on UDP, not TCP.
RPC on TCP (reliable)RPC on UDP (unreliable)
acknowledgmentsTCP acks the request AND the replynone from the transport โ€” confirmation rides on the natural request/response
who provides reliabilityTCP doesthe RPC layer adds its own timeout/retry + sequence numbers
semanticsreliable byte streamexactly-once (no failure) or at-most-once (on failure)
verdictsimpler, but 2 extra messages per callmore efficient โ†’ the common choice for RPC
Dotted-underlined cells have explanations โ€” click one.
  • Long-running calls. A slow remote call can look like a failure and trigger a needless retry; the run-time can send an explicit ack and let the client periodically ask โ€œstill working?โ€.
  • Large arguments. Bigger than one packet? The run-time fragments on the sender and reassembles on the receiver.
  • Byte order. Machines differ (big- vs little-endian), so RPC defines a canonical wire format โ€” Sun RPCโ€™s XDR (eXternal Data Representation) โ€” and converts when a machineโ€™s native order differs.
  • Asynchrony. Normally an RPC is synchronous (the client waits). An asynchronous RPC returns immediately so the client can do other work, then later collects the result โ€” overlapping computation with communication.

Aside: The End-to-End Argument

The end-to-end argument says certain functionality can only be fully implemented at the highest level โ€” the application โ€œat the endโ€. Take reliable file transfer: even if the network delivers every byte reliably (timeout/retry, acks, sequence numbers), the bytes could have been corrupted in the senderโ€™s memory before transmission, or mangled when the receiver writes them to disk. Only an end-to-end check โ€” read the file back on the receiver, checksum it, and compare to the senderโ€™s โ€” truly guarantees the transfer. The corollary: lower-level machinery can still be worth adding for performance, just not for the ultimate guarantee.

48.6 Summary

Distributed systems introduce one dominant new problem โ€” failure, now a commonplace event (โ€œwith thousands of machines, failure is happening all the timeโ€). Their heart is communication, and its most common abstraction is RPC, which hides timeout/retry, acknowledgments, marshaling, and byte-order details behind something that closely mirrors a local procedure call. The best way to really get it is to use one: Sunโ€™s rpcgen, or modern takes like Googleโ€™s gRPC and Apache Thrift. Next, weโ€™ll see these ideas made concrete in two real distributed file systems โ€” NFS and AFS.

Check yourself: remote procedure call

1.What is the goal of a remote procedure call (RPC) system?

2.What does the stub generator produce from an interface, and why is that useful?

3.What is marshaling (serialization) in an RPC call?

4.Why are many RPC packages built on UDP rather than the reliable TCP?

5.The end-to-end argument: why doesn't a reliable network layer alone make a file transfer reliable?

6.How do machines with different byte orderings (big- vs little-endian) exchange values in an RPC?

6 questions