[ip-servers.pptx]

advertisement
Duke Systems
Servers
and
A Little Bit of Networking
Jeff Chase
Duke University
Unix process view: data
A process has multiple channels
for data movement in and out of
the process (I/O).
The channels are typed.
I/O channels
(“file descriptors”)
stdin
stdout
tty
Process
stderr
Each channel is named
by a file descriptor.
pipe
Thread
socket
The parent process and
parent program set up and
control the channels for a
child (until exec).
Files
Program
Servers and the cloud
Where is your application?
Where is your data?
Where is your OS?
networked
server “cloud”
Cloud and Software-as-a-Service (SaaS)
Rapid evolution, no user upgrade, no user data management.
Agile/elastic deployment on clusters and virtual cloud utilityinfrastructure.
Networked services: big picture
client host
NIC
device
client
applications
kernel
network
software
Internet
“cloud”
Data is sent on the
network as messages
called packets.
server hosts
with server
applications
Sockets
socket
A socket is a buffered
channel for passing
data between
processes over a
network.
client
int sd = socket(<internet stream>);
gethostbyname(“www.cs.duke.edu”);
<make a sockaddr_in struct>
<install host IP address and port>
connect(sd, <sockaddr_in>);
write(sd, “abcdefg”, 7);
read(sd, ….);
• The socket() system call creates a socket object.
• Other calls establish connections between socket pairs (e.g, connect).
• A file descriptor for a connected socket is bidirectional.
• Write bytes at one end; read returns them at the other end.
• The read syscall blocks if the (stream) socket is “empty”.
• The write syscall blocks if the (stream) socket is “full”.
• Both read and write fail if there is no valid connection.
Sockets: client/server example
request
“GET /images/fish.gif HTTP/1.1”
reply
client (initiator)
server
sd = socket(…);
connect(sd, name);
write(sd, request…);
read(sd, reply…);
close(sd);
s = socket(…);
bind(s, name);
listen(s, 10);
sd = accept(s);
read(sd, request…);
write(sd, reply…);
close(sd);
Socket syscalls
connect(csd, <IP address and port>). For a client: connect the socket named
by descriptor csd to a server at the specified IP address and port. Block until
the connection is established.
bind(sd, <…port>). For a server: associate the socket named by descriptor sd
with a port number reachable at an IP address of the host machine. Does not
block, but may fail, e.g., if some other process is already bound to the port.
listen(sd, qsize). For a server: indicate that the socket named by descriptor sd
is a server socket. When a connect request arrives for its port, establish the
connection and place it on the accept queue (unless the accept queue is full).
Listen does not block: it merely sets some parameters on the socket.
accept(sd, …). For a server: accept a connection from the accept queue for
the server socket named by descriptor sd. Block if the accept queue is empty.
Returns the IP address and port of the client for this connection, and a new
socket descriptor csd for the connection.
Given a socket descriptor csd for an established connection (from a completed
connect or accept) a process may use write (or send) to send bytes to the
connection peer, and may use read (or recv) to receive bytes sent by the peer.
catserver
...
struct sockaddr_in socket_addr;
sock = socket(PF_INET, SOCK_STREAM, 0);
memset(&socket_addr, 0, sizeof socket_addr);
socket_addr.sin_family = PF_INET;
socket_addr.sin_port = htons(port);
socket_addr.sin_addr.s_addr = htonl(INADDR_ANY);
if (bind(sock, (struct sockaddr *) &socket_addr, sizeof socket_addr) < 0) {
perror("bind failed");
exit(1);
}
listen(sock, 10);
while (1) {
int acceptsock = accept(sock, NULL, NULL);
forkme(acceptsock, prog, argv); /* fork/exec cat */
close(acceptsock);
}
}
Web Server
Inside your Web server
Server application
(Apache,
Tomcat/Java, etc)
accept
queue
packet
queues
listen
queue
disk
queue
Server operations
create socket(s)
bind to port number(s)
listen to advertise port
wait for client to arrive on port
(select/poll/epoll of ports)
accept client connection
read or recv request
write or send response
close client socket
Socket descriptors in Unix
Disclaimer:
this drawing
user space
kernel
spaceis oversimplified
file
int fd
pointer
per-process
descriptor
table
pipe
Inbound
traffic
socket
tty
“open file table”
global
port
table
There’s no magic here: processes use read/write (and other syscalls) to
operate on sockets, just like any Unix I/O object (“file”). A socket can
even be mapped onto stdin or stdout.
Deeper in the kernel, sockets are handled differently from files, pipes, etc.
Sockets are the entry/exit point for the network protocol stack.
Ports
• Each IP transport endpoint on a host has a logical port
number (16-bit integer) that is unique on that host.
• This port abstraction is an Internet Protocol concept.
– Source/dest port is named in every IP packet.
– Kernel looks at port to demultiplex incoming traffic.
• What port number to connect to?
– We have to agree on well-known ports for common services
– Look at /etc/services
– Ports 1023 and below are ‘reserved’ and privileged: generally
you must be root/admin/superuser to bind to them.
• Clients need a return port, but it can be an ephemeral
port assigned dynamically by the kernel.
Ports and packet demultiplexing
The IP network carries data packets addressed to a destination
node (host named by IP address) and port. Kernel network stack
demultiplexes incoming network traffic: choose process/socket to
receive it based on destination port.
Incoming network packets
Network adapter hardware
aka, network interface
controller (“NIC”)
Apps with
open
sockets
Wakeup from interrupt handler
return to user mode
trap or fault
sleep
queue
sleep
wakeup
ready
queue
switch
interrupt
Example 1: NIC interrupt wakes thread to receive incoming packets.
Example 2: disk interrupt wakes thread when disk I/O completes.
Example 3: clock interrupt wakes thread after N ms have elapsed.
Note: it isn’t actually the interrupt itself that wakes the thread, but the interrupt
handler (software). The awakened thread must have registered for the wakeup
before sleeping (e.g., by placing its TCB on some sleep queue for the event).
The network stack, simplified
Internet client host
Internet server host
Client
User code
Server
TCP/IP
Kernel code
TCP/IP
Sockets interface
(system calls)
Hardware interface
(interrupts)
Network
adapter
Hardware
and firmware
Network
adapter
Global IP Internet
Note: the “protocol stack” should not be confused with a thread stack. It’s
a layering of software modules that implement network protocols:
standard formats and rules for communicating with peers over a network.
The Internet concept wasn’t
obviously compelling, at least
not to everyone.
It had to be marketed, even
within the tech community.
Insert “Power of TCP/IP”
slide, /usr/net/87.
(The poster in my office)
In 1986, the US National
Science Foundation (NSF)
opened the door to a
commercial Internet (then
NSFNET). IP support in
sockets (Berkeley Unix) was
widely used among academics.
The driving force for adopting
TCP/IP was a collection of Unixoriented startups and upstarts
arrayed against a few large
companies with their own
proprietary network standards.
Stream sockets with
Transmission Control Protocol (TCP)
TCP user
(application)
user transmit buffers
COMPLETE
TCP send buffers (optional)
SEND
user receive buffers
COMPLETE
TCP rcv buffers (optional)
TCP
implementation
transmit
queue
get
receive
queue
data
data
checksum
ack
outbound
packets
window
flow
flow
TCP/IP protocol sender
RECEIVE
TCB
ack
inbound
packets
TCP/IP protocol receiver
checksum
network path
Integrity: packets are covered by a checksum to detect errors.
Reliability: receiver acks received data, sender retransmits if needed.
Ordering: packets/bytes have sequence numbers, and receiver reassembles.
Flow control: receiver tells sender how much / how fast to send (window).
Congestion control: sender “guesses” current network capacity on path.
Illustration only
Who governs the Internet?
IANA: a department of ICANN.
ICANN: a US nonprofit organization
that is responsible for the coordination
of…unique identifiers related to the
namespaces of the Internet, and
ensuring the network's stable and
secure operation.
I/O syscalls: quick primer 1
read(fd, buf, len): read len bytes from the I/O object named by the descriptor fd
and store it in the process VAS at address buf.
Block until the data is available:
•
If fd is a file: wait for disk I/O to complete; e.g., the calling thread blocks
awaiting a notify triggered by the disk interrupt handler.
•
If fd is a socket: block until the data arrives from the network peer; e.g., the
calling thread blocks awaiting a notify triggered by the NIC interrupt handler
after the packet arrives. (The recv syscall is equivalent.)
•
If fd is a pipe: block until a writer writes the data into the write end of the
pipe; e.g., the calling thread blocks awaiting a notify triggered by the write
(like soda machine).
If the object named by fd will never produce the data, then read returns an
EOF: end of file. A read returns the number of bytes transferred: zero  EOF.
•
If fd is a file, and all bytes of the file have already been read through fd.
•
If fd is a stream socket or a pipe, and the writer(s) closed the other end.
I/O syscalls: quick primer 2
write(fd, buf, len): write len bytes to the I/O object named by the descriptor fd,
fetching them from the process VAS at address buf.
Generally, the write call is asynchronous / nonblocking: it returns
immediately, and completes when the data arrives at the destination some time
later. However, uncompleted writes require buffering in the system, and write
blocks when some bound on the buffer memory is reached:
•
If fd is a file: block if there are “too many” pending writes in progress; e.g.,
the calling thread blocks until “enough” of those writes reach the disk.
•
If fd is a stream socket: block if there are “too many” packets in transit to
the receiver; e.g., the calling thread blocks until the receiving process
consumes “enough” of that data. (The send syscall is equivalent.)
•
If fd is a pipe: block if the kernel’s bounded buffer for the pipe is exhausted;
e.g., the calling thread blocks until a reader consumes “enough” bytes,
freeing up sufficient kernel buffer space for the write to complete.
If bytes written to the object named by fd will never be consumed, then write
returns an error. E.g., this may occur if the receiver(s) closed its end of the
pipe or socket, or the network is unreachable.
Servers in “classic” Unix
• Single-threaded processes
• Blocking system calls
– Synchronous I/O: calling process blocks until is “complete”.
• Each blocking call waits for only a single kind of a event
on a single object.
– Process or file descriptor (e.g., file or socket)
• Add signals when that model does not work.
– Oops, that didn’t really help.
• With sockets: add select system call to monitor I/O on
sets of sockets or other file descriptors.
– select was slow for large poll sets. Now we have various
variants: poll, epoll, pollet, kqueue. None are ideal.
Services
Network services run as
processes that listen for requests
arriving on ports.
Before
Each network service receives
requests at a designated port
number (assigned and
standardized by IANA).
See /etc/services
Inetd forks service processes on
demand (lazily) on incoming
connect requests for their ports.
After
What if no process is listening on the
destination port for a request?
inetd
• Classic Unix systems run an
inetd “internet daemon”.
• Inetd receives requests for
standard services.
– Standard services and ports
listed in /etc/services.
– inetd listens on all the ports
and accepts connections.
• For each connection, inetd
forks a child process.
• Child execs the service
configured for the port.
• Child executes the request,
then exits.
[Apache Modeling Project: http://www.fmc-modeling.org/projects/apache]
Request/reply messaging
client
server
Remote Procedure
Call (RPC) is one
common example of
this pattern.
request
compute
reply
The Web is another.
P2: break a simple web server
• The web server is based on:
– */c-samples/buggyserver.c
• This server has a bug that makes it vulnerable to a stack
smash attack (previously discussed).
• Stack smash attacks may enable remote execution of
code chosen by the attacker, to “own” the web server.
• Each group gets their own instance to attack. If you
crack it you get the points.
• Test your talents, but please do not abuse them.
• These attacks have unleashed untold pain into the
world…and it never stops.
Stack smash defenses
• Modern systems have various defenses.
– NX: no-execute segments. The classic attack injects code
onto a buffer that resides on the stack, and overwrites a return
address to branch to the injected code. NX makes this harder by
disabling execute privilege on the stack segment. Any attempt
to execute the attacker’s code (or any code) on the stack
generates a fault.
– ASLR: address space layout randomization. The attacker
guesses where the stack resides in order to overwrite a frame’s
return address to branch to injected code. Randomizing the
layout makes this harder.
• These are disabled in the web server instances or p2.
Server listens on a socket
struct sockaddr_in socket_addr;
sock = socket(PF_INET, SOCK_STREAM, 0);
int on = 1;
setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &on, sizeof on);
memset(&socket_addr, 0, sizeof socket_addr);
socket_addr.sin_family = PF_INET;
socket_addr.sin_port = htons(port);
socket_addr.sin_addr.s_addr = htonl(INADDR_ANY);
if (bind(sock, (struct sockaddr *)&socket_addr, sizeof socket_addr) < 0) {
perror("couldn't bind");
exit(1);
}
listen(sock, 10);
Illustration only
Accept loop: trivial example
while (1) {
int acceptsock = accept(sock, NULL, NULL);
char *input = (char *)malloc(1024*sizeof (char));
recv(acceptsock, input, 1024, 0);
int is_html = 0;
char *contents = handle(input,&is_html);
free(input);
…send response…
close(acceptsock);
}
If a server is listening on only one
port/socket (“listener”), then it can
skip the select/poll/epoll.
Illustration only
Send HTTP/HTML response
const char *resp_ok = "HTTP/1.1 200 OK\nServer: BuggyServer/1.0\n";
const char *content_html = "Content-type: text/html\n\n";
send(acceptsock, resp_ok, strlen(resp_ok), 0);
send(acceptsock, content_html, strlen(content_html), 0);
send(acceptsock, contents, strlen(contents), 0);
send(acceptsock, "\n", 1, 0);
free(contents);
Illustration only
Servers and concurrency
• Servers receive requests from many different clients.
– Many clients send requests “at the same time”.
• Servers should handle those requests concurrently.
– Don’t leave a server CPU idle if there is a request to work on.
• But how to do that with the classic Unix process model?
– Unix had single-threaded processes and blocking syscalls.
– If a process blocks it can’t do anything else until it wakes up.
• Systems with GUIs also face them.
• What to do?
Event-driven programming
• Event-driven programming is a design
pattern for a thread’s program.
• The thread receives and handles a
sequence of typed messages or events.
– Handle one event at a time, in order.
• In its pure form the thread never blocks,
except to get the next event.
events
– Blocks only if no events to handle (idle).
• We can think of the program as a set of
handler routines for the event types.
– The thread upcalls the handler to
dispatch or “handle” each event.
• A handler should not block: if it does, the
thread becomes unresponsive to events.
Dispatch events by invoking
handlers (upcalls).
Handling a Web request
Accept Client
Connection
may block
waiting on
network
Read HTTP
Request Header
Find
File
may block
waiting on
disk I/O
Send HTTP
Response Header
Read File
Send Data
We want to be able to process requests concurrently.
Web server (serial process)
 Option 1: could handle requests serially
Client 1
WS
Client 2
R1 arrives
Receive R1
Disk request 1a
R2 arrives
1a completes
R1 completes
Receive R2
 Easy to program, but painfully slow (why?)
Web server (“pure” event-driven)
 Option 2: use asynchronous I/O
 Fast, but hard to program (why?)
Client 2 Client 1
WS
Disk
R1 arrives
Receive R1
Disk request 1a
R2 arrives
Receive R2
1a completes
R1 completes
Start 1a
Finish 1a
Web server (multiprogrammed)
 Option 3: assign one thread per request
Client 1
WS1
WS2
Client 2
R1 arrives
Receive R1
Disk request 1a
R2 arrives
Receive R2
1a completes
R1 completes
 Where is each request’s state stored?
Events vs. threading
• Classic Unix system call APIs are blocking. Requires multiple
processes/threads to build responsive/efficient systems.
• Even so, kernel networking and I/O stacks are mostly event-driven
(interrupts, callbacks, event queues, etc.). Example: Windows I/O
driver stack is a highly flexible event-driven system.
• Some system call APIs may be non-blocking, i.e., asynchronous
I/O. E.g., polling APIs like waitpid() with WNOHANG. This is an
event-driven model: notify thread by an event when op completes.
• Modern systems combine events and threading
– Event-driven model is natural for GUIs, servers.
– But to use multiple cores effectively, we need multiple threads.
And every system today is a multicore system.
– Multi-threading also enables use of blocking APIs without
compromising responsiveness of other threads in the program.
Multi-process server architecture
Process 1
Accept
Conn
Read
Request
Find
File
Send
Header
Read File
Send Data
…
separate address spaces
Process N
Accept
Conn
Read
Request
Find
File
Send
Header
Read File
Send Data
Multi-threaded server architecture
Thread 1
Accept
Conn
Read
Request
Find
File
Read File
Send Data
Send
Header
Read File
Send Data
…
Send
Header
Thread N
Accept
Conn
Read
Request
Find
File
This structure might have lower cost than the multi-process architecture
if threads are “cheaper” than processes.
Thread pool: idealized
Magic elastic worker pool
Resize worker pool to match
incoming request load:
create/destroy workers as
needed.
idle workers
Workers wait here for next
request dispatch.
Workers could be
processes or threads.
worker
loop
dispatch
Incoming
request
queue
Handle one
request,
blocking as
necessary.
When request
is complete,
return to
worker pool.
Ideal event poll API for thread pooling
Poll()
1. Delivers: returns exactly one event (message or
notification), in its entirety, ready for service (dispatch).
2. Idles: Blocks iff there is no event ready for dispatch.
3. Consumes: returns each posted event at most once.
4. Combines: any of many kinds of events (a poll set) may
be returned through a single call to poll.
5. Synchronizes: may be shared by multiple processes or
threads ( handlers must be thread-safe as well).
Event/request queue
We can synchronize an event
queue with a monitor: a
mutex/CV pair.
Protect the event queue data
structure itself with the mutex.
threads waiting on CV
Workers wait on the CV for
next event if the event queue
is empty. Signal the CV when
a new event arrives. This is a
producer/consumer
problem.
worker
loop
handler
dispatch
Incoming
event
queue
handler
handler
Handle one
event,
blocking as
necessary.
When handler
is complete,
return to
worker pool.
But what’s an “event”?
• A system can use an event-driven design pattern to
handle any kind of asynchronous event.
– Arriving input (e.g., GUI clicks/swipes, requests to a server)
– Notify that an operation started earlier is complete
• E.g., I/O completion
– Subscribe to events published/posted by other threads
– Including status of children: stop/exit/wait, signals, etc.
• You can use an “event” to represent any kind of message that
drives any kind of action in the receiving thread.
– In Android: intents, binder RPC, UI events
• But the system must be designed for it, so that operations the
thread requests do not block; the request returns immediately
(“asynchronous”) and delivers a completion event later.
Android: threading model
• An app is launched as a process when
any of its components is first instantiated.
• The process main thread is event-driven,
e.g., by User Interface (UI) events.
– Also called the “UI thread”.
– UI toolkit code is not thread-safe, so it
should execute only on the UI thread.
– UI thread should not block (except for next
event), or app becomes unresponsive.
• UI thread also receives incoming intents,
launches and tears down components,
and receives various upcalls.
• An app may spawn other background
threads (workers) for other uses.
• Binder RPC manages a thread pool.
events
Threads in Android
Three examples/models for use of threads in Android.
1. Main thread (UI thread): receives UI events and other upcall
events on a single incoming message queue. Illustrates eventdriven pattern: thread blocks only to wait for next event.
2. ThreadPool: an elastic pool of threads that handle incoming calls
from clients: Android supports “binder” request/response calls from
one application to another. When a request arrives, a thread from
the pool receives it, handles it, responds to it, and returns to the
pool to wait for the next request.
3. AsyncTask: the main thread can create an AsyncTask thread to
perform some long-running activity without blocking the UI thread.
The AsyncTask thread sends progress updates to the main thread
on its message queue.
These patterns are common in many other systems as well.
Adapted from
http://developer.android.com/guide/components/processes-and-threads.html
Summary: By default, all components of the same application run in the same process and
thread (called the "main" thread). The main thread controls the UI, so it is also called the
UI thread. If the UI thread blocks then the application stops responding to the user. You
can create additional background threads for operations that block, e.g., I/O, to avoid doing
those operations on the UI thread. The background threads can interact with the UI by
posting messages/tasks/events to the UI thread.
Details: When an application component starts and the application does not have any
other components running, the Android system starts a new Linux process for the
application with a single thread of execution called the main thread.
All components that run in the same process are initialized by its main thread, and system
upcalls to those components (onCreate, onBind, onStart,…) run on the main thread.
The main thread is also called the UI thread. It is in charge of dispatching events to user
interface widgets and interacting with elements of the Android UI toolkit. For instance,
when the user touches a button on the screen, your app's UI thread dispatches the touch
event to the widget, to set its pressed state and redraw itself.
If you have operations that might require blocking, e.g., to perform I/O like network
communication or database access, you should run them in separate threads. A thread
that is not the UI thread is called a background thread or "worker" thread.
Adapted from
http://developer.android.com/guide/components/processes-and-threads.html
Your app should never block the UI thread. When the UI thread is blocked, no events can
be dispatched, including drawing events. From the user's perspective, the application
appears to “hang” or “freeze”.
Even worse, if the app blocks the UI thread for more than a few seconds, Android presents
the user with the infamous "application not responding" (ANR) dialog. The user might
then decide to quit your application and uninstall it.
In a correct Android program the UI thread blocks only to wait for the next event, when it
has nothing else to do (it is idle). If you have an operation to perform that might block for
any other reason, then you should arrange for a background/worker thread to do it.
Additionally, the Android UI toolkit is not thread-safe: if multiple threads call a module
that is not thread-safe, then the process might crash. A correct app manipulates the user
interface only from a single thread, the UI thread. So: your app must not call UI widgets
from a worker thread.
So how can a worker thread interact with the UI, e.g., to post status updates?
offers several ways for a worker to post operations to run on the UI thread.
Android
Note: this concept of a single event-driven main/UI thread appears in other systems too.
Android: AsyncTask
[http://techtej.blogspot.com/2011/03/android-thread-constructs-part-3.html]
Summary/recap/segue
• Servers need concurrency. The standard model today
is multi-threaded servers with a thread pool. We can
implement it with our thread primitives.
• Clients need concurrency. It isn’t 1974 anymore, and
byte streams just aren’t enough. Modern apps have
GUIs and likely interact with one or more servers. And
they need to be responsive.
• Android apps “are servers”. They accept
upcalls/events from the GUI, from the OS, and from
other apps. So their threading models are similar to
servers.
• Android uses RPC to communicate across app
boundaries. RPC is an important structuring element in
modern distributed systems.
Remote Procedure Call (RPC)
• “RPC is a canonical structuring paradigm for client/server
request/response services.”
• Used in .NET, Android, RMI, distributed component frameworks
• First saw wide use in 1980s client/server systems for workstation
networks (e.g., Network File System).
client
server
Auto-generate this “stub”
code from API spec (IDL).
“glue”
[sockets]
Humans focus on
getting this code right.
[sockets]
This code is “canned”,
independent of the
specific application.
RPC
Remote Procedure Call (RPC) is request/response interaction
through a published API, using IPC messaging to cross an interprocess boundary.
API stubs generated from an Interface
Description Language (IDL)
Establishing an RPC connection
to a named remote interface is
often called binding.
RPC is used in many standard Internet services. It is also the basis for
component frameworks like DCOM, CORBA, and Android. Software is
packaged into named “objects” or components. Components may publish
interfaces and/or invoke published interfaces of other components.
Components may execute in different processes and/or on different nodes.
Threads and RPC
Q: How do we manage these
“call threads”?
A: Create them as needed,
and keep idle threads in a
thread pool.
When an RPC call arrives,
wake up an idle thread from
the pool to handle it.
On the client, the client thread
blocks until the server thread
returns a response.
[OpenGroup, late 1980s]
RPC: a classic picture
Implementing RPC
Birrell/Nelson 1984
RPC: Language integration
Stubs link with the client/server code to “hide” the boundary crossing.
– They “marshal” args/results
– i.e., translate to/from some standard
network stream format
– Also known as linearize, serialize
– …or “flatten”
– Propagate PL-level exceptions
– Stubs are auto-generated from an
Interface Description Language
(IDL) file by a stub compiler tool at
software build time, and linked in.
– Client and server must agree on the
protocol signatures in the IDL file.
Stubs
• RPC stubs are procedures linked into the client and server.
– RPC stubs are similar to system call stubs, but they do more than just
trap to the kernel.
– The RPC stubs construct/deconstruct a message transmitted through a
messaging system.
– Binder is an example of such a messaging system, implemented as a
Linux kernel plug-in module (a driver) and some user-space libraries.
• The stubs are generated by a tool that takes a description of the
application’s RPC API written in an Interface Description
Language.
– Looks like any interface definition…
– List of method names and argument/result types and signatures.
– Stub code marshals arguments into request message, marshals results
into a reply message.
Marshaling: a metaphor
Android Architecture and Binder
Dhinakaran Pandiyan
Saketh Paranjape
RPC Failure
Do RPC calls ever fail?
Yes. This is a key difference between RPC calls and local procedure calls. Complete
network transparency is neither achievable no desirable. If the client cannot reach the
server, you may want it to fail, so the app can decide if/how to report the problem to the
user, as opposed to (say) hanging forever. Generally, the problem of stalled network
communication is a major reason why thread systems provide thread alert APIs and
timeouts on CV wait.
RPC systems built over sockets (such as Java RMI) can be expected to reflect any socketlevel errors back to the process. For example, if a server process has failed (its port is no
longer bound), but its host is still up, then its host kernel rejects an incoming TCP
connection request with "connection refused”. The RPC client should report this and other
errors back to the app. For example, a socket connect request also fails if the network is
unreachable. Similarly, an established connection may timeout after a time if one end fails:
the RPC system may retry or it may report the error.
Timeouts are a configuration issue. Defaults should be reasonable, but they vary.
Binder: object-based RPC channels
Activity
Manager
Service
etc.
Services
register to
advertise for
clients.
JVM+lib
Bindings are
reference-counted.
A client binds
to a service.
JVM+lib
Android binder
an add-on kernel driver for
/dev/binder object RPC
Linux kernel
Android services and libraries communicate by sending messages
through shared-memory channels set up by binder.
Extra slides and resources
• Some useful reading on sockets and socket
programming, covering most of the ground in this deck:
– http://www.cs.dartmouth.edu/~campbell/cs50/socketprogramming.html
• For event-based concurrency, see OSTEP #33.
– http://pages.cs.wisc.edu/~remzi/OSTEP/threads-events.pdf
• For RPC, I recommend ch2 of Andy Tanenbaum’s text,
Distributed Systems: Principles and Paradigms.
– http://www.cs.vu.nl/~ast/books/ds1/02.pdf
Multi-process server architecture
• Each of P processes can execute one request at a time,
concurrently with other processes.
• If a process blocks, the other processes may still make
progress on other requests.
• Max # requests in service concurrently == P
• The processes may loop and handle multiple requests
serially, or can fork a process per request.
– Tradeoffs?
• Examples:
– inetd “internet daemon” for standard /etc/services
– Design pattern for (Web) servers: “prefork” a fixed number of
worker processes.
High-throughput servers
• Various server systems use various combinations
models for concurrency.
• Unix made some choices, and then more choices.
• These choices failed for networked servers, which
require effective concurrent handling of requests.
• They failed because they violate properties for “ideal”
event handling.
• There is a large body of work addressing the resulting
problems. Servers mostly work now. We skip over the
noise.
Concurrency/Asynchrony in Unix
Some partial answers and options
Shells face similar problems in tracking their children,
which execute independently (asynchronously).
1. Nonblocking (asynchronous) syscalls.
– Example: wait*(WNOHANG). But you have to keep asking to
know when a child changes state. (polling)
– What about starting asynchronous operations, like a read? How
to know when it is done without blocking?
2. Asynchronous notification messages (events).
– Signals? E.g., SIGCHLD: “Your child has died.”
– Interrupted syscalls w/ EINTR: “Look: something happened.”
3. Threads etc. (or multiple processes with
messaging)…but how to structure their interactions?
Download