바인딩

  • 함수 호출을 해당 함수의 정의와 결합하는 것을 말한다.  
  • 즉 어떠한 크래스의 객체로 해당 클래스의 멤버함수를 호출하고자 할때 멤버함수와 객체는 결합하여 호출을 하는데, 이떄를 바인딩 된다 라고 하는 것이다.  
  • 함수를 만들어 컴파일을 하면 각각의 코드가 메모리 어딘가에 저장된다.  
  • 그리고 함수를 호출하는 부분에는 그 함수가 저장된 메모리 번지수(주소값)이 저장된다.  

프로그램 실행 → 함수 호출 → 함수가 저장된 주소로 점프 → 함수 실행 → 원래 위치


위 과정에서 함수를 호출하는 부분에 함수가 위치한 메모리 번지로 연결시켜 주는 것을 바인딩(Binding) 이라고 한다.  
바인딩이란 프로그램 소스에 쓰인 각종 내부 요소, 이름, 식별자들에 대해 값 혹은 송석을 확정하는 과정을 말한다. 
빌드 중에 이루어지면, 정적바인딩, 실행 중 이루어지면 정적바인딩 이라고 한다  

함수를 바인딩하는 2가지 방법  


(1) 정적 바인딩 (일반 함수)  

* 컴파일 시간에 호출될 함수로 점프할 주소가 결정되어 바인딩 되는 것.
* 컴파일 시간에 호출될 함수를 미리 정해두는 방법
* 빌드 중에 이루어진다.  

void main()
{
 int iValue = 2;
}


(2) 동적 바인딩 (가상 함수)  

* 실행 파일을 만들 때 바인딩 되지 않고 보류 상태 둔다.
* 실행 시간에 실제로 사용된 객체의 클래스형에 의해 호출될 함수가 결정됩니다.  
* 포인터가 내용이 없다가, 값을 받고 내용일 생겻을떄
* 점프할 메모리 번지를 저장하기 위한 메모리 공간(4 byte)을 가지고 있다가 런타임에 결정.

void main() 
{ 
 A* a = new [어떤것] 
 a->abc(); 
} 


* 단점 : 타입 체킹으로 인한 수행 속도 저하 / 메모리 공간 낭비 => 가급적 정적 바인딩 사용

위 2 가지의 단점이 있음에도 불구하고 동적 바인딩을 하는 이유

   
* 어떤 포인터에 의해 접근되었는 지에 상관없이 참조된 인스턴스의 실제 클래스형에 따라 재정의된 함수 호출이 가능  


 

가상메모리 (virtual Memory)

  • 가상 메모리는 메모리를 관리하는 방법의 하나로
  • 각 프로그램에 실제 메모리 주소가 아닌 가상의 메모리 주소를 주는 방식을 말한다.
  • 이러한 방식은 멀티태스킹 운영 체제에 흔히 사용되며, 실제 주기억장치(RAM)보다 큰 메모리 영역을 제공하는 방법으로 사용된다.

프로그램 실행하는 데 필요한 최소한의 메모리는 얼마인가?

  • 10000바이트의 프로그램이 실행하는데 필요한 최소한의 처리 순서를 다음과 같이 가정해보자.

1) 메모리에서 명령어를 읽어온다.
2) 명령어가 필요로하는 데이터도 메모리에서 읽어온다.
3) 명령이 완료된 후, 결과가 메모리에 기록된다.

예를 들어, 이 각각의 명령처리에 100바이트 씩의 메모리가 할당되어야 한다면, 고작 300바이트만으로 프로그램을 실행 시킬 수 있다는 것이다. 즉, 프로그램 실행과 상관있는 300바이트만 메모리에 올리면 된다는 뜻이다.

나머지 기능을 수행할 코드나 데이터부분의 처리를 위한 메모리 할당은 어떻게 해야 되나?

  • 바로, 보조기억장치(하드디스크 등)이다.
  • 현재 필요하거나 향후 필요한 어플리케이션의 일부분을 필요한만큼 계속 RAM에 올려놓도록 가상 메모리 하부 시스템을 구축하여 동작 할 수 있다면
  • 하드디스크를 RAM의 보조기억장치로 쓰는데 아무 문제가 없는 것이다.

가상 주소(Virtual Address)와 MMU(Memory Management Unit, 메모리 관리 장치)

  • 가상 주소(Virtual Address)는 논리 주소(logical Address)라고도 하며
  • 실제 메모리 상에서 유효한 주소는 물리 주소(Physical Address)또는 실주소(Real Address)라고 한다.
  • 가장 주소 공간의 가상 주소는 MMU에 의해서 실제 물리 주소로 변환된다.
  • 그러나 수억만 바이트에 이르는 메모리를 하나씩 가상 주소에서 물리적 주소로 변역하게 되면 작업부하가 너무 높아지므로, MMU는 RAM을 여러 부분, 즉 일정한 크기를 가진 블록인 페이지(PAGE)로 나누어 각 페이지를 하나의 독립된 항목으로 처리하게 된다.

페이지 폴드

  • 페이지 폴트란 프로그램이 자신의 주소 공간(가상메모리공간)에는 존재하지만
  • 시스템의 RAM에는 현재 없는 데이터나 코드에 접근 시도하였을 경우 발생하는 현상을 말한다.
  • 페이지 폴트가 발생하면 운영체제는 그 데이터를 메모리로 가져와서 마치 페이지 폴트가 전혀 발생하지 않은 것처럼 프로그램이 계속적으로 동작하게 해준다.

  • 앞에서 설명한 바와 같이 CPU는 우선 원하는 주소(12374)를 MMU에게 보내게 된다.
  • 그러나 MMU는 주소 변환 과정에서 페이지 테이블(Page Table)에 이 주소에 대한 항목이 없다고 표시할 것이다. 그 다음 MMU는 CPU를 인터럽트 한 후, 페이지 폴트 처리기라는 소프트웨어가 실행되도록 한다.
  •  페이지 폴트 처리기가 원하는 페이지를 디스크 상 어디에 위치하는지 찾은 후 읽어오게 하는 작업을 한다.

작업 세트

  • 현재 특수 프로세스 전용으로 할당된 물리적 메모리 페이지 그룹을 그 프로세스의 작업세트라고 한다.
  • 메모리 완전 부족 현상을 방지하기 위해서는 프로세스의 작업 세트에서 페이지를 삭제하여 나중에 사용 가능한 여유 공간으로 변경시켜야 하는데, 운영 체제는 다음과 같은 방법을 사용하여 프로세스의 작업 세트를 줄여준다.

-수정된 페이지를 대용량 기억장치의 전용 공간(보통 스와핑(Swapping) 또는 페이징(Paging)공간이라고 부름)에 기록하기 
-수정되지 않은 페이지는 여유 페이지로 표시함(이 페이지는 변경되지 않았으므로 디스크로 가져와 기록할 필요가 없다.)

  • 또한, 운영체제는 모든 프로세스에 적절한 작업 세트를 할당하기 위하여 모든 페이지에 대한 사용 정보를 기록해야 하는데, 이렇게 함으로써

-운영 체제는 어느 페이지가 자주 사용되었으며(이러한 페이지는 메모리에 계속 두어야 한다.)
-어느 페이지가 사용되지 않았는지(따라서 메모리에서 삭제 할 수 있다) 결정할 수 있게 된다.

페이지의 동적 주소 변환

  • 페이징 기법이 적용된 시스템에서 가상주소는 순서쌍(p,d)
  • p는 가상 메모리 내에서 참조될 항목에 속해 있는 페이지 번호(page number)
  • d는 페이지 p 내에서 참조될 항목이 위치하고 있는 곳의 거리(distance)

1) 수행 중인 프로세스가 가상주소 V(p,d)를 참조한다.
2) 페이징 기법을 통해 페이지 p가 페이지 프레임 p'에 있음을 알아낸다.
3) 실주소 r = p' + d를 구한다.

스와핑(Swapping)

  • 스와핑은 수정된 페이지를 하드 디스크와 같은 보조기억 장치 내부의 시스템 스왑 페이지에 기록하는 작업을 한다.
    1) 프로세스에서 페이지가 스와핑 됨
    2) 프로세스가 실행 가능 상태가 되어 스왑된 페이지에 접근 시도함
    3) 페이지 폴트로 메모리로 다시 기록됨(이 때, 대부분의 경우 다른 프로세스 페이지가 스와핑되어 나가게 됨)
    4) 얼마 지나지 않아 이 페이지가 다시 스와핑 됨

  • 이러한 상황이 너무 자주 발생하면 더 많은 CPU 및 입/출력 작업을 무리하게 요구 당하게 되며, 프로그램의 처리 속도가 급격히 떨어지게 된다.

  • 극단적인 경우엔 시스템은 아무런 작업도 실행하지 못 한채, 페이지를 메모리에서 가져오고 빼내는 작업에만 모든 자원을 소모하게 되는 경우도 발생할 수 있다.

@ 이와 같은 경우를 쓰레싱(Thrashing)이라고 하며, 현재 작업을 실행하는데 RAM이 부족하다는 것을 나타낸다.

'프로그래밍 > C++' 카테고리의 다른 글

심볼 Symblos  (0) 2019.07.05
절차지향 + 객체지향  (0) 2019.05.01
CAST 종류 + RTTI  (0) 2019.04.30
virtual 순수가상함수 + 가상함수테이블  (0) 2019.04.30
람다식  (0) 2019.04.29

메모리 단편화

  • RAM에서 메모리의 공간이 작은 조각으로 나뉘어져 사용가능한 메모리가 충분히 존재하지만 할당(사용)이 불가능한 상태를 보고 메모리 단편화가 발생했다고 한다.

단편화의 종류

  1. 내부 단편화(Internal Fragmentation)
    - 메모리를 할당할 때 프로세스가 필요한 양보다 더 큰 메모리가 할당되어서 프로세스에서 사용하는 메모리 공간이 낭비 되는 상황

  2. 외부 단편화(External Fragmentation)
    - 메모리가 할당되고 해제되는 작업이 반복될 때 작은 메모리가 중간중간 존재하게 된다. 이 때 중간중간에 생긴 사용하지 않는 메모리가 많이 존재해서 총 메모리 공간은 충분하지만 실제로 할당할 수 없는 상황

메모리 파편화 문제 해결 방법

1. 페이징(Paging) 기법 (가상메모리사용, 외부 단편화 해결, 내부 단편화 존재)

  • 논리(가상) 메모리는 페이지(Page)이라 불리는 고정 크기의 블록으로 나누어지고, 물리 메모리는 프레임(Frame)라 불리는 페이지과 같은 크기의 블록들로 나누어짐. 보조 메모리 역시 프레임과 같은 크기의 블록들로 나누어짐.
  • 사용자는 하나의 주소를 지정(하드웨어의 의해 페이지 번호와 변위로 분할)
  • 가상메모리를 같은 크기의 단위로 분할
  • 페이지 테이블에는 각 페이지 번호와 그에 해당하는 프레임의 시작 물리 주소를 저장
  • 할당은 항상 프레임의 정수 배로 할당되는데, 이 때 프로세스가 페이지 경계와 일치하지 않는 크기의 메모리를 요구하게 되면 마지막 페이지 프레임은 전부 사용되지 않고 남아버리는 문제가 발생한다.(내부 단편화)
  • 페이징 기법을 사용하면 연속적이지 않은 공간도 활용할 수 있기 때문에 외부 단편화 문제를 해결할 수 있으나, 페이지 단위에 알맞게 꽉채워 쓰는게 아니므로 내부 단편화 문제는 여전히 있다.
  • 페이지 단위를 작게하면 내부 단편화 문제도 해결할 수 있겠지만 대신 page mapping 과정이 많아지므로 오히려 효율이 떨어질 수 있다.
2. 세그멘테이션(Segmentation) 기법(가상메모리사용, 내부 단편화 해결, 외부 단편화 존재)
  • 세그먼트들의 크기가 다르기 때문에 미리 분할해 둘 수 없고 메모리에 적재될 때 빈 공간을 찾아 할당하는 기법이다.
  • 가상메모리를 서로 크기가 다른 논리적 단위인 세그먼트로 분할해서 메모리를 할당하여 실제 메모리 주소로 변환을 하게 된다.
  • 페이징에서처럼 논리 메모리와 물리 메모리를 같은 크기의 블록이 아닌, 서로 다른 크기의 논리적 단위인 세그먼트(Segment)로 분할
  • 사용자가 두 개의 주소로 지정(세그먼트 번호 + 변위)
  • 세그먼트 테이블에는 각 세그먼트의 기준(세그먼트의 시작 물리 주소)과 한계(세그먼트의 길이)를 저장 * 각 세그먼트는 연속적인 공간에 저장되어 있다.
  • 서로 다른 크기의 세그먼트들이 메모리에 적재되고 제거되는 일이 반복되다 보면, 자유 공간들이 많은 수의 작은 조각들로 나누어져 못 쓰게 될 수도 있다.(외부 단편화)
  • 프로세스가 필요한 메모리 만큼 할당해주기 때문에 내부단편화는 일어나지 않으나 여전히 중간에 프로세스가 메모리를 해제하면 생기는 구멍, 즉 [외부 단편화] 문제는 여전히 존재한다.

3. 메모리 풀(Memory Pool) 

  • 필요한 메모리 공간을 필요한 크기, 개수 만큼 사용자가 직접 지정하여 미리 할당받아 놓고 필요할 때마다 사용하고 반납하는 기법
  • 메모리 풀 없이 동적할당과 해제를 반복하면 메모리의 랜덤한(실제로는 알고리즘에 의한) 위치에 할당과 해제가 반복되면서 단편화를 일으킬 수 있겠지만 미리 공간을 할당해놓고 가져다 쓰고 반납하기 때문에 할당과 해제로 인한 외부 단편화가 발생하지 않는다.
  • 또한 필요한 크기만큼 할당을 해놓기 때문에 내부 단편화 또한 생기지 않는다.
  • 하지만 메모리 단편화로 인한 메모리 낭비량보다 메모리 풀을 만들었지만 쓰지 않았을 때 메모리 양이 커질 경우 사용하지 않아야 한다.
  • 메모리의 할당, 해제가 잦은 경우에 메모리 풀을 쓰면 효과적이다.
  • 미리 할당해놓고 사용하지 않는 순간에도 계속 할당해놓으므로 메모리 누수가 있는 방식이다.

메모리 풀(Memory Pool) 구현

  • 큰 메모리 블록(페이지)을 힙으로 부터 할당
  • 할당 받은 페이지를 각 객체의 크기의 블록으로 나눔
  • 각 객체를 위한 블록을 순차적으로 링크
  • 이 때 현 시점에서 할당할 블록을 특정 포인터가 가리키게 함
  • 메모리 요청이 생기면 현재 헤더 포인터가 가리키는 블록을 돌려준다.
  • 할당이 일어난 후 헤더 포인터는 할당 직전에 가리키던 블록이 가리키던 블록을 가리킨다.
  • 사용되던 메모리가 해제되어 메모리 풀로 돌아올 경우 헤더 포인터는 그 블록을 가리키고 방금 전까지 헤더 포인터가 가리키던 블록을 돌아온 블록의 다음 포인터가 가리키게 한다.

절차지향 프로그래밍

  • 정해진 순서의 흐름처리
  • 순차적인 처리가 중요시되며, 유기적으로 연결이 되는 프로그래밍 기법
  • C언어가 이에 해당
  • 컴퓨터의 처리구조와 유사해서 실행속도가 빠르다.
  • 유지보수가 어렵다-> 관리가 어렵다

객체지향 프로그래밍

  • 실제 세계를 모델링하여 프로그래밍 하는 방법.
  • 캡슐화 : public, protected, private
  • 상속 : 클래스 상속, virtual, 가상함수 테이블
  • 다형성 : 오버로드, 오버라이딩
  • 코드를 재사용하기 쉽다.
  • 관리가 편하다.
  • 절차지향보다 느림
  • 설계에 시간이 걸린다.

C 언어는 원시적인 형태로 볼 수 있다. 포인터와 어드레스를 직접사용하는 단계의 언어 객체지향이라고 하는 개념이 등장하고 나서, C 언어에 그 개념이 구현되도록 발전시킨것이 C++

 

 

STATIC CAST

  • 가장 기본적이고, 일반적인 캐스트 연산자
  • 형변환에 대한 타입체크를 런타임에 하지 않고, 컴파일타임에 정적으로 수행한다.

dynamic_cast

  • 형변환 타입체크를 런타임 중 RTTI (중요함)을 한다 - > 그 만큼 비용이 듬
  • 상속 클래스 간의 캐스팅 유용
  • 실행 중에 캐스트의 대상이 되는 데이터를 능동적으로 판단하여 실행 코드가 캐스트를 수행합니다. 
  • 변환이 잘못된 경우'에 'NULL'을 던진다

 


> RTTI  

* 실행시간 타입정보 (Run Time Type Information)
* 실행 시간에 객체들의 정보(타입)를 얻는 표준화된 방법을 제공
* 런타임에 타입의 정보를 가지고 오류를 검사하는 목적으로 사용
* RTTI는 가상 함수들을 가지고 있는 클래스 계층에 대해서만 사용할 수 있다. 
* 이유는 그들이 파생 객체들의 주소를 기초 클래스 포인터들에 대입해야 하는 유일한 클래스 계층이기 때문이다.
* 실행 시간에 객체들의 정보를 얻고자할 때 사용됩니다.
* 런타임에 타입을 검사한다.

> RTTI의 구성요소  
  
* type_info (구조체)      : 실행 시간에 확인하고자 하는 타입에 대한 정보 저장 구조체.
* typeid (연산자)          : 객체 타입을 식별하고 반환하는 연산자
* dynamic_cast (연산자) : 실행시간 중 객체를 형변환하는 연산자. 


const_cast

  • 상수포인터를 비상수 지시포인터로 바꾸고 싶을떄 사용한다. (const 제거)
  • 캐스팅의 실패는 NULL(포인터)이거나 예외(참조자)를 보고 판별할 수 있다.
  • 상속관계에서만 사용가능함, 다형성을 띄지 않은 객체간 변환은 불가능하며, 시도시 컴파일 에러

reinterpret_cast

  • 임의의 포인터 타입끼리 변환을 허용하는 캐스트 연산자

'프로그래밍 > C++' 카테고리의 다른 글

절차지향 + 객체지향  (0) 2019.05.01
페이지 폴트 + 가상메모리 시스템  (0) 2019.05.01
virtual 순수가상함수 + 가상함수테이블  (0) 2019.04.30
람다식  (0) 2019.04.29
함수 객체  (0) 2019.04.29

+ Recent posts