서론.
CS_Study를 진행하면서 커널스택과 유저스택에 대한 이해도가 부족한 것 같아 다시 톺아보았다.
시스템 메모리 구조
인텔 8086 32비트 시스템은 다음과 같이 구성되어 있다.
메모리는 크게 유저 공간(User Space)과 커널 공간(Kernel Space)으로 구분된다.
- 유저 영역(2GB): 사용자 프로그램이 실행되는 공간으로, 일반적으로 프로세스마다 세그먼트라는 논리적인 섹션으로 나누어 독립적인 메모리 공간을 제공한다.
- 커널 영역(2GB): 운영체제가 관리하는 보호된 메모리 공간으로, 사용자 모드에서는 접근할 수 없다.
유저 영역 (User Space)
-
Null Pointer 할당 영역 (64KB)
- Null 포인터 접근을 방지하기 위한 보호 영역으로, 잘못된 메모리 접근 시 Segmentation Fault를 발생시킨다.
- 해당 영역은 어떠한 데이터도 저장되지 않으며, 프로세스의 오류를 조기에 감지하는 역할을 한다.
-
유저 영역 (2GB)
유저 영역은 프로그램 실행에 필요한 메모리를 제공하며, 다음과 같은 구성 요소로 나뉜다
- 코드 섹션: 실행 가능한 기계어 코드가 저장된다.
- 데이터 섹션: 초기화된 전역 변수 및 정적 변수가 포함된다.
- BSS 섹션: 초기화되지 않은 전역 변수 및 정적 변수가 저장된다.
- 힙(Heap): 동적으로 할당되는 메모리 공간으로, 실행 중 프로그램이 메모리를 요청할 때 사용된다.
- 스택(Stack): 함수 호출 시 매개변수, 복귀 주소, 지역 변수 등을 저장하는 공간으로, LIFO(Last-In, First-Out) 방식으로 관리된다.
-
OFF-Limit 영역 (64KB)
- 유저 영역과 커널 영역 사이에 위치한 보호 공간으로, 유저 모드에서 커널 영역으로의 잘못된 접근을 방지한다.
커널 영역 (Kernel Space)
커널 영역은 다음과 같이 8가지 주요 영역으로 구성되어 있다:
- 커널 코드와 데이터
- 커널 텍스트(segment): 커널의 실행 코드가 위치하는 영역. 읽기 전용으로, 일반적으로 수정되지 않음.
- 커널 데이터: 커널의 전역 변수와 정적 변수.
- 초기화된 데이터와 초기화되지 않은 데이터(BSS 세그먼트) 포함.
- 페이지 테이블
- 전역 페이지 테이블: 사용자 공간과 커널 공간 모두에 대한 페이지 매핑 정보가 포함됨.
- 공통 매핑: 모든 프로세스가 공통으로 커널 공간을 참조할 수 있도록 페이지 테이블에 매핑.
- 커널 힙
- kmalloc API: 커널에서 사용하는 동적 메모리 할당 영역.
- vmalloc 영역: 비연속적인 가상 메모리를 동적으로 할당받는 영역.
- I/O 메모리 맵핑
- 장치 메모리 매핑(Device Memory Mapping): 하드웨어 장치와 상호작용하기 위해 사용되는 영역.
- 메모리 매핑 파일: 하드웨어 레지스터를 참조.
- 고정된 매핑(Fixed Mapping)
- 특정 물리 메모리 또는 하드웨어를 커널 공간에 고정적으로 매핑.
- 예: APIC(Advanced Programmable Interrupt Controller)와 같은 프로세서 장치.
- 스택 영역
- 커널 스택: 프로세스가 커널 모드로 전환될 때 사용.
- 프로세스 또는 스레드마다 별도로 할당되며, 컨텍스트 스위칭 시 작업 기록을 저장.
- 모듈(Module) 영역
- 동적으로 로드된 커널 모듈(예: 장치 드라이버)의 코드와 데이터가 저장됨.
- 커널 실행 중 동적으로 할당 및 해제.
- 예약되지 않은 영역
- 하드웨어 의존적이거나 특수 목적으로 사용될 수 있는 영역.
- 예: 커널 디버깅 데이터 또는 특정 장치와 관련된 데이터.
유저 스택과 커널 스택
자 이제 커널 허옇게 보이던 커널스택과 유저스택이 보인다. 시스템 메모리의 유저영역의 프로세스마다 독립적인 세그먼트에 올라가는 스택 섹션이 유저스택이고 커널스택은 커널 공간의 프로세스마다 독립적으로 존재하는 섹션이었다. 다시 정리해보자.
유저 스택 (User Stack)
- 위치: 프로세스의 사용자 주소 공간 내에 존재하며, 각 프로세스마다 독립적으로 할당된다.
- 용도:
- 함수 호출 시 매개변수, 복귀 주소, 지역 변수 등을 저장한다.
- 함수 호출과 복귀를 관리하며, 프로세스가 사용자 모드에서 실행되는 동안 사용된다.
- 특징:
- 프로세스마다 고유하며, 가상 메모리 상에서 동적으로 크기가 조정될 수 있다.
- 사용자 프로세스의 코드 실행 흐름을 보조한다.
커널 스택 (Kernel Stack)
- 위치: 커널 메모리 영역(32비트 시스템 기준 3GB~4GB)에 위치하며, 각 프로세스마다 고정된 크기로 할당된다.
- 용도:
- 커널 모드에서 함수 호출, 로컬 변수 저장, 인터럽트 처리 등에 사용된다.
- 프로세스 생성 시 프로세스별로 고정 크기로 할당되며, 일반적으로 8KB(32비트 기준)로 설정된다.
- 특징:
- 커널 스택은 커널 모드에서만 접근 가능하며, 보안성과 안정성을 보장한다.
- 재귀 호출이나 과도한 지역 변수 사용은 커널 스택 오버플로우를 초래할 수 있으므로 제한적으로 사용해야 한다.
유저 스택과 커널 스택의 연관성
유저 스택과 커널 스택은 프로세스 실행 중 시스템 호출이나 인터럽트가 발생할 때 상호작용한다.
- 모드 전환:
- 시스템 호출이나 인터럽트 발생 시, CPU는 유저 모드에서 커널 모드로 전환된다.
- 이 과정에서 스택 포인터가 유저 스택에서 커널 스택으로 변경된다.
- 정보 저장:
- 전환 시, 프로세스 상태(예: 프로그램 카운터, CPU 레지스터 등)는 커널 스택에 저장된다.
- 이를 통해 유저 스택과 독립적으로 커널 작업이 안전하게 수행될 수 있다.
- 보안 및 안정성:
- 유저 스택은 사용자 코드에 의해 수정될 가능성이 있으므로, 커널 작업은 커널 스택에서 보호된 환경에서 수행된다.
- 주소 공간 분리:
- 커널 스택은 모든 프로세스에서 동일한 물리적 주소를 참조하며, 커널 코드가 일정한 메모리 주소에서 실행되도록 보장한다.
컨텍스트 스위칭과 커널 스택
컨텍스트 스위칭(Context Switching)
컨텍스트 스위칭은 CPU가 실행 중인 프로세스 또는 스레드의 상태를 저장하고, 다른 프로세스 또는 스레드로 전환하는 작업이다. 이는 멀티태스킹 환경에서 필수적인 작업으로, 다음과 같은 과정을 포함한다.
- 현재 프로세스 상태 저장:
- 실행 중인 프로세스의 CPU 레지스터, 프로그램 카운터(PC), 스택 포인터(SP) 등의 상태 정보를 커널 스택에 저장한다.
- 이 정보는 해당 프로세스의 PCB(Process Control Block)에 보관된다.
- 운영 체제 커널로 전환:
- 하드웨어 인터럽트, 시스템 호출, 또는 스케줄러 호출로 인해 CPU 제어가 커널로 넘어간다.
- 새로운 프로세스 상태 복원:
- 스케줄러는 다음 실행할 프로세스를 선택하고, 해당 프로세스의 커널 스택에 저장된 정보를 복원한다.
- 프로세스 실행 재개:
- 새로운 프로세스가 선택되고, 복원된 상태 정보로 실행을 시작한다.
컨텍스트 스위칭 중 커널 스택 활용
컨텍스트 스위칭 시, 커널 스택에는 다음 정보가 저장된다.
- CPU 레지스터 상태:
- 프로그램 카운터(PC), 스택 포인터(SP), 범용 레지스터 값 등.
- 프로세스 상태:
- 메모리 관리 정보:
- 페이지 테이블, 세그먼트 테이블 등 메모리 매핑 관련 정보.
- I/O 상태 정보:
- 열려 있는 파일, 사용 중인 I/O 장치의 상태 정보.
- 스케줄링 정보:
- 우선순위, CPU 사용 시간 등 스케줄링 결정에 필요한 정보.
결론.
커널 스택과 유저 스택에 대해 다시 톺아보며, 각각의 역할과 구조를 명확히 이해할 수 있었다.
공부할수록 운영체제가 얼마나 정교하게 설계되어있는지 느낄 수 있었다…
KOCW도 하나 등록했다. 너무 깊게 파는건 아니겠지
참고.
• 리눅스 커널 내부구조 - 메모리 관리
• 리눅스 커널 메모리 관리 훑어보기
• Linux Kernel Memory Addressing