Lec05 ConcurentServer
Lec05 ConcurentServer
SERVER
Tong Van Van, SoICT, HUST
1
Content
• I/O Models
• Concurrent TCP server: one child per client
• Concurrent TCP server: one thread per client
2
I/O MODELS
3
I/O Models
• blocking I/O
• nonblocking I/O
• I/O multiplexing (select and poll)
• signal driven I/O (SIGIO)
• asynchronous I/O (the POSIX aio_functions)
4
Blocking I/O Model
• Blocking I/O model: I/O function block process/thread until
returning.
• accept(), connect(), send(), recv(),…
5
Non-blocking I/O Model
• Non-blocking I/O model: I/O function returns immediately
• If there is no data to return, so the kernel immediately
returns an error of EWOULDBLOCK instead
6
I/O Multiplexing Model
• With I/O multiplexing, we call select or poll and block in
one of these two system calls, instead of blocking in the
actual I/O system call
7
Signal-Driven I/O Model
• Use signals, telling the kernel to notify app with the SIGIO
signal when the descriptor is ready
8
Asynchronous I/O Model
9
Asynchronous I/O Model (2)
10
Comparison of the I/O Models
11
Review TCP Echo Server
while(1){
//accept request
connfd = accept(listenfd, (sockaddr *) & clientAddr,
&clientAddrLen);
//receive message from client
rcvBytes = recv(connfd, buff, BUFF_SIZE, 0);
if(rcvBytes < 0){
perror("Error :");
}
else{
buff[rcvBytes] = ‘\0’;
printf("Receive from client: %s\n",buff);
//Echo to client
sendBytes = send(connfd, buff, strlen(buff), 0);
if(sendBytes < 0)
perror("Error: ",);
}
closesocket(connfd);
} //end while
12
Blocking I/O Model and TCP server
socket() TCP Server
TCP client
bind()
socket()
listen()
establish
connect() accept()
data Cannot
send()
recv() accept new
connection
Block until until the
data from current
client connection
recv() data closes.
send()
close()
close() 13
Iterating server
• Simple server
• But when a client request can take longer to service,
we can’t handle other clients
→Use a concurrent server
• One child per client: fork() a child process to
handle each client
• One thread per client: create a thread to handle each
client by using pthread_create()
14
MULTI-PROCESS SERVER
15
fork()
#include <unistd.h>
pid_t fork(void);
16
One child per client
17
One child per client
18
Use fork()
pid_t pid;
int listenfd, connfd;
//Step 1: Construct socket
//Step 2: Bind address to socket
//Step 3: Listen request from client
19
Handling SIGCHLD Signals
• When a child process ends, it sends the SIGCHLD signal
to the parent
• Information about the child process is still maintained in “process
table” in order to allow its parent to read the child exit status
afterward.
• If we ignore the SIGCHLD, the child process will enter the
zombie state
• We need to wait and handle SIGCHLD signal
20
Signaling
• A signal is a notification to a process that an event
has occurred.
• Signals are sometimes called software interrupts.
• Signals usually occur asynchronously. By this we
mean that a process doesn't know ahead of time
exactly when a signal will occur.
• Signals can be sent
• By one process to another process (or to itself)
• By the kernel to a process
21
Signal (cont.)
• Typing certain key combinations at the controlling terminal
of a running process causes the system to send it certain
signals:
• Ctrl-C sends an INT signal ("interrupt", SIGINT)
• Ctrl-Z sends a TSTP signal ("terminal stop", SIGTSTP)
• Ctrl-\ sends a QUIT signal (SIGQUIT)
• SIGHUP is sent to a process when its controlling terminal
is closed (a hangup)
• SIGTERM is sent to a process to request its termination.
• Unlike the SIGKILL signal, it can be caught and interpreted or
ignored by the process.
22
Handling SIGCHLD Signals
• The purpose of the zombie state is to maintain information
about the child for the parent to fetch at some later time.
• They take up space in the kernel and eventually we can
run out of processes
→Whenever we fork children, we must wait for them to
prevent them from becoming zombies → establish a
signal handler to catch SIGCHLD, and within the handler,
we call wait
→Establish the signal handler by adding the function call :
signal (SIGCHLD, handler);
23
wait() and waitpid()
#include <sys/wait.h>
pid_t wait (int *statloc);
pid_t waitpid (pid_t pid, int *statloc, int options);
25
waitpid()
26
waitpid()
pid_t waitpid (pid_t pid, int *statloc, int options);
• pid < 0: wait for any child process whose process group ID is
equal to the absolute value of pid.
• pid = -1: wait for any child process.
• pid = 0: wait for any child process whose process group ID is
equal to that of the calling process
• pid > 0: wait for the child whose process ID is equal to the
value of pid
• Without option WNOHANG, waitpid blocks until the status
change
• With option WNOHANG, waitpid returns immediately
• Return
• Pid of the child whose state has changed
• with option WNOHANG, return 0 if the specified process has not
changed status.
27
void sig_chld(int signo)
void sig_chld(int signo)
{
pid_t pid;
int stat;
pid = waitpid(-1, &stat, WNOHANG );
printf("child %d terminated\", pid);
}
28
Forking server
pid_t pid;
int listenfd, connfd;
//Step 1: Construct socket
//Step 2: Bind address to socket
//Step 3: Listen request from client
while(1)
if ((connfd = accept (listenfd...) < 0) {
if (errno == EINTR)
continue; /* back to for () */
else
perror ("Error: ");
}
30
Other problems
• Connection abort before accept return
• Termination of server process
• Crashing of sever host
• Crashing and Reboot of server host
31
MULTI-THREAD SERVER
32
pthread_create()
#include <pthread.h>
int pthread_create(pthread_t *tid, const pthread_attr_t *attr,
void *(*routine) (void *), void *arg);
34
Multi-thread TCP Echo Server
pthread_t tid;
int listenfd, connfd;
//Step 1: Construct socket
//Step 2: Bind address to socket
//Step 3: Listen request from client
35
Multi-thread TCP Echo Server(cont.)
void *client_handler(void *arg){
int clientfd;
int sendBytes, rcvBytes;
char buff[BUFF_SIZE];
pthread_detach(pthread_self());
clientfd = (int) arg;
while(1){
rcvBytes = recv(clientfd, buff, BUFF_SIZE, 0);
if (rcvBytes < 0){
perror("\nError: ");
break;
}
sendBytes = send(clientfd, buff, rcvBytes,0);
if (sendBytes < 0){
printf("\nError:");
break;
}
}
close(clientfd);
} 36
Synchronize threads
• Since multiple threads can be running concurrently,
accessing the shared variables:
• The order of the accessing shared memory is unpredictable, so
• The processing flow of the thread may be incontrollable, and/or
• The process crash
• Synchronize threads so that only one thread can access
shared meory:
• Inter-lock
• Semaphore
• Mutex
37
Mutex
#include <pthread.h>
int pthread_mutex_lock(pthread_mutex_t * mptr);
int pthread_mutex_unlock(pthread_mutex_t * mptr);