Transcript 14. 입출력 다중화
14. 입출력 다중화 김진홍 [email protected] 2015.12.04. I/O 모델 • 구분 • I/O 모드 • Blocking I/O • Non-Blockin I/O • I/O 이벤트 통지 모델 • 동기 • 비동기 2/36 I/O 모델 • Blocking I/O • 유저레벨에서 커널에 I/O를 요청(그림) • 커널레벨에서 I/O를 수행 • 단점 • I/O 작업이 진행되는 동안 유저레벨은 결과를 대기함 • • I/O 리소스 낭비가 심함 서버를 Blocking I/O로 구현 할 경우 • 하나의 클라이언트당 쓰레드를 만듬 • 쓰레드 증가에 따른 CPU의 컨텍스트 스위칭이 증가함 • 비효율적임 3/36 I/O 모델 • Non-Blocking I/O(select(), poll(), epoll()) • I/O를 요청하면 바로 결과를 반환 • I/O를 진행 하는 동안 유저 프로세스의 작업을 중단시키지 않음 • 호출 • 유저 프로세스가 커널에게 데이터를 받아오고 싶다고 요청 • recvBuffer에 데이터가 없으면 EWOULDBLOCK • recvBuffer에 데이터가 있으면 버퍼로부터 데이터 복사 • 단점 • 리소스 남용 • 버퍼가 준비되었는지 recv를 통해 확인해야함 • 해결 • 버퍼를 체크할 수 있는 방법을 구현하여 추가 함 4/36 I/O 이벤트 통지 모델 • 개요 • I/O 이벤트 통지 모델의 대화주체 • 커널과 프로세스 • 프로세스는 커널에게 I/O 처리를 요청함 • 커널은 프로세스에게 I/O 상황을 통지함 • I/O 이벤트 통지 모델은 Non-Blocking의 문제점을 해결하기 위해 고안 됨 • 입력 버퍼에 데이터가 수신되어 USER로 전송 가능하거나 • 출력 버퍼가 비어서 데이터의 Buffer로 전송 가능하거나 • 구분 • 커널이 프로세스에게 어떤 방식으로 통지하는가? • 동기형 통지 모델 • 비동기형 통지 모델 5/36 I/O 이벤트 통지 모델 • 동기형 통지 모델 • 프로세스가 커널에게 지속적으로 현재 I/O 준비 상황을 체크 • 커널이 준비되었는지 계속 확인하여 동기화 • 유저 프로세스가 적극적으로 확인함 • 커널은 수동적으로 현재 상황을 보고함 • 비동기형 통지 모델 • 커널에게 I/O 작업을 맡김 • 프로세스는 커널의 진행 사항을 인지 할 필요 없음 • 유저의 프로세스가 I/O 동기화를 신경 쓸 필요가 없음 • 유저 프로세스는 수동적 • 커널은 적극적 6/36 Select() • 개요 • 싱글 쓰레드로 다중 I/O를 처리하는 멀티플렉싱 통지모델의 대표적인 방법 • 프로세스가 커널에게 상황 체크를 요청하는 동기형 통지 방식 • 호출시 timeout에 따라 non-blocking 또는 blocking 형태가 됨 • 특징 • 파일 디스크립터가 I/O 준비가 되었는지를 알 수 있다면 • 해당 파일 디스크립터가 할당받은 커널 Buffer에 데이터를 복사 해가면 됨 파일 디스크립터의 상황을 파악할 수 있어야 함 • int select( int maxfdNum, //파일 디스크립터의 관찰 범위 (0 ~ maxfdNum -1) fd_set *restrict readfds, //read I/O를 통지받을 FD_SET의 주소, 없으면 NULL fd_set *restrict writefds,//write I/O를 통지받을 FD_SET의 주소, 없으면 NULL fd_set *restrict errorfds,//error I/O를 통지받을 FD_SET의 주소, 없으면 NULL struct timeval *restrict timeout //null이면 변화가 있을 때까지 계속 Block, //아니면 주어진 시간만큼 대기후 timeout. ); //반환값 : 오류 발생시 -1, timeout에 의한 반환은 0, 정상 작동일때 변경된 파일 디스크립터 개수 • 한계 • FD_SET 구조체를 사용하여 유저에게 파일 디스크립터의 상황을 알려줌 모든 파일 디스크립터에 대해 FD_ISSET 으로 체크하는 불필요한 검사 과정 • 일반적으로 검사할 수 있는 fd 개수가 최대 1024개로 제한 됨 7/36 epoll • 개요 • select의 단점을 보완하여 만든 I/O 통지 기법 • 동작 구조는 select와 크게 다르지 않음 • 프로세스가 커널에게 상황 체크를 요청하는 동기형 통지 방식 • 호출시 timeout에 따라 non-blocking 또는 blocking 형태가 됨 • 개선사항 • 커널에게 정보를 요청하는 함수(select)를 호출시 발생하는 비효율 부분 • 전체 관찰 대상 파일 디스크립터에 대한 정보를 넘김 • 어떻게? • 관찰 대상 fd의 정보를 담은 저장소를 OS가 직접 담당 • 특정 fd에 대한 정보(epoll_fd) 를 유지 할 수 있음 • 전체 FD를 순회하며 FD_ISSET을 하는 문제를 해결 8/36 비교 • select • • • 등록된 fd를 하나하나 체크함 • 커널과 user 공간 사이에 데이터 복사가 있음 • 관리 fd 수에 제한이 있음 poll • 관리 fd 무제한 • 좀더 low level로 system call 호출이 select보다 적음 epoll • 관리 fd 무제한 • select, poll과 달리 fd의 상태가 kernel에서 관리됨 • 일일이 fd 세트를 kernel에 보낼 필요가 없음 • kernel이 fd를 관리하고 있기 때문에 kernel <-> User 통신 오버헤드가 대폭 줄어듬 9/36 네트워크 디바이스 드라이버의 poll() • 개요 • 네트워크 디바이스 드라이버 교유의 처리 개념 • 이전에 설명한 poll과 개념적으로 유사하나 동작하는 방식과 구현방법이 전혀 다름 • 기존 처리 방식 • 하드웨어에 수신된 데이터는 수신 인터럽트를 발생시킴 • 하나의 수신 인터럽트는 하나의 패킷을 커널에 전달 • NAPI 방식(polling) • 기존 방식에서 데이터를 처리할 때 폴링 처리를 함 • 하드웨어 FIFO에 수신된 데이터와 이를 처리하는동안 • 새로 도착한 수신 데이터를 폴링 => 수신된 패킷을 한번에 하나씩 처리하지 않고, 한꺼번에 처리 함 10/36 목차 1. 입출력 다중화 2. 다중 입출력의 구현 3. 프린터 포트 예제 개요 • 개요 • 원인 • 블록킹 I/O 로 인해 프로세서가 잠들수 있기에 여러 장치를 사용하는 프로그램에선 주의가 필요함 • 입출력 다중화 • 응용프로그램에서 select() 나 poll()을 사용함 • 등록된 여러 디바이스 파일이 입출력 가능한 상태가 되거나 에러 같은 event가 발생 하면 프로세 1. 입출력 다중화 2. 다중 입출력의 구현 3. 프린터 포트 예제 서를 깨움 12/36 1. 입출력 다중화 개요 • 배경 • 하나의 프로세스로 여러 개의 디바이스를 처리할 수 있다면? • 리눅스에서는 select()와 poll(), epoll()을 지원 1. 입출력 다중화 2. 다중 입출력의 구현 3. 프린터 포트 예제 14/36 select() 함수 • 사용 • 여러 디바이스 파일에 대해 입력, 출력, 에러와 같은 사건을 등록 • 하나 이상의 파일 디스크립터의 상태가 변하면 종료 • 응용 프로그램은 각 이벤트를 조사해서 이벤트가 일어난 장치에 대해 처리 1. 입출력 다중화 2. 다중 입출력의 구현 3. 프린터 포트 예제 15/36 select() 함수 • 형식 • int select(int n, fd_set *readfds, fd_set *writefds, fdset *exceptfds, struct timeval * timeout); • readfds에열거된 파일 디스크립터들은 해당 파일 내 데이터를 read() 함수를 이용해 읽을수 있는지 검사하는 목록 • writefds에 열거된 파일 디스크립터들은 매개변수 readfds와 비슷하게 파일에 데이터를 쓸 수 있는지 검사하는 목록 • exceptfds에 열거된 파일 디스크립터들은 에러가 있는지 검사하는 목록 1. 입출력 다중화 2. 다중 입출력의 구현 • 위 세가지 중 어느 하나라도 상태가 변하거나 사건이 생기면 • select() 함수가 종료 되면서 프로세스를 깨움 • 반환값으로 이 함 수에 적용시킨 FD의 상태가 실제로 어떻게 변경되었는지 가리키도록 수정 됨 • timeout에 기록된 시간이 지나면 사건이 없어도 자동적으로 프로세스를 깨우고 함수가 종료됨 • fd_set 집합변수를 처리하기 위한 매크로 함수 • FD_ZERO : fd_set 구조체 변수를 초기화 한다 • FD_SET : fd_set 구조체 변수에 파일 디스크립트를 등록한다 • FD_ISSET : select 함수가 종료되었을때 fd_set 구조체 변수에 해당 사건이 발생했는지 조사한다 3. 프린터 포트 예제 16/36 poll() 함수 • 개요 • select() 처럼 입출력 다중화를 처리하는 함수 • select()보다 사용하기 쉽고 좀 더 직관적임 • select() 와 마찬가지로 호출되는 즉시 프로세스를 재움 • select()와 달리 관리 file Descriptor 무제한 1. 입출력 다중화 2. 다중 입출력의 구현 3. 프린터 포트 예제 17/36 select() 함수 • 형식 • int poll(struct pollfd * ufds, unsigned int nfds, int timeout); • struct pollfd 1. 입출력 다중화 { int fd; //파일 디스크립터 short events; //요구된 이벤트 short revents; //반환된 이벤트 }; • fd는 파일 디스크립터를 지정 • event는 프로세스가 깨어날 조건을 지정 • revents는 poll 함수가 종료 되었을때 발생된 사건이 지정됨 • 2. 다중 입출력의 구현 3. 프린터 포트 예제 events와 revents는 다음 매크로들의 조합을 이용해 깨어날 사건을 지정하거나 조사 가능 • • POLLIN /POLLOUT/POLLERR/POLLHUP • events 필드에 설정하면 읽을/쓸/에러 /hungup 상태가 되면 poll() 함수가 종료되도록 설정 • revents 필드에 설정하면 poll() 함수가 종료되었을때 읽을/쓸/에러/hungup 상태인지 검사 POLLPRI • 주로 네트워크 패킷의 데이터 수신을 처리하기 위해 사용 됨 • 디바이스 파일에서 긴급하게 데이터를 읽을 수 있는 상태가 되면 poll() 함수에서도 긴급하게 종료될 수 있게 events 필드에 설정 • POLLNVAL • 등록된 파일디스크립터가 유효하지 않은 값 일때 poll() 함수가 종료되도록 events에 설 정 • poll() 함수가 종료되었을때 파일디스크립터 가 유효한지 검사하기 위해 revents필드에 사용 • 주로 네트워크를 처리할때 소켓 핸들러가 무 효화 되었는지를 검사하는 방법으로 사용됨 poll함수가 종료되었을 때 디바이스 파일에서 긴급하게 데이터를 읽어야 할 경우 가능한 상태인지 검사하기 위 해 revents 필드에 설정 18/36 2. 다중 입출력의 구현 개요 • 개요 • 응용 프로그램의 입출력 다중화 함수는 select() 함수와 poll() 함수를 구현하여 해결 • 이를 처리하는 디바이스 함수인 poll을 구현해야 함 • 파일 오퍼레이션의 poll 함수 형태 • unsigned int xxx_poll(struct file *file, poll_table *wait); • 역할 1. 입출력 다중화 2. 다중 입출력의 구현 3. 프린터 포트 예제 • 커널의 poll_table에 폴링 대상이 되는 사건에 대한 대기 큐를 등록 • 커널에서 디바이스 파일에 입출력 다중화 처리를 구현하기 위한 마스크값을 반환 20/36 poll 함수의 구현 • 디바이스 함수 xxx_poll • 형태 • 1. 입출력 다중화 정형화 되어 있음 • DECLARE_WAIT_QUEUE_HEAD(WaitQueue_Read); DECLARE_WAIT_QUEUE_HEAD(WaitQueue_Write); 2. 다중 입출력의 구현 : unsigned int xxx_poll(struct file *file, poll_table *wait) 3. 프린터 포트 예제 { int mask; poll_wait(file, &WaitQueue_Read, wait); poll_wait(file, &WaitQueue_Write, wait); if(처리해야 할 입력 데이터가 있다) mask |=(POLLIN | POLLRDNORM); if(출력이 가능하다) mask |= (POLLOUT | POLLWRNORM); return mask; } 21/36 poll 함수의 구현 • poll() 함수를 위한 대기 큐 • poll() 함수를 구현하기 위해 보통 두가지 상태의 대기 큐 변수가 필요함 • 읽기용 대기 큐 • • • 읽기 상태를 감시하기 위함 쓰기용 대기 큐 • 1. 입출력 다중화 2. 다중 입출력의 구현 쓰기 상태를 감시하기 위함 poll() 함수를 위한 대기 큐라기 보다 디바이스 드라이버의 블록킹 처리를 위한 대기 큐 임 3. 프린터 포트 예제 22/36 poll 함수의 구현 • poll_wait() 함수 • DD의 poll() 함수는 입출력 다중화 처리를 위한 대기큐를 poll_table에 추가 해야 함 • • poll_table은 커널에서 관리 poll 함수에 전달되는 매개변수 • file 구조체 변수인 file • • 2. 다중 입출력의 구현 디바이스 파일의 정보를 담고 있음 poll_table 구조체의 wait 변수 • • 1. 입출력 다중화 3. 프린터 포트 예제 디바이스 드라이버에서 추가하는 폴링 대상이 되는 커널의 테이블 정보 두 매개 변수를 이용해 DD의 poll() 함수에서는 poll_wait() 함수를 이용함 • poll_wait() 함수는 커널에 입출력 다중화 조건에 대한 내용을 등록 • static inline void poll_wait(struct file *filp, wait_queue_heat_t * wait_address, poll_table *p) 23/36 poll 함수의 구현 • poll 함수의 반환값 1. 응용 프로그램에서 select나 poll 함수를 호출 2. DD에 이벤트가 있는지 조사 • poll 함수의 반환값을 조사 • 0이 아닌값 • 프로세스를 재우지 않고 종료 • 응용 프로그램에서의 입출력 다중화 이벤트에 반영됨 • POLLIN, POLLPRI, POLLERR, POLLHUP, POLLNVAL, POLLRDNORM, POLLRDBAND, 1. 입출력 다중화 2. 다중 입출력의 구현 3. 프린터 포트 예제 POLLWRNORM, POLLWRBAND, POLLMSG, POLLREMOVE • 읽기 가능 상태 : POLLIN | POLLRDNORM의 조합 • 쓰기 가능 상태 : POLLOUT | POLLWRNORM의 조합 • 에러 상태 : POLLERR 사용 3. 커널은 프로세스를 재움 24/36 Q&A 25/40