메모리는 접근속도, 용량, 비용간에 상호 절충(tradeoff)관계가 있다.
위의 사진은 a-d까지 내려감에 따라 성립되는 것들을 적어놓은 것이다. 보면은 서로가 서로를 보완하는 관계이다.
CPU와 비슷한 속도인 캐시 메모리의 경우 너무 비싸기 때문에 캐시의 용량은 그리 크지 않은 캐시메모리를 사용하되, 주기억장치(RAM)의 경우에는 캐시보다 느리지만 가성비가 높아 캐시 메모리와 함께 사용하면 시너지를 낸다.
어떤 메모리의 한 지점을 엑세스할 때 메모리는 CPU로 직접 보내는 것이 아니라 1kb정도의 단위로 캐시 메모리에 보내고 캐시를 통해 CPU로 가기 때문에 CPU가 메모리를 직접 참조하는 경우는 없다. 항상 캐시메모리에 요청을 하고 캐시 메모리에 해당 데이터가 있으면 hit이라고 하고 없으면 miss라고 한다. 여기서 만약 100번을 요청해서 90번의 hit와 10번의 miss가 일어났다면 90%의 적중률(hit ratio) 를 갖게 된다. 이러한 적중률은 1mb정도의 캐시 메모리만 갖고 있으면 거의 99%가 넘는 적중률을 가진다.
참조지역성(locality of reference)
d. 처리기에 의한 메모리 접근 회수 감소의 성립 근거.
프로그램에서 메모리의 어떤 지점을 읽었으면 가까운 시간 내에 그 지점을 다시 읽을 가능성이 높고 비슷한 곳에 메모리가 몰리는 경향
이러한 적중률은 각 단계에서 메모리 접근 시간에 영향을 미친다. 더 가까운 메모리의 접근 비율이 높아질수록 평균 총 접근시간은 더 가까운 메모리의 접근 시간에 가까워진다. 그렇기 때문에 우리는 적당한 수준의 캐시 메모리를 두고 그 다음 단계인 메모리(RAM)을 가성비를 위해 함께 두는 것이다.
자기디스크, CD-ROM, CD-RW, DVD-RW, DVD-RAM, Blu-Ray 등 디스크의 경우는 메모리보다 느리지만 저장 용량이 높다. CD와 DVD의 경우 레이저를 표면에 쏴 빛이 반사되는 경우와 반사되지 않는 경우를 구분하여 기록하는 방식이다.
cpu는 디스크에 있는 내용들을 읽을 때 나중에 다시 읽는 경우 계속해서 그때그때 새롭게 읽게 되면 속도가 느리므로 버퍼라는 곳에 메모리에서 읽은 내용을 일부 보관한다.
자기 테이프의 경우가 오프라인 저장 장치에 해당한다. 자기 테이프는 우리가 데이터를 저장할 때 디스크와 같이 회전하면서 임의의 위치에 빠른 속도로 저장한다. 녹음 테이프와 같은 원리이다.
테이프는 얇은 비닐로 이루어져 있기 때문에 분해해보면 엄청 긴 테이프를 볼 수 있다. 그렇기에 해당 테이프에 데이터를 저장한다면 대용량의 데이터가 저장이 가능한 것이다. 하지만 대용량의 데이터가 저장이 가능한 만큼 감겨져있는 테이프가 많기 때문에 데이터를 따르게 읽을 수가 없다. 그래서 보통 백업의 용도로 많이 사용한다.
캐시의 구조는 위와 같이 되어 있다.
블록과 같은 구조처럼 되어있는걸 볼 수 있는데 여기에 있는 칸마다 1kb 혹은 설계된 캐시메모리에 따라서 일정 단위가 블록에 들어오게 되고 블록 내에 있는 태그는 주소를 빠르게 검사하기 위한 목적으로 만들어진 것이다.
캐시 메모리는 CPU만큼의 속도를 자랑하지만 그만큼 비싸다는게 단점이다. 따라서 각 캐시를 단계별로 나누어 속도에는 차이가 있지만 가성비로는 가장 최고의 효율을 낼 수 있도록 설계된다.
단일 캐시의 경우 직접적으로 CPU와의 데이터 교환이 이루어지기 때문에 빠른 것을 볼 수 있다. 또한 참조 지역성 때문에, 어떤 메모리의 참조를 위해 하나의 데이터 블록을 캐시로 가져오게 되면, 그 이후의 참조는 그 블록 내에 있는 데이터가 될 확률이 높다.
삼단계 캐시 구조의 경우는 캐시가 3단계로 이루어져 있다. 단계 1의 L1 캐시는 CPU만큼 빠르고, L2는 그보다는 조금 더 느리며, L3는 L2보다는 조금 느리다. 하지만 느린 만큼 단가는 떨어지기 때문에 해당 캐시를 하나 두는 것보다는 저렴하고 비슷한 효율을 낼 수 있도록 비슷한 수준으로 유지시킬 수 있다.
가장 먼저 캐시는 CPU로부터 입력, 즉 RA(Read Address)를 받는다. 그러면 CPU는 캐시 메모리에서 RA를 포함한 블록이 있는지 확인한다. RA를 포함한 블록이 있을 경우(hit)는 캐시에서 읽어들이면 되고, 아닐 경우(miss)에는 RA를 포함한 주기억장치에 있는 블록을 캐시에 갖다 쓰기 위해 캐시 슬롯을 할당한다.
캐시 슬롯은 비어있을 수도 있지만 슬롯이 모두 차 있는 경우도 있다. 이런 경우에 미래에 참조될 가능성이 가장 낮은 캐시를 쫓아내는데, 이를 time locality, 즉 LRU(Least Recently Used)가 높은 캐시를 쫓아내게 되는데, 이러한 쫓겨나는 캐시 내의 블록을 victim이라고 한다. 해당 victim이 write 연산이 이루어져 실제 메모리와의 정보가 서로 다르다면, 이러한 블록을 dirty block이라고 한다. 이러한 경우에 flush 하여 메모리에 반영하는 과정이 필요하다.
캐시슬롯을 할당 후 아래에 있는 블록을
프로그램된 입출력은 메모리에 I/O 엑세스하는 방법 중 하나이다.
I/O장치는 키보드, 마우스, 모니터 등 다양하다. 이러한 다양한 I/O장치에 각각의 명령어를 보내야 한다. 각각의 I/O장치는 해당 장치 안에 CPU가 있고(장치 내의 CPU는 컨트롤러라고 한다), 레지스터가 있다. 이러한 레지스터에 명령어를 쓰면 해당 명령어에 해당하는 동작을 해야 하는데 문제는 이러한 장치들이 명령어를 전달하는 방법이 다 다르다. 그렇게 된다면 각각 다른 명령어에 대응해서 받는 로직은 너무 복잡해지게 된다.
이를 해결하기 위해 메모리의 특정 부분을 각 레지스터마다 매핑(Mapping)을 해놓는 것이다. 해당 메모리가 비어 있다가 엑세스하게 되면 운영체제에서 자동으로 해당 주소에다가 값을 넣을 수 있게끔 하는 것이다. 각 I/O장치마다 특정 주소에 매핑되어 있기 때문에 각 I/O 장치의 주소만 알면 되고, 표준적인 방법을 가질 수 있다. 이러한 매핑의 경우는 운영체제가 알아서 핸들링을 해주는 것이고 이를 프로그램된 입출력(Programmed I/O)라고 한다.
하지만 프로세서에 대해 인터럽트를 포함한 더 이상의 어떤 처리도 행하지 않기 때문에 처리기가 I/O 연산이 언제 완료되는지 알지 못한다. 이를 위해 프로세서는 I/O 연산이 완료될 때까지 주기적으로 점검해야 하고, 이러한 상태를 I/O 데이터가 들어오기 전 대기 기간에도 계속 수행해야 하기 때문에 전체 시스템의 성능 수준이 현저히 떨어질 수 있다는 단점이 있다.
다른 대안으로 나온 기법이다. 앞서 말했던 인터럽트를 생각하면 된다.
프로세서가 모듈에게 I/O 명령을 보낸 후 다른 작업을 계속 수행하는 것이다. 프로세서는 데이터 전송 후 이전에 수행하던 작업을 재개하는 방식이다.
하지만 이러한 방식은 데이터가 많을 경우에 문제가 된다. 많은 데이터를 읽고 메모리에 저장하기까지의 과정을 계속해서 CPU가 관여하게 되는데, 두 가지의 문제점이 있다.
1. I/O 전송률이 프로세서가 장치를 점검하고 서비스하는 속도에 제한을 받음
2. I/O 전송마다 많은 명령어들이 수행됨
I/O 장치와 메모리를 직접 회로로 연결해놓고 DMA 모듈에 I/O 연산을 위임하는 방식이다. 프로세서를 거치지 않고 그대로 메모리에 접근하기 때문에 CPU는 전송의 시작과 끝에 걸리는 인터럽트에만 관여한다. 해당 기능은 시스템 버스 상의 별도 모듈이 있거나 I/O 모듈에 포함된다.
DMA 모듈에 입출력을 명령하는 정보는 다음과 같다.
이러한 DMA 모듈은 데이터를 메모리로부터 또는 메모리로 전송하기 위하여 버스를 제어할 필요가 있다. 프로세서는 한 번의 버스 사이클(버스를 통해 한 워드 전송하는데 걸리는 시간)동안 일시정지하게 되고, 이 때문에 프로세서의 수행이 조금 느려지게 되는 단점이 있지만 다수의 I/O전송의 경우 DMA가 훨씬 효과적이다.
하나의 컴퓨터에 CPU를 여러개 붙여서 여러 개의 기계 명령어를 동시에 실행할 수 있다. 이러한 멀티프로세서는 이런 특징을 갖는다.
1. 두 개 이상 유사한 수행 능력을 갖는 프로세서로 구성
2. 버스나 내부 연결 방식에 의해 상호 연결된 주기억장치와 I/O 장치 공유
3. 동일한 장치에 이르는 경로들을 제공하는 채널이 동일하거나 달라도 모든 프로세서는 I/O 장치 접근 공유
4. 모든 처리기는 동일한 기능 수행
5. 시스템은 프로세서들과 작업, 태스크 파일, 그리고 데이터 요소 수준에서 프로그램들 간의 상호작용을ㄹ 제공하는 하나의 통합된 운영체제에 의해 제어
멀티프로세서 구조를 가짐으로써 단일 프로세서에 비해 가질 수 있는 장점은 확실하다.
1. 성능 : 단일 처리기보다 동시에 처리하는 연산이 많으므로 성능 향상
2. 가용성: 모든 처리기들이 동일한 기능 수행 -> 하나의 처리기가 고장나도 동작 가능
3. 점진적 확장: 성능 향상 -> 처리기의 추가 설치 가능
4. 크기 조정(scaling) : 벤더들은 처리기의 수에 따라 가격과 성능이 다른 다양한 제품 공급
하지만 이러한 장점들을 살리기 위해서는 운영체제가 이러한 멀티프로세서의 활용성을 높여주는 도구와 기능이 존재해야 한다.
하지만 이러한 멀티프로세서에도 문제가 있는다. 요즘 컴퓨터들은 한 단계 이상의 캐시 메모리를 가지고 있다. 각 지역 캐시들은 주기억장치 일부 이미지를 포함하고 있어 하나의 워드가 하나의 캐시에서 변경되면, 다른 캐시에 있는 워드를 무효화시킬 수 있는 문제가 발생한다.
이러한 현상을 방지하기 위해 다른 프로세서들은 갱신이 발생하면 변경되어야 하며 이는 일반적으로 하드웨어 분야에서 논의된다.
칩 멀티프로세서로 알려져 있으며 코어라고 불리는 두 개 이상의 프로세서가 다이(die)라고 불리는 단일 실리콘 조각에 결합되어 있는 형태이다.
쉽게 설명하면 연산들을 하는 회로들을 CPU 안에 여러개 복제해놓은 거라고 생각하면 된다. 위의 그림에서는 코어가 6개가 있기 때문에 기계어 명령어를 6개까지 동시에 실행할 수 있다.
하지만 만약에 이전 연산을 이용해서 연산을 하는 경우, 동시에 실행할 수 없는 경우가 생기기도 하는데, 이러한 경우를 잘 고르면서도 코어들을 효율적으로 상관할 수 있게 하려면 컴파일러를 통해 순서를 바꿔 상호 독립적인 작업을 수행할 수 있도록 만들 필요가 있다.