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 |