티스토리 뷰

요즘 응용프로그램을 짤때 서로다른 프로토콜을 사용하는 서로 다른 2개이상의 소켓을 사용해서 구현하는게 일반적이다.

예를 들어 비디오 실시간 서비스를 하는 서버가 있을때 로그인 같은 경우엔 신뢰성 서비스가 필요하므로 TCP로 구현하지만 실시간 스트리밍 같은 경우에는 빠른 전송을 해야하고 굳이 재전송을 할 필요가 없으므로 UDP를 사용해서 구현한다.


block I/O -> 읽어야할 데이터가 없으면 블락된 상태로 기다린다.



recvfrom은 블락킹 I/O model의 한 예이다.(read와 사용법이 거의 유사)

recvfrom을 호출했을때 커널로 제어가 넘어가게 되는데 이때 커널의 네트워크 패킷 버퍼에 데이터가 없으면 블락 된다.


커널은 하드웨어 네트워크 인터페이스에 데이터가 있는경우 하드웨어 인터럽트 루틴을 통해서 커널의 버퍼안에 있는 데이터의 아이피헤더(L3헤더)를 벗기고 TCP/IP(Layer4)에 올려 준다.


커널이 Layer3에서 Layer4로 패킷을 올려줄때 그 패킷의 내용을 복사해서 올리지 않는다.(memory to memory copy X)

포인터만 복사해서 그 패킷의 내용을 가리키게끔만 해준다.


memory to memory copy가 일어나게 되면 굉장히 성능이 떨어지기 때문에 요즘에는 하드웨어에서 

바로 유저 메모리의 char[]에 쓰는 기능을 하는 하드웨어도 있지만 비싸서 잘 사용이 안된다. 

그래서 어쩔수없이 커널메모리를 유저메모리에 copy한다.


read를 호출하면 커널의 버퍼에 어떤 데이터가 도착하기를 기다리고, 도착했으면 유저 메모리에 그 데이터를 복사하는 과정을 하는동안 블락 되게 된다.



Nonblock I/O는 Nonblock용 시스템콜이 따로 준비되어 있는것이 아니라 소켓에 옵션을 줘서 Nonblock소켓으로 만드는것이다.

오픈 파일 테이블에 어떤 소켓 만들었다고 써놓고 그 소켓은 non-block 특성을 갖는다고 써놓는다.


그 소켓을 가지고  recvfrom을 호출할 경우 block이 일어나지 않고, -1을 리턴시키며 errno에 EWOULDBLOCK을 세팅한다.

그림에서 네번째 recvfrom을 호출시켰을때 데이터가 준비 되어 있었다. 그런 경우 커널의 버퍼에서 유저 프로세스의 배열에 copy를 해야 한다.

잠깐동안 이지만 이 기간 동안 블락이 마찬가지로 일어나게 된다.(Non-block I/O model에서도 block은 잠깐이나마 일어난다)


서버에서 Non block i/o 사용 예


1.서버 기계에서 우리의 프로그램 처럼 하나의 프로세스가 대부분의 일을 처리하고 다른 프로세스들은 거의 일을 하지 않는다고 했을때

read 블락된 상태로 데이터가 오기를 기다렸다가 다시 깨어나서 일을 처리하는것 보다(Block->Run =>Context switch가 자주 일어남),


non block i/o 소켓을 사용해서 커널에 들어가지 않고 그 프로세스에게 할당된 cpu-time동안 계속해서 그 프로세스를 실행시키는게 더 좋은 상황이 많다.  => SPIN LOCK을 걸었다고 표현한다. -> CPU를 점유하여 유효한 일을 하진 못하지만 유효한 결과가 나올때까지 반복한다.


Non-blocking i/o를 하더라도 잠시동안은 블락된다.


2.데이터를 받겠다고 했는데 데이터가 없는 경우 앞에서 받았던 데이터를 처리한다. block 모델의 경우 

데이터가 많을때 엄청 많은 수의 데이터를 읽은 다음, 그것들을 모두 write하고 난 뒤에 다시 이 과정을 반복하게 된다.

하지만 non blocking 모델을 사용할 경우 엄청나게 많이 수신된 데이터의 일부를 write하고 다시 

(서버의 경우에는 데이터가 burst하게 수신되고 송신된다.) read를 하게끔 만들어 준다.


서버에서 여러개의 소켓을 사용할때 Non blocking 소켓의 장점

block i/o를 사용하면 데이터가 올때까지 블락 될 것이다. 그런데, 소켓 A와 B가 있을때 어느 소켓에 먼저 데이터가 올지 모르기 때문에

블락킹 i/o를 사용해서 A에 데이터가 오기를 기다리면 Non blocking i/o를 했을때 보다 데이터 처리가 느릴 수 있다.


(소켓 B에 데이터가 와있을 수도 있으므로)Non blockng i/o를 사용하면 A에 데이터 있는지 확인하고 없으면 바로 B에 

데이터가 왔는지 확인하기 때문에 더 빨리 데이터 수신된걸 알아 낼 수 있다.


근데 non blocking i/o를 사용해서 스핀락을 거는게 blocking i/o 보다는 낫지만 그래도 여전히 깔끔한 프로그램은 아닌것 같다.

CPU를 차지해서 계속 소모하기 때문이다.


그렇기 때문에 I/O Multiplexing을 사용하는 것이다.


I/O multiplexing 은 select나 poll 시스템콜을 통해서 이루어 지게 된다.


read에 대한 select의 사용예

select => 어떤 보따리에(read보따리) 소켓을 담는다. 그 소켓들은 데이터가 도착했는지 안했는지 검사를 해야하는 소켓들이다.

그 보따리에 있는 소켓들중 1개이상의 소켓에 어떤 데이터가 도착했을때 select는 블락 에서 깨어나고 몇개의 소켓에 

데이터가 도착했는지를 숫자로 리턴한다. 


만약 어떤 소켓에 write할 수 있는지 없는지를 검사하고 싶으면 write전용 보따리에 

소켓들을 담고나서 select를 호출하면 된다.이때 write 보따리와 read 보따리는 FD_SET이라는것을 통해 담기게 된다.

 Fild Descriptor SET의 약자이며, 파일디스크립터(소켓) 세트 즉, 소켓 보따리라고 생각하면 된다.


블락킹 모델을 사용할 경우 블락 됬다가 다시 깨어나고 이런 과정 자체가 커널을 들어갔다 나왔다 하는 것이므로 오버헤드가 크다.

이것을 해결하기 위해서 nonblock모델을 사용할 수 있지만 이 모델을 사용해서 프로그래밍 할 경우에 프로그래머가 신경써줘야 

할 것이 많아지고, CPU를 busy waiting(spin lock)하게 되므로 여전히 문제가 존재한다.


그렇기 때문에 select 시스템 콜을 사용한 i/o 멀티플렉싱 기법을 사용하는것이 통례이다.



select 시스템 콜로 i/o를 깔끔하게 처리하는 것 말고 위 처럼 Signal을 통해서 I/O 처리를 하는것을 알아보자.


프로세스가 실행중에 i/o가 필요한 상황(커널로 데이터가 도착함)

이 생기면 SIGIO시그널이 발생된다. 그래서 우리 프로그램에서 이 시그널을 처리할 핸들러를 만들어주고 

그곳에서 read를 하게끔 프로그래밍 할 수 도 있다. 


이렇게 하면 프로세스가 select를 호출해서 블락된 상태로

소켓 보따리에서 데이터가 수신된 소켓이 생길때까지 대기하는것 보다 더 좋아 보일 수 있다. 

왜냐면 시그널 핸들러 처리는 블락된 상태로 기다리지 않고 자기 하던일 하다가 커널에 데이터가 도착하면 그때되서야 read하면 되기 때문이다.


하지만 문제가 발생 할 수 있다. 왜 그럴까?


1.시그널은 큐잉하지 않는다.(no-queueing) 

첫번째 커널 버퍼에 데이터가 도착후, SIGIO 시그널이 발생해서 시그널 핸들러가 이 시그널을 처리하는 와중에 다시 데이터가 도착해서

똑같은 SIGIO 시그널이 2번더 발생했다고 했을때, 시그널 핸들러는 이 시그널에 대해서 3번 처리를 해주는게 아니라 

1번만 처리하고 나머지 2개는 무시해 버린다

 시그널 핸들러 처리중에 발생한 똑같은 이벤트에 관한 시그널은 큐잉하지 않기 때문이다.


그렇기 때문에 시그널 핸들러로 I/O를 처리하는것은 커널에 도착한 데이터가 제대로 처리되지 않을 수 있다는것을 의미한다.



마지막으로 비동기적 I/O model을 알아보자.


이것은 Signal I/O model과 비슷하다.

프로세스는 자기가 해야할일을 실행하고 있다. 이때 커널로 데이터가 도착하면 커널은 버퍼에 있는 데이터를 유저 메모리에 카피한다.

이때까지 커널은 유저프로세스에게 어떤 하드웨어 인터럽트도 걸지 않는다.


최종적으로 유저메모리에 데이터를 카피하고 나면 그때서야 signal을 발생시켜서 유저 프로세스의 시그널 핸들러가 적절한

처리를 하게끔 하는 모델이다.


Signal I/O 에서는 커널에 데이터가 도착하면 그때 SIGIO를 발생시켜서 유저 프로세스의 시그널 핸들러가 호출되게끔 

했지만, 비동기적 I/O model에서는 데이터가 도착하고 유저메모리에 데이터를 카피한 다음에 시그널을 발생시킨다는 차이가 있다.


근데 이 모델은 애플기계나,핸드폰과 같은 기계에서는 구현이 안되어있을 경우가 있으므로 portablity에 문제가 있다.







댓글
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/11   »
1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30
글 보관함