601.229 (F19): Final exam review problems
This page has final exam review problems on network communication and threads.
Problem 1)
What system call(s) would be used to create a server socket?
socket
: creates the socket
bind
: associates the socket with a network address (to determine which network interface(s) the server will listen on)
listen
: makes the socket a server socket so it can accept incoming connections
What system call(s) would be used by a server to wait for a network connection from a client?
accept
: uses a server socket to wait for an incoming client connection
What system call(s) would be used by a client to initiate a connection to a server?
socket
: creates the socket
connect
: initiates connection to specified server/port
What system call would be used to read data from a socket?
read
or recv
What system call would be used to write data to a socket?
write
or send
Briefly explain why IP is an unreliable protocol, in the sense that an IP datagram sent might not reach its destination.
When datagrams are sent to a destination on the network, they must be forwarded via intermediate routers. Each router is forwarding datagrams from many communication streams to outgoing, fixed-capacity network links. Since the links are fixed-capacity, datagrams may need to be queued until capacity on the appropriate outgoing link is available. These queues are fixed-size, so if a datagram arrives and the queue associated with the appropriate outgoing link is full, the datagram is discarded (dropped.)
Briefly explain why a TCP connection might be abruptly terminated without without either peer explicitly closing the connection.
TCP requires explicit acknowledgment for all data sent to the remote peer. If the acknowledgment is delayed for too long, the OS will assume that the host has become unreachable and terminate the connection. Because IP is unreliable, this can happen even if neither the sender nor the receiver have explicitly closed the connection.
Problem 2)
State one advantage and one disadvantage of using processes for concurrency.
Advantages: relatively easy to create, can inherit open files from parent, each process is isolated from other processes.
Disadvantages: heavyweight, more time to create and destroy, more time to context-switch between.
State one advantage and one disadvantage of using threads for concurrency.
Advantages: relatively lightweight, can easily share resources because they run in a common process context and address space.
Disadvantages: communication between threads using shared data requries synchronization.
Briefly explain why server programs typically require some form of concurrency in their implementation.
In general many clients can be connected to a server simultaneously, so some form of concurrency is needed to handle communication with clients.
Problem 3)
The following queue implementation uses a singly-linked list
as the underlying data structure. Assume that we would like to
allow multiple threads to use a single Queue
instance by
calling the q_enqueue
and q_dequeue
functions. Show what
modifications need to be made to the data types and/or functions
in order to allow multiple threads to enqueue and dequeue items
safely.
Note: you may assume that q_destroy
will only be called once
there are no longer any threads using the Queue
.
[See the questions for the original code.]
typedef struct Node_ {
int val;
struct Node_ *next;
} Node;
typedef struct {
Node *head, *tail;
pthread_mutex_t lock;
} Queue;
Queue *q_create(void) {
Queue *q = malloc(sizeof(Queue));
q->head = q->tail = NULL;
pthread_mutex_init(&q->lock, NULL);
return q;
}
void q_destroy(Queue *q) {
Node *n = q->head;
while (n != NULL) { /* delete remaining nodes */
Node *d = n;
n = n->next;
free(d);
}
pthread_mutex_destroy(&q->lock);
free(q);
}
void q_enqueue(Queue *q, int val) {
Node *n = malloc(sizeof(Node));
n->val = val;
n->next = NULL;
pthread_mutex_lock(&q->lock);
if (q->tail == NULL) {
q->head = q->tail = n; /* add to empty queue */
} else {
q->tail->next = n;
q->tail = n;
}
pthread_mutex_unlock(&q->lock);
}
/* Returns -1 if queue is empty */
int q_dequeue(Queue *q) {
int result;
pthread_mutex_lock(&q->lock);
if (q->head == NULL) {
result = -1;
} else {
Node *n = q->head;
result = n->val;
q->head = n->next;
if (q->head == NULL) {
q->tail = NULL; /* queue became empty */
}
free(n);
}
pthread_mutex_unlock(&q->lock);
return result;
}
Problem 4)
In a computation, 90% of the computation can be parallelized perfectly (using as many processors as are available), while 10% of the computation is inherently sequential, and can only be executed on a single processor.
What is the best speedup we can expect from parallelizing this computation? Note that a speedup of 5 means that the parallel computation is 5 times faster (completes in 1/5 the time) compared to a sequential computation.
Let ts be the running time of the sequential program.
Running time of parallel program (tp):
tp = 0.1 × ts + (0.9 × ts / P)
A P increases, (ts / P) approaches 0, so with a large number of processors, tp approaches
0.1 × ts
Speedup (S) is ts / tp, which as P → ∞ becomes
S = ts / (0.1 × ts) = 10
So, the largest speedup for this computation, assuming that the proportion of sequential vs. parallel computation is fixed regardless of problems, size, is 10.
Note that assuming that the proportion of inherently sequential computation is independent of problem size is not, in general, a good assumption.