- 개인적으로 공부하면서 지속적으로 정보를 추가, 수정, 삭제합니다.
- 정확하지 않은 부분 피드백 주시면 감사합니다.
- 노란색 하이라이트는 블로그 주인의 생각 + 이해가 더 필요한 부분을 개인적으로 표시한 것입니다.
참고: https://www.youtube.com/watch?v=LBqJwmFMQHI&list=PLVsNizTWUw7FCS83JhC1vflK8OcLRG0Hl&index=2
https://www.youtube.com/watch?v=B8TDaBp3UWo&list=PLVsNizTWUw7FCS83JhC1vflK8OcLRG0Hl&index=8
1. 컴퓨터 구조의 큰 그림
- 컴퓨터 구조(computer architecture)를 알아야 하는 이유
- 개발자는 코드만 잘 짜면 되는거 아닌가? → 컴퓨터의 근간도 알아야 함! → 컴퓨터의 구조와 운영체제
- 문제해결능력 향상
- 내 환경에서는 잘 동작하는데 상대방의 환경에서는 동작하지 않는 경우 → 컴퓨터를 분석의 대상으로 바라보고 내부 동작 원리에 대해 이해하지 않으면 해결하기 어려움
- 성능, 용량, 비용을 고려한 프로그래밍이 가능해짐
- 개발에서 가장 중요한 이야기 중 하나
- 다양한 사용자를 위해 필요한 서버 컴퓨터를 구매하는 경우, 굉장히 많은 옵션 → 컴퓨터의 구조를 알아야 자신에게 필요한 것을 적절한 비용으로 살 수 있음
- 컴퓨터 구조의 큰 그림
- 컴퓨터가 이해하는 정보
- 우리가 실행하는 프로그램은 결국 다음 두 가지 정보로 이루어짐
- 데이터
- 숫자, 문자, 이미지, 동영상과 같은 정적인 정보
- 컴퓨터와 주고 받는/내부에 저장된 데이터
- 명령어
- ex: (명령) 1과 2를 더하라 → (데이터) 1, 2
- “컴퓨터는 결국 명령어를 처리하는 기계”
- 컴퓨터를 실질적으로 움직이는 정보
- 데이터는 명령어를 위한 재료
- 기계어: 컴퓨터가 이해하는 (이진수, 16진수로 작성된) 명령어의 집합(instruction set)
- 어셈블리어
- 컴퓨터의 네 가지 핵심 부품
- CPU
- 메모리에 저장된 명령어를 읽고, 해석하고, 실행함
- CPU의 내부 구성 장치
- ALU: 계산기
- 계산을 위해 존재하는 회로들의 모음
- 레지스터
- CPU 내부의 작은 임시 저장장치
- 여러 개가 존재
- 제어장치
- 제어 신호를 내보내고 명령어를 해석하는 장치
- 제어신호: 컴퓨터 부품을 관리하고 작동시키기 위한 전기 신호(ex. 메모리 읽기 신호, 메모리 쓰기 신호)
- ALU: 계산기
- 메모리(RAM)
- 현재 실행되는 프로그램의 명령어와 데이터를 저장
- 메모리에 접근할 때 메모리 주소(번지 수 같은 것)로 접근
- 비싸고, 전원이 꺼지면 저장된 내용을 잃음 (휘발성 저장장치)
- 보조기억장치
- 하드디스크, SSD, USB 등
- 전원이 꺼져도 프로그램과 데이터를 저장할 수 있는 저장 장치
- 램보다 저렴
- 입출력장치
- 키보드, 모니터, 마우스, 프린터 등
- 컴퓨터 외부에 연결되어 컴퓨터 내부와 정보를 교환
- CPU
- 네 가지 핵심 부품을 연결하는 부품
- 메인보드
- 핵심 부품이 붙어 있는 일종의 판
- 버스를 통해 정보를 주고
- 시스템 버스
- 컴퓨터에는 다양한 종류의 버스가 존재 → 일종의 통로
- 메인보드에 붙어 있는 핵심 부품들은 그 중 시스템 버스를 통해 정보를 주고 받음 (척추 같은 버스)
- 시스템 버스의 내부 구성
- 주소 버스: 주소를 주고 받는 통로 (ex: 메모리 5번지)
- 데이터 버스: 명령어와 데이터를 주고 받는 통로 (ex: 220)
- 제어 버스: 제어 신호를 주고 받는 통로 (ex:메모리 쓰기 신호)
→ 메모리 5번지에 220을 써라
- 메인보드
- 컴퓨터가 이해하는 정보
2. 운영체제의 큰 그림
-
시스템 자원(resource)이란?
- 프로그램이 실행되기 위해 마땅히 필요한 요소
- 컴퓨터의 네 가지 핵심 부품 포함
-
운영체제란?
- 컴퓨터 부품은 전기만 공급하면 마법처럼 작동하지 않는다!
- 시스템 자원을 관리하는 특별한 프로그램
- 운영체제는 마치 나라의 자원을 적절히 효율적으로 관리하는 정부의 역할을 함!
- 실행 중인 프로그램(= 프로세스!!!)을 관리하는 특별한 프로그램 (현존하는 프로그램 중 규모가 가장 큰 프로그램 중 하나 )
- 여타 프로그램과 마찬가지로 메모리에 저장되어 있음 (실행 중인 모든 프로그램은 메모리에 저장된다고 했으므로) → 커널 영역이라고 하는 메모리 상의 공간
-
운영체제의 역할
- 운영체제의 자원(메모리) 관리
- 운영체제는 사용자 영역에 프로그램을 적재하고,
- 종료하는 프로그램은 메모리 안에서 비워 줌
- 물리적인 메모리 크기보다 큰 프로세스를 실행하고자 할 때도(e.g.: 나의 램은 4GB인데 적재할 프로세스가 4GB일 때) 효율적인 메모리 관리를 통해 실행할 수 있음 → 페이징과 스와핑
- 운영체제의 자원(CPU) 관리 (운영체제의 CPU 스케줄링)
- 모든 프로세스는 실행되기 위해 CPU가 필요하다! + 다양한 프로세스는 굉장히 빠르게 번갈아가면서 실행된다!
- → 어떤 프로세스를, 몇 번째로, 얼마나 오랫동안 실행할 것인가?
- 운영체제의 프로세스 관리
- 개발자 입장에서 운영체제를 공부해야 하는 가장 큰 이유
- 많은 프로그램을 동시에 실행되는데, 일목요연하게 실행을 관리 (프로세스 동기화)
[1]
- e.g.: 프로세스 C는 프로세스 D 다음에 실행하게 하기, 특정 자원에는 프로세스가 동시에 접근하지 못하게 하기
- e.g.: 워드 프로세서가 프린터를 사용하는 경우, 메모장이 프린터를 이용하면 안 되게끔 제어.
- 프로세스와 스레드, 교착상태 해결 등
- 문지기 역할(시스템 호출, system call)을 통한 자원 보호
- 여러 프로세스가 동시 다발적으로 실행되기 위해서는 자원이 필요함(CPU, 메모리, 하드디스크 등과 같은)
- 자원은 보호되어야 하기 때문에 프로세스가 자원에 직접 접근하면 안됨 → 프로세스가 운영체제를 통해 자원에 접근함(시스템 호출을 통해)
- 운영체제가 제공하는 아주 중요한 역할 중 하나
- 운영체제의 자원(메모리) 관리
-
운영체제를 알아야 하는 이유
- 운영체제는 사용자를 위한 프로그램이 아니다! → 프로그램을 위한 프로그램
- 프로그램을 만드는 개발자는 운영체제를 알아야 함 (내가 개발한 프로그램은 운영체제로부터 어떤 도움을 받을까?) → 하드웨어와 가장 밀접하게 맞닿아 있으므로, 하드웨어에 문제가 생겼을 때 가장 먼저 문제를 알아차림
- 오류 메시지를 내보내는 근원적인 주체가 대부분 운영체제 → 오류 메시지에 대한 깊은 이해 → 문제 해결 능력 향상
-
운영체제의 큰 그림
- 커널이란?
- 운영체제의 핵심 서비스를 담당하는 부분
- UI는 운영체제에는 속하지만 커널에는 속하지 않음
- 시스템 콜과 이중 모드란?
- 시스템 콜(=시스템 호출)
- 사용자가 실행하는 프로그램은 자원에 직접 접근할 수 있을까? NO! 자원에 직접 접근은 위험하다!
- 응용 프로그램이 자원에 접근하려면 운영체제에 도움을 요청(=운영체제의 코드를 실행)해야 함
- 이러한 자원 접근 제한은 이중모드로 구현이 된다
- 이중모드: CPU가 명령어를 실행하는 모드를 크게 사용자 모드와 커널 모드 두 가지로 구분하는 방식
- 사용자 모드:
- 운영체제 서비스를 제공받을 수 없는 실행모드
- 커널 영역의 코드를 실행할 수 없는 실행 모드
- 자원 접근 불가
- 커널 모드:
- 운영체제의 서비스를 제공받을 수 있는 실행 모드
- 자원 접근을 비롯한 모든 명령어 실행 가능
- 어떤 모드인지 CPU가 어떻게 알 수 있을까?
- 플래그 레지스터에 슈퍼바이저 플래그 → 1인 경우 커널 모드, 0일 경우 사용자 모드
- 언제 커널 모드, 사용자 모드로 바뀔까?
- 시스템 호출을 하면 커널 모드로 전환 됨 → 일종의 소프트웨어 인터럽트
- 운영체제의 서비스 종류? 가장 핵심적인 서비스?
- 프로세스 관리
- 프로세스(or Task) == 실행 중인 프로그램
- 수많은 프로세스들이 동시에 실행 → 동시다발적으로 생성/실행/삭제되는 다양한 프로세스를 일목요연하게 관리
- paging, swapping을 통해 모든 프로세스를 메모리에 다 올리지 않고서도 실행할 수 있음
- 자원 접근 및 할당
- CPU
- 메모리(페이징, 스와핑)
- 입출력장치(인터럽트 서비스 루틴 )
- 파일 시스템 관리
- 보조저장장치의 정보 덩어리를 파일이라는 단위로 묶어서 저장
- 파일을 묶은 단위를 폴더, 디렉토리
- 프로세스 관리
- 시스템 콜(=시스템 호출)
- 커널이란?
3. 명령어
- 소스코드와 명령어
- 내가 프로그래밍 언어로 작성한 소스 코드가 어떻게 프로그램을 동작시키는 명령어로 변환되는가?
- 개발자가 직접 작성한 소스코드는 컴퓨터가 바로 직독직해 하지 못함
- 고급 언어(파이썬, C, 자바 등 개발자가 읽고 쓰기 쉽게 만들어진 언어) → 저급 언어(=명령어, 컴퓨터가 이해하고 실행할 수 있는 언어)로의 변환이 필요함
- 저급 언어의 종류
- 기계어: 2진수, 16진수로 표현된 언어
- 어셈블리어: 2진수, 16진수로 표현된 기계어를 읽기 편한 형태로 번역한 저급 언어. 소스코드에 직접 명시하기도 함 → 직군에 따라 어셈블리어를 알아 두면 좋은 직군도 있음
- 컴파일 언어와 인터프리터 언어
- 고급 언어가 저급 언어로 변환되는 대표적인 두 가지 방식
- 컴파일 언어
- 소스 코드 → 컴파일러 → 목적 코드(object code)
- 컴파일러가 내가 쓴 소스 코드를 전체를 한번 훑어봄 → 오류 있는지 등 검사 → 소스코드 중 오류가 발생하면 소스코드 전체가 실행되지 않음(컴파일 에러)
- 마치 책 한권을 번역본으로 전달해주는 것과 같음
- ex: C언어
- 인터프리트 언어
- 소스코드가 인터프리터에 의해 한줄 씩 저급 언어로 바뀌고 실행 됨 → 소스 코드 전체가 저급 언어로 변환되기까지 기다릴 필요 없음
- 오류 코드 직전까지 실행 됨
- 마치 책을 한 줄 한 줄 번역해서 친구에게 들려주는 것과 같음
- ex: 파이썬
- 컴파일 언어, 인터프리트 언어 무 자르듯 나뉘지는 않음
- 내가 프로그래밍 언어로 작성한 소스 코드가 어떻게 프로그램을 동작시키는 명령어로 변환되는가?
- 명령어의 구조
- 명령어 하나 하나는 어떻게 생겼을까?
- 명령어의 구성 요소 두 가지
- 연산 코드
- 연산 코드가 담는 것: 수행할 연산
- 연산코드의 종류 & 생김새는 CPU 마다 다름
- 대표적인 연산코드의 유형
- 데이터 전송: 데이터를 옮겨라, 저장해라, 가져와라 등
- 산술/논리 연산: 더해라, 빼라, 비교해라, and, or, 등
- 제어 흐름 변경: 특정 메모리 주소로 실행 순서를 옮겨라, 프로그램의 실행을 멈춰라, 되돌아올 주소를 저장한 채 특정 주소로 실행 순서로 옮겨라(함수를 호출할 때) 등
- 입출력 제어: 읽어라, 써라, 시작하라 등
- 오퍼랜드(operand)
- 오퍼랜드 공간에 담기는 두 가지 데이터
- 연산에 사용될 데이터
- 혹은, 연산에 사용될 데이터가 저장된 위치 (이게 훨씬 더 자주 저장됨 → 오퍼랜드가 담기는 필드를 주소 필드라고 부르기도 함)
- 오퍼랜드는 없을 수도, 여러 개 있을 수도 있음
- 오퍼랜드 공간에 담기는 두 가지 데이터
- 연산 코드
- 명령어 주소 지정 방식
- 앞서 오퍼랜드 공간에 데이터가 저장된 주소가 담기는 이유?
- 하나의 오퍼랜드 공간에 담길 수 있는 데이터의 크기는 한정되어 있음, 오퍼랜드가 많아질 수록 더 적어짐 → 메모리 주소를 담아 표현하고자 하는 데이터의 크기의 제한을 받지 않게 함
- 유효주소 (effective address)
- 연산에 사용할 데이터가 저장된 위치
- 오퍼랜드에 메모리 주소가 담기면 유효 주소는 그 메모리 주소, 레지스터 주소가 담기면 레지스터 주소.
- 명령어 주소 지정 방식(addressing modes)이란?
- 연산에 사용할 데이터가 저장된 위치를 찾는 방법
- 유효 주소를 찾는 방법
- 대표적인 명령어 주소 지정 방식 ^j9m8mw
- 즉시 주소 지정 방식 (immediate addressing mode)
- 연산에 사용할 데이터를 오퍼랜드 필드에 직접 명시
- 가장 간단
- 데이터의 크기가 작아질 수 있음
- 빠름
- 직접 주소 지정 방식 (direct addressing mode)
- 오퍼랜드 필드에 유효 주소 직접적으로 명시함
- 유효 주소를 표현할 수 있는 크기가 연산 코드만큼 줄어듦
- 간접 주소 지정 방식 (indirect addressing mode)
- 오퍼랜드 필드에 유효 주소의 주소를 명시
- 앞선 방식들보다 속도가 느림
(※ CPU가 메모리를 뒤적거리는 것은 매우 느리므로, 메모리 접근을 최소화하는 것이 속도면에서 이득)
- 레지스터 주소 지정 방식 (register addressing mode)
- 연산에 사용할 데이터가 저장된 레지스터를 명시함
- 메모리에 접근하는 것보다 레지스터에 접근하는 것이 빠름 (⇒ 직접 주소 지정 방식보다 좀 더 빠름 )
- 레지스터 간접 주소 지정 방식 (register indirect addressing mode)
- 연산에 사용할 데이터를 메모리에 저장
- 그 주소를 저장한 레지스터를 오퍼랜드 필드에 명시
- 즉시 주소 지정 방식 (immediate addressing mode)
- 앞서 오퍼랜드 공간에 데이터가 저장된 주소가 담기는 이유?
4. CPU와 작동 원리
- ALU와 제어장치
- CPU 안의 ALU와 제어장치가 내보내고 받아들이는 정보는 무엇일까?
- ALU가 받아들이는 정보
- ALU는 계산을 함 ⇒ 계산을 하기 위해서는 피연산자(ex. 숫자 1, 2)와 수행할 연산(ex. 더하기, 빼기 등)이 필요
- 피연산자는 레지스터에서 받음
- 수행할 연산은 제어장치에서 받음
- ALU가 내보내는 정보
- 연산의 결과값(ex. 숫자, 문자, 주소 등)을 레지스터에 저장(※ 메모리에 접근하는 것은 느리기 때문에 이와 같이 레지스터에 즉각 저장하고 이것을 나중에 메모리에 저장하든지, 다른 레지스터와 연산을 하는 등 작업을 함)
- 플래그를 플래그 레지스터에 저장
- 플래그란 연산 결과에 대한 부가 정보
- 플래그의 종류
- 부호 플래그: 연산된 결과의 부호(양수인지, 음수인)
- 제로 플래그: 연산 결과가 0인지 아닌지
- 캐리 플래그: 연산 결과 올림수나 빌림수가 발생했는지
- 오버플로우 플래그: 오버플로우가 발생했는지(연산 결과가 결과를 담을 공간보다 너무 클 때)
- 인터럽트 플래그: 인터럽트가 가능한지 (추후 내용과 연결되는 부분)
- 슈퍼바이저 플래그: 커널 모드로 사용중인지, 사용자 모드로 실행 중인지 (추후 내용과 연결되는 부분)
- 제어장치가 받아들이는 정보
- 클럭 신호를 받음
- 클럭 신호: 컴퓨터의 모든 부품을 박자에 맞춰 일사불란하게 움직일 수 있게 하는 시간 단위
- 명령어 레지스터로부터 해석할 명령어를 받음
- 플래그 레지스터로부터 플래그를 받음 (※ 명령어 레지스터에서 명령어를 받으면서 연산에 대한 부가 정보도 받아야 하기에)
- 제어 신호를 받음 (※ 제어 신호를 내보내는 것은 제어 장치뿐만이 아님)
- 클럭 신호를 받음
- 제어장치가 내보내는 정보
- CPU 내부에 전달
- 레지스터에 보내는 제어 신호(레지스터를 움직이게 하는 제어 신호)
- ALU에 보내는 제어 신호(어떤 연산을 수행할지 지시하는 제어 신호 )
- CPU 외부에 전달
- 메모리에 내보내는 제어 신호(메모리를 읽거나 쓰라고 지시하는 신호 )
- 입출력장치에 내보내는 제어 신호 (입출력 장치를 읽고, 쓰고, 테스트 지시하는 신호)
- CPU 내부에 전달
- 레지스터
- 레지스터의 종류는 무엇이고 레지스터는 어떤 역할을 하는가?
- 레지스터란?
- CPU 내부의 작은 임시 저장장치
- ALU와 제어장치보다 프로그래머 입장에서 조금 더 중요 (레지스터에 비해 직접적으로 다룰 일은 적음)
- 프로그램 속 명령어와 데이터가 실행 전후로 레지스터에 저장
- 다양한 레지스터가 존재하고 각기 다른 역할 (※ CPU마다 레지스터의 종류, 이름은 다름)
- 반드시 알아야 할 레지스터의 종류 이거 되게 헷갈림
- 프로그램 카운터(=instruction pointer(명령어 포인터 )): 메모리에서 가져올 명령어의 주소 (메모리에서 읽어 들일 명령어의 주소)
- 명령어 레지스터: 해석할 명령어 (방금 메모리에서 읽어 들인 명령어) → 제어 장치가 해석하고 제어 신호를 내보냄
- 메모리 주소 레지스터: 메모리의 주소를 저장(CPU가 읽어 들이고자 하는 주소를 주소 버스로 보낼 때 거치는 레지스터 )
- 메모리 버퍼 레지스터: 메모리와 주고 받을 값 (데이터와 명령어 )
- 플래그 레지스터: 연산 결과 또는 CPU 상태에 대한 부가 정보
- 범용 레지스터: 다양하고 일반적인 상황에서 자유롭게 사용. 여러 개 존재.
- 스택 포인터: 특정 레지스터를 이용한 주소 지정 방식 中 스택 주소 지정 방식에서 사용. 스택의 꼭대기를 가리키는 레지스터.
- 베이스 레지스터: 특정 레지스터를 이용한 주소 지정 방식 中 변위 주소 지정 방식에서 사용.
- 특정 레지스터를 이용한 주소 지정 방식
- 스택 주소 지정 방식: 메모리 안의 스택 영역과 스택 포인터를 이용한 주소 지정 방식
- 변위 주소 지정 방식: 오퍼랜드 필드의 값(변위)과 특정 레지스터(프로그램 카운터 or 베이스 레지스터) 의 값을 더하여 유효 주소 얻기
- 상대 주소 지정 방식: 오퍼랜드 필드의 값(변위)과 프로그램 카운터의 값을 더하여 유효 주소 얻기
- 베이스 레지스터 주소 지정 방식: 오퍼랜드 필드의 값(변위)과 베이스 레지스터의 값을 더하여 유효 주소 얻기(※ 베이스 레지스터에는 말 그대로 베이스, 기준이 되는 주소가 담김)
'운영체제' 카테고리의 다른 글
멀티 스레드 & 멀티 프로세스 실험하기 with Python (0) | 2023.04.16 |
---|