기본 콘텐츠로 건너뛰기

네트워크 프로그래밍에서의 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 개의

[C언어] epoll 설명

출처 -  http://biscuit.cafe24.com/moniwiki/wiki.php/epoll 1  준비 2  socket 프로그래밍 기본 3  비동기 입출력 (Asyncronous I/O) & 입출력 다중화 (I/O Multiplexing) 4  select 5  select 와  poll  그리고 epoll. 그 차이 6  epoll 프로그래밍 흐름 7  epoll 함수들 8  epoll References 빈폴도 아니고, 이폴이란 대체 무엇일까? 당신은 서버한대로 몇 명의 동시접속자를 수용할 수 있습니까? 최근에 인터넷에 떠돌아다니는  c10k_problem 은 대당 10K, 즉 1만명의 동시접속(concurrent users)을 받아보자는 문제다. 서버 프로그래밍을 해 본 사람이라면 이게 그리 만만한 문제가 아니라는 것을 직감할 듯 --; 요즘의 Massive 온라인게임은 '분산처리'가 기본이라 한 대에서 많은 이용자를 커버하기보다는 여러대가 하나의 세트로써 구성하는 것이 인기가 있고 다수의 커넥션보다는 소수 커넥션에서의 대용량 전송이 더 중요한 요소이기도 하다. c10k problem에 나또한 관심을 가지게 되었고, epoll 이 최근 급부상하는 솔루션으로 인기가 있다기에 한 번 파보자 하고 결심하고 이 글을 시작했다. 마침 wiki에도 관심이 있던 차라, wiki 공부도 할 겸해서 epoll 을 연구하는 과정을 이 wiki에 담아 보고자 한다. 1  준비  # * 누구를 위한 epoll 인가? epoll은 '한 대의 서버에서 아주많은 동시접속자를 처리하기 위한 수단'이다. 이미 당신이 그 수단을 알고 있다면 - epoll 이건 아니건 - 이 글은 별로 도움이 안될듯하다. 동시접속자가 천명을 넘지않는다면 구닥다리 방법을 이용하는 것과 큰 차이가 없으리라 본다. 또한, epoll은  Linux 프로그래머의 도구 이다. M$ wind