I/O Multiplexing

hyoooooooooopark·2022년 7월 28일
0

I/O Multiplexing 왜 필요할까요?

입출력 관련된 함수들은 보통 블록킹/동기 방식으로 동작을 합니다.

블록킹 방식은 직관적이라는 장점이 있지만

두 개 이상의 파일을 처리할때 문제가 됩니다.

예를 들면 하나의 파일에서 블록킹이 되어 버리면
다른 파일은 블록킹이 끝나기를 기다리다가 영원히 읽지 못할 수도 있습니다.

블록킹/동기 방식을 유지하면서 이를 해결하는 방법으로는 멀티 프로세스와 멀티 쓰레드를 이용하는 방법이 있습니다.

이러한 방식은 파일에 프로세스 혹은 쓰레드를 할당하여 두 개 이상의 파일을 동시에 처리를 합니다. 간단하게 해결책으로 보이지만 다음과 같은 여러 이슈들을 고려해야합니다.

  1. 프로세스와 쓰레드 생성시 비용
  2. 프로세스와 쓰레드간의 통신
  3. 프로세스와 쓰레드 동기화

이러한 상황에서 I/O Multiplexing은 단일 프로세스에서 논블록킹/비동기를 응용하여 여러 파일을 제어할 수 있도록 합니다.

I/O Multiplexing 어떻게 하나요

I/O Multiplexing 여러 개의 파일에서 발생하는 입출력을 관리하는 기술입니다. I/O Multiplexing을 가능케 하는 함수로는 select, poll, epoll, kqueue 등이 있습니다.

select

입출력을 관리하고자하는 파일의 그룹을 fd_set이라는 파일 비트 배열에 넣고, 비트 배열의 값이 변했는지 확인하는 방식

int select (int nfds, fd_set *readfds, fd_set *writefds, 
    fd_set *exceptfds, struct timeval *timeout);

nfds : 관리하는 파일의 개수를 등록한다. 파일의 개수는 최대 파일 지정 번호 + 1로 지정하면 된다.

fd_set : 관리하는 파일의 지정번호가 등록되어 있는 비트 배열 구조체

  • readfds : 읽을 데이터가 있는지 검사하기 위한 파일 목록
  • writefds : 쓰여진 데이터가 있는지 검사하기 위한 파일 목록
  • exceptfds : 파일에 예외 사항들이 있는지 검사하기 위한 파일 목록

timeout : select함수는 fd_set에 등록된 파일들에 데이터 변경이 있는지를 timeout동안 기다린다. 만약 timeout시간동안 변경이 없다면 0을 반환 한다. timeout을 NULL로 하면, 데이터가 있을 때까지 무한정 기다리고, 멤버 값이 모두 0이면 즉시 반환한다.

epoll

select의 단점을 보완하여 사용할 수 있도록 만든 I/O통지 모델입니다. fd를 사용자가 아닌 커널이 관리하며, 이로인해 CPU는 fd 상태변화를 감시할 필요가 없어졌습니다.

select는 다음과 같은 단점이 존재했습니다.
1. 비트 테이블을 검사 비용
데이터 확인시 고정 비트 테이블을 검사함에 있어 비용 발생
2. 데이터복사 비용
데이터를 대기하다 데이터가 오면 fd_set을 갱신함(이전 fd_set정보를 잃어버리고 매번 fd_set을 복사)에 있어 비용 발생

이러한 단점들을 epoll은

  1. 이벤트가 발생한 파일의 목록을 반환
  2. 파일의 목록읠 순환하며 데이터 처리

를 통해 개선했습니다.

#include <sys/epoll.h>

int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event)

epfd : epoll_create로 생성한 epoll fd 값

op : 관찰 대상의 추가, 삭제, 변경 여부 옵션

fd : 등록할 fd

event : 관찰 대상의 이벤트 유형

kqueue

select를 개선한 epoll과 비슷하게 사용되고 동작합니다.
커널에 할당된 폴링공간에 모니터링할 이벤트를 등록하고, 발생한 이벤트를 return받아 Multiple I/O Event를 처리할 수 있도록 도와줍니다.

#include <sys/time.h>
#include <sys/event.h>
#include <sys/types.h>

int kqueue(void);

int kevent(int kq, const struct kevent *changelist, int nchanges, 
            struct kevent *eventlist, int nevents,
            const struct timespec *timeout);

kq : kqueue()로 만들어진 kqueue fd

changelist : kevent 구조체의 배열(changelist 배열에 저장된 kevent 구조체(이벤트)들은 kqueue에 등록됨)

nchanges : 등록할 이벤트의 개수

timeout : timespec 구조체의 포인터, NULL을 전달할 경우 이벤트 발생까지 block됩니다.

profile
슈뢰딩거의 개발 : 정답일 수도 아닐 수도 있습니다.

0개의 댓글