Implementing Remote Procedure Calls Andrew Birrell and Bruce Nelson Slide Set by Phil Howard and others
Why?
Consider #include <stdio.h> int main() { printf("hello world!\n"); return 0; }
What Happens main main printf glibc kernel IO kernel update_window X update_display kernel IO kernel
But what if Laptop Linux Box X Client X Server Console Window main
What Happens Laptop Linux Box X Client main main printf glibc update_display X kernel IO kernel kernel IO kernel update_window X update_display
How do you do this? Fork/Join? SendMessage/AwaitReply? Virtual Shared Memory? Remote Procedure Call?
Remote Procedure Call (RPC)
What's Involved in RPC Remote Procedure Call should look and feel like local call Remote Procedure Call should be independent of where it executes Remote Procedure Call should be "efficient"
Issues Finding the callee Passing data Implementation Exceptions Optimizations
Finding the Callee (Binding) Where - Either server has known static address - Or client broadcasts request - Or use central database of interfaces What - Servers export named interfaces - Clients request a particular interface - Need Globally Unique Identifiers for interfaces
Interfaces Type Instance - Like object oriented programming interfaces - Method names, parameter and result types - Underlying functionality is hidden Instance - Which specific provider of this type
Binding Callee exports an interface via a network accessible database. RPCRuntime Server Stub Server Database Export Interface Export Interface Update Database
Binding Caller binds by specifying the type of the interface in a database query Selects from a list of instances returned Caller User User Stub RPCRuntime Who’s available? Database Import Interface Import Interface Query Database Interface Info Interface Info Interface Info Available Interfaces
Issues Passing data Finding the callee Implementation Exceptions Optimizations
Passing Data Can't use the stack! Can't use shared memory! Generally use message passing
Passing Data #include <stdio.h> int main() { } union } my_var; unsigned long stuff1; unsigned char stuff2[4]; } my_var; my_var.stuff1 = 0x12345678; printf("%X %X %X %X\n", my_var.stuff2[0], my_var.stuff2[1], my_var.stuff2[2], my_var.stuff2[3]); return 0; }
Passing Data What's the output? 12 34 56 78 or 78 56 34 12
Passing data Build a message that includes: Who and what's being called Identity of the caller Data values in known byte order Using an intermediate data representation
Issues Implementation Finding the callee Passing data Exceptions Optimizations
Implementation foo(a,&b) foo(int a, int *b) return;
Implementation foo(int a; int *b) { build_msg(a,b); send_msg(); wait_for_reply(); get_from_msg(&b); } do_foo(msg_t msg) { int a,b; get_from_msg(&a,&b); foo(a,&b); build_msg(b); send_reply(); }
Implementation Function prototype is (almost) all that's needed to build the client stub Also need binding information Function prototype is (almost) all that's needed to build server stuff Also need method to wait for message
Implementation Network Network Caller Callee User User Stub RPCRuntime Procedure call Pack arguments Send packet Transmit packet(s) procedure return Unpack result(s) Receive packet(s) Receive packet Callee RPCRuntime Server Stub Server Network Receive packet Receive packet(s) Unpack arguments Procedure call Send packet Transmit packet(s) Pack result(s) Procedure return
Implementation Clients Threaded Servers Event Driven
Issues Exceptions Finding the callee Passing data Implementation Optimizations
Exceptions What can happen in "normal" procedures? Procedure generates an exception Procedure infinite loops Procedure generates wrong results
Exceptions What can happen in "remote" procedures? Client stub generates exception Transmission failure knowable failure unknowable failure Remote procedure generates an exception Remote procedure infinite loops Remote procedure generates wrong results
Issues Optimizations Finding the callee Passing data Implementation Exceptions Optimizations
Optimizations Servers maintain no state on clients No penalty for a large number of clients Messages must be ack'd for short calls, result serves as ack for frequent calls, next call ack's last result for long requests, only last request message gets ack'd for long results, only last result message gets ack'd Bound to "closest" server Minimum transmission delay
Protocol-Level Optimizations Caller Callee User RPCRuntime RPCRuntime Server Procedure call Transmit first packet Call[Ids, packet=0] Receive packet 0 Ack[Ids, packet=0] Receive ack Transmit ack Call[Ids, packet=1] Transmit next packet Receive packet 1 Procedure call Retransmit next packet Call[Ids, packet=1, needAck] Receive packet 1 Ack[Ids, packet=1] Receive ack Transmit ack procedure return Receive result Result[Ids] Transmit result procedure return Receive result Result[Ids, needAck] Transmit ack request Ack[Ids] Transmit ack Receive ack
Conclusion Remote Procedure Call should look and feel like local call Remote Procedure Call should be independent of where it executes Remote Procedure Call should be "efficient"