Post

[한 권으로 읽는 컴퓨터 구조와 프로그래밍]5장 컴퓨터 아키텍처와 운영체제

컴퓨터는 어떻게 프로그램과 메모리를 조직적으로 관리할까

서론

  • 컴퓨터 아키텍처: 컴퓨터의 여러 구성요소를 배치하는 방법
  • 멀티태스킹 ⇒ 여러 프로그램 실행(또는 실행하는 것처럼 보이게) ⇒ 프로그램 실행 제어를 위한 감독 프로그램 필요 ⇒ 운영체제(operating system, OS)

기본적인 구조 요소들

  • 가장 흔한 두가지 컴퓨터 구조
    • 두 구조의 유일한 차이는 메모리 배열
    • 폰 노이만 구조
      • 데이터 버스와 주소 버스가 하나뿐 ⇒ 메모리에서 동시에 명령어와 데이터를 가져올 수 없어 약간 더 느림
    • 하버드 구조
      • 동시에 명령어와 데이터를 가져올 수 있어 좀 더 빠름
      • 두 번째 메모리를 처리하기 위한 버스가 추가로 필요

프로세서 코어

  • 가장 흔한 두 컴퓨터 구조는 모두 CPU가 하나뿐
    • CPU는 ALU, 레지스터, 실행 장치의 조합
  • 멀티프로세서 시스템
    • 단일 CPU보다 더 좋은 성능을 얻어내기 위한 방법
    • 여러 CPU를 활용할 수 있도록 프로그램을 병렬화 하는건 어려움
      • 그래픽 사용자 인터페이스(GUI)를 제공하는 초기 워크스테이션은 여러 프로그램 동시 실행 가능이 큰 이점이었기에 어렵지만 그래도 이를 이용
  • 전력 장벽(power wall)
    • 반도체 회로 크기가 줄어들고 기계는 빨라지며 전역을 더 많이 소모하고 단위 면적당 열 발생이 많아진 현상
    • 열로 인해 회로가 녹지 않으면서 소형화, 고성능화 하기 어려워짐
  • 전력 장벽을 극복한 해결책이 프로세스 코어이다.
    • 예전에 CPU라고 부르던 것이 요즘은 프로세서 코어
    • 코어가 여러개면 멀티코어 프로세서

마이크로프로세서와 마이크로컴퓨터

  • 물리적인 패키징에 따른 구조
    • 마이크로프로세서
      • 메모리와 I/O가 프로세서 코어와 같은 패키지에 들어있지 않는 경우
      • 칩 안에서 메모리가 차지하는 영역이 크기 때문에 일반적으로 마이크로컴퓨터보다 강력
      • 보통 큰 시스템에 들어가는 부품
    • 마이크로컴퓨터
      • 모든 요소를 한 칩 안에 패키징하는 경우
      • 마이크로컨트롤러: 마이크로컴퓨터가 가리키는 대상
      • 식기세척기 등에서 찾을 수 있는 단일 칩으로 된 작은 컴퓨터
  • 단일 칩 시스템(System on a Chip, SoC)
    • 더 복잡한 마이크로 컴퓨터
    • 상대적으로 간단한 온칩(on-chip, 칩 내부에 존재하는) I/O를 제공하는 대신, WiFi 회로 등 더 복잡한 장치 포함
    • 핸드폰 등에서 볼 수 있다.

프로시저, 서브루틴, 함수

  • 프로시저, 서브루틴, 함수 모두 코드를 재사용하는 주요 수단을 뜻하는 같은 말이다.
  • 함수를 실행하려면 함수를 호출하는 부분에서 함수를 실행하고 다시 원래 자리로 돌아와야 한다 ⇒ 어디서 함수로 들어갔는지 기억해야 한다 ⇒ 이 위치가 프로그램 카운터의 값
    • 함수 실행 예시
      1. 함수 실행 후 돌아와야 하는 주소 계산
        1. 주소 계산과 다른 여러 명령을 위해 차지하는 주소를 감안하여 반환 주소 결정
      2. 계산된 주소를 함수에서 돌아올 주소 저장을 위해 미리 확보해둔 메모리 위치에 저장
      3. 함수 호출
      4. 함수 실행
      5. 간접 분기(반환)
      6. 함수 반환 뒤 실행을 이어감
  • 함수 호출에는 많은 작업이 필요해 일반적으로 이런 과정을 돕는 명령어가 존재한다.
    • ARM 프로세서에는 링크 레지스터를 사용해 분기(Branch with Link, BL) 명령어가 존재
      • 함수로 호출하는 명령어와 현재 명령어의 다음 위치를 저장하는 명령어를 합친 것

스택

  • 재귀적 분할로 JPEG를 압축하는 경우를 예로 든다면,
    • 정사각평 안의 픽셀이 모두 같은 색이 될 때까지 분해
    • 이를 트리로 표현할 수 있다.
      • 트리: 유향 비순환 그래프(directed acyclic graph, DAG)
        • 화살표가 있으면 거슬러 올라갈 수 없기 때문에 유향
      • 쿼드 트리: 각 노드에서 가지가 4개 뻗어나가는 트리로 공간 데이터 구조(spatial data structure)에 속한다.
  • 위 방법을 사용하는 경우, 함수의 반환값을 저장할 위치는 한 군데 ⇒ 이러한 재귀 함수는 이미 들어 있던 반환값을 덮어써서 돌아갈 위치를 잃어버려 자기 자신을 호출할 수 없음
    • 따라서 반환 주소를 여럿 저장할 수 있어야 하고, 호출된 지점으로 반환할 때 어떤 주소를 선택할지 결정할 수 있어야 한다.
    • 이를 위해 사용하는게 스택
  • Last In, First Out
  • push해 넣고 pop 해서 제거
    • 스택 오버플로: 더 넣을 공간이 없는데 push하는 경우
    • 스택 언더플로: 빈 스택에서 pop하는 경우
  • 한계 레지스터: 스택 오버플로를 항상 검사하지 않도록 도움
  • 스택 프레임: 함수가 호출될 때마다 스택에 저장되는 데이터의 모음
    • 반환 주소 뿐만 아니라 지역 변수도 스택에 함께 저장

인터럽트

  • 순서도: 작업이 이뤄지는 순서를 표현하는 다이어그램
  • 폴링: 하나의 작업을 더 작은 하위 작업으로 나누고 각 하위 작업 사이에 다른 과정(주의를 기울여야 하는 외부 요소)을 수행
    • 다른 과정을 자주 수행해 원하는 결과를 얻을 수는 있겠지만 매번 다른 과정을 수행해 시간이 오래 걸림
    • 따라서 실행 중인 프로그램을 잠깐 중단(interrupt)시켜서 주의를 기울여야 하는 외부의 요소에 대응할 수 있는 방법이 필요

인터럽트 시스템

  • 적절한 신호가 들어오면 CPU 실행을 잠깐 중단시킬 수 있는 핀이나 전기 연결을 포함
    • 핀: 칩에 연결된 전기적 접점
  • 많은 프로세서 칩(특히 마이크로컴퓨터)에는 통합 주변장치(integrated peripheral 또는 on-chip I/O 장치)이 들어있고 이러한 장치는 내부적으로 인터렙트 시스템에 연결됨
  • 작동 방식
    1. CPU가 주의를 기울여야 하는 주변 장치는 인터럽트 요청을 생성
    2. 프로세서는 보통 현재 실행 중인 명령어를 끝까지 실행
    3. 프로세서는 현재 실행 중인 프로그램을 잠시 중단시키고 인터럽트 핸들러라는 프로그램을 실행
      1. 인터럽트 핸들러는 함수다.
    4. 인터럽트 핸들러가 필요한 작업을 다 마치면 원래 실행중이던 프로그램이 중단된 위치부터 다시 실행
  • 고려해야 할 사항
    • 인터럽트에 대한 응답 시간(response time)
      • 서비스: 인터럽트에 대응하는 것
      • 인터럽트 처리를 정해진 시간 안에 끝내야 한다.
    • 인터럽트를 서비스한 뒤 돌아오기 위해 현재 상태 저장
      • 인터럽스 시스템은 서비스 후 돌아올 프로그램 위치를 스택에 저장
      • 자신이 덮어쓸 레지스터를 모두 저장해야 한다.
      • 이러면 저장해야 하는 요소를 인터럽트 핸들러가 최소화해서 가장 빨리 인터럽트를 서비스할 수 있다.
        • 인터럽트 핸들러가 인터럽트 당하는 프로그램과 인터럽트 하는 프로그램 사이의 데이터를 모두 처리하니까 저장해야 하는 요소가 최소화 되는것
  • 일반적으로 인터럽트 핸들러 주소가 저장될 메모리 주소가 존재
    • 이 주소에는 여러 인터렙트 벡터가 들어 있고 각 인터럽트 벡터는 CPU가 지원하는 각 인터럽트에 대한 핸들러 주소를 지정
      • 인터럽트 벡터: 메모리 위치를 가르키는 포인터
    • 인터럽트가 일어나면 컴퓨터는 인터럽트 벡터에 저장된 주소를 보고 제어를 그 주소로 옮김
      • 프로그램 카운터를 인터럽트 벡터에 저장된 값으로 설정
    • 많은 기계는 물리적인 주소를 벗어나는 주소를 사용하려 하거나, 스택 오버플로가 일어나는 등의 예외 상황에 대해 인터럽트 벡터 제공
  • 인터럽트를 중단시킬 수 있는 마스크(mask)가 있을 수 있음
  • 인터럽트간에는 우선순위가 있을 수 있음
    • 우선순위가 낮은 인터럽트를 서비스하는 인터럽트 핸들러는 높은 우선순위의 인터럽트가 발생시 일시 중단될 수 있음
  • 일정 시간이 지나면 또는 일정 간격으로 인터럽트를 발생하는 내장 타이머가 있을 수 있음
  • 운영체제는 다른 일반 프로그램이 접근 못하는 물리적(하드웨어) 인터럽트에 접근 가능
  • 운영체제는 가상 인터럽트나 소프트웨어 인터럽트 시스템을 제공하기도 함
    • 유닉스 운영체제는 시그널 매커니즘 제공
    • 최근 개발된 시스템은 인터럽트 시스템을 이벤트라 함

상대 주소 지정

  • 운영체제 또는 운영체제 커널: 여러 프로그램을 동시에 실행할 때 각 프로그램을 서로 전환시켜줄 수 있는 관리자 프로그램
  • OS를 시스템 프로그램, OS가 관리하는 다른 모든 프로그램을 사용자 프로그램 또는 프로세스라고 한다.

시분할(time slicing)

  • 사용자 프로그램의 실행 시간을 조절하는 스케줄링 기법
  • 시간을 정해진 간격으로 나누고 정해진 시간 간격 동안 사용자 프로그램 실행
  • 사용자 프로그램 상태 또는 문맥: 레지스터의 상태와 프로그램이 사용 중인 메모리 상태
    • 메모리에는 스택도 포함
  • 프로그램을 메모리로 불러들이려면 시간이 걸리기 때문에 아주 느리다.
    • 따라서 각 프로그램에게 각기 다른 공간을 허용하면 더 빠르게 시분할 실행 가능

인덱스 레지스터

  • 1000번지에서 실행되도록 만들어진 프로그램이 있다고 가정하자. 절대 주소 지정을 사용하는 컴퓨터로 이 프로그램을 2000번지에서 읽어 들이면 제대로 실행하지 않는다.
  • 인덱스 레지스터를 사용하면 인덱스 레지스터의 값을 명령어에 들어있는 주소와 더해서 유효 주소를 계산한다.
  • 사용자 프로그램이  2000번지에서 실행되도록 만들어졌다면 OS는이 프로그램을 3000번지에서 실행하기 위한 인덱스 레지스터를 2000으로 설정할 수 있다.
    • 인덱스 레지스터 2000과 1000을 더하면 3000번지가 되니.

상대 주소 지정

  • 명령어 주소를 기준으로 하는 상대적인 주소
  • 이러한 상대 주소 계산은 프로그래밍 언어 도구가 알아서 함
  • 상대 주소 지정을 사용하면 프로그램을 원하는 위치로 자유롭게 재배치할 수 있다.

메모리 관리 장치(memory management unit, MMU)

배경

  • 통신작업은 백그라운드에서 실행돼야 하기 때문에 멀티태스킹이 필수
  • 문제가 생겨 메모리가 덮여 써지거나 누군가 다른 실행중인 프로그램을 훔쳐 본다거나 하는 일을 막아야 함
    • 인덱스 레지스터와 상대 주소 지정은 멀티테스크에 도움이 되지만 이를 막을 순 없음

MMU

  • MMU가 들어있는 프로그램은 가상주소 (virtual address)와 물리 주소(physical address)를 구분
    • 프로그램은 가상주소를 사용해 작성되고 MMU는 가상주소를 물리주소로 변환
  • MMU로 인해 폰 노이만 구조와 하버드 구조 구분 사라짐
    • 단일 메모리 버스만 사용하는 폰 노이만 구조의 시스템도 명령어 메모리와 데이터 메모리를 분리해 제공 가능

MMU와 인덱스 레지스터의 차이

  • 보통 MMU의 가상 주소 범위는 물리적 메모리 주소보다 크다
    • MMU는 가상 메모리 주소를 두 부분으로 나눔
      • 주소 하위(LSB 쪽)부분은 물리적 주소범위와 같음
      • 주소 상위(MSB 쪽)부분은 페이지 테이블이라는 RAM 영역을 통해 주소를 변환
        • 페이지: 메모리를 일정한 바이트 크기로 분할 한 것

페이지 테이블

  • 각 페이지가 물리 메모리상에서 차지하는 실제 위치정보로, 이를 통해 가상메모리에서 시작하는 프로그램을 물리메모리에 넣을 수 있다.
    • 모든 내용은 페이지 경계 안에 들어있어야 한다.
    • 프로그램 입장에서 가상 메모리는 연속적인 것 처럼 보이지만 실제 물리 메모리 상 위치는 연속적일 필요 없다.
      • 프로그램 실행 중에 프로그램이 위치한 물리적 메모리 공간이 바뀔수도 있다.
    • 페이지 테이블의 내용은 프로그램 문맥의 일부이다.
    • 페이지 크기를 줄이면 페이지 테이블 크기가 늘어난다.
  • 공유 메모리 기능: 프로그램들이 협력하는 경우 여러 프로그램의 가상 메모리 중 일부가 같은 물리메모리를 함께 사용
  • 페이지 테이블 항목(page table entry)
    • 현대 프로세서의 MMU는 페이지 테이블 크기 고정
    • 주 메모리에 저장되거나 디스크에 저장
    • 일부를 필요할 때에만 자신의 페이지 테이블로 읽음
  • 페이지 테이블 제어 비트
    • 일부 MMU 설계에서 제공
    • 실행 불가 비트(no execute bit)
      • 어떤 페이지에 이 비트가 설정되어 있는 경우 CPU가 페이지의 명령어 실행 불가
        • 프로그램이 실수로 자기 데이터를 실행하는 경우 방지
        • 데이터 부분을 실행할 수 있으면 프로그램을 실행하는 시점에 마음대로 바꿔쓸 수 있어 보안 문제도 발생
    • 읽기 전용으로 만드는 비트
  • 페이지 폴트(page fault) 예외
    • 프로그램이 물리적 메모리에 연관되지 않은 주소에 접근할때 발생하는 예외
    • 스택 오버플로 등에 유용하게 사용

가상 메모리

  • OS는 프로그램들 사이의 자원 분배를 관리하며 메모리는 이 자원에 포함된다 ⇒ OS는 MMU를 사용해 사용자 프로그램에게 가상 메모리를 제공한다.

요구불 페이징(demand paging)

  • 스왑 아웃과 스왑 인 과정을 통해 페이지를 처리하는 방식
  • 스왑 아웃(swap out): 프로그램이 요청한 메모리가 사용 가능한 물리적 메모리보다 더 큰 경우 OS는 현재 필요하지 않은 메모리 페이지를 더 느리지만 더 용량이 큰 대용량 저장장치인 디스크로 옮김
  • 스왑 인: 스왑 아웃한 페이지에 프로그램이 접근하면 OS가 다시 메모리로 불러들임

최소 최근 사용(least recently used, LRU)

  • 스와핑이 일어나면 시스템 성능이 저하되지만 프로그램을 실행 못하는 것보단 낫다.
  • 최소 최근 사용은 성능 저하를 막기 위해 페이지 접근을 추적해 스왑 아웃할 페이지를 결정하는 알고리즘
  • 최근에 가장 자주 사용된 페이지는 물리 메모리에 남겨두고 최근에 가장 덜 사용된 페이지를 스왑 아웃함

시스템 공간과 사용자 공간

문제

  • 사용자 프로그램이 OS가 맞춘 인터럽트 타이머를 변경하거나, MMU의 설정을 바꿔버린다면 MMU는 프로그램을 서로 격리할 수 없다.

해결

  • CPU에는 컴퓨터가 시스템 모드에 있는지 사용자 모드에 있는지 결정하는 비트가 어떤 레지스터 안에 들어 있다.
  • I/O를 처리하는 명령어 등 일부 명령어는 특권(privileged) 명령어라서 오직 시스템 모드에서만 실행할 수 있다.
  • 트랩, 시스템 콜과 같은 특별한 명령어를 이용하면 사용자 모드에서 실행중인 프로그램이 시스템 모드 프로그램(즉 운영체제)에게 요청을 보낼 수 있다.

장점

  • 사용자 프로그램으로부터 운영체제를, 사용자 프로그램을 다른 사용자 프로그램으로부터 보호한다.
  • 사용자 프로그램이 MMU 등 요소에 손을 댈 수 없어 운영체제가 프로그램에 대한 자원 할당을 전적으로 제어할 수 있다.
    • 하드웨어 예외는 오직 시스템 공간에서만 처리된다.

메모리 계층과 성능

문제

  • 이제는 CPU가 너무 빨라졌지만 메모리는 그러지 못해 CPU가 메모리를 기다려야 한다.

사용 메모리 종류

  • 레지스터: 프로세서에 들어있는 빠르고 비싼 메모리
    • 공간이 제일 작지만 빨리 접근 가능
  • 주 메모리: 프로세서가 주로 통신하는, 일반적으로 RAM으로 이뤄진 메모리
    • 프로세서보다 느리다
    • CPU와 주 메모리 사이에는 작은 덩어리가 오간다.
  • 디스크 드라이브 등의 대량 저장장치: 프로세서보다 훨씬 느림
    • ROM
    • 공간이 제일 크지만 제일 느림
    • CPU와 디스크 사이에는 큰 덩어리가 오간다.

캐시

  • 아주 빠른 온칩 메모리로써 CPU에 추가되는 하드웨어
    • 주 메모리보다는 작지만 더 빠르고, 프로세서와 같은 속도로 작동
  • 주 메모리에서 데이터를 가져와 캐시에 채운다.
    • CPU 메모리 컨트롤러 하드웨어는 메모리에서 연속된 열에 있는 데이터를 한꺼번에 가져와 채운다.
      • 열에 있는 데이터를 가져오는 이유
        • 열로 DRAM을 읽는게 행으로 읽는 것보다 더 빠르기 때문
        • 분기가 없는 경우 프로그램 메모리를 순서대로 읽기 때문
        • 프로그램이 사용하는 데이터가 한데 모여 있는 경우가 많기 때문
        • 대부분은 연속된 위치에 있는 데이터가 필요하기 때문
  • 캐시 실패(cache miss): CPU가 캐시에서 어떤 내용(어떤 주 메모리 주소에 들어있는 내용)을 찾았는데 캐시에 그 데이터가 없어 메모리를 읽어야 하는 경우
    • 캐시 실패가 일어나도 CPU는 고속 메모리 접근 모드가 가능해서 캐시를 사용하는게 유리
  • 캐시 적중(cache hit): CPU가 원하는 내용을 캐시에서 찾은 경우

캐시의 계층

  • 캐시가 CPU에서 멀어질수록 더 느려지고 더 커진다.
  • 여러 구성 요소
    • 디스패처: 여러 크기의 데이터 덩어리를 가져오고, 채워 넣는 일을 담당하는 논리 회로
    • 분기 예측(branch prediction): 캐시를 프리페치해 준비하기 위해 조건 분기 명령어 결과를 예측하는 회로
      • 프리페치(prefetch): 미리 페치해 가져옴
    • 순서를 벗어나는 실행(out-of-order execution) 처리 회로
  • 캐시 일관성
    • 어떤 메모리 위치의 데이터를 한 프로세서나 코어가 변경했을 때 이를 다른 프로세서 또는 코어가 어떻게 알 수 있는가? 캐시 일관성을 통해.
    • 라이트 스루(write through): 데이터를 캐시에 기록하는 동시에 메모리에도 기록
      • 단점: 캐시를 사용하는 장점 대부분을 없앰

코프로세서

  • 몇 가지 연산을 코프로세서 회로에 위임하면 프로세서 코어가 더 많은 공간을 확보할 수 있다.

직접 메모리 접근(direct memory access, DMA)

  • 주 메모리와 디스크 사이에 단순한 데이터 복사가 자주 일어난다.
    • 프로그램이 디스크 드라이브 등의 너 느리고 값싼 메모리에 들어있어 이를 불러올 때
    • 가상 메모리 시스템이 스와핑을 하며 디스크에 메모리 내용을 저장하거나 디스크에서 메모리로 데이터나 프로그램을 읽어올 때
    • 디스크는 바이트 단위로 접근이 불가능하므로
  • 일부 코프로세서는 이러한 데이터 접근(복사 등)을 담당하고 이러한 방식을 DMA라 한다.

메모리상의 데이터 배치

정적 데이터

  • 메모리에 담긴, 필요한 크기를 알고있는 데이터
  • MMU가 없을 때 폰 노이만 구조와 하버드 구조의 메모리 배치 차이
    • 하버드 구조에서는 명령어가 별도의 메모리에 존재

동적 데이터

  • 프로그램 실행 전까지는 크기를 알 수 없는 데이터
  • 정적 데이터가 차지하는 영역 위 영역에 쌓이며 이를 힙이라 함
    • 스택은 아래로 자라며 힙은 위로 자란다.
    • 스택과 충돌하지 않게 해야한다.

메모리 배치 변형

  • 일부 프로세서는 메모리의 시작 부분이나 끝 부분에 인터럽트 벡터를 저장하는 영역이나 온칩 I/O 장지를 제어하기 위한 레지스터를 확보

MMU가 쓰이는 경우

  • 마이크로컴퓨터는 MMU가 없는 경우가 많아 위와 같은 메모리 배치가 흔하다.
  • MMU가 쓰이는 경우에는 명령, 데이터, 스택이 각기 다른 물리적 메모리 페이지에 매핑
    • 필요에 따라 할당된 크기 변경 가능
    • 프로그램이 바라보는 메모리(가상 메모리)는 여전히 위와 같은 메모리 배치 사용

프로그램 실행

  • 라이브러리: 목표와 관련된 함수의 모임

여러 조각으로 나뉜 프로그램

  • 여러 사람이 한 프로그램을 같이 개발할 수 있다는 장점이 있다.
  • 여러 조각을 하나로 엮거나 연결(link)하기 위해 링커(linker)를 이용해 실행
    • 각 프로그램은 링크하기 편한 형식의 매개 파일(intermediate file)로 나뉨
    • 실행과 링크가 가능한 형식(executable and linkable format, ELF) 형식이 가장 유명
      • 여러 섹션으로 나누는 방식

링크

  • 정적 링크: 라이브러리를 프로그램의 나머지 부분과 직접 연결해 실행 파일 생성
  • 동적 링크: 공유 라이브러리를 사용
    • 여러 프로그램이 같은 라이브러리를 사용하는 경우가 많아 정적 링크를 쓰면 메모리 낭비
    • 공유 라이브러리 함수를 작성할 때에는 호츨하는 프로그램의 스택과 힙을 사용할 수 있도록 설계되어야 한다.

런타임 라이브러리

  • 진입점(entry point): 프로그램의 첫 번째 명령어가 위치한 주소
  • 프로그램의 조각이 모두 합쳐져 하나의 실행파일(executable)을 이룰 때 런타임 라이브러리가 추가
    • 이 런타임 라이브러리에 있는 명령어가 먼저 실행된 뒤 진입점 명령어가 실행됨
  • 역할
    • 메모리 설정을 책임짐 ⇒ 스택과 힙 영역을 설정
    • 데이터의 초깃값 설정
      • 이런 값은 실행 파일에 들어 있고 시스템에서 메모리를 할당받은 직후 실행 파일에서 메모리로 복사되어야 함

메모리 전력 소비

  • 데이터를 메모리에서 옮기면 전력이 소비되며 모바일 장치에서 이는 중요
This post is licensed under CC BY 4.0 by the author.