1. User Mode and Kernel Mode
일단 커널부터
- 커널은 운영체제의 핵심이다.
- 리소스를 효율적으로 관리하기 위해 굉장히 다양한 일들을 수행한다.
- 예를 들면 CPU 스케쥴링, 메모리 관리, 입출력 관리, 파일 시스템 등
- 왜 모드를 나눠서 관리하는가?
- 커널에서 중요한 자원들을 관리하기 때문에 프로그래머가 그 중요한 자원에 함부로 접근하지 못하도록 모드를 2가지로 나눴다.
User Mode
- 프로그래머는 유저 모드에서 코드를 작성하고, 프로세스를 실행하는 등의 행동을 할 수 있다.
Kernel Mode
- 모든 자원에 접근해 명령을 내릴 수 있다.
- 유저 모드와는 비교가 안되게 컴퓨터 내부에서 모든 짓을 할 수 있다.
- 프로세스를 생성하거나 메모리 할당, 해제 같은 일들을 할 수 있다.
- 대표적으로 malloc() !
모드의 전환
- 프로세스가 실행되는 동안 프로세스는 굉장히 자주 유저모드와 커널 모드를 왔다 갔다 하면서 실행된다.
- 유저 모드 → 커널 모드
- 프로세스가 유저 모드에서 실행되다가 특별한 요청이 필요할 때 system call을 이용해서 커널에 요청한다.
- 커널 모드 → 유저 모드
- system call을 요청 받은 커널이 그 요청에 대한 일을 하고 결과값을 system call의 리턴 값으로 전달한다.
2. User Stack과 Kernel Stack
- 유저 스택과 커널 스택은 프로세스가 생성될 때 동시에 생성된다.
- 유저 스택은 유저 모드에서 쌓인다.
- 보통 애플리케이션 코드가 열기서 실행되며, 이 스택 위에서 함수를 호출하고 지역 변수를 할당한다.
- 유저 모드에서 함수를 실행하고 함수 호출이나 데이터 저장을 위한 임시 메모리 공간으로 기능한다.
- 커널 스택은 커널 모드에서 쌓인다.
- 커널 스택은 프로세스가 커널 모드로 전환되면서 사용하는 스택이다.
- 예를 들면 시스템 콜이나 인터럽트가 발생해서 커널 모드로 들어가면 이 스택을 사용한다.
- 커널 스택에는 유저 모드로 다시 돌아갈 때 필요한 정보를 저장한다.
- 유저 스택은 유저 모드에서 쌓인다.
- 스택에는 프로세스에서 일어나는 일들이 순차적으로 기록된다.
- Why?
- 함수가 끝난 후 원래 위치로 돌아가기 위해서
- 프로세스가 다시 유저 모드로 돌아가기 위해서
- 정리하자면, 프로세스가 함수 호출을 할 때마다 이전 상태를 기억해뒀다가, 함수 실행이 끝나면 그 정보를 복구하면서 원래의 실행 흐름으로 돌아와야 하기 때문이다.
- Why?
3. System Call
- 프로세스가 운영체제의 서비스를 받고 싶으면 시스템 콜 형태로 서비스를 요청해야 한다.
- 파일 리드, 프로세스 생성 등
- 커널 자원을 사용자가 사용할 수 있도록 만들어 놓은 함수(인터페이스) → 호출시 커널 모드로 전환되어 실행된다.
- 시스템 콜이 수행되는 메커니즘
4. 파일 디스크립터
- 파일 디스크립터는 프로그램이 파일, 소켓, 파이프 같은 리소스에 접근할 때 사용하는 고유한 번호다. 할당 주체는 커널이다.
- 프로세스마다 운영체제가 관리하는 파일 디스크립터 테이블에 각 디스크립터가 등록돼서 번호를 통해 리소스에 접근하게 된다.
- 프로세스에는 표준 입력(0), 표준 출력(1), 표준 에러(2) 세 개의 기본 파일 디스크립터가 있다. 이 개념을 통해 파일 디스크립터의 기본 사용 방식을 알 수 있다.
- 파일 디스크립터와 파일 디스크립션은 구분이 필요하다.
- 파일 디스크립터(File Descriptor)는 사용자가 생성해 사용하는 번호이다.
- 파일 디스크립션(File Description)은 커널이 관리하는 실제 리소스 정보다. 여러 파일 디스크립터가 동일한 파일 디스크립션을 참조할 수 있다.
- 처음 리소스를 열면 FD와 파일 디스크립션이 모두 생성되지만, 이후에는 FD만 복사하여 여러 개 사용할 수 있다.
- 파일 디스크립터를 모두 닫아야만 커널에서 파일 디스크립션도 삭제된다.
5. 레지스터 vs 메모리
- 레지스터와 메모리를 둘다 데이터를 처리하는 기억장치다.
- 컴퓨터는 본질적으로 같은 용도의 기억 장치를 왜 2개나 사용할까? 이 둘은 어떤 차이점이 있을까?
- 위치: 레지스터는 CPU 내부에, 메모리는 CPU 외부에 위치한다.
- 속도: 레지스터는 메모리보다 훨씬 빠르다.
- 용량: 레지스터는 매우 작고, 메모리는 상대적으로 큰 용량을 가진다.
- 사용 목적:
- 레지스터는 실행 중인 프로그램의 현재 연산에 필요한 데이터나 주소만 저장한다.
- 예를 들어, 연산을 위해 불러온 두 숫자나 연산 결과, 다음 실행 위치(프로그램 카운터) 등이 레지스터에 저장된다.
- 메모리는 프로그램 전체와 데이터를 저장하고 관리하는 장치이다.
- 프로그램이 시작될 때 운영체제는 메모리에 프로그램 코드와 데이터, 전역 변수 등을 로드해 놓고, 프로그램 실행 중에 이 데이터를 CPU가 불러다 사용할 수 있게 한다.
- 레지스터는 실행 중인 프로그램의 현재 연산에 필요한 데이터나 주소만 저장한다.
6. 캐시
- 위치: CPU와 메모리 사이
- 속도와 용량: 캐시는 레지스터보다는 느리지만 메모리보다 빠르고, 메모리보다는 용량이 작다.
- 계층 구조: L1, L2, L3 레벨로 나뉘며 각 레벨이 속도와 용량에 차이가 있다.
- 목적: CPU가 자주 접근하는 데이터를 빠르게 제공해 CPU와 메모리 간 병목 현상을 줄이는 역할이다.
- 캐시는 지역성(Locality) 개념을 활용해 CPU의 성능을 높여주는 고속 메모리다.
- 시간 지역성(Temporal Locality): 최근에 접근한 데이터가 다시 사용될 가능성이 높아, 캐시는 최근 사용 데이터를 저장해 빠르게 접근할 수 있게 한다.
- 공간 지역성(Spatial Locality): 특정 위치의 데이터에 접근할 때, 그 주변 데이터도 함께 사용할 가능성이 커서 캐시는 인접한 메모리 블록을 함께 저장해 연속적인 접근을 효율적으로 지원한다.
- 이렇게 지역성을 잘 활용하면 캐시의 히트율이 높아져 CPU가 메모리에 자주 접근할 필요가 줄어들어 시스템 성능이 향상된다.
7. Atomic Operation
- 원자적 연산은 중간에 끼어들 수 없이 완전히 수행되거나 전혀 수행되지 않은 상태를 보장한다.
- 멀티스레드 환경에서 데이터 경쟁을 방지하고, 일관된 결과를 보장하는 데 필수적이다.
- 락, 하드웨어 명령어, Atomic 변수 등을 통해 원자성을 구현할 수 있다.
8. Rax Register
- rax는 시스템 호출 번호를 전달하는 데 사용된다.
- 운영체제가 제공하는 시스템 호출에는 각기 고유한 번호가 있는데, 프로세스가 커널에 특정 작업을 요청할 때 이 번호를 rax에 넣는다.
- 예를 들어, 파일을 읽기 위해 read 시스템 호출을 할 때 rax에 해당 호출 번호를 넣어서 커널에 요청을 전달한다.
- 커널 모드로 진입한 이후, rax에 들어있는 번호를 보고 커널이 어떤 시스템 호출을 수행할지 판단한다.
9. 32bit OS vs 64bit OS
- 비트 : 데이터 버스가 한번에 전송할 수 있는 데이터의 양
- 64비트 운영체제에서 32비트 프로그램을 실행할 수 있다. 호환이 된다!
- 그리고 64비트에 32비트 크기만 사용할 때 남은 빈 공간은 0으로 채워진다. → 제로 확장 (zero extension)
10. 세그멘테이션 폴트
- 프로그램이 잘못된 메모리 영역에 접근할 때 발생하는 오류다.
- 세그멘테이션 폴트는 프로그램이 허용되지 않은 메모리에 접근하려고 할 때 시스템이 이를 감지하고 오류를 발생시켜 프로그램을 강제 종료시키는 메커니즘이다.
- 왜 발생할까?
- 잘못된 포인터 사용: 포인터가 NULL 포인터이거나, 초기화되지 않은 포인터일 때 이 포인터를 사용해서 메모리에 접근하려고 하면 세그멘테이션 폴트가 발생한다.
- 유효하지 않은 메모리 접근: 프로그램이 허용되지 않은 메모리 영역에 접근하려고 할 때 발생한다.
- 예를 들어, 배열의 범위를 벗어나거나, 프로그램에 할당되지 않은 메모리 주소에 접근하려고 할 때 이런 문제가 생긴다.
- 읽기/쓰기 권한 오류: 읽기 전용 메모리나 커널이 사용하는 특정 메모리 영역을 쓰려고 할 때도 이 오류가 발생한다.
- 스택 오버플로우: 재귀 호출을 너무 많이 하거나, 메모리를 과도하게 사용하는 경우 스택 영역이 넘쳐버리면 세그멘테이션 폴트로 이어질 수 있다.
11. 인터럽트
- 예기치 않은 상황이나 이벤트 발생 시 프로그램의 흐름을 잠시 멈추고 특정 처리를 수행하도록 하는 메커니즘 → CPU를 뺏어옴
- 인터럽트 처리 과정
'TIL' 카테고리의 다른 글
컴퓨터 구조와 운영체제 50분 만에 핵심 개념 정복하기 (1) | 2024.09.30 |
---|---|
[TIL] LCS 알고리즘 (0) | 2024.09.29 |
[TIL] 그리디 알고리즘(탐욕법, Greedy Algorithm) (1) | 2024.09.28 |
[TIL] 다이나믹 프로그래밍(Dynamic Programming, DP) (0) | 2024.09.27 |
최소 신장 트리(Minimum Spanning Tree, MST) : 가장 경제적인 방식으로 모든 지점을 연결한다 (4) | 2024.09.25 |