Concurrent Programming with Processes
- client1에서 connect를 호출하여 connection request를 server에 전송한다.
- server는 accept한 순간에 바로 return하고 fork를 호출하여 자식 프로세스를 생성한다.
- fork를 호출하기 전에 client2에서 보낸 connection request가 왔다면 client2를 kernel의 TCP manager에 queue된다.
- client1의 request로 호출한 fork에서 자식 프로세스(child1)가 connectedfd로 client1과 통신한다.
- 부모 프로세스는 connection이 있다면 accept하여 fork를 하고 자식 프로세스(child2)에서 client2와 통신한다.
Process-Based Concurrent Echo Server
#include "csapp.h"
void echo(int connfd);
void sigchld_handler(int sig) {
while (waitpid(-1, 0, WNOHANG) > 0)
;
return;
}
int main(int argc, char **argv) {
int listenfd, connfd;
socklen_t clientlen;
struct sockaddr_storage clientaddr;
if (argc != 2) {
fprintf(stderr, "usage: %s <port>\n", argv[0]);
exit(0);
}
Signal(SIGCHLD, sigchld_handler);
listenfd = Open_listenfd(argv[1]);
while (1) {
clientlen = sizeof(struct sockaddr_storage);
connfd = Accept(listenfd, (SA *)&clientaddr, &clientlen);
if (Fork() == 0) {
Close(listenfd); /* Child closes its listening socket */
echo(connfd); /* Child services client */
Close(connfd); /* Child closes connection with client */
exit(0); /* Child exits */
}
Close(connfd); /* Parent closes connected socket (important!) */
}
}
- server는 일반적으로 오랜 시간 동안 실행되므로 zombie 프로세스를 reaping하는 SIGCHLD 핸들러를 포함해야 한다. SIGCHLD 핸들러는 실행되는 동안 SIGCHLD signal이 block되고 Linux signal가 대기열에 있지 않으므로 SIGCHLD 핸들러는 여러 zombie 프로세스가 reaping될 수 있도록 준비해야 한다.
- 부모 프로세스와 자식 프로세스는 각각 connfd를 닫아야 한다.
memory leak을 방지하기 위해 connected descriptor의 복사본을 닫아야 한다. - socket의 file table entry에 있는 reference count 때문에, connfd의 부모, 자식 copy가 닫힐 때까지 client에 대한 연결이 종료되지 않는다.
Concurrent Server: accept Illustrated
자식 프로세스는 listenfd를 close해야 하고
부모 프로세스는 connfd를 close 해야 한다.
부모 및 자식 프로세스에서 connected descriptor가 각각 동일한 file table entry를 가리키기 때문에 부모 프로세스에게 connected descriptor의 복사본을 close하는 것은 중요하다. 그렇지 않으면 connfd에 대한 file table entry가 release되지 않으며 결과적으로 memory leak가 사용 가능한 메모리를 소모하여 시스템이 중단된다.
Pros and Cons of Processes
프로세스에는 부모 및 자식 프로세스 간에 state 정보를 공유하기 위한 model이 있다.
file table은 공유되고 user address space는 공유되지 않는다.
프로세스를 위한 별도의 주소 공간을 갖는 것은 장점과 단점이다.
장점
한 프로세스가 실수로 다른 프로세스의 가상 메모리를 덮어쓰는 것은 불가능하여 이로 인한 오류가 없다.
descriptor 공유 안 함.
global variables 공유 안 함.
file table 공유함.
단점
별도의 주소 공간은 프로세스들이 state 정보를 공유하기 어렵게 만든다.
정보를 공유하려면 명시적 IPC 메커니즘을 사용해야 한다.
프로세스 제어 및 IPC에 대한 오버헤드가 높기 때문에 속도가 느린 경향이 있다.