다른 명령
편집 요약 없음 |
|||
| 17번째 줄: | 17번째 줄: | ||
==Iterative Servers== | ==Iterative Servers== | ||
[[파일:Iterative server control flow.png|대체글=Figure 1. Iterative server control flow|섬네일|Figure 1. Iterative server control flow]] | |||
Iterative 서버(server)는 여러 클라이언트의 요청을 하나씩 순차적으로 처리하는 서버이다. 이러한 서버의 control flow는 figure 1에 잘 나타나있다. Figure 1은 다음과 같은 control flow를 설명하고 있다: | Iterative 서버(server)는 여러 클라이언트의 요청을 하나씩 순차적으로 처리하는 서버이다. 이러한 서버의 control flow는 figure 1에 잘 나타나있다. Figure 1은 다음과 같은 control flow를 설명하고 있다: | ||
# | # Client 1이 서버에 <code>connect()</code> 함수를 호출하여 연결을 요청하고, 서버가 이를 <code>accept()</code> 한다. | ||
# Client 1이 <code>write()</code> 함수를 호출하여 데이터를 서버로 전송한다. | |||
# 서버가 <code>read()</code> 함수를 통해 전송된 데이터를 읽고, 이를 처리하여 <code>write()</code> 함수를 통해서 client 1에게 데이터를 전송한다. | |||
# Client 1은 연결을 <code>close()</code>하고, 서버는 비로소 client 2의 연결 요청을 <code>accept()</code> 한다. | |||
이를 client 2 입장에서 control flow를 다시 살펴보면, 다음과 같다: | |||
# <code>connect()</code>: 서버는 클라이언트의 연결 요청을 listen backlog 큐에 저장하고, 이에 따라 <code>connect()</code> 함수는 즉시 반환된다. | |||
#* 클라이언트가 <code>connect()</code> 함수를 호출했을 때, 클라이언트는 SYN 패킷을 서버로 보내고, 서버는 이에 대한 응답으로 ACK 패킷을 보낸다. | |||
#* 클라이언트는 서버로부터 ACK 패킷을 받고, 최종 ACK 패킷을 다시 서버로 보내고 handshake를 완료한다. | |||
#* 이 상태에서 서버의 커널은 연결을 listen backlog 큐에 저장해두며, 서버가 나중에 <code>accept()</code> 함수를 호출했을 때 해당 큐에서 연결을 하나 꺼내와서 처리한다. | |||
#* 즉, 서버는 연결을 즉시 수락하지 않으며, 보류된 연결로서 큐에 저장한다. 다른 의미로 보면, <code>accept()</code> 함수는 이 큐에서 연결 하나를 꺼내는 함수이다. | |||
# <code>rio_writen()</code>: 데이터를 클라이언트 TCP 소켓 버퍼에 작성하고, 커널이 이를 서버로 전송힌다. | |||
#* 클라이언트의 I/O 동작은 서버의 상태와는 무관하게 동작하므로,<ref>즉, 서버가 데이터를 실제로 read() 했는지 여부는 확인하지 않는다.</ref> 해당 함수를 호출한 즉시 반환된다. | |||
# <code>rio_readlineb()</code>: 해당 함수는 서버가 연결을 실제로 accept() 하기 전까지 블로킹(blocking)된다. | |||
#* 이는 서버가 아직 client 1과 통신 중이기 때문에 client 2의 요청을 아직 처리하지 않았기<ref>서버는 아직 Client 1만 처리 중이므로, Client 2에 대해 accept()도 하지 않았고, 데이터를 읽지도 않은 상태이다.</ref> 때문이다. | |||
이러한 관점에서 볼 때, client 2는 매우 큰 불편함을 겪는다고 볼 수 있다. 또한 client 1이 서버와의 상호작용 도중 잠시 자리를 비운다면, 해당 서버는 그 동안 어떤 작업도 수행하지 않으므로 매우 큰 비효율성이 초래된다. 즉, 이에 대한 해결책이 필요하며, 그것이 바로 concurrent server이다. | |||
==Concurrent Programming with Processes== | |||
==각주== | ==각주== | ||
[[분류:컴퓨터 시스템]] | [[분류:컴퓨터 시스템]] | ||
2025년 5월 14일 (수) 03:56 판
상위 문서: 컴퓨터 시스템
개요
컴퓨터 공학에서, 동시성(concurrent)이란 control flow들이 시간상으로 겹치는 것을 의미한다. 이는 컴퓨터 시스텀의 여러 계층에서 나타나며, OS 커널이 여러 애플리케이션을 실행하기 위해 사용하는 메커니즘 중 하나이다. 하지만 concurrent라는 개념은 애플리케이션 수준에서도 적용되어 아래와 같이 사용될 수 있다:
- 느린 I/O 장치에 접근: I/O 장치는 상대적으로 느리게 실행되며, 이에 따라 CPU는 커널이 해당 작업을 수행하는 동안 다른 프로세스를 실행한다.
- 사람과의 상호 작용: 컴퓨터를 사용하는 사람들은 동시에 여러 작업을 수행하기를 원하며, 이를 지원하기 위해 concurrent 개념이 사용된다.
- 여러 네트워크 클라이언트 처리: Iterative server는 사실상 현실적이지 않은 서버이다. 이에 따라 concurrent 개념을 사용하여 각 클라이언트에 대해 별도의 control flow를 통해 처리할 수 있어야 한다.
- 멀티코어 머신에서의 병렬 계산: 요즘 최신 시스템은 여러 CPU로 구성된 멀티코어 프로세서를 사용한다. 여러 control flow를 활용하는 애플리케이션은 멀티코어 프로세서를 통해 더욱 빠르게 실행될 수 있는데, 이는 각각의 flow들이 교대로 실행되는 것이 아니라 병렬적으로 실행되기 때문이다.
애플리케이션 수준의 동시성을 사용하는 애플리케이션을 concurrent program이라고 하며, 현대의 OS는 이를 만들기 위해 세 가지 접근 방식을 기본적으로 제공한다:
- Process-based: 각각의 control flow를 커널이 스케쥴링하고 관리하는 하나의 프로세스로 다룬다.
- 이때 프로세스는 서로 다른 가상의 주소 공간을 가지므로, 서로 통신하기 위해서는 명시적인 프로세스 간 통신(IPC) 메커니즘을 사용해야 한다.
- Event-based: 애플리케이션이 하나의 프로세스 내에서 control flow들을 명시적으로 스케쥴링하는 방식이다.
- 이때 control flow는 FSM으로 모델링되며, 파일 디스크립터에서 데이터가 도착함에 따라 메인프로그램이 상태를 전이시키며, 이를 위해서 I/O multiplexing이라는 기술을 사용한다.
- 이 방식은 하나의 프로세스로 구성되어 있기 때문에 모든 flow들이 하나의 주소 공간을 공유한다.
- Thread-based: 해당 방식에서는 커널이 단일 프로세스 내에서 실행되는 thread들을 자동으로 관리한다.
- 이 방식은 process-based 방식과 같이 커널에 의해서 스케쥴링되면서, 동시에 event-based 방식과 같이 하나의 프로세스 내에서 같은 주소 공간을 공유한다는 점에서 하이브리드 방식이라고 볼 수 있다.
Iterative Servers
Iterative 서버(server)는 여러 클라이언트의 요청을 하나씩 순차적으로 처리하는 서버이다. 이러한 서버의 control flow는 figure 1에 잘 나타나있다. Figure 1은 다음과 같은 control flow를 설명하고 있다:
- Client 1이 서버에
connect()함수를 호출하여 연결을 요청하고, 서버가 이를accept()한다. - Client 1이
write()함수를 호출하여 데이터를 서버로 전송한다. - 서버가
read()함수를 통해 전송된 데이터를 읽고, 이를 처리하여write()함수를 통해서 client 1에게 데이터를 전송한다. - Client 1은 연결을
close()하고, 서버는 비로소 client 2의 연결 요청을accept()한다.
이를 client 2 입장에서 control flow를 다시 살펴보면, 다음과 같다:
connect(): 서버는 클라이언트의 연결 요청을 listen backlog 큐에 저장하고, 이에 따라connect()함수는 즉시 반환된다.- 클라이언트가
connect()함수를 호출했을 때, 클라이언트는 SYN 패킷을 서버로 보내고, 서버는 이에 대한 응답으로 ACK 패킷을 보낸다. - 클라이언트는 서버로부터 ACK 패킷을 받고, 최종 ACK 패킷을 다시 서버로 보내고 handshake를 완료한다.
- 이 상태에서 서버의 커널은 연결을 listen backlog 큐에 저장해두며, 서버가 나중에
accept()함수를 호출했을 때 해당 큐에서 연결을 하나 꺼내와서 처리한다. - 즉, 서버는 연결을 즉시 수락하지 않으며, 보류된 연결로서 큐에 저장한다. 다른 의미로 보면,
accept()함수는 이 큐에서 연결 하나를 꺼내는 함수이다.
- 클라이언트가
rio_writen(): 데이터를 클라이언트 TCP 소켓 버퍼에 작성하고, 커널이 이를 서버로 전송힌다.- 클라이언트의 I/O 동작은 서버의 상태와는 무관하게 동작하므로,[1] 해당 함수를 호출한 즉시 반환된다.
rio_readlineb(): 해당 함수는 서버가 연결을 실제로 accept() 하기 전까지 블로킹(blocking)된다.- 이는 서버가 아직 client 1과 통신 중이기 때문에 client 2의 요청을 아직 처리하지 않았기[2] 때문이다.
이러한 관점에서 볼 때, client 2는 매우 큰 불편함을 겪는다고 볼 수 있다. 또한 client 1이 서버와의 상호작용 도중 잠시 자리를 비운다면, 해당 서버는 그 동안 어떤 작업도 수행하지 않으므로 매우 큰 비효율성이 초래된다. 즉, 이에 대한 해결책이 필요하며, 그것이 바로 concurrent server이다.