Linux 기반 멀티프로세스 IPC 서버 (select 모델) #C #Linux #System-Programming #IPC #select
- 개요 (Overview) 이 프로젝트는 Linux 환경에서 멀티프로세스(Multi-process) 아키텍처와 비동기 I/O (Non-blocking I/O) 모델을 학습하고 구현한 C언어 기반의 IPC(프로세스 간 통신) 서버입니다.
부모 프로세스(서버)는 fork()를 통해 생성된 두 자식 프로세스(클라이언트)로부터 pipe를 통해 데이터를 수신합니다. select() 시스템 콜을 사용하여 여러 개의 I/O 채널을 동시에 감시하며, 한 클라이언트의 지연이 다른 클라이언트 처리에 영향을 주지 않는 효율적인 서버 모델을 구현했습니다.
- 주요 특징 (Key Features) 멀티프로세스 환경 구성: fork()를 이용해 서버-클라이언트 구조를 시뮬레이션하고, 각 프로세스의 역할을 분담하여 병렬적으로 작업을 수행합니다.
프로세스 간 통신 (IPC): pipe()를 사용하여 부모-자식 프로세스 간의 안정적인 단방향 데이터 통신 채널을 구축했습니다.
비동기 I/O 멀티플렉싱: select() 모델을 적용하여 여러 개의 파이프 입력을 블로킹(blocking) 없이 동시에 처리함으로써 서버의 반응성과 효율성을 높였습니다.
좀비 프로세스 방지: waitpid()와 WNOHANG 옵션을 사용하여 자식 프로세스의 종료 상태를 비동기적으로 확인하고, 리소스를 안전하게 회수(reap)하여 시스템 안정성을 확보했습니다.
안정적인 자원 관리: 프로세스별로 사용하지 않는 파이프의 파일 디스크립터(File Descriptor)를 명시적으로 close()하여 자원 누수를 방지했습니다.
- 시스템 아키텍처 (Architecture) +---------------------------+ | Parent Process (Server) | | - I/O Multiplexing | | - Data Aggregation | +-------------^-------------+ | (Data Flow via Pipes) | +-----------------+-----------------+ | | +--------v--------+ +--------v--------+ | Pipe 1 Read End | | Pipe 2 Read End | +-----------------+ +-----------------+ ^ ^ | (write) | (write) +--------+------------------+ +----------+----------------+ | Child Process 1 (Client) | | Child Process 2 (Client) | | - Auto Number Generator | | - User Input Receiver | | - Sends data every 1 sec | | - Exits on 'q' input | +---------------------------+ +---------------------------+
- 빌드 및 실행 방법 (Build & Run) 요구사항: gcc 컴파일러, Linux/macOS 등 POSIX 호환 운영체제
Bash
git clone https://github.com/your-username/ipc-select-server.git cd ipc-select-server
gcc main.c -o server_program
./server_program 실행 예시 (Execution Example) [메인] 프로그램 시작. PID: 23101 [서버] 서버 프로세스 시작. PID: 23101, 자식1 PID: 23102, 자식2 PID: 23103 [자식1] 자식 프로세스 1 시작. PID: 23102, 부모 PID: 23101 [자식2] 자식 프로세스 2 시작. PID: 23103, 부모 PID: 23101 [자식2] 숫자를 입력하세요 ('q' 입력 시 종료): [서버] 현재 연산 값: 0 [자식1] '-10'를 서버에게 전달했습니다. [서버] 자식1로부터 받은 숫자: -10 [서버] (변환된 숫자: -10, 현재 누적 합: -10) [서버] 현재 연산 값: 1 [자식2] 숫자를 입력하세요 ('q' 입력 시 종료): 123 [자식2] '123'를 서버에게 전달했습니다. [서버] 자식2로부터 받은 숫자: 123 [서버] (변환된 숫자: 123, 현재 누적 합: 113) [서버] 현재 연산 값: 2 ... 5. 핵심 코드 분석 (Technical Deep Dive) select()를 통한 I/O 멀티플렉싱 만약 서버가 단순히 read() 함수를 순차적으로 호출했다면, 첫 번째 클라이언트로부터 데이터가 올 때까지 무한정 기다리게 되어 두 번째 클라이언트의 요청은 처리되지 못하는 '블로킹' 문제가 발생합니다.
select()는 여러 파일 디스크립터(FD)를 동시에 감시하다가, 데이터 수신 등 변화가 생긴 FD만 알려줍니다. 이를 통해 서버는 대기 시간 없이 실제 작업이 필요한 클라이언트에만 반응할 수 있어 리소스를 효율적으로 사용하게 됩니다.
waitpid()를 통한 좀비 프로세스 관리 자식 프로세스가 먼저 종료되면, 부모 프로세스가 해당 종료 상태 정보를 읽어갈 때까지 커널에서 최소한의 정보(PID, 종료 상태 등)가 남아있는 '좀비 프로세스'가 됩니다. 좀비 프로세스가 많아지면 시스템의 PID 테이블이 가득 차 더 이상 새로운 프로세스를 생성하지 못할 수 있습니다.
이를 방지하기 위해 서버의 메인 루프에서 waitpid(-1, &status, WNOHANG)를 주기적으로 호출합니다. WNOHANG 옵션 덕분에 종료된 자식이 없더라도 부모 프로세스는 기다리지 않고, 종료된 자식이 있다면 해당 자원의 정보를 즉시 수거하여 좀비 상태를 방지합니다.
- 향후 개선 및 확장 계획 (Roadmap) 이 프로젝트는 시스템 프로그래밍의 기본 개념을 구현한 것이며, 다음과 같은 방향으로 확장하여 실제 서비스에 더 가까운 모델로 발전시킬 수 있습니다.
✅ epoll로의 전환: select는 감시할 FD의 수가 많아질수록 성능이 저하되는 단점이 있습니다. Linux 환경에서 훨씬 더 높은 확장성과 성능을 제공하는 epoll 모델로 전환하여 대규모 접속을 처리하는 서버로 개선할 계획입니다.
✅ IPC 방식 확장 (소켓 통신): 현재 pipe는 부모-자식 관계처럼 유전적 관계에 있는 프로세스 간 통신에 주로 사용됩니다. 이를 Unix Domain Socket이나 TCP/IP Socket으로 변경하여, 전혀 다른 프로세스나 원격 호스트와도 통신할 수 있는 범용적인 서버로 확장할 것입니다.
✅ 모듈화 및 Makefile 도입: 현재 단일 파일로 구성된 코드를 server.c, client_logic.c, common.h 등으로 모듈화하여 가독성과 유지보수성을 높이고, Makefile을 작성하여 빌드 과정을 자동화할 것입니다.
✅ 스레드 풀(Thread Pool) 적용: 클라이언트로부터 받은 데이터 처리 로직이 복잡해질 경우를 대비하여, I/O는 메인 스레드에서 전담하고 실제 연산은 스레드 풀에 위임하여 서버의 처리량을 극대화하는 모델을 도입할 수 있습니다.
- 라이선스 (License) This project is licensed under the MIT License.