기본 콘텐츠로 건너뛰기

네트워크 프로그래밍에서의 blocking, non-blocking, syncronous, asyncronous

출처 - http://devsw.tistory.com/142

TCP/IP 네트워크 프로그래밍 책을 보다보면 봉쇄(blocking), 비봉쇄(non-blocking), 동기(syncronous), 비동기(asyncronous)에 대해서 나옵니다. 이 개념에 대해 자세히 알아보려 합니다.

소켓 프로그래밍에서의 blocking
소켓 프로그래밍 책을 보다보면 read, write 함수를 가장 쉽게 보게 됩니다. 리눅스에서 read, write는 소켓 프로그래밍에서 패킷을 보내거나 받을때 사용하는 함수입니다. 이 함수는 소켓이 blocking이냐 non-blocking이냐 에 따라 작동방식이 살짝 다릅니다. 소켓이 blocking일 경우 Server 가 Client의 메시지 요청을 받기 위해 read에서 기다릴 수가 있습니다. Client가 write하기 전에는 read에서 빠져나오지를 못하는 것이지요. 

Blocking I/O 모델에서의 작동 방식 : 어플리케이션(프로그램)에서 recvfrom(read)를 호출 할 때, 커널은 데이터가 들어올 때까지 봉쇄(blocking)을 시킨다. 데이터가 들어오면 recvfrom(read)함수는 return이 되고, 프로그램 흐름은 어플리케이션(프로그램)으로 돌아온다.

소켓 프로그래밍에서의 non-blocking
위와 같이 blocking형태의 프로그래밍을 하다보면 서버 프로그램 입장에서는 여러 클라이언트의 처리가 어렵습니다. 그래서 나온 방법이 클라이언트 접속 별로 쓰레드를 생성하여 클라이언트별 read 함수를 호출 하는 것인데, 하지만 이 방법도 대규모 처리를 하기 위해서는 context switching에 발생하는 비용이 만만치 않아 프로그램의 성능을 떨어뜨립니다. 이러한 방법을 피할 수 있는 방법이 비봉쇄(non-blocking) 방법입니다. non-blocking은 위의 그림처럼 시스템함수 호출 후 멈출 필요가 없습니다. 읽을 데이터가 있으면 읽고, 없으면 넘어가게 되는 방법입니다.


Non-blocking I/O 모델에서의 작동 방식 : 어플리케이션(프로그램)에서 recvfrom(read)를 호출 할 때, 커널은 읽을 데이터가 없으면 EWOULDBLOCK(또는 EAGAIN, 둘의 의미는 똑같다.)을 return 한다.
위의 그림과 같이 소켓에 읽을 데이터가 있는지 없는지 recvfrom(read)을 loop 돌면서 계속 호출해주어야 합니다. (이 부분이 나중에 설명할 비동기 I/O와 살짝 다릅니다.)  이 방법은 결국 모든 소켓에 대한 체크를 해야하는 busy-waiting 상태가 됩니다. 리눅스에서는 적절한 sleep이 없는 loop는 100%의 CPU 점유율을 갖게 됩니다. 참고로 윈도우즈에서는 그런 현상이 없습니다.모든 소켓에 대해 확인을 할 수가 없어 사용하는 방법이 멀티 플렉스를 사용하는 것입니다. 리눅스 계열은 select, poll, epoll을 사용하게 됩니다. 즉, 이 기술들은 데이터 입출력에 직접적으로 필요한 기술이 아니라 파일디스크립터(소켓)을 관리하기 위한 것이라 보면 됩니다.

blocking과 non-blocking의 정리
여기까지 봉쇄(blocking), 비봉쇄(non-blocking)에 대해서 알아 봤습니다. 봉쇄와 비봉쇄의 차이점은  파일디스크립터 관련 함수에서 기다리냐 마느냐의 차이점입니다. 비봉쇄에서 read는 읽을 데이터가 없을 시 에러(-1)을 return을 하게 되어 errno.h의 errno을 확인하여 EWOULDBLOCK(또는 EAGAIN) 일 경우 적절한 처리를 해주어야 하기 때문에 프로그래밍이 조금 더 복잡해 질 수도 있습니다. 참고로 non-blocking 소켓으로 connect를 할 때에도 connect 함수가 에러를 return (-1) 할 수가 있습니다. 하지만 이것도 errno를 확인해보면 EINPROGRESS가 발생하는데 이 말은 즉, 연결요청이 진행중이다 라는 에러 입니다. 즉, '연결 완전 실패가 아닌 진행 중이지만 일단 에러로 return 하니 프로그래머가 알아서 처리를 하라' 라는 뜻입니다. 이렇듯 non-blocking 소켓 프로그래밍을 할 경우 에러처리를 톡톡히 해주어야 하는 단점이 있습니다. 하지만 에러처리를 잘하면 어떠한 빈틈도 없는 멋진 프로그램이 될 수도 있다는 뜻이지요.

동기(Syncronous)
이제 동기가 무엇인지 궁금해 졌습니다. 동기는 사실상 blocking과 차이점이 없다고 보시면 됩니다. "내(어플리케이션) 너(커널)의 작업(recvfrom)이 끝날 때 까지 기다려 줄께. 또는 시간을 맞춰줄께. 또는 동기화 해줄께" 라고 생각하시면 됩니다. 어플리케이션이 커널에 작업을 요청하고 커널에서 작업이 끝날때 까지 어플리케이션이 기다려 주는 것이지요. 즉, 동기화 하기 위해서 봉쇄(blocking)을 하는 것입니다. 그렇다면 비동기화(Asyncronous)하기 위해서 비봉쇄(non-blocking) 하는 것일 까요?

비동기(Asyncronous)
위에도 설명하였 듯이 비동기화 하기 위해 비봉쇄를 한다고 설명할 수 있을까요? 그렇다고 하기엔 내부적으로 작동하는 방법에 차이가 있습니다. 비동기의 간단한 개념을 따지자면 비동기 read함수를 호출하면 바로 return이 됩니다. 비동기 함수를 호출할 때는 작업이 완료가 될 때 알려줄 수 있는 event나 callback함수를 설정하게 됩니다. 즉, 어플리케이션이 커널에게 "읽을게 있으면 나에게 알려주던가(event) callback함수를 호출해~" 라고 하는 것입니다. 


Asyncronous I/O 모델에서의 작동 방식 : 어플리케이션(프로그램)에서 aio_read를 호출 할 때, 커널은 읽을 데이터가 있던 없던 return을 하게 되며, 읽을 데이터가 발생하게 되면 event를 발생하거나 callback함수를 호출하게 된다.
위의 그림과 같이 어플리케이션에서 aio_read를 호출하고 바로 return을 받게 됩니다. 커널은 데이터가 들어오면 event를 발생시키거나 callback함수를 호출하게 됩니다. 비동기 네트워크 프로그래밍을 할 때, event나 callback함수를 사용하게 됩니다. 즉, 비동기 프로그래밍은 non-blocking처럼 지속적인 관찰이 필요한 프로그래밍이 아니라 일단 작업을 걸어 두고 다른 작업을 진행한다가 어떠한 신호나 작업이 들어올 경우 걸어둔 작업을 진행한다. 라고 생각하시면 좀 더 수월해 질 것입니다.

마무리
비봉쇄(non-blocking)과 비동기(Asyncronous)는 다른 개념입니다. 특히 비동기 프로그래밍을 택한다면 소켓을 비봉쇄 소켓으로 만들면 안된다는 이야기가 있습니다. 제가 직접 테스트를 해본 것은 아니지만 stack overflow 사이트에서는 섞어서 사용하면 안된다고 나와 있네요. 아마도 커널 내부적으로 소켓이 blocking되어 작업이 끝나고 어플리케이션에 알려주어야 하는데 비봉쇄로 나와 버린다면 적업에 에러가 발생할 것 같습니다. 이부분에 대해서는 좀 더 연구가 필요하겠습니다.   

댓글

  1. 안녕하세요. 도움이 되는 글 감사합니다. 제가 완벽히 이해하지는 못한채 일단 비봉쇄 비동기 소켓 서버를 만들어서 구현을 해봤는데 작동은 됩니다만, 이게 권장되지 않는다는 건가요?? 제가 아직 확실히 몰라서 여쭤봅니다.

    답글삭제

댓글 쓰기

이 블로그의 인기 게시물

UNIX C errno 정리( 에러 번호 )

#define EPERM   1   /* Operation not permitted      */ #define ENOENT  2   /* No such file or directory        */ #define ESRCH   3   /* No such process          */ #define EINTR   4   /* interrupted system call      */ #define EIO 5   /* I/O error                */ #define ENXIO   6   /* No such device or address        */ #define E2BIG   7   /* Arg list too long            */ #define ENOEXEC 8   /* Exec format error            */ #define EBADF   9   /* Bad file descriptor          */ #define ECHILD  10  /* No child processes           */ #define EAGAIN  11  /* Resource temporarily unavailable */ #define ENOMEM  12  /* Not enough space         */ #define EACCES  13  /* Permission denied            */ #define EFAULT  14  /* Bad address              */ #define ENOTBLK 15  /* Block device required        */ #define EBUSY   16  /* Resource busy            */ #define EEXIST  17  /* File exists              */ #define EXDEV   18  /* Improper link            */ #define ENODEV  19  /* No such

Baud Rate 와 Bit Rate 의 차이점

출처 - http://solvline.com/technical_info/tech_note_view.php?no=22&fno=&PHPSESSID=f5f096a5b1090ca017552de78745b8aa 1. Bit Rate 와 Baud Rate 의 정의 1.1 Bit Rate 정의 비트 레이트 (Bit Rate) 는 초당 얼마나 많은 데이터 비트 (‘1’ 또는 ‘ 0’) 를 전송할 수 있는가를 나타내는 말이다 . 또 우리가 자주 쓰는 BPS 라는 말이 있는데 이는 Bit Per Second 로서 초당 보낼 수 있는 비트의 수를 나타낸다 . 이는 모뎀의 전송속도를 측정하는 단위로 사용되어 졌다 . 일반적으로 28,800 모뎀이라면 28,800bps 의 전송속도를 나타내는 것이다 . 예를 들어 , 2400bit/second(bps) 라면 초당 2400 개의 비트 정보를 전달할 수 있다는 뜻이다 . 이는 반대로 이야기하면 1 비트 정보를 보내기 위해서 416.6us 의 시간 (1s/2400bit) 이 필요하다는 이야기이다 . 1.2 Baud Rate 보 레이트 (Baud Rate) 는 초당 얼마나 많은 심볼 (Symbol, 의미 있는 데이터 묶음 ) 을 전송할 수 있는가를 나타내는 말이다 . 이는 이론적인 통신 단위로 초당 신호 (Signal) 요소의 수 를 나타낸다 . 만약 하나의 신호요소가 2 비트로 구성되어 있는 경우라면 보는 BPS 의 반이 된다 . 즉 , 1 보 동안 2 비트가 전송된다 . 일반적으로 신호를 1 비트로 여긴다면 BPS 와 같은 단위가 된다 . 심볼이란 말은 의미 있는 데이터 비트의 묶음이다 . 일반적으로 시리얼 통신에서는 데이터 비트가 8-bit 를 사용하므로 이를 하나의 심볼이라고 이야기 할 수 있다 . 1 개의 symbol ( 또는 character) 는 8 개의 bit 정보와 같다 . 예를 들어 , 2400 baud rate 를 갖는다는 말은 1 초에 2400 개의

시리얼(Serial) 이란?

출처 - http://www.ni.com/white-paper/2895/ko/#toc4 시리얼은 거의 모든 PC에서 표준으로 사용되는 디바이스 통신 프로토콜입니다. 시리얼의 개념을 USB의 개념과 잘 구분하십시오. 대부분의 컴퓨터에는 2개의 RS232 기반 시리얼 포트가 있습니다. 시리얼은 또한 여러가지 디바이스에서 계측을 위한 일반 통신 프로토콜이며, 여러 GPIB 호환 디바이스에는 RS232 포트가 장착되어 있습니다. 뿐만 아니라, 원격 샘플링 디바이스로 데이터 수집을 하는 경우에도 시리얼 통신을 사용할 수 있습니다. 시리얼 통신의 개념은 간단합니다. 시리얼 포트는 정보의 바이트를 한번에 한 비트씩 순차적으로 송수신합니다. 한번에 전체 바이트를 동시에 전달하는 병렬 통신과 비교하면 시리얼 통신은 속도가 느리지만 훨씬 간단하며 장거리에도 사용할 수 있습니다. 예를 들어, 병렬 통신용 IEEE 488 스펙을 보면 기기간 케이블링은 총 20 m 미만이어야 하며, 두 개의 디바이스간은 2 m 미만이어야 합니다. 반면 시리얼 통신은 최대 1.2 Km의 통신거리를 보장합니다. 통상 엔지니어들은 ASCII 데이터를 전송할 때 시리얼 통신을 사용합니다. 이 때 송신용 (Tx), 수신용 (Rx), 그라운드용 (GND)의 세 가지의 전송 라인을 사용하여 통신합니다. 시리얼은 비동기식이므로 포트는 한 라인에서 데이터를 전송하고 다른 라인에서 데이터를 수신합니다. 핸드쉐이킹용 라인도 사용 가능하지만 필수 요구사항은 아닙니다. 시리얼 통신의 가장 중요한 특징에는 보드 속도 (baud rate), 데이터 비트, 정지 비트, 패리티가 있습니다. 두 개의 포트가 통신하기 위해서는 이러한 파라미터가 반드시 적절하게 맞춰져야 합니다. 보드 속도는 통신의 속도를 측정하는 수치이며 초당 비트 전송 숫자로 표시됩니다. 예를 들어 300 보드 속도는 초당 300 비트를 의미합니다. 엔지니어들이 흔히 말하는 클럭 주기는 보드 속도를 의미합니다. 따라서 프로토콜에 4800