더보기

기수법 진수변환 보수 고정소수점 부동소수점 아스키코드 유니코드

 


01장 CPU
02장 운영체제 - 프로세스와 스레드
03장 운영체제 - 메모리
04장 프로그래밍 언어, 컴파일러
05장 변수, 정수, 실수, 문자열
06장 함수
07장 객체지향
08장 클래스
09장 자료구조 1
10장 자료구조 2
11장 알고리즘
12장 네트워크
13장 데이터베이스


 

 

 

 

 

1. 변수

  • 변수 : 데이터를 저장할 수 있는 메모리 공간
  • 저장되는 데이터 : 숫자, 문자, 객체(class), 메모리 주소(포인터 변수), 함수

💡 이번 페이지는 데이터들을 변수에 어떻게 저장하는지에 대해서 설명합니다.

변수에 값이 담겨 있는 모습

 

 

 

 

 

 

2. 정수

2-1. 기수법

  • 기수법 : 수를 시각적으로 나타내는 방법
  • 기수법의 종류
    • 10진수 : 0, 1, 2, 3, 4, 5, 6, 7, 8, 9
    • 2진수 : 0, 1
    • 16진수 : 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, A, B, C, D, E, F

2-2. 진수 변환

1) 2진수 → 10진수

  • 11001이라는 2진수를 10진수로 변환하는 예시
    • 2진수의 각 자릿수 제곱을 계산한 숫자를 전부 더한다.
    • 정답 : 16 + 8 + 1 = 25

 

2) 10진수 → 2진수

2-3. 보수

1) 보수

  • n진수에는 n-1의 보수, n의 보수가 존재
    • 10진수 n의 9의 보수 : n의 모든 자릿수를 9으로 만들 수 있는 숫자
    • 10진수 n의 10의 보수 : 거기에 1을 더한 것

  • 예시
    • 10진수 26의 9의 보수 : 73
    • 10진수 26의 10의 보수 : 74
    • 2진수 1010의 1의 보수 : 0101
    • 2진수 1010의 2의 보수 : 0110

2) 보수를 사용하는 이유

  • 컴퓨터가 뺄셈 연산을 간단하게 하기 위함
    • 음수를 2진수 2의 보수로 표현하고, 보수를 사용해서 뺄셈을 덧셈 연산으로 처리한다.

 

 

 

 

 

 

 

3. 실수

3-1. 실수의 진수 변환

  • 10진수 실수 → 2진수 실수
    • 7.75 = 4 + 2 + 1 + 0.5 + 0.25 = 111.11

3-2. 고정 소수점 (fixed-point)

  • 특징 : 비효율적이지만 계산하기 쉬움 (요즘 잘 안 씀)
  • 부호(sign) 부분 : 0 양수, 1 음수

 

3-3. 부동 소수점 (floating-point)

  • 특.징 : 둥.둥 떠다.니는 소.수점... (비트를 좀 더 효율적으로 쓸 수 있어서 요즘 많이 씀)
  • 종류
    • Single Precision(32bit) : Float
    • Double Precision(64bit) : Double

  • 지수 부분 (exponent)
    • 음수를 표현하기 위해서 보수 기법 대신(뺄셈을 사용하지 않기 때문) bias 표현법을 사용
    • exp - bias = 실제 지수

bias 표현법 예제 (지수부가 8bit인 부동 소수점을 사용할 경우)

  • 가수 부분 (manissa) : 길면 자르고, 남으면 0으로 채움

 

 

 

 

 

 

 

4. 문자와 문자열

3-1. 문자 인코딩

  • 문자를 숫자로 표현하는 것

3-2. 아스키 코드

  • 아스키 코드 : 7비트(0~127) 숫자에 문자를 하나씩 대응한 것
    • 자료형 char에 저장할 수 있음 (1Byte = 8Bit = 0~255)
  • 아스키 코드표

 

3-3. 유니코드

  • 유니코드 : 16bit(0~65535) 범위에 문자를 하나씩 대응해 놓은 것.
  • 기존 아스키 코드는 유지
  • 유니코드 코드표

https://hanseul-lee.github.io/2020/08/27/20-08-27-code/

  • 유니코드 평면
    • 유니코드로 부족해서 도입
    • 기본 다국어 평면(BMP)을 포함해 평면이 17개 있으므로, 모든 문자를 표현하려면 최소 3Byte의 자료형이 필요
  • UTF(Unicode Transformation Format)를 이용한 유니코드 인코딩
    • UTF : 유니코드 문자를 인코딩하는 방식으로, 몇 Bit를 사용하여
    • 종류
      • UTF-8
        • 문자 하나를 표현할 때 최소 8bit가 필요
          • 0000~007F (8bit로 표현)
          • 0080~07FF (16bit로 표현)
          • 0800~FFFF (32bit로 표현)
        • BMP는 2Byte로 표현 가능하지만, 구분자를 표현하기 위해 3Byte까지 늘어남비트는 순서대로 x로 표시된 비트에 들어감 (아래 예제사진)
        • 가변형 인코딩 방식
        • 아스키 코드와 호환됨
      • UTF-16
        • 문자 하나를 표현할 때 최소 16bit가 필요
        • 마찬가지로, 가변형 인코딩 방식
        • 자료형 wchar은 UTF-16을 이용하여 인코딩 (2Byte = 16Bit)
      • UTF-32
        • 문자 하나를 표현할 때 최소 32bit가 필요
        • 저장공간의 낭비가 심함

비트는 순서대로 x로 표시된 비트에 들어감

 

3-4. 프로그래밍 언어 문자열

 

 

 


01장 CPU
02장 운영체제 - 프로세스와 스레드
03장 운영체제 - 메모리
04장 프로그래밍 언어, 컴파일러
05장 변수, 정수, 실수, 문자열
06장 함수
07장 객체지향
08장 클래스
09장 자료구조 1
10장 자료구조 2
11장 알고리즘
12장 네트워크
13장 데이터베이스


 

 

 

 

 

1. 언어 종류


1-1. 언어의 종류

예시 언어 실행 방식 특징
컴파일러 언어 C, C++ 소스코드 → 기계어 → ✅ → 실행 1. 각 OS에 맞는 기계어로 코딩해 주어야 함
컴파일러 언어 C#, JAVA 소스코드 → 바이트 코드 → ✅ →실행 (가상 머신에서) 1. 실행 전 바이트 코드가 컴파일됨
인터프리터 언어 JavaScript, Python 소스코드 → ✅ → 바이트 코드 → 실행 (가상 머신에서) 1. 한 줄씩 실행됨
2. 실행 후에 바이트 코드가 컴파일됨
3. 따라서, 느림 

✅  : 유저가 실행을 클릭한 순간 (유저가 실행을 클릭하기 전까지의 단계를 프로그래머가 코딩하고 컴파일 해야 함)

  • 인터프리터 언어
    • 여기서 나오는 VM은 해당 언어를 이용할 때 설치하는 그 언어의 VM을 일컫는다.
    • (그림1) 소스 코드가 바이트 코드(VM이 이해할 수 있는 인스트럭션)로 번역되면, VM이 OS가 이해할 수 있는 인스트럭션으로 번역해 준다.
    • (그림2) OS 안에 위치한 VM에서 코드가 실행되는 구조
    • 인터프리터 언어는 하나의 코드로 여러 OS에서 동작한다는 장점이 있다. (플랫폼에 대한 독립성)

 

(그림1) 소스 코드가 바이트 코드(VM이 이해할 수 있는 인스트럭션)로 번역되면, VM이 OS가 이해할 수 있는 인스트럭션으로 번역해 준다

 

(그림2) OS 안에 위치한 VM에서 코드가 실행되는 구조

 

 

1-2. JIT(Just-in-Time) 컴파일러

  • 인터프리터 언어의 단점을 보완하기 위해 도입된 컴파일러
  • 유저가 실행 버튼을 누르면 바이트 코드를 컴파일해서 기계어로 번역한 후 실행 (속도 향상)
    • 소스 코드 → ✅ → 바이트 코드 → 기계어 → 실행 (가상 머신에서)

 

 

 

 

 

 

2. 컴파일


2-1. 컴파일 과정

2-2. 컴파일러 과정

컴파일러 과정 설명 예제로 사용할 소스 코드

def func(a, b):
	return a + b

 

① Scanner(Lexer, Lexical Analyzer)

  • 소스 코드를 분석해서 Token(요소) 생성
NAME    'def'
NAME    'func'
OP      '('
NAME    'a'
OP      ','
NAME    'b'
OP      ')'
OP      ':'
NEWLINE '\\n'
INDENT  '\\t'
NAME    'return'
NAME    'a'
OP      '+'
NAME    'b'

 

② Parser(Syntax Analyzer)

  • Token을 분석해서 Parse tree 생성

 

③ Code Generator

  • Parse tree를 분석해서 어셈블리어 생성

 

 

 

 

 

 

 

 

3. 언어와 문법


이것은 그 중에서도 Parser를 위한 이론이고, 컴공과 오토마타 과목에 포함된다.

3-1. 정의

  • 언어 : 유한한 종류의 문자로 이루어진 유한한 길이의 문자열의 집합
  • {a, ab, cab, baa ... }
  • 문법 : 문자들로부터 문장을 만드는 규칙
  • 문법 표기법
    • 문법 도표 : 초보자가 쉽게 이해할 수 있도록 문법을 도식화하는 방법
    • BNF 표기법
      • 프로그래밍 언어의 형식적 정의를 위해 가장 널리 사용되는 방법으로, 언어 생성 규칙들의 집합
      • 언어의 문장들은 BNF의 규칙을 적용해가며 생성되는데, 시작 기호(start symbol)라 불리는 비단말 기호에서 시작된다. 이러한 문장 생성 과정을 유도(derivation)라고 한다.
      • 문법과 유도 예시 
      • # 유도 E → E+T ------ ①에 의해 E를 E+T로 대치 → T+T ------ ①에 의해 E를 T로 대치 → F+F ------ ②에 의해 T를 F로 대치 → id+**id ----** ③에 의해 F를 id로 대치
      • # 문법 <E> → <E+T> / <T> ------ ① <T> → <F> --------------- ② <F> → id ---------------- ③
      • BNF 표기법 사전
        • 비단말 기호(non-terminal symbol) : '<>'로 묶인 기호들
        • 단말 기호(terminal symbol) : 'id' 부분처럼 직접 나타낼 수 있는 기호
      • 참고 사이트 : https://blog.shar.kr/969

문법 도표

 

3-2. 종류

문법 언어  인식기
type 0 (무제약 문법) 재귀 열거 언어 튜링 기계 (turing machine)
type 1 (문맥인식 문법) 문맥인식 언어 선형한계 오토마타 (linear-bounded automata)
type 2 (문맥자유 문법) 문맥자유 언어 푸시다운 오토마타 (push-down automata)
type 3 (정규 문법) 정규 언어 유한 오토마타 (finite automata)

 

 

3-3. 정규 언어

  • 정규 언어 : 유한 오토마타로 인식 가능한 언어
  • 정규 표현식 : 정규 언어를 가장 잘 표현할 수 있는 방법

 

 

3-4. 오토마타

다시 한 번, Parser를 위한 이론
조금 더 자세히 공부해 보고 싶다면 참고 사이트, 참고 사이트2

 

3-2-1. 유한 오토마타

  • 유한 오토마타 : 유한한 상태를 갖고, 입력을 받아 입력에 따라 일정하게 상태를 전이(State 이동)하며, 출력(True, False)을 내놓는다.
  • 종류
    • DFA(Deterministic finite Automata) : State가 확실히 정해지는 오토마타
    • NFA(Non-Deterministic Finite Automata) : State가 확실히 정해지지 않고 가능한 State가 여러개가 되는 오토마타
  • NFA에서 가능한 State들의 집합을 State로 DFA로 변환할 수 있다. (NFA → DFA 변환)
  • 단점
    • 문법의 생성 규칙이 우선형 선택의 규칙과 좌선형 형태의 규칙으로 혼합되어 있다.
    • 그동안 처리했던 정보를 저장할 공간이 유한 오토마타에는 없다.

 

3-2-2. 푸시 다운 오토마타

a,0,10: a를 만났는데 스택에 0이 있으면 스택에서 0제거하고 10을 넣는다. b,1,&lambda;: b를 만났는데 스택에 1이 있으면 스택에서 1제거한다.

 

  • 푸시 다운 오토마타 : 무한한 크기의 Stack을 저장장치로 갖는 유한 오토마타

 

3-2-3. Parser

  • 우리가 프로그래밍한 소스코드도 언어이기 때문에, 오토마타 이론을 통해 소스 코드를 분석한다.
  • 푸시 다운 오토마타를 주로 사용한다.

 

Parser의 종류

  • LL 파서 : 하향식 파서 (Non-Teminal부터 시작)
  • LR 파서 : 상향식 파서 (Terminal부터 시작)

LL 파서 : 하향식 파서 (Non-Teminal부터 시작)

 

LR 파서 : 상향식 파서 (Terminal부터 시작

 

  • LR(1) 파서
    • 이동 축소 파싱(shift-reduce parsing) : Shift와 Reduce를 반복하여 파싱

 

Shift : 한 칸 이동 / Reduce : 규칙에 맞게 바꾸기

 

 

 

 

 

티스토리 에디터 너무 구리다 ㅜㅜ

더보기

지역성 가상메모리 페이징 페이지폴트 페이지테이블 TLB 커널 힙/스택


01장 CPU
02장 운영체제 - 프로세스와 스레드
03장 운영체제 - 메모리
04장 프로그래밍 언어, 컴파일러
05장 변수, 정수, 실수, 문자열
06장 함수
07장 객체지향
08장 클래스
09장 자료구조 1
10장 자료구조 2
11장 알고리즘
12장 네트워크
13장 데이터베이스




1. 메모리 계층

메모리 계층 구조

  • 메모리 계층의 특징
    • 전달되는 데이터가 아래쪽에 있을 경우, CPU에 도달하려면 위에 있는 모든 계층을 거쳐야 함
      • 정보가 이렇게 책처럼 이동하는 느낌
    • 단계별 접근이 비효율적일 것 같은데 이게 더 빠른 이유 : 지역성
  • 지역성
    • 시간적 지역성 : 한 번 접근한 데이터는 가까운 시간 내에 다시 접근할 확률이 높음
    • 공간적 지역성 : 이번에 접근할 데이터가 이전에 접근했던 데이터 근처에 있을 확률이 높음 (예를 들어 배열...)
    • (이런 특성 때문에 메모리 계층을 저렇게 구성한 것임)
  • CPU가 필요한 데이터가 캐시에 있으면 캐시 히트, 없으면 캐시 미스 (캐시 히트가 잘 일어나도록 고려해서 작성한 코드를 캐시 프렌들리 코드라고 함)
  • 메모리 저장 방식
    • 빅 엔디언(big-endian) : 숫자를 1, 2, 3, 4 순서대로 저장하는 방식
    • 리틀 엔디언(little-endian) : 숫자를 4, 3, 2, 1 역순으로 저장하는 방식

 

2. 가상 메모리

  • 고정된 메모리 주소를 사용하기에는 물리 메모리가 너무 작아서 도입
    • 하드 디스크를 메인 메모리(RAM)처럼 사용할 수 있게 됨 (메인 메모리가 하드 디스크의 캐시 메모리 역할)
    • 각 프로세스(실행된 프로그램)가 독립된 가상 주소 공간을 가짐
    • 프로세스의 모든 데이터를 메인 메모리로 적재하지 않고 필요한 시점에만 적재
      • 예를 들어, 32bit 시스템에서는 프로세스가 실행되면 4GB(2^32) 메모리 주소를 할당 받는다.
  • 주요 키워드
    • 주소
      • 논리 주소(logical address) : 가상메모리의 주소
      • 물리 주소(physical address): 실제 주소 (메인 메모리의 메모리 주소)
    • MMU(Memory Management Unit) : 논리 주소를 물리 주소로 젼환해 메인 메모리를 사용할 수 있게 해주는(런타임에 대응시키는) 하드웨어, CPU 내부에 있음

 

2-1. 가상 메모리 관리 기법

  세그먼테이션 기법
segmentation
페이징 기법
paging
요약 메모리를 가변적인 크기의 세그먼트로 분할해서 관리 메모리를 일정한 크기의 페이지 단위로 나눠서 관리
  • 현재 운영체제는 대부분 두 가지를 함께 사용함 (segmentation on with paging)

 

2-2. 페이징

가상 주소 공간과 메인 메모리를 일정한 크기로 쪼개어 다룸

페이징 기법이 일어나는 모습

 

  • 페이지(page) : 일정한 크기로 나눈 가상 메모리의 한 부분
    • VPN(Virtual Page Number) : 페이지 순서를 나타내는 비트 (페이지 넘버)
  • 프레임(frame) : 일정한 크기로 나눈 물리 메모리(RAM)의 한 부분
    • PPN(Physical Page Number) : 프레임 순서를 나타내는 비트 (프레임 넘버)
  • 요구 페이징(demand paging) : 가상 메모리를 구현하는 방법. 프로세스를 실행할 때 모든 페이지를 프레임에 매핑하는 것이 아니라, 필요한 페이지만 메인 메모리에 올려 실행하는 것
    • 프리페어링(preparing) : 프로세스가 처음 실행될 때 운영체제는 페이지 테이블을 메인 메모리에 만들고 실행에 필요한 페이지만 먼저 프레임에 매핑하는 것
  • 페이지 테이블 : 하단 설명
  • 페이지 폴트(page fault) : 요청한 페이지가 메인 메모리에 없을 때 발생, 하드디스크에서 가져와야 함
    • 이때 메인 메모리가 이미 페이지로 꽉 차 있다면 교체 알고리즘에 따라 페이지를 내린다. (가장 먼저 들어온, 가장 오래 전에 사용한, 가장 적게 사용한 페이지)
    • 페이지 폴트가 자주 일어나면 데이터를 읽어 오는 속도 때문에 성능이 떨어지므로, 페이지 폴트가 일어날 확률을 줄이기 위해 지역성을 고려하면서 프로그래밍해야 함 (자료 구조를 배열로 변경하거나, 다이내믹 힙을 사용하는 등)
  • 변환 색인 버퍼(Translation Lookaside Buffer, TLB) : 페이지 테이블을 캐싱해 주는 (일종의) 캐시 메모리 (얘도 TLB 히트 남) 

 

2-2-1. 페이지 테이블

  • 어떤 프로세스의 VPN, PPN, 상태(contral bit) 등을 저장하는 테이블
    • 테이블은 메인 메모리에 저장됨
    • CPU에는 페이지 테이블의 시작 주소(PTBR, Page Table Base Assress)를 가리키는 레지스터가 있음
    • MMU가 이것을 참조하여 논리 주소를 물리 주소로 바꿈
  • 유효 비트(Vaild bit) : 프레임이 실제 메모리에 있는지(1), 하드디스크에 있는지(0)





2-3. 프로세스와 메모리

가상 메모리가 공간을 할당받으면 각 영역으로 나누어짐

OS가 커널의 위치와 용량을 고정

---- 가상 메모리 공간 ----
커널 영역
OS가 사용하는 영역
유저 영역
프로그램 사용 영역
코드 세그먼트
함수, 클래스 등이 저장됨 (인스트럭션이 들어감)
데이터 세그먼트
전역 변수가 저장됨 (main 밖에 있는 변수)
힙 세그먼트
자유롭게 메모리를 할당하고 해제하는 공간 (동적 할당)
malloc 하면 원하는 사이즈만큼 힙에 할당 (컴파일할 때 size를 알 수 없는 경우 사용)
동적 할당 후 해제하지 않을 경우 메모리 누수가 일어남
메모리 단편화 : 그때그때 할당할 공간을 찾기 때문에 공간이 쪼개짐, 지역성의 원리가 적용되지 않아 캐시 미스/페이지 폴트 확률이 높아짐

할당할 공간을 찾아야 하므로 느림
스택
함수에서 사용할 지역 변수 등이 쌓이는 공간
최대 크기가 정해져 있음
함수 호출(콜 스택)에 따라 아래 이미지처럼 스택 프레임이 쌓임

 

각 세그먼트 사용 예시

#include <iostream> 

// ----- CODE ----- // 
int add(int a, int b){
		return a + b; } 

int sub(int a, int b){ 
		return a-b; } 

// ---- DATA ----- // 
int globalX = 10; 

int main(void){ 
		// ---- STACK ----- // 
		int localX = 20; 

		// ---- HEAP ----- // 
		int *heapX = (int*)malloc(sizeof(int)); 
		*heapX = 30; 
		free(heapX); 

		return 0; 
}






더보기

운영체제 SystemCall Shell
프로세스 생명주기
스케줄링 라운드로빈 FCFS SJF 컨텍스트스위칭 PCB TCB
멀티스레드 동기화문제 RaceCondition CriticalSection 뮤텍스 세마포어

 

1. 운영체제

1-1. 운영체제가 하는 일

운영체제는 하드웨어와 소프트웨어를 관리하기 위해 만들어졌다. OS 규격과 관리 목적은 제품에 따라 다르지만 대부분 아래 기능을 수행한다.

  • 메모리, CPU, 저장장치, 네트워크 등을 관리
  • 파일 시스템
  • 프로세스 실행 관리
  • 하드웨어 추상화 계층 (HAL)
    • 프로그래머가 하드웨어에 신경쓰지 않게 해주는 것
    • 운영체제와 운영체제가 컴퓨터 장치를 사용하기 위한 드라이버 사이에서, 사용자가 필요한 동작들에 대하여 표준적인 접근 방법을 제공하는 것 

 

1-2. 운영체제의 종류

  • 유닉스: 벨 연구소
    • macOS, iOS
  • 리눅스: 대학원생 리누스 토르발스
    • 안드로이드
  • 윈도우: MS
  • Mac OS: apple

 

1-3. 운영체제의 구조

컴퓨터 계층

  • 커널
    • 운영체제의 핵심. 항상 메모리에 상주
    • Linux의 경우 OS가 항상 메모리에 상주하기 때문에 운영체제와 커널이 같다
  • System Call
    • 운영체제의 기능을 사용할 수 있게 해주는 API
    • 커널 모드, 유저모드
      • 보안을 위해 존재
      • 프로세스는 유저 모드와 커널 모드 두 가지 모드로 작동됨.
      • 시스템콜이 이뤄지면 커널 모드로 전환. 할 일이 끝나면 다시 유저 모드로 돌아감
  • Shell 
    • 커널과 사용자 사이에 끼어서 명령을 해석하고 처리 결과를 보여주는 프로그램
    • 명령줄 셸 : cmd, powershell, sh, bash
    • 그래픽 셸 : 윈도우 탐색기

 

2. 프로세스

2-1. 프로그램과 프로세스

프로그램 하드디스크에 저장된 (프로그램의) 실행 파일
프로세스 프로그램이 실행되고 있는 상태
즉, 하드디스크에서 메인 메모리로 코드와 데이터를 가져와 현재 실행되고 있는 상태 (프로세스를 실행하려면 독자적인 메모리 공간과 CPU가 필요)
동시에 여러 개가 존재할 수 있음
cmd에 tasklist 명령어를 입력하면 현재 실행 중인 프로세스 목록을 볼 수 있음
프로세스 상태는 상황에 따라 변화함 (생명 주기)

 

2-2. 프로세스의 상태 (생명 주기)

생명 주기

  • 생명 주기
    • Created : 막 생성해서 실행 준비를 하는 단계, 실행 중인 프로세스와 우선순위를 비교
    • Waiting : 순서에 맞춰 실행을 기다리는 상태
    • Running : 운영체제로 부터 CPU를 할당 받아 실행되고 있는 상태 (순서에 맞춰 실행을 기다리는 상태)
      • Dispatch 디스패치 : 다음으로 실행될 프로세스에 CPU를 할당하는 것
      • Preemption 프리엠션 : 실행 중이던 프로세스에서 CPU를 해제하는 것
    • Blocked : I/O(읽고 쓰기처럼 디스크에서 정보를 불러오는 아주 간단한 일) 같이 오랜 시간 걸리는 작업에 들어간 경우 해당 상태로 변경됨 (I/O 작업이 끝나기 전까지 실행 불가능, 이때 실행 가능 상태의 프로세스 중 하나가 CPU를 할당받음)
    • Termiated : 종료, 소멸

 

2-3. 스케줄링

디스패치와 프리엠션이 일어날 때 운영체제가 어떤 프로세스에게 CPU를 할당할까?

  • 스케줄링 : 운영체제가 여러 프로세스의 CPU 할당 순서를 결정하는 것
  • 스케줄러 : 스케줄링을 하는 프로그램
  • 스케줄링 종류
    • 선점형 : 어떤 프로세스가 실행 중에 있어도 스케줄러가 강제로 프로세스를 중지하고 다른 프로세스를 실행(CPU에 할당)
    • 비선점형 : 실행 중인 프로세스가 종료되거나, I/O 작업에 들어가거나, CPU를 반환하기 전까지는 계속해서 실행됨. 우선순위가 높은 프로세스가 생성되어도 실행 중인 프로세스가 자발적으로 CPU를 양보하지 않으면 사용하지 못함
  • 스케줄링 알고리즘의 종류 (제품에 따라 다름)
    • 우선순위(Priority) : 우선순위를 매겨 높은 우선 순위부터 실행 선점형 
      • 기아 상태(starvation) : 낮은 우선순위가 계속 일을 하지 못하는 상황
      • 에이징(aging) : 오랫동안 실행되지 못한 프로세스의 우선순위를 높여 기아 상태 방지
    • 라운드로빈(Round-Robin) : 일정 시간동안 순서대로 할당 비선점형
      • 일정시간 : 타임 슬라이스(time slice) 또는 퀀텀(quantum)이라고 불림
    • FCFS(First Come First Served) : 실행 가능 상태에 먼저 들어온 순서대로 처리 비선점형
    • SJF(Shortest Job First) : 평균 대기시간을 최소화하기 위해 CPU 할당 시간이 가장 짧을 것으로 예측되는 것부터 처리 선점형&비선점형
      • 단점 : 할당 시간이 긴 프로세스는 계속해서 실행되지 않는 기아 상태에 빠질 수 있고, CPU의 실제 할당 시간을 알 수 없으니 예측에 의존해야 함

 

2-4. 컨텍스트 스위칭

  • 컨텍스트 스위칭 : 실행되고 있는 프로세스가 변경될 때(디스패치, 프리엠션이 일어날 때) CPU에 프로세스를 실행하기 위한 정보(PCB)가 교체되는 것
    • 이때 현재 CPU의 레지스터 값들을 전환, 컨텍스트 스위칭이 일어날 때 기존 프로세스의 CPU 상태 정보는 PCB에 기록해 둠
      • CPU의 상태 정보 : 다음에 CPU를 점유하면 실행할 다음 인스트럭션의 위치(프로그램 카운터 정보), 현재 쌓인 스택 프레임의 정보(스택 포인터와 프레임 포인터의 정보), 현재 연산 중인 데이터가 저장된 레지스터 값(범용 레지스터 정보) 등
    • 잦은 컨텍스트 스위칭은 성능을 떨어트림
  • PCB(Process Control Block) : 프로세스의 CPU 상태와 프로세스의 상태를 저장해 둔 메모리 블록
    • 프로세스의 상태 : 현재 실행 중인 인스트럭션, 스택 프레임, 프로그램 카운터 값, 스택 포인터와 프레임 포인터와 범용 레지스터의 값
    • 이 정보는 커널에 위치함 (OS가 사용하는 영역)

 

3. 멀티 스레드

3-1. 멀티 프로세스와 멀티 스레드

  멀티 프로세스 멀티 스레드
그게 뭐죠? 프로세스 : 알잖아
멀티 프로세스 : 프로세스 여러 개를 동시에 실행하는 것
스레드 : 프로세스 안의 실행 흐름 단위로, 스레드 단위로 CPU를 할당받을 수 있다.
멀티 스레드 : 스레드 여러 개를 동시에 실행하는 것
가진 것 PCB TCB (PCB랑 거의 같음)
이미지
통신 방법 소켓, 메시지 큐, 파이프 공유되는 메모리 공간 (ex. 전역변수)
장점 독립된 구조로 안정성이 있음 컨텍스트 스위칭이 단순해 성능이 좋아짐
단점 프로세스 컨텍스트 스위칭이 일어나 성능이 떨어짐 (통신 비용이 큼) 자원을 공유할 때 동기화 문제 발생 가능성이 있음 (아래에서 설명할 공유 자원 문제)

 

3-2. 멀티 스레딩을 Python으로 구현하기

아래는 공유 자원 문제(동기화 문제)가 일어나는 코드이다.

# 공유 자원 문제가 일어나는 코드

import threading

g_count = 0

def thread_main():
    global g_count
    for i in range(1000000):
        g_count += 1

threads = [ ]
for i in range(50): #3
    th = threading.Thread(target = thread_main)
    threads.append(th)

for th in threads:
    th.start()

for th in threads:
    th.join()

print('g_count :{:,}'.format(g_count))

경쟁 조건에 의해 실행할 때마다 다른 값이 나옴


이러한 문제가 발생하는 이유는?

위의 경우 단순히 변수에 1을 더하는 작업이지만 instruction 기준으로 보면 여러 단계. 어느 시점에 컨텍스트 스위칭이 일어나는지에 따라 결과가 잘못 나온다.

  • 경쟁 조건(race condition) : 스레드 여러 개가 공유 자원에 동시에 접근하는 것
  • 임계 영역(critical section) : 공유 자원에 접근해 변경을 시도하는 코드 영역

 

3-3. 공유 자원 문제(동기화 문제) 해결

문제를 해결하기 위해, 공유 자원에 여러 스레드가 동시에 접근하지 못하도록 제어해야 한다.

# 문제 해결 코드 

import threading

g_count = 0

def thread_main():
    global g_count
    lock.acquire()    
    for i in range(1000000):
        g_count += 1
    lock.release()

lock = threading.Lock()
threads = [ ]
for i in range(50): 
    th = threading.Thread(target = thread_main)
    threads.append(th)

for th in threads:
    th.start()

for th in threads:
    th.join()

print('g_count :{:,}'.format(g_count))

이것을 상호 배제라고 한다.

  • 뮤텍스 (상호배제, Mutual Exclusion = Mutex)
    • 스레드 하나가 공유 자원을 이용하는 동안 다른 스레드가 접근하지 못하게 막는 것
    • 1칸 짜리 화장실
  • 세마포어 (더 좋은 방법, Semaphore)
    • 공유 자원에 최대 접근 가능한 스레드 개수를 제한하는 것
    • 자원에 접근 중인 스레드를 count해서 구현
    • 최대 접근 가능한 스레드 개수를 1로 하면 뮤텍스가 됨
      • 세마포어는 뮤텍스가 될 수 있지만, 뮤텍스는 세마포어가 될 수 없음
    • N칸 짜리 화장실

 

3-4. 교착 상태 (DeadLock)

둘 이상의 스레드가 다른 스레드가 점유하고 있는 자원을 서로 기다릴 때와 같이 무한 대기에 빠지는 상황

더보기

트랜지스터 논리게이트 진리표 논리회로 플립플롭 가산기 레지스터 시스템버스 클록 인스트럭션 어셈블리어


01장 CPU
02장 운영체제 - 프로세스와 스레드
03장 운영체제 - 메모리
04장 프로그래밍 언어, 컴파일러
05장 변수, 정수, 실수, 문자열
06장 함수
07장 객체지향
08장 클래스
09장 자료구조 1
10장 자료구조 2
11장 알고리즘
12장 네트워크
13장 데이터베이스



트랜지스터

전기로 ON/OFF 할 수 있는 스위치


논리 게이트

트랜지스터를 여러 개 모아서 만듦
Bool 함수를 구현하기 위한 것

진리표



연산 장치

논리 게이트를 모아서 만드는 계산 장치 (논리 회로)

  조합 논리회로 순차 논리회로
특징 현재 입력에 의해서만 출력이 결정된다. 현재 입력 + 과거 출력에 의해 출력이 결정된다.
(예전 출력값이 유지(저장) 된다는 의미)
태그 기본 논리 게이트 플립플롭
순차 회로의 기본 요소로, 1비트의 정보를 보관/유지한다.
예전 출력값 유지를 위해 전기 신호가 계속 공급되어야 정보 유지 가능
'저장'하기 때문에, 메모리 소자라고도 불린다.
레지스터는 플립플롭의 묶음
가산기
ALU 안에 있는 논리 게이트
레지스터
CPU 안의 메모리로, 종류와 쓰임새가 다양하다.

- PC(Program Counter): 다음으로 실행할 명령어의 주소 저장
- IR(Instruction Register): 읽어온 명령어를 저장
- EAX(Accumulation): 산술연산의 변수값 저장, 함수의 return 값
- EBX(Base Register): 산술연산의 변수값 저장



CPU

CPU의 간략한 구조

  • 시스템 버스 : 데이터를 주고받는 통로
  • 클록
    • CPU 혹은 프로세서 속도의 지표, 명령어 한 사이클
    • 펄스 상승 때 인스트럭션 실행

  • 인스트럭션 (instruction) : 우리가 작성한 코드가 CPU에서 실행되기 위해 최종적으로 변환된 기계어 명령어 (0101110010... 같은 거)
  • 인스트럭션 세트 : CPU가 인식해서 실행할 수 있는 기계어 (CPU 제조사마다 다름)
  • 어셈블리어 : 기계어를 사람이 읽을 수 있도록 1:1 대응한 문자 상태의 명령어
  • GPU (graphics processing unit) : 그래픽 처리 장치




+ Recent posts