A와 B 컴퓨터가 있고, A의 클럭은 100Hz, B의 클럭은 200Hz



I/O 버퍼가 있고, 버퍼를 비우는 기준은 10클럭에 한번, 그래서 A컴퓨터는 초당 10번, B컴퓨터는 초당 20번.



A컴과 B컴에 A, B, C를 입력했을 때, A컴퓨터는 버퍼에 자료가 모두 들어가고 나서 버퍼가 비워진다.


이 때, 만약 자료의 이동이 네트워크를 통해 다른 컴퓨터에 전달될 시 A, B, C가 들어있는 버퍼는 한번의 통신 싸이클(Ack, TCP/IP)로 해결할 수 있다.


그러나 B컴퓨터는 A가 들어오고 버퍼를 바로 비우고, B가 들어오고 비우고, C가 들어올 때 비운다. 그러면 통신 싸이클을 세번을 각기 다르게 해줘야 한다.



I/O(Bus 클럭) 연산이 묶이는 경우에는 CPU 클럭에 영향이 덜하다


Buffer는 I/O의 기본 메커니즘이다.




동기 I/O

함수의 호출과 데이터 전송이 동기화

함수의 반환과 전송 끝이 동기화


바로 반환을 안함, 아무것도 못함


이게 동기화 I/O





write 함수 호출 시작과 동시에 반환하고 내부적으로 계속 전송하는 것


클라이언트가 서버에게 요청을 하는 경우를 예를 들면,

클라이언트가 서버에게 요청을 보내고 서버가 그 결과를 보낼 때까지

다른 일을 하지 않고 기다렸다가 결과를 받으면 다음 단계로 진행하는

방식을 "동기" 방식이라고 합니다.

반대로 클라이언트가 서버에게 요청을 보낸 후, 그 결과가 넘어올 때까지

기다리지 않고 다른 작업을 하다가 결과값이 왔다는 신호를 받으면

결과값을 받아 처리하는 방식을 "비동기" 방식이라고 합니다.


출처: https://linuxism.ustd.ip.or.kr/757 [linuxism]



I/O는 느려서 상대적으로 빠른 CPU는 I/O 처리를 기다리는 동안 다른 일을 할 수 있음



동기 I/O의 CPU 사용을 보면 CPU가 부분부분 쉬는것이 보이고


비동기 CPU 사용을 보면 여유를 두고 꾸준히 사용중인 것을 볼 수 있다


속도가 중요한 프로그램이 아니면 동기 I/O 모델로 프로그램을 만드는 것이 가독성 면에서도 좋다


대신 아니라면 비동기 I/O가 중요하다.


비동기 I/O가 필요한 프로그램은 50%가 채 안된다고 함



I/O를 여러개 두고 중첩시켜서 CPU를 진행시킨다. 느린 I/O처리를 기다릴 바에 이렇게 중첩시켜 처리하겠다는 뜻



A컴퓨터에 B, C컴의 네트워크 I/O 빨대 꽂고 처리를 해도


I/O는 느려터져서 그냥 B, C 꽂고 동시처리 해도 처리가 가능하다는 것


대신 동시처리중인 I/O의 작업이 완료되었는지 확인을 해줘야 한다.


근데 I/O들의 목적이 다 다르기 때문에 이 목적 확인이 힘들다고 함



그래서 나온게 완료루틴이다.


I/O가 끝나면 해당 I/O에 묶인 함수를 실행한다. C I/O가 끝나면 함수 F를 실행하는 방식



 


Offset과 OffsetHigh는 유니온


완료루틴 I/O는 중첩 I/O의 확장형, OVERLAPPED함수가 둘 다 필요한 이유


hEvent가 EVENT 객체를 가리킨다. EVENT 객체가 상태를 변경하면 hEvent 핸들값이바뀌고 연산의 끝을 알 수 있다.



WriteFileEx 함수는 중첩 I/O방식과 같지만 완료루틴 콜백함수가 추가되어있다.


WriteFileEx는 CompletionRoutine과 인자(PIPE) I/O를 연결시켜준다.


콜백이기 때문에 함수의 호출과 인자의 전달을 윈도우가 해준다.


lpOverLapped를 보면 hEvent를 받는다.



그래서 완료루틴 I/O에도 기존 중첩 I/O 함수를 사용해서 중첩 I/O에 사용되던 hEvent 핸들에 추가로 데이터를 완료루틴 함수로 전달할 수 있다.





A가 I/O 요청했다. 이 I/O가 연산이 끝나면 CompletionRoutine 함수를 호출한다.

즉, A I/O 작업이 끝나면 자동적으로 CompletionRoutine 함수 시작될 것이기 때문에 A는 작업을 계속해야 한다.

근데 I/O 작업이 언제 끝날지는 모른다.

I/O 연산 끝나면 CompletionRoutine 을 하러 가야하는데, 프로그래머 입장에서 보면 일을 하다가 CompletionRoutine 에게 우선순위를 뺏기는 셈이다.

이 일의 우선순위를 A에게 줄지, CompletionRoutine에게 줄지 결정할 수 있어야 한다.

이게 가능해야 안정적으로 SW를 디자인할 수 있다.

만일, I/O 작업이 끝나 CompletionRoutine을 해야할 때, 지금 하고 있는 일에 상관 없이 CompletionRoutine에 우선순위를 주어

CompletionRoutine을 시작하도록 하고 싶다.

이 상태를 알람이 가능한 상태, Alertable State라 한다.

그러면 이를 명시적으로 선언해야 하는데, 이게 그림의 세가지 함수이다.

이 함수들을 호출하면, I/O 작업이 끝났을 때 CompletionRoutine이 시작하게 할 수 있다.


APC Queue라는 것이 있다.  이는 각각의 쓰레드에 독립적이다.

쓰레드가 알람 가능한 상태가 되었을 때, 호출할 콜백 함수들을 모아둔 queue이다.

즉, Alertable State가 되었을 때 호출할 콜백 함수들을 모아둔 queue이다.

그러면 큐에 있는 것은 대상이 Function&Param에 들어가있는 정보를 참조하여 해당 함수를 호출하게 되는데

언제 호출하게 되냐면 쓰레드가 Alertable State가 되었을 때 함수들이 다 호출이 된다.

총 세개의 함수가 등록되어 있다 해서 쓰레드가 Alertable State로 세번 들어가야 하는 건 아니다 .

무조건 한번 들어가면 queue는 전부 지워진다.

WirteFileEx() 함수의 경우에도 I/O가 완료되었을 때 단순하게 APC 큐에 콜백함수 정보를 입력시킨다.




'운영체제 > 윈도우 시스템' 카테고리의 다른 글

윈도우 프로시저  (0) 2020.05.15
메모리 관리 (가상 메모리, 힙, MMF)  (0) 2019.10.18
파일 I/O와 디렉터리 컨트롤  (0) 2019.10.15
예외처리(SEH)  (0) 2019.10.15
메모리 계층  (0) 2019.10.15

+ Recent posts