컴퓨터구조

컴퓨터란

  • 전자 회로를 이용하여 데이터를 처리하는 장치

    전기는 작은 입자의 흐름이고, 이 작은 입자를 전자라고 함

  • 방대하고 복잡한 데이터 저장, 계산
  • 입출력, 제어, 기억, 연산 등의 기능을 가지고 있음
  • 네트워크 통신 가능
  • 유래없는 발명품
  1. 1940년대 쯤 폰노이만 아키텍쳐 등장(현재까지도 범용적)

    https://github.com/baecheese/ios_study/issues/7

  2. 1970년 C, UNIX 등장
  3. 1980년 MS-DOS, 매킨토시 등장
  4. 1990년 Linux, Windows 등장
  5. 1990년 인터넷 포탈(야후, 구글) 등장
  6. 2000년 스마트폰, 빅데이터, AI

컴퓨터 분야의 8가지 위대한 아이디어

  1. 무어의 법칙
    • 18~24개월마다 집적회로에 집적되는 소자의 수가 2배가 된다는 법칙
  2. 추상화
    • 자원의 수가 많아져서 설계시간이 길어지고, 생산성이 저하됨
    • 하위 수준의 상세한 사항을 안보이게 상위 수준 모델을 단순화함으로써 설계시간을 줄임
  3. Common case fast
    • 자주 발생하는 일은 빠르게
    • Common case가 무엇인지 알고있다는 가정
    • 캐싱
  4. 병렬성
    • 큰 문제를 여러개의 작은 문제로 나누어서 해결
    • 쓰레드
  5. 파이프라이닝
    • 병렬성의 특별한 형태
    • 이전단계 출력이 다음단계 입력으로 이어지는 구조
  6. 예측
  7. 메모리 계층구조
    • 최상위는 비싸고 제일 빠른 메모리
    • 최하위는 느리고 값이 싼 메모리
    • 레지스터 -> 램 -> 디스크
  8. 신용도 개선
    • 가용성 향상
    • 장애대처, 백업

컴퓨터 구성요소

  1. 프로세서
    • 인간의 두뇌와 같음
    • 제어유닛(CU)은 메모리로부터 명령을 받아와 해독하여 제어신호로 변환
    • ALU에서 제어신호를 연산하고(산술, 논리연산 가능) 결과를 다시 제어유닛에 돌려줌
      cpu 동작과정 (출처 : http://blog.daum.net/dasomcap/829)
    • 연산의 결과를 주변장치에 전송함으로써 한개의 명령이 끝남
    • 연산에 필요한 데이터를 레지스터에 저장하고, 연산 결과도 레지스터에 저장함
    • GPU도 프로세서임 https://light-tree.tistory.com/25
  2. 메모리
    • 명령어 및 데이터 적재
    • 정보를 저장해뒀다가 필요할 때 읽어들이는 저장소
    • 레지스터의 용량이 너무 작아서 출시됨
    • RAM, ROM, 캐시 등
  3. 입/출력장치
    • 데이터를 입력하거나 출력하기 위한 컴퓨터 외부장치
    • 입력 : 키보드, 마우스, 스캐너 등
    • 출력 : 모니터, 프린터, 스피커 등

고급언어, 어셈블리, 기계어

프로그램은 고급언어로 작성되고, 컴파일러에 의해 어셈블리어로 변환되고, 어셈블러에 의해 기계어로 변환된다.

  • 고급언어
    • 일반적인 프로그래밍 언어(C, Java, Python 등)
  • 어셈블리어
    • 기계 사고방식의 언어
    • 컴파일러에 의해 변환됨
    • 추상화가 높은 언어일수록 같은 명령어에 대해 생성되는 어셈블리 명령이 더 많음
    • C는 좀 더 로우레벨에 가까우므로 생성되는 어셈블리어가 다른 언어보다 적다. 어셈블리가 적다는것은 속도가 빠르다는 것을 의미한다.
  • 기계어
    • 0,1(bit)로 이루어진 집합
    • 컴퓨터만이 이해할 수 있음

보통 고급언어를 High Level Language라고 하고, 어셈블리어를 Low Level Language라고 한다.
Low Level Language로 작성할 경우 불필요한 instruction이 생성되지 않아 속도가 빠를순 있으나, 작성하는데 시간이 너무 오래 걸리고 어렵다.
(printf 한줄을 쓰는데도 수라인의 어셈블리어를 써야한다)
게다가 요즘은 고급언어로 작성해도 속도차이가 그리 크지 않다고 하니 고급언어로 개발하자…

  • OllyDBG, Immunity DBG, IDA Pro 같은 툴을 사용하면 프로그램의 어셈블리 코드를 볼 수 있다. 사용하는 CPU에 따라 생성되는 어셈블리가 조금씩 다르다.
  • Hex Editor를 사용하면 기계어를 볼 수 있다. 16진수로 출력된다.

컴퓨터 구조 유튜브 강좌

https://www.youtube.com/watch?v=uEzDvDw-L0o&t=1734s

  • 컴퓨터에는 많은 구성요소가 있지만 대표적으로 CPU, RAM, HDD/SSD 만 기억하면 된다

    • 그 중에서도 CPU와 RAM이 중요하다
  • CPU는 연산장치이고, RAM, HDD/SSD는 메모리(정보를 저장하는 공간) 이다

  • CPU는 정말 중요하지만, 정작 다룰일은 많이 없다

    • CPU 제조사마다 생긴게 다 다르다
    • 굉장히 복잡한 장치이다
  • RAM은 주기억장치이다

    • 변수를 가지고, 동적할당/해제가 가능하다
    • 일반적으로 메모리라고 하면 RAM이라고 보면 된다
  • HDD/SSD는 보조기억장치이고, 파일시스템으로 관리된다

    • 파일시스템은 기본적으로 보조기억장치를 다루는데 사용된다
    • 주기억장치도 파일시스템으로 다룰 수 있으나, 그러지 않는다(왜?)
  • 컴파일, 링크 : https://opentutorials.org/module/1594/9734

    • 오브젝트 파일은 어셈블리까지 완료된 기계어 파일이다
    • 보통 컴파일하면 어셈블리까지 같이 진행된다
    • 링커는 나뉘어진 오브젝트 파일들을 연결하여 실행가능한 프로그램으로 만드는 역할을 한다
    • 자바는 동적 링킹을 한다(사용할 때 클래스패스에서 찾음, 이는 JVM이 다 알아서 해줌)

      https://homoefficio.github.io/2019/01/31/Back-to-the-Essence-Java-컴파일에서-실행까지-1/

    • 기계어는 CPU마다 다를텐데 이는 어떻게 처리되는 걸까?
  • 메모리 계층구조

    1
    2
    3
    4
    5
    6
    - CPU 영역
    Register // 현재 보는 책
    Cache(Layer1, Layer2, Layer3) // 보려는 책 : 책상
    - Memory 영역
    RAM(Main) // 책장
    HDD, SSD(Secondary) // 도서관

    자주 쓸것은 가까이 놓고, 나중에 볼 것은 멀리 놓는, 아주 간단한 구조이다

    • 위로 갈수록 양은 적고, 속도는 빠르다
    • 상위 계층은 하위 계층으로 부터 데이터를 가져온다
    • CPU가 메모리로부터 데이터를 가져와서 연산하고, 연산한 데이터를 다시 메모리에 저장한다

      CPU와 메모리 사이에 데이터가 굉장히 많이, 자주 왔다갔다 한다

성능

시간과 리소스에 대응되어 컴퓨터 시스템이 수행하는 작업의 양
사용자에 따라 성능측정 척도는 다양하다.
개인사용자에겐 응답속도(Response time)가 척도가 될 것이고, 데이터센터 같은곳에서는 처리량(Throughput)이 척도가 될 것이다.

  • 성능과 실행시간과의 관계

    1
    2
    성능 = 1/실행시간  
    성능A > 성능B == 1/실행시간A > 1/실행시간B == 실행시간A < 실행시간B
  • A가 B보다 n배 성능이 좋다(빠르다)

    1
    2
    성능A/성능B = n  
    성능A = n성능B
    • 같은 프로그램이 컴퓨터A에서는 10초, 컴퓨터B에서 15초 걸린다면 A는 B보다 얼마나 빠른가?
      1
      2
      3
      4
      5
      6
      실행시간이 주어졌으므로 실행시간으로 식을 실행시간으로 바꿔줘야함  

      실행시간B/실행시간A = n
      15/10 = 1.5

      즉, 1.5배 빠르다
  • 클럭(Clock)

    • CPU 연산이 발생하는 시점
    • 클럭 사이클 : 클럭의 시간 간격. 다음 연산으로 넘어가는데 걸리는 시간을 말한다.
    • 클럭 속도 : CPU가 1초에 처리할 수 있는 연산의 개수를 말한다. 클럭 사이클을 역수로 바꾸면 된다.
  • CPU시간

    • 궁극적인 CPU의 성능척도
    • 프로세서가 순수하게 프로그램을 수행하기 위해 소비한 시간
    • 입출력 시간, 실행하기 위해 소비한 시간은 측정하지 않음
    • 프로그램의 CPU 실행시간 == 프로그램이 필요로 하는 CPU 연산 횟수 / 클럭 속도
    • 프로그램이 필요로 하는 CPU 연산 횟수를 줄이거나 클럭 속도를 높이는게 CPU 실행시간을 줄이는 방법이다

2GHz 클럭속도를 가진 컴퓨터 A에서 10초에 수행되는 프로그램에서 필요로 하는 CPU 연산 횟수

10 = CPU 연산 / 2 * 10^9(G는 10^9)
CPU 연산 = 2 * 10 * 10^9

위의 프로그램보다 CPU 연산이 1.2배 더 필요한 프로그램을 6초만에 돌리려면, 클럭속도가 몇이 되어야하는가?

6 = (2 * 10 * 10^9) * 1.2 / 클럭속도
클럭속도 = 4GHz

  • CPI
    • Clock cycle Per Instruction
    • 명령어 하나의 실행에 필요한 평균 클럭 사이클 수
    • 프로그램이 필요로 하는 CPU 연산 횟수 == 명령어 수 * CPI
    • 프로그램의 CPU 실행시간 == 명령어 수 * CPI / 클럭 속도

전력

  • 컴퓨터가 동작하는데 소비되는 전기의 힘

  • 파워서플라이를 통해 공급받음

  • 클럭속도가 높아지면 전력도 커진다

    • 트랜지스터가 열일하기 때문이다(0,1 스위칭)
    • 전력이 커지면 그 전력은 저항에 의해 열로써 낭비된다
    • 이런 이유 때문에 다른 전류를 들고와서 냉각을 시켜줘야 한다
  • CPU 클럭속도가 4GHz 벽을 맞이한 뒤로, 멀티코어를 사용하기 시작했다

    • 멀티코어를 사용하면 전력을 줄일 수 있다

      인텔 코어2 부터 멀티코어를 사용함으로써 전력이 줄어들었다
      cpu는 한번에 1가지 일만 처리하는데, 멀티코어라니?

    • 멀티코어를 사용하려면 병렬 프로그래밍을 잘 해줘야한다

CPU

CPU 처리 과정

https://mr-g.tistory.com/65
I/O bus를 통해 메모리로부터 데이터를 가져와서 레지스터들에 저장하고,
컨트롤 유닛이 레지스터의 데이터들을 ALU에 넣어가면서 연산함(?)

CPU 발전 순서(intel 기준)

참고하면 좋은 글
https://m.post.naver.com/viewer/postView.nhn?volumeNo=7627623&memberNo=10558726

  1. 80386 CPU

    • 386이라는 이름으로 유명함
    • 최초의 32bit 아키텍쳐 CPU
      • 최초의 64bit CPU는 AMD 애슬론64
    • 전 세계적으로 널리 사용됨
  2. 펜티엄

    • 최초로 이름을 갖게된 CPU이다
    • 펜티엄4는 최초의 2GHz, 3GHz를 돌파한 CPU이다
      • 최초의 1GHz 돌파는 AMD 애슬론이다
      • 발열사고로 생산이 중단되는 사고가 있었다
    • 슈퍼스칼라 아키텍쳐를 채용했다
      • pipe 2개를 이용해서 한 클럭당 명령어가 여러개 수행되게 하는 구조(잘 모르겠다)
  3. 코어2

    • 최초의 멀티코어 프로세서
    • 인텔이 CPU 시장을 잠식하게 된 계기
  4. i3/i5/i7

    • i3 : 2코어 4스레드
    • i5 : 4코어 4스레드
    • i7 : 4코어 8스레드

32bit cpu, 64bit cpu

32bit 64bit 차이 : https://zeddios.tistory.com/61
레지스터 : https://orang.tistory.com/entry/레지스터-Register-의-이해

CPU가 처리하는 데이터의 최소단위인 레지스터의 크기가 몇 bit냐를 말한다
즉 32bit cpu라는것은 레지스터의 최대 크기가 32bit임을 말하고,
bit는 0과 1로 표현되니 32bit가 표현가능한 최대 수는 2^32(4GB)가 된다
즉 42억개의 주소까지밖에 표현하지 못하므로, 32bit cpu에 8GB 램을 넣어도 4GB까지 밖에 인식되지 않는것이다
(주소를 담는 레지스터는 EIP)

오버클럭

  • 설계 당시의 클럭속도보다 강제로 더 높게 설정하는 법
  • 전력을 더 많이 소모하게 되지만 더 높은 사양의 프로그램을 돌릴 수 있음
  • 하지만 이로인해 발열이 커지고, 하드웨어에 무리가 갈 수 있다

멀티코어

물리적인 연산부가 n개 추가된 형태이다

왜 멀티코어가 생겼을까?
단일코어에 트랜지스터를 늘리는 방식의 성능향상에는 한계가 있기 때문이다(암달의 법칙)
하지만 이렇게 코어를 늘린다고 무조건 성능이 좋아지는 것은 아니고, 그에 맞춰 프로그래밍을 해줘야한다. 그렇지 않으면 실행속도가 더 떨어질수도 있다.

하이퍼스레딩(코어/스레드)

  • 성능을 높이기 위해 인텔이 독자적으로 개발한 기술
  • 하나의 코어에 또 다른 가상의 코어를 만들어 CPU가 사실은 하나인데 2개로 인식하게 하는 방법
    • 이 논리적인 작업을 스레드라고 한다
  • 코어수보다 스레드수가 많으면 하이퍼스레딩을 지원하고, 코어수와 스레드수가 같으면 하이퍼스레딩을 지원하지 않는다
  • 대표적인것 : i3

CPU 스레드와 프로세스 스레드의 차이

https://kldp.org/node/154708

그냥 비유 하나 들어서 두 줄로 요약하죠. 4코어 8스레드란 건 예컨대 상/하권으로 분권된 어떤 책이 4세트(=8권) 있는 겁니다.
그 4세트를 도서관에 두고 사서가 관리하면 대출/반납규정 지켜가며 수십~수백 명의 도서관 회원이 읽을 수 있죠. 한 순간엔 최대 8명까지밖에 못 읽지만.

프로세스 스레드는 여러개 생길 수 있고, cpu 스레드는 한번에 최대로 처리할 수 있는 스레드의 개수이다

명령어

  • 컴퓨터가 하드웨어에게 일을 시키기 위한 수단
  • 기본적으로 위에서 아래로(하향식) 실행되며, 다음 명령어를 가리키는 레지스터(Instruction Pointer)를 따라가므로 위아래로 움직일 수 있다(조건문, 반복문 등)

명령어의 구조

  • 크게 두 부분으로 나뉜다

  • 실행코드(OPCODE), 피연산자(OPERAND)

    1
    2
        ADD     R1 R2 R3
    <-OPCODE-> <-OPERAND->
    • 32bit 기준으로 OPCODE 1byte, OPERAND 3byte 이다

    메모리 주소와 명령어의 관계?

Instruction Pointer

  • 실행되고 있는 실행코드가 저장된 메모리의 주소를 가리키는 상태 레지스터
  • 이 레지스터를 참고하여 다음 명령을 진행하게 됨
    • 프로그램의 실행이 진행됨에 따라 자동 증가
    • 제어문의 실행에 따라 자동으로 변경
  • 직접 접근이 불가능함
    • 이 부분이 수정가능하면 프로그램의 실행제어가 가능해지기 때문(제품기 입력 스킵 등)

명령어 집합구조(Instcution Set Architecture)

  • 기계어를 매핑해놓은 어셈블리어 세트 정도로 보면 될 듯 하다
  • MIPS, ARM, x86 등이 있다(CPU마다 명령어 세트가 다르다)

명령어 집합구조 설계

  • 어셈블리어 세트를 만드는 작업이다(맞나? 이게?)
  • 하드웨어, 운영체제 등을 다 고려해야 하는 작업이라 매우 어렵다
  • 아래의 기준등에 따라 ISA를 설계한다
    • 연산의 종류는 어떤것을 사용할 것인지?
      • 처리, 제어, 입출력 등
    • 데이터 형식과 처리는 어떻게 할 것인지?
      • 데이터 의미, 저장방식 등
    • 명령어 형식은 어떻게 할 것인가?
    • 피연산자를 위한 주소 지정방식은 어떻게 할 것인가?
      • 위치를 어떻게 할 것인가?
      • 메모리에 담을것인가, 레지스터에 담을 것인가?

하드웨어 연산

산술연산

  • 덧셈, 뺄셈, 곱셉, 나눗셈의 사칙연산 계산을 하는 것
  • 컴퓨터는 레지스터와 ALU를 통해 산술연산을 수행

MIPS 산술명령어

  • 반드시 한 종류의 연산만을 지시한다
  • 항상 피연산자 3개를 갖는 형식을 가진다(간단하게 설계하기 위해서는 규칙적인 것이 좋으므로)
  • a에 b,c,d,e 의 합을 넣는 경우(operand 3개씩 순차적으로 진행해줘야 한다)

    a = b + c
    a = a + d
    a = a + e

MIPS 레지스터

레지스터란 CPU내에 값을 저장해놓는(연산 등을 위해) 아주 작은 공간이다.
MIPS에서 사용하는 레지스터는 아래와 같다.

MIPS 레지스터

  • t와 s가 가장 많이 사용된다
    • s : 일반적인 변수
    • t : 대체적으로 임시 값들이 저장(연산을 했지만 나머지 연산이 남아있는 경우라는데, 잘 모르곘음)
  • zero : 고정된 0
  • v : 리턴값. 리턴은 항상 하나만 하므로 여러 값이 필요하지 않으므로 2개면 충분하다
  • sp, fp : 스택프레임에서 사용되는 레지스터로, 가장 중요한 레지스터이다
  • ra : 함수호출 후 돌아올 주소가 저장된 곳

스택프레임

  • 함수가 실행될 때 마다 가지는 자신만의 고유한 스택 영역
  • CPU 스택은 어셈블리 명령 하나에 대한 스택이고, 메모리 스택은 프로그래밍 언어 함수 하나에 대한 스택이다(?)
    fp : 스택프레임의 시작점
    sp :

t와 s가 가장 많이 사용됨
가장 중요한 레지스터 : sp, fp가 가장 중요한 레지스터
함수를 처리하기 위해 스택프레임으로 이동한다
sp : 스택프레임의 베이스 포인트, fp : 값이 증가할 때 마다

산술

피연산자가 반드시 3개인 경우는 규칙적인것이 간단한 설계가 편하고, 최적화된, 오류없는 명령어를 만들기 편함

a에 b,c,d,e 합을 넣는 경우
operand 3개씩 순차적으로 진행해줘야 한다

레지스터
s = 변수, t = 임시값들이 대체적으로 저장
(연산을 했지만 나머지 연산이 남아있는 경우 t에 저장함)
zero = 그냥 값이 0으로 고정
v는 리턴값. 리턴은 하나밖에 하지 않기떄문에 2개밖에 존재하지 않음

스택프레임
처음엔 fp, sp가 같은 위치에 있는 형태의 스택이었다가
add 연산이 수행될때 전달받은 매개변수의 크기만큼 스택의 크기를 증가시킨다(sp의 값을 늘린다)
스택에 있는 2개의 변수를 add해서 임시공간(v)에 저장

ra = 리턴포인트
를 통해 add를 출력하기 전으로 돌아오는것


피연산자
레지스터는 데이터를 저장하는 장치중 속도가 가장 빠르므로, 일반적인 계산을 위해 레지스터를 전부 투입시키는 것은 비용적으로 효율이 떨어지고, 실제로도 그러지 않는다.
s 레지스터가 꽉 찼다고 해서 ra레지스터를 변수 저장에 사용하지 않는다는 의미이다. ra 레지스터를 변수저장에 사용하게 되면 결과적으로 리턴포인트 저장을 다른 공간(레지스터보다 느린 공간)에 해야하므로 비용적으로 손해보는게 훨씬 많다.
그러므로 더 적은 레지스터를 필요로 하는 순서로 연산을 진행하는 것이 좋다.

32bit라서 피연산자 데이터 단위의 최대가 32bit라고 하는데, 주소값 + 명령어 길이로 해야할텐데 그럼 4GB가 넘을텐데…

32bit CPU에서 명령어는 1byte opcode + 3byte operand로 이루어져 있음
레지스터의 크기는 32bit CPU가 읽을 수 있는 최대크기인 32bit로 구성되어 있음. 레지스터는 CPU에 있는 영역이므로 메모리 주소… 뭐 그딴거랑 관계없음

배열은 레지스터에 어떻게 담을 수 있을까?

못담음. 메모리 공간에 저장하여 불러오는 방식으로 사용할 수 있음
배열은 스택에 담기게 됨
메모리 주소를 레지스터에 담게 됨

메모리와 레지스터간 데이터를 주고받는 명령어를 데이터 전송 명령어라고 함

메모리의 내용을 레지스터에 담는 것을 적재라고 함

  • 메모리에 주소에 저장된 데이터의 크기가 4바이트가 넘는다면?

4(s3) == s3의 주소위치에서 4를 더함
12(s3) == s3의 주소위치에서 12를 더함

A[1] == 4($s3). 1은 2번쨰 원소이기 때문이다.
자료구조의 시작을 가르키는 부분을 base register라고 함
4바이트 단위로 저장하므로 무조건 4의 배수여야함

레지스터의 값을 메모리에 저장하는것을 저장이라고 함

t0에 있는 값을 32(s3)의 위치에 저장하겠다 라는 것

상수의 경우 레지스터나 메모리에 저장하지 않고 바로 연산을 수행할 수 있다?