2015년 1월 4일 일요일

TCP/IP 소켓 프로그래밍 17강

TCP/IP는 내가 처음으로 해보는 공부라 그런지 영 더디게 강이 지나가곤 한다.
학교 다닐 때 리눅스 좀 해봤어야 했는데 ㅡㅜ
물론 집에서 개인적으로 깔아보고 이것저것 만져보다가
게임도 안되고, 재미도 없어서 깔았다 지웠다를 반복했었다.
그러다 회사에서 갑자기 하게 되어 무지 고생했던 기억이 있다.
그때는 어찌어찌 복사 붙이기 신공으로 넘겼지만 아찔했던 기억이 있다.
2015년 새해가 밝았으니 다시 기운 차리고 해보고자 한다.

이번 17강은 select 보다 나은 epoll 이라는 것을 배우고자 한다.
그리고 내용이 길어서 함수랑 약간의 설명만 하겠다.
소스는 가장 아래 소스 받기 링크에서 받아서 하길 바란다 -_-;

17-1 : epoll 의 이해와 활용


select 는 오래 전에 개발된 멀티플랙싱 기법이라고 한다.
이 기법은 접속자 수가 100명만 넘어가도 버벅대는 기법이다.

select 기반의 IO 멀티플렉싱이 느린이유
- select 함수호출 이후에 항상 등장하는, 모든 파일 디스크립터를 대상으로 하는 반복문
-select 함수를 호출할 때마다 인자로 매번 전달해야 하는 관찰 대상에 대한 정보들

select 함수를 호출할 때마다 관찰 대상에 대한 정보를 매번 운영체제에게 전달해야 한다.
이것을 극복하고자 epoll 이라는 것이 생겼다.

-상태 변화의 확인을 위한, 전체 파일 디스크립터를 대상으로 하는 반복문이 필요 없다.
-select 함수에 대응하는 epoll wait 함수호출 시, 관찰대상의 정보를 매번 전달할 필요가 없다.

epoll 함수는 3개가 있다.
-epoll_create : epoll 파일 디스크립터 저장소 생성
-epoll_ctl : 저장소에 파일 디스크립터 등록 및 삭제
-epoll_wait : select 함수와 마찬가지로 파일 디스크립터의 변화를 대기한다.

epoll 함수는 리눅스 커널 2.5.44 이상에서 부터 쓸 수 있다.

리눅스 커널 버전 확인 명령어다.

cat /proc/sys/kernel/osrelease

각 함수를 자세히 보자.
epoll_create

#include <sys/epoll.h>
int epoll_create(int size);
-> 성공 시 epoll 파일 디스크립터, 실패시 -1 반환
-size : epoll 인스턴스의 크기 정보.

위 함수 호출시 생성되는 파일 디스크립터의 저장소를 가리켜 'epoll 인스턴스'라 한다.
그리고 매개 변수로 통해서 전달되는 값은 epoll 인스턴스 크기를 결정하는 정보로 사용된다. 하지만 이 값은 리눅스 커널 2.6.8 이후에는 무시된다.
그 전 버전에서는 그냥 리눅스 보고 이렇게 할당해줘라고 요청을 하는 것이지만
실제는 커널 맘대로 정한다(-_-);

epoll_ctl

#incldude <sys/epoll.h>
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
-> 성공시 0, 실패시 -1 반환
-epfd : 관찰 대상을 기록할 epoll 인스턴스의 파일 디스크립터
-op : 관찰 대상의 추가, 삭제 또는 변경여부 지정
-fd : 등록할 관찰 대상의 파일 디스크립터
-event : 관찰 대상의 관찰 이벤트 유형

살짝 복잡해 보이지만 예를 들면 쉽다.

epoll_ctl(A, EPOLL_CTL_ADD, B, C);

"인스턴스 A에, 파일 디스크립터 B를 등록하되, C를 통해 전달된 이벤트의 관찰을 목적으로 등록하라"

epoll_ctl(A, EPOLL_CTL_DEL, B, NULL);

"epoll 인스턴스 A에서 파일 디스크립터 B를 삭제한다."

epoll_ctl 에서 4번째 인자인 event의 유형을 정리한다

-EPOLLIN : 수신할 뎅이터가 존재하는 상황
-EPOLLOUT : 출력버퍼가 비워져서 당장 데이터를 전송할 수 있는 상황
-EPOLLPRI : OOB 데이터가 수신된 상황
-EPOLLRDHUP : 연결이 종료되거나 Half-close 가 진행된 상황, 이는 엣지 트리거 방식에서 유용하게 사용될 수 있다.
-EPOLLERR : 에러가 발생한 상황
-EPOLLET : 이벤트의 감지를 엣지 트리거 방식으로 동작 시킨다.
-EPOLLONESHOT : 이벤트가 한번 감지 되면, 해당 파일 디스크립터에서는 더이상 이벤트를 발생시키지 않는다. 따라서  epoll_ctl 함수의 두번 째 인자로 EPOLL_CTL_MOD를 전달해서 이벤트를 재설정 해야한다.

epoll_wait

#include <sys/epoll.h>
int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout);
-> 성공시 이벤트가 발생한 파일 디스크립터의 수, 실패시 -1 반환
-epfd : 이벤트 발생의 관찰영역인 epoll 인스턴스의 파일 디스크립터
-events : 이벤트가 발생한 파일 디스크립터가 채워질 버퍼의 주소 값
-maxevents : 두번째 인자로 전달된 주소 값의 버펄 등록 가능한 최대 이벤트 수
-timeout : 1/1000초 단위의 대기 시간, -1 전달시, 이벤트가 발생할 때까지 무한 대기

17-2 : 레벨 트리거, 엣지 트리거


레벨 트리거와 엣지 트리거의 차이는 이벤트가 발생하는 시점이다.

"레벨 트리거는 입력 버퍼에 데이터가 남아있는 동안 계속 해서 이벤트가 등록 된다."
"엣지 트리거는 입력 버퍼로 데이터가 수신된 상황에서 딱 한번만 이벤트가 등록 된다."

엣지 트리거 기반의 서버 구현을 위해서는 두가지를 알아야 한다.

-변수 errno를 이용한 오류의 원인을 확인하는 방법
-넌_블록킹(Non-blocking) IO를 위한 소켓의 특성을 변경하는 방법

일반적으로 리눅스에서 제공하는 소켓 관련 함수는 -1을 반환함으로써 오류의 발생을 알린다. 오류의 발생을 인식은 할 수는 있으나 오류의 원인을 정확히 확인할 수 없다.
그래서 추가적인 정보 제공을 위해 int errno 라는 변수를 전역으로 선언해 놓고 있다.

이 변수를 확인해서 어떤 오류인지 추측하는 것이다.

이번에는 소켓을 넌-블록킹 모드로 변경하는 법을 알아보자.

#include <fcntl.h>
int fcntl(int filedes, int cmd, .....);
-> 성공시 매개변수 cmd에 따른 값, 실패시 -1 반환
-filedes : 특성 변경의 대상이 되는 파일의 파일 디스크립터 전달
-cmd : 함수 호출의 목적에 해당하는 정보 전달

위 함수는 가변인자의 형태로 정의되어 있다. 
소켓을 넌-블록킹 모드로 변경하기 위해서는 다음의 두 문장을 실행하면 된다.

int flag =  fctl(fd, F_GETFL, 0);
fctl(fd, F_SETFL, flag | O_NONBLOCK);

이번 강은 내가 몸이 안좋아서 그런지 내용이 긴건지 유난히 머리에 안들어왔다.

역시 소스 코드를 다운 받아서 직접 해보는 것이 이해가 빠를 것이다.
소스 코드 받기

댓글 없음:

댓글 쓰기