26. 다중 입출력 - select()
'다중 입출력'은 프로그램(단일 스레드)에서 여러 개의 파일을 작업하고자 할 때 사용할 수 있는 메커니즘입니다. 사실 '단일 스레드'에서 여러 개의 파일을 작업하고자 할 때 사용할 수 있는 다른 방법으로는 '논블록(Non-Block) 입출력'을 사용하는 방법도 있긴 합니다만, '논블록(Non-Block) 입출력'은 프로그래밍 작업이 까다롭습니다. 이제부터 설명할 'select'는 '블록/비동기적 입출력'에서의 '다중 입출력' 모델입니다.
A, B, C, D 4개의 파일을 다루는 작업을 하고싶다고 가정해볼때 다음과 같은 시나리오를 생각해볼 수 있습니다.
- A 파일에 대한 작업을 합니다.
- A 파일에 대한 작업을 마칩니다.
- B 파일에 대한 작업을 합니다.
- B 파일에 대한 작업을 하다가 프로그램이 '블록(Block)' 상태에 빠집니다.
파일에 대한 작업 중 '블록(Block)' 상태가 되는 이유야 다양하겠지만 대표적으로 read() 함수를 호출했는데 읽을 파일이 없다면 프로그램은 읽을 내용이 생길 때까지 '블록(Block)'됩니다. 그럼 위의 상황에서 '블록' 없이 어떻게 4개의 파일에 대한 작업을 정상적으로 실행할 수 있을까요? 답은 간단합니다. A, B, C, D 파일을 순서대로 작업할게 아니라 작업할 준비가된 파일에 대해서만 작업을 하면됩니다. 이러한 생각이 시초가 되어 최초로 탄생한 함수가 'select' 시스템콜입니다.
#include <sys/select.h>
int select(int n,
fd_set* readfds,
fd_set* writefds,
fd_set* exceptfds,
struct timeval* timeout);
FD_CLR(int fd, fd_set* set);
FD_ISSET(int fd, fd_set* set);
FD_SET(int fd, fd_set* set);
FD_ZERO(fd_set* set);
select()
는 해당 파일 디스크립터가 입출력을 수행할 준비가 되거나 마지막 매개변수인 timeout
변수에 정해진 시간이 경과할 때까지만 '블록'됩니다. '감시 대상 파일 디스크립터'는 3가지 집합으로 나뉘어 각각 다른 '이벤트'를 기다립니다.
readfds
: '읽기'가 가능한지 감시합니다. (블록되지 않고 read() 작업이 가능한지)
writefds
: '쓰기'가 가능한지 감시합니다. (블록되지 않고 write() 작업이 가능한지)
exceptfds
: 예외가 발생했거나 대역을 넘어서는 데이터(소켓)가 존재하는지 감시합니다.
만약 select() 함수의 매개변수로 NULL을 넘기면 해당 이벤트는 감시하지 않습니다. select() 시스템콜의 예제를 살펴보겠습니다.
#include <stdio.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
#define TIMEOUT 5
#define BUF_LEN 1024
int main() {
struct timeval tv;
fd_set readfds;
int ret;
// 표준 입력에서 입력을 기다리기 위한 준비를 합니다.
FD_ZERO(&readfds);
FD_SET(STDIN_FILENO, &readfds);
// select가 5초 동안 기다리도록 timeval 구조체를 설정합니다.
tv.tv_sec = TIMEOUT;
tv.tv_usec = 0;
// select() 시스템콜을 이용해 입력을 기다립니다.
ret = select(STDIN_FILENO + 1, &readfds, NULL, NULL, &tv);
if (ret == -1) {
perror("select");
return 1;
}
else if (!ret){
printf("%d seconds elapsed.\n", TIMEOUT);
return 0;
}
// select() 시스템콜이 양수를 반환했다면 '블록(block)'없이 즉시 읽기가 가능합니다.
if (FD_ISSET(STDIN_FILENO, &readfds)) {
char buf[BUF_LEN + 1];
int len;
// '블록(block)'없이 읽기가 가능합니다.
len = read(STDIN_FILENO, buf, BUF_LEN);
if (len == -1) return 1;
if (len) {
buf[len] = '\0';
printf("read: %s\n", buf);
}
return 0;
}
}
'임베디드 > [ Linux Kernel ]' 카테고리의 다른 글
[ Linux Kernel ] 28. 가상 파일 시스템 (0) | 2020.11.12 |
---|---|
[ Linux Kernel ] 27. 다중 입출력 - poll() (0) | 2020.11.10 |
[ Linux Kernel ] 25. 파일 입출력 - open(), read(), write() (1) | 2020.11.10 |
[ Linux Kernel ] 24. 커널 프로그래밍에서 쉘 명령을 실행하는 방법 (0) | 2020.11.09 |
[ Linux Kernel ] 23. Platform Device & Driver (4) | 2020.11.05 |