## 개요
- 입출력 관련 함수는 기본적으로 동기적으로 작동.
- 여러 개의 파일을 동기적으로 처리하기 위해서는 멀티 프로세스나 멀티 스레드 필요
- 프로세스 간 통신, 세마포어, 뮤텍스 등을 다뤄야 하는데 단점이 많다.
- 입출력 다중화는 단일 프로세스에서 비동기적으로 여러 개의 파일을 처리한다.
## 입출력 다중화
### select
- 관찰할 file descriptor를 배열 구조체(fd_set)로 관리한다.
```c
int select( int maxfdNum, //파일 디스크립터의 관찰 범위 (0 ~ maxfdNum -1)
fd_set *restrict readfds, //read I/O를 통지받을 FD\_SET의 주소
fd_set *restrict writefds,//write I/O를 통지받을 FD\_SET의 주소
fd_set *restrict errorfds,//error I/O를 통지받을 FD\_SET의 주소
struct timeval *restrict timeout //주어진 시간만큼 대기후 timeout. null이면 변화가 있을 때까지 계속 block.
);
```
- 배열에 포함된 fd에 데이터 변경(read, write, error)이 발생하면 해당 배열에 1로 표시한다.

- 변경된 fd의 개수를 리턴한다.
#### 단점
- 한 프로세스의 최대 fd 개수가 보통 1024개로 제한되어 있는데, select를 사용하기 위해서는 프로세스에서 fd_set을 관리해야하므로 fd를 1024까지만 사용할 수 있다.
- 데이터 변경이 일어나면 fd_set의 모든 값을 전부 검사해야 한다.
- 프로세스가 fd의 변경을 알기 위해서는 커널에 select를 계속 호출해야 한다.
### epoll
- fd를 커널이 관리하기 때문에 프로세스가 fd의 변경을 감시할 필요가 없다.
- fd들에 대한 입출력 이벤트를 저장할 수 있는 공간을 만들어야 한다.
- size는 일반적으로 `예상 최대 동접 * 1.5` 정도로 한다고 한다.
```c
int epoll_create(int size); //size는 이벤트를 저장하기 위한 크기를 전달한다.
//반환 값 : 실패 시 -1, epoll에 등록된 fd를 조작할 수 있는 epoll_fd를 리턴.
```
- 감시할 fd와 감시할 이벤트 종류를 epoll에 등록해야 한다.
```c
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
// epfd는 epoll fd 값을 전달한다.
// op는 fd에 대해서 어떤 작업을 할 것인지(fd 등록, 수정, 삭제)를 전달한다.
// fd는 대상이 되는 fd 값을 전달한다.
// event는 fd를 등록할 때 어떤 이벤트를 감시할지에 대해 전달한다.
```
- 이벤트 발생하는 여부 및 결과는 `epoll_wait`로 확인할 수 있다.
```c
int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);
// epfd는 epoll fd 값을 전달한다.
// events는 이벤트가 발생한다면 기록할 구조체 배열을 전달한다.
// maxevents는 최대 몇 개의 event만 처리할 것인지 전달한다.
// timeout은 대기할 시간을 전달한다. 0보다 작으면 이벤트가 발생할 떄까지 기다린다.
// 이벤트가 발생한 개수를 리턴한다.
```
#### 단점
- 리눅스가 아닌 다른 unix에서는 사용할 수 없다.
## 참고
- https://www.joinc.co.kr/w/Site/system_programing/File/select
- https://www.joinc.co.kr/w/Site/Network_Programing/AdvancedComm/epoll24
- https://rammuking.tistory.com/entry/Epoll%EC%9D%98-%EA%B8%B0%EC%B4%88-%EA%B0%9C%EB%85%90-%EB%B0%8F-%EC%82%AC%EC%9A%A9-%EB%B0%A9%EB%B2%95
#os #io #select #epoll