단일 thread 프로그램에서, 동시에 여러가지 입출력 장치를 처리하고자 할 때 사용하는 매커니즘 이다. Multiplexed I/O 다중 file descriptor 들을 동시에 block 되도록 한다. Block 되어 있는 fd들중에 하나가 읽기나 쓰기 준비가 되면 block 상태에서 해제되는 신호를 보낸다. Multiplexed I/O는 다음과 같은 방식으로 동작한다.
1. file descriptor 들 중에서 하나가 입출력 준비 되었을 때 알려줌.
2. 하나 이상의 file descriptor가 준비될 때 까지 sleep 함
3. blocking 없이, 입출력 준비된 모든 file descriptor들을 제어함
5. 단계 1로 되돌아 가서, 다시 시작함.
select() 함수의 경우, Multiplexed I/O 의 한가지 종류이다.
주어진 file descriptor가 입출력을 수행할 준비가 될 때까지, 혹은 선택적으로 주어진 timeout이 경과할 때 까지 select() 는 block 된다.
fd(file discriptor) 란 4 byte 정수로 된 , 파일 또는 장치의 고유한 식별 번호이다. 이 값은 같은 프로그램 내에서 고유한 값을 가진다.
fd_set 이란 아래와 같이 선언된 구조체 이며 fd를 그룹 짓기 위해서 사용됩니다. fd-set 구조체는 최대 1024개의 fd를 그룹 지을 수 있도록 만들어져 있고, 각 fd 값은 비트 단위로 나누어서 저장되기 때문에 4바이트 크기의 정수가 32개만 있으면 모두 저장할 수 있다는 뜻이다.
#defien __FD_SETSIZE 1024 //fd_set 에서 groupping 할 수 있는 fd의 최대 개수
typedef long int __fd_mask; //long int = 4byte ,
#define __NFDBITS (8 * (int) sizeof (__fd_mask)) //32 bytes
typedef struct
{
__fd_mask fds_bits[__FD_SETSIZE / __NFDBITS];
} fd_set;
아래는 표준입력장치(키보드) 1개에 대한 데이터 입력 작업을 수행한다.
#include <stdio.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
#define TIMEOUT 5
#define BUF_LEN 1024
int main (void)
{
struct timeval tv;
//(1) fd_set을 하나 정의 한다.
fd_set readfds; //fd_set 은 4 * 23 bytes 의 크기를 가진다.
int ret;
//(2) fd_set을 초기화 한다.
FD_ZERO (&readfds); //readfds 의 4*32 bytes 의 모든 bit 값을 0으로 만든다.
//(3) fd_set 에 fd를 등록한다. 여기서는 Standared Input fd가 등록됨
FD_SET (STDIN_FILENO, &readfds); //STDIN_FILENO fd를 readfds fd_set 에 설정
tv.tv_sec = TIMEOUT; //timeout 을 5초로 설정한다.
tv.tv_usec = 0;
//(4) fd_set 감시를 시작함. Timeout은 5초로 설정
ret = select (STDIN_FILENO + 1, &readfds, NULL, NULL, &tv);
if(ret == -1) { //error 발생시
printf("select(): error\n");
return 1;
} else if (!ret) { //timeout 될 동한 아무런 event가 발생하지 않았다면
printf("%d seconds elapsed. \n", TIMEOUT);
return 0;
}
//(5) 해당 fd_set의 내용이 변경 되었다면. FD_ISSUE() 는 true를 반환함
if (FD_ISSET (STDIN_FILENO, &readfds)) {
char buf[BUF_LEN+1];
int len;
//STDIN_FILENO 으로 부터 변경된 값을 읽어와서 BUFFER 에 저장한다.
len = read (STDIN_FILENO, buf, BUF_LEN);
if(len==-1) {
printf("read(): error\n");
return 1;
}
if (len) {
buf[len] = '\0';
printf("read: %s\n", buf); //Buf를 출력한다.
}
return 0;
}
fprintf(stderr, "This should not happen!\n");
return 1;
}
표준 입력/출력/에러 상수 종류 ( in unistd.h )
STDIN_FILENO - default standard input file descriptor number which is 0
STDOUT_FILENO
STDRERR_FILENO
int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);
ndfs : 감시할 fd의 갯수 인데, 마지막 fd 번호 + 1 을 지정해 준다. (fd_set은 배열인데 배열의 index는 0부터 시작하므로 +1을 해준다.)
readfds : 읽을 데이터가 있는지 감시하는 파일의 집합
writefds : 파일에 데이터를 쓸 수 있는지 검사하기 위한 파일 집합
exceptfds : 파일의 예외 사항이 있는지 검사하기 위한 파일 집합
timeout : 데이터 변화를 감지하기 위해서 사용하는 time out 값. 예를 들어 readfds에 지정된 시간동안 읽을 데이터가 없다면 select()는 0을 반환. 만약 timeout NULL로 지정하게 되면 무기한으로 대기한다.
return value :
성공시 fd 개수
timeout 시 0
error 시 -1
아래는 표준 입력장치와 출력장치 2개에 대해서 입출력을 수행해 본다.
아래 코드를 실행해 보면 표준출력장치인 화면은 항상 출력 대기 상태 이므로 "write(STDOUT)" 은 항상 출력된다. 키보드로부터 문자열을 입력하면 입력 데이터를 읽어서 화면에 데이터를 출력한다.
#include <stdio.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/select.h>
#include <string.h>
#define TIMEOUT 5
#define BUF_LEN 1024
int main (void)
{
struct timeval tv;
fd_set readfds; //read fd_set 선언
fd_set writefds; //write fd_set 선언
int ret;
char buf[BUF_LEN+1];
int len;
_RETRY:
memset(buf, 0, sizeof(buf));
FD_ZERO(&readfds); // 초기화
FD_ZERO (&writefds); // 초기화
FD_SET (STDIN_FILENO, &readfds); // STDIN_FILENO 를 readfds에 등록
FD_SET (STDOUT_FILENO, &writefds); // STDOUT_FILENO 를 writefds에 등록
tv.tv_sec = TIMEOUT;
tv.tv_usec = 0;
ret = select (STDOUT_FILENO+1, &readfds, &writefds, NULL, &tv);
if(ret == -1) {
printf ("select(): error \n");
return 1;
}
else if (!ret) {
printf ("%d seconds elapssed. \n", TIMEOUT);
return 0;
}
if (FD_ISSET (STDIN_FILENO, &readfds))
{
len = read (STDIN_FILENO, buf, BUF_LEN);
if (len == -1) {
printf ("read(): error\n");
return 1;
}
if (len) {
buf[len] = '\0';
printf ("read(STDIN): %d: %s\n", len, buf);
}
}
if (FD_ISSET (STDOUT_FILENO, &writefds))
{
len = write (STDOUT_FILENO, buf, sizeof(buf));
if (len == -1) {
printf ("write(): error\n");
return 1;
}
if (len) {
printf ("write(STDOUT): %d: %s\n", len, buf);
}
}
sleep(2);
goto _RETRY;
fprintf( stderr, "This should not happen!\n");
return 1;
}
'Linux_system' 카테고리의 다른 글
c 에서 pthread 를 사용하여 shell script 실행 (0) | 2022.12.06 |
---|---|
Multiplexed I/O - poll() (0) | 2022.09.18 |
QT (0) | 2022.09.07 |
POSIX.1 interfaces (0) | 2022.08.23 |
netplan 고정 IP 설정시, 서브넷 마스크 작성법 (0) | 2022.08.19 |