웹 서버 구현기 3-0: 멀티 프로세싱

Plato·2023년 6월 11일
0

웹서버 구현기

목록 보기
11/11

서론

운영체제는 프로세스를 실행 단위로 사용한다. 프로세스는 독립적인 메모리 공간을 갖는데, 왜 웹서버를 멀티 프로세스로 구현하는 경우가 있을까? 그것은 소켓을 통해 데이터를 수/송신할 때 사용하는 read/write 함수가 블로킹 함수이기 때문이다. 모든 경우에서 블로킹이 일어나는 것은 아니고 특정 종류의 파일 디스크립터를 통해 read/write할 때 버퍼가 비어있거나 가득차면 발생한다. 이번 글을 통해 read/write 블로킹과 프로세스를 복사하여 멀티 프로세스로 동작하는 프로그램의 구현 방법에 대해 알아보자.

본론

파일 디스크립터 종류

파일 디스크립터는 크게 "빠른 파일 디스크립터"와 "느린 파일 디스크립터"로 나눌 수 있다. 빠른 파일 디스크립터와 느린 파일 디스크립터의 결정적인 차이는 블로킹 여부이다. 빠른 파일 디스크립터를 read하거나 디스크립터에 write할 때 블로킹이 일어나지 않고 느린 파일 디스크립터를 read하거나 디스크립터에 write할 때 블로킹이 일어날 수 있다는 차이가 있다. 일반적인 파일을 나타내는 디스크립터는 빠른 디스크립터이다. 즉 우리가 touch 명령을 통해 생성한 파일을 read/write할 때 블로킹이 일어날 수 없다. 하지만 파이프나 소켓과 같은 파일 디스크립터는 느린 디스크립터이고 블로킹이 일어날 수 있다.

블로킹이 일어나는 경우

상대에게 수신한 데이터가 없는데 read를 하거나 출력 버퍼가 가득찼는데 write를 하면 블로킹이 발생한다. 수신한 데이터가 없을 때 read를 하면 상대로부터 데이터를 수신할 때까지 블로킹되고 출력 버퍼가 가득차있는데 write한 경우 출력 버퍼에 빈 공간이 생길 때 까지 블로킹된다.

병렬적인 서버 구현

블로킹이 일어나기 때문에 서버를 단순히 구현하면 병렬적으로 병렬적으로 여러 클라이언트와 통신하지 못한다. 여러 방법을 사용하여 구현할 수 있는데 크게 세 가지가 있다.

  1. select, poll, epoll을 통해 여러 파일 디스크립터의 변화가 있을 때 반응하는 서버를 구현한다.
  2. 여러 스레드를 사용하는 서버를 구현한다.
  3. 여러 프로세스를 사용하는 서버를 구현한다.

이 글에서는 여러 프로세스를 사용하는 프로그램을 구현하는 방법을 알아볼 것이다.

멀티 프로세싱

프로세스는 fork를 통해 복사할 수 있고 부모/자식 여부는 fork 함수의 반환 값을 통해 판단할 수 있다. 이를 통해 부모와 자식이 다른 로직을 수행하도록 구현할 수 있다.

좀비 프로세스

자식 프로세스는 실행이 완료되면 혹은 exit 함수가 호출되면 운영체제에 값을 반환한다. 이 때, 부모 프로세스가 자식 프로세스의 반환 값을 읽어들일 때까지 프로세스 테이블에서 자식 프로세스는 지워지지 않는다. 어떻게 부모 프로세스가 자식 프로세스의 반환 값을 읽을 수 있을까? 두 개의 함수를 통해 이를 할 수 있다.

wait

wait 함수는 자식 프로세스 중에 값을 반환한 프로세스가 있을 때 까지 대기하는 함수이다. 이 함수는 자식 프로세스의 상태에 대한 정보를 인자 포인터를 통해 부모 프로세스에게 전달한다.

waitpid

waitpid는 특정 자식 프로세스가 값을 반환할때까지 부모 프로세스의 흐름을 블로킹한다. wait과는 다르게 WNOHANG 옵션을 줘서 블로킹하지 않도록 만들 수 있다. 종료된 자식 프로세스가 존재하지 않을 때, waitpid가 0을 반환한다.

시그널 핸들링

wait을 무분별하게 사용하면 블로킹이 일어나서 부모 프로세스가 정상적으로 기능할 수 없고 waitpid의 경우도 WNOHANG 옵션을 주더라도 반복적으로 waitpid를 호출해야한다는 문제를 갖고있다. 이러한 문제는 시그널 핸들링을 통해 해결할 수 있는데 자식 프로세스의 종료를 나타내는 시그널이 존재하기 때문이다.

0개의 댓글