Multiplexed I/O
Multiplexed I/O는 하나의 프로세스가 여러 개의 I/O 작업(파일, 소켓, 디바이스 등)을 동시에 처리할 수 있도록 하는 기술이다. 이를 통해 비동기적으로 여러 데이터 소스를 효율적으로 관리할 수 있다.
단일 프로세스는 1개 이상의 파일 디스크립터(fd)를 동시에 블로킹할 수 없다. 따라서 read() 함수가 호출될 경우, 해당 파일에 데이터가 없으면 다음과 같은 동작을 수행한다.
- 일반 파일: read()는 0을 반환하며, 이는 EOF(End of File) 상태를 의미한다.
- 파이프, 소켓, FIFO: 기본적으로 블로킹 동작을 수행하며, 데이터가 준비될 때까지 대기하므로 다른 fd에 대한 처리가 불가능하다.
이러한 한계를 극복하기 위해 I/O Multiplexing이 사용된다.
I/O Multiplexing (다중화된 I/O)
I/O Multiplexing은 여러 개의 fd를 모니터링하여, I/O가 가능한 fd가 있을 때 이를 알려주는 방식이다. 이를 통해 다중 I/O 처리를 효율적으로 수행할 수 있다.
동작 방식
- Multiplexed I/O는 I/O가 준비된 fd가 생기면 이를 감지한다.
- 하나 이상의 fd가 준비될 때까지 프로세스는 대기(sleep)한다.
- 특정 fd가 준비되면 프로세스는 깨어난다(woken up).
- 준비된 fd에 대한 I/O 작업을 수행하며 블로킹되지 않는다.
- 다시 1단계로 돌아가서 I/O가 준비된 fd를 기다린다.
FD Macros (파일 디스크립터 매크로)
I/O Multiplexing에서는 fd_set을 사용하여 여러 개의 파일 디스크립터를 다룬다. 이를 위한 매크로는 다음과 같다.
- FD_CLR(int fd, fd_set *set): 특정 파일 디스크립터를 fd_set에서 제거한다.
- FD_ISSET(int fd, fd_set *set): 특정 파일 디스크립터가 fd_set에 포함되어 있는지 확인한다.
- FD_SET(int fd, fd_set *set): 특정 파일 디스크립터를 fd_set에 추가한다.
- FD_ZERO(fd_set *set): fd_set을 초기화한다.
Select 시스템 호출
int select(int n, // 모든 fd_set 중에서 가장 큰 fd + 1
fd_set *readfds,
fd_set *writefds,
fd_set *exceptfds,
struct timeval *timeout); // 시간 정보 sec, usec
select()는 주어진 fd가 I/O 준비 상태가 될 때까지 또는 지정된 timeout 시간이 지나기 전까지 블로킹된다.
timeout 매개변수
- NULL로 설정하면 무한정 대기한다.
- fd가 미리 준비되면 timeout 값이 변경된다.
반환값
- -1 : 오류 발생
- 0 : timeout 만료
- 양수 : 준비된 fd 개수 반환
예제 1: STDIN이 준비될 때까지 대기
int main(void){
fd_set readfds;
int ret;
FD_ZERO(&readfds);
FD_SET(0, &readfds); // {STDIN} 한 개 포함
ret = select(1, &readfds, NULL, NULL, NULL); // 사용자가 입력할 때까지 대기
if(ret == -1){
printf("select error\n");
exit(1);
}
if(FD_ISSET(0, &readfds)){
char buf[1024];
int len;
len = read(STDIN_FILENO, buf, sizeof(buf));
if(len == -1){
perror("read");
exit(1);
}
buf[len] = '\0';
printf("read: %s\n", buf);
}
return 0;
}
예제 2: STDIN과 STDOUT이 준비될 때까지 대기
#define BUFSIZE 1024
int main(void){
fd_set readfds, writefds;
int ret;
FD_ZERO(&readfds);
FD_ZERO(&writefds);
FD_SET(0, &readfds); // STDIN
FD_SET(1, &writefds); // STDOUT
ret = select(STDOUT_FILENO + 1, &readfds, &writefds, NULL, NULL);
if(ret == -1){
printf("select error\n");
exit(1);
}
printf("fd 0: %d\n", FD_ISSET(0, &readfds));
printf("fd 1: %d\n", FD_ISSET(1, &writefds));
return 0;
}
출력 예시
fd 0: 0
fd 1: 1
STDIN은 준비되지 않았으며, STDOUT은 준비된 상태임을 알 수 있다.
Pselect (POSIX)
pselect()는 select()와 유사하지만, 다음과 같은 차이점이 있다.
- timeval 대신 timespec을 사용한다.
- sigmask를 이용하여 신호를 블로킹할 수 있다.
int pselect(int n,
fd_set *readfds,
fd_set *writefds,
fd_set *exceptfds,
const struct timespec *timeout, // sec, nano
const sigset_t *sigmask); // 블로킹할 신호 집합
결론
Multiplexed I/O는 단일 프로세스가 여러 개의 파일 디스크립터를 동시에 감시하고, 준비된 fd가 있을 경우 효율적으로 처리할 수 있도록 한다. select() 및 pselect()를 활용하여 I/O 작업을 최적화할 수 있으며, 이를 통해 서버 소켓 및 이벤트 기반 프로그래밍에 유용하게 활용할 수 있다.
'CS 지식 > 시스템 프로그래밍' 카테고리의 다른 글
[C언어] 리눅스 Time과 Clock의 종류와 POSIX Clocks (0) | 2025.03.02 |
---|---|
[C언어] 메모리 매핑 입출력(Memory Mapped I/O) 관련 시스템 콜(System call) (0) | 2025.03.02 |
[C언어] File I/O (Unix vs Standard IO)관련 시스템 콜(System call) (0) | 2025.03.02 |
[C언어] 리눅스 파일 시스템과 file table의 구조(inode, offset등) (0) | 2025.03.02 |
[C언어] pthread 라이브러리를 이용해서 스레드에게 signal 보내기 (0) | 2025.01.28 |
댓글