https://www.youtube.com/watch?v=82h8ndWuaXk&list=PLVsNizTWUw7E2KrfnsyEjTqo-6uKiQoxc&index=57 

 

.

 

 

가상 메모리가 물리 메모리에 매핑될 때 페이지 단위(4kb)로 할당이 되는데

 

기준이 되는 가상 메모리 페이지에 윈도우즈는 상태를 부여한다.

 

Commit - 할당되어있는 상태(malloc)

Reserve - 할당을 예약해둔 상태

Free - 할당되어있지 않은 상태(Free)

 

 

 

근데 실제로 사용되는건 페이지 1~2개 정도인데, 가끔 사용량이 높아질 때를 대비해 몇 배 크게 할당해준다는건 메모리 낭비라는 것이다.

 

문제1. 연결된 메모리 공간 필요

문제2. 메모리를 미리 할당하면 너무 낭비

 

그래서 Reserve 상태라는게 나왔다.

 

 

주로 실제 사용되는 페이지만 Commit하고, 나머지 예비 페이지는 Reserve 상태로 두자는 것이 그 개념이다.

 

 

아무튼 그렇다 치고, 메모리 할당에 관련해 더 살펴보면

 

Allocation Granularity Boundary라는 것이 있다.

 

이건 메모리를 할당할 때 페이지 크기의 몇 배수 크게 할당하겠다는 의미인데

 

페이지 단위 그 자체로 메모리에 할당해버리면 메모리 단편화가 생길 가능성이 크다.

 

메모리를 할당해서 ABAB같이 자료가 떨어져서 할당되면 문제가 된다.

 

그래서 AABB처럼 뭉쳐서 할당하자는 취지다.

 

 

그 크기는 최소 1페이지부터 시작된다.

 

기본값은 64kb이다.

 

(기본 페이지 4kb)

 

 

4kb를 할당해달라고 하면 64kb번지부터 4kb를 할당하고,

 

그 뒤 8kb를 할당해달라고 하면 다음 번지인 128kb번지부터 8kb를 할당한다.

 

만약 6kb를 할당해달라고 한다면(잘못된 값이다. 기본 페이지 단위는 4kb이다) 윈도우는 지가 알아서 8kb로 바꿔서 할당하기도 한다

 

 

이 함수는 메모리를 데이터 스택 디폴트 힙, 다이나믹 힙 밖의 다른 동적 메모리 영역에 할당한다

 

 

 

VirtualAlloc & VirtualFree는 Reserve 상태를 지원해주는 시스템 함수다

 

3번째 인자를 잘 보면 나온다.

 

 

VirtualFree는 Free 하거나 Release(Reserve 상태로 변환) 시킨다.

 


 

 

 

힙엔 두가지 힙이 있는데 하나는 디폴트 힙이고 나머지 하나는 다이나믹 힙이다.

 

기존 디폴트 힙에서 리스트와 같은 자료구조로 메모리를 할당하면

 

할당 해제시 리스트를 다 순회하며 하나씩 지워야 하는데

 

다이나믹 힙에 이러한 할당을 하게 되면

 

굳이 힙 내부에 들어가서 하나씩 할당을 해제할 필요가 없이

 

힙 자체를 삭제시켜버린다.

 

 

 

메모리 단편화 해소

 

사용자가 직접 메모리를 할당해주면 디폴트 힙처럼 윈도우 맘대로 할당하느라 생긴 단편화를 피할 수 있다.

 

그 결과로 로컬리티가 괜찮아 진다는 것이다

 

동기화 문제에서 자유

 

쓰레드별로 힙을 생성해 메모리 침범이 일어나지 않는다

 

다이나믹 힙은 데이터 스택 디폴트 힙을 벗어난 다른 메모리 공간에 할당한다.

 

HeapCreate 함수를 사용하면 핸들이 나온다. 그 핸들로 HeapAlloc을 사용하면 다이나믹 힙에 할당이 되는 것이다.

 

HeapFree를 하면 할당된 메모리 힙을 통채로 날려버린다. 리스트로 하나씩 순회하면서 메모리를 지울 필요가 없는 것이다.

 


 

메모리를 파일에 매핑시키겠다는 개념이다.

파일의 일부 메모리 공간을 프로세스의 가상메모리에 연결시킨다

연결이 일단 되면 프로세스의 가상메모리에 내용을 쓰면 그 위치만큼 이동하여 파일에 데이터를 대신 써준다

이점은 일단 성능 이슈도 있지만, 프로그램의 편리성에 이슈를 둘 수 있다

만일 데이터를 sorting 해야 한다고 치면 파일에 있는 데이터를 메인메모리로 불러들여 sorting하고 다시 저장해야한다.

하지만 이 기능을 사용한다면, 데이터가 파일에 매핑이 됐으므로 그 자리에서 sorting만 하면된다.

 

 

편하다!

 

데이터 매핑을 시켜버리면 데이터 write시, 알아서 파일에 저장된다.

파일에 매번 데이터를 쓸때마다 반영해야 할 필요가 있을까?

메모리에 데이터를 write, read 하므로 실질적인 최신의 데이터는 메모리가 가지고 있다.

즉, 데이터가 변경되었을 때, 파일에 정보를 반영할 필요가 없다는 것이다.

주기적으로, 또는 특별한 상황이 됐을 경우에만 파일에 반영해도 된다.

 

 

ㅁㅁㅇㄴㄹ

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

윈도우 프로시저  (0) 2020.05.15
I/O와 CPU 클럭의 관계  (1) 2019.10.16
파일 I/O와 디렉터리 컨트롤  (0) 2019.10.15
예외처리(SEH)  (0) 2019.10.15
메모리 계층  (0) 2019.10.15


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


ANSI는 껍데기다.


사용자가 fopen 함수를 호출하면 ANSI는 내부적으로 해당 OS 시스템 함수를 호출한다.



ANSI는 공통된 부분만 담는다.






검사

dwPtrLow = SetFilePointer( . . . . . )

if( (dwPtrLow == INVALID_SET_FILE_POINTER) && (GetLastError() != NO_ERROR) )

{

진짜 오류..

}

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

메모리 관리 (가상 메모리, 힙, MMF)  (0) 2019.10.18
I/O와 CPU 클럭의 관계  (1) 2019.10.16
예외처리(SEH)  (0) 2019.10.15
메모리 계층  (0) 2019.10.15
쓰레드 동기화 기법  (0) 2019.10.11




CPU 내부에는 CPU가 설정해놓은 예외 상황이 존재한다(예를 들어, 0 나누기 등)


그러한 예외들을 1, 2, 3, 4, ...n개 등록해 놓는것을 하드웨어 예외라고 한다.


소프트웨어 또한 그러한 예외 상황을 지정해두고 있다.


예외가 발생해 CPU가 예외를 알리면 소프트웨어는 해당 예외를 확인해 추후 상황을 결정한다.


즉 CPU는 예외를 알리기만 하지만, 그에 대한 결정은 소프트웨어가 결정한다.


예외의 결정은 운영체제가 하거나, 사용자가 제작한 어플리케이션이 한다



즉, CPU가 발생시킨 하드웨어 예외를 OS가 받아 소프트웨어 예외를 처리한다.


이를 처리하는 방식은 OS 자체적으로 해결하거나 / 다시 전달해 사용자가 전달받아 사용자 어플리케이션에서 처리한다.


이러한 예외를 SEH라고 한다. (소프트웨어 예외 / 어플리케이션 예외)



윈도우 기반에서 APP 예외는 잘 안다룬다고 함




SEH



예외 처리 코드는 거추장스럽따



예외 처리 코드를 따로 분리하면 가독성에 상당히 도움이 된다.




종료 핸들러



예외 핸들러




result = num1 / num2; 에서 예외가 발생하면 바로 __except로 넘어간다.



스택 풀기의 개념


C++ try catch 참고


아무런 except가 없으면 윈도우는 프로세스를 종료시킨다. 이게 윈도우 예외처리 방식


std::exception 참고



return EXCEPTION_CONTINUE_EXECUTION; 에 의해 에러가 난 코드부터 다시 실행한다.


그냥 함수 스택 풀어버리고 기존 함수로 이동해서 예외처리해버림


하드디스크는 저장, 메인 메모리엔 실행으로 알고 있지만 하드디스크는 실행의 기능도 있다.




프로그램의 실행은 지역적인 특성이 있다.


메모리 크기가 1 ~ 100까지 있다면 난수처럼 산발적으로 실행되는 것이 아니라,


한 부분이 실행되면 그 주변을 돌면서 작동하고 시간이 지나면 다른 곳으로 점프해서 그 주변을 실행한다.


그 주변을 더 빠르게 실행하기 위해 나온 것이 캐시로, 메인 메모리보다 CPU에 더 가깝게 위치한다. 속도는 빠르지만 용량은 메인 메모리보다 작다


이 방법이 효과가 좋았기 때문에, 메모리를 또 놨다.



기존 캐시보다 더 빠르지만, 용량은 더 작다.


하드디스크 또한 이러한 구조와 같다.


하드디스크는 실행을 빨리 하기 위해 메인 메모리에 데이터를 넣고,


메인메모리는 더 실행을 빨리 하기 위해 L2캐쉬에 데이터를 넣는다.


ALU는 L1에 데이터를 요청하면, L1은 L2에 요청하고 ... 하드디스크에 요청한다.


하드디스크에 데이터가 있으면 그 데이터를 메인 메모리, L2, L1, 레지스터에 올려 실행시킨다.


즉, 하드디스크의 저장이라는 관점은 파일 시스템이다. 메모리 매니지먼트 측면으로 이해하면 안된다.





모든 프로그램은 지역성을 갖는다.



CPU가 데이터를 요청하면 캐쉬에 그 데이터가 있을 확률이 90%가 넘는다


와우!


좋은 프로그램은 지역성 또한 좋을 것이다.



캐쉬 메모리로 성능이 향상되는 이유는 바로 Locality 때문


Temporal Locality :

만약 int a를 선언했다면 CPU는 int a에 다시 접근할 가능성이 높다


Spatial Locality :

int a에 접근하는 경우 int a와 함께 선언할 int b에도 접근할 가능성이 높다.



로컬리티의 예


Temporal Locality와 Spatial Locality를 만족하는 것을 볼 수 있다.



캐시에 적절한 데이터가 있을 확률이 90%가 넘는 이유는 Spatial Locality의 특성때문이다.


하드가 메인메모리에 데이터를 넘겨줄 때, 하드가 메모리에, 메모리가 L2에, L2가 L1에, L1이 레지스터에 넘겨주는 데이터는 특정 용량의 기준이 있다.




10mb도 5mb도 2mb도 아니지만 예를 들어 이러한 기준이 있다고 보면


메모리 계층간 데이터 이동은 블록단위로 이동한다.


그래서 캐시 히트가 90% 넘는 확률로 발생하는 이유가 바로 블록단위 데이터 이동 때문이다.


일반적으로 짜는 코드 역시 이러한 방식이기도 한데, 캐시 친화적인 코드란 메모리를 순서대로 읽게 되는 코드를 뜻한다.


하지만 캐시에 대해 공부하려면 그 양이 너무 방대해 밑도끝도 없는 공부를 하게 될수도 있다고 하네





동대문좌


실제로 필요한 메모리가 20개인데 실제 가지고 있는 메모리는 10개라면 소유중인 메모리를 자유자재로 매핑할 필요가 있다.



메인 메모리가 부족하면 하드 디스크까지 확장하는 것을 가상 메모리라고 한다.




선 할당으로 인한 부담


특정 프로세스가 선 할당으로 메모리 4gb를 점유해버리면

다른 프로세스는 메모리 사용을 못하고 발만 동동 구르게 된다.


느린 속도의 개선



1gb는 램, 1gb는 하드에 데이터가 올라가 있으면 언제는 빠르고 언제는 느리고 할 것이다.

하지만 속도가 일정해야 하니 이것을 개선해야 한다.



CPU가 어떤 데이터 할당을 요청하면 MMU는 물리 메모리(램)에 가상 메모리를 할당한다.


1K번지부터 20바이트를 할당을 요청하면 MMU는 가상 메모리로 0K ~ 4K를 매핑해 물리 메모리 0 ~ 4K에 할당하고,


36K번지부터 20바이트를 할당을 요청하면 MMU는 가상 메모리로 36K ~ 40K를 매핑해 물리 메모리 4 ~ 8K에 할당한다.


만약 CPU가 36K번지를 요청하면 MMU는 이를 가상 메모리에서 물리 메모리로 치환해서 실제로 데이터가 존재하는 4K번지에 있는 것을 CPU로 전달한다.


가상 메모리 관점으로 페이지, 물리 메모리 관점으로 페이지 프레임이라고 하네


만약 프로세스를 실행해서 가상 메모리에 2gb를 할당할 일이 있다면, 하드디스크에 2gb를 다 넣어둔다. 그리고 필요한 데이터를 램에다 올리는 것이다.


즉, 램을 일종의 캐쉬처럼 사용하는 것이다.



램이 꽉 찬 상태에서 4 ~ 8K를 CPU로부터 요청받으면


LRU는 램에서 사용된지 오래된 것을 하드디스크에 스토어한다.( 8 ~ 12K )


그리고 4~8K를 부른다.


하지만 그러자마자 8 ~ 12K를 다시 요청받으면


램에 존재하는 단위 중 사용된지 가장 오래된것을 다시 하드에 스토어하고, 하드에 있는 8 ~ 12K를 다시 로드한다.


하드에 데이터를 저장할 때 파일 시스템, 즉 파일로 저장한다. 이러한 임시 데이터를 스왑파일이라고 한다.



그림이 지존 멋있군


컨텍스트 스위칭은 이러한 경우에도 일어난다.



+ Recent posts