week9. 공부 키워드
: 운영체제의 핵심 부분 -> 하드웨어와 직접 상호작용하며 시스템 자원을 관리함
=> 커널 모드: 커널이 동작하는 특권 모드: 모든 시스템 리소스에 대한 접근 권한을 가짐
정리: 운영체제의 핵심 기능을 담당하는게 커널이다!
- 커널: 하드웨어와 직접 상호작용하고 시스템 자원 관리
- 운영체제는 사용자와 어플리케이션에게 이러한 기능을 제공하는 전체 시스템
User mode(사용자 모드) vs. Kernel mode(커널 모드)
Kernel Mode
커널 모드 -> CPU가 운영체제의 커널을 실행할 때 사용하는 모드
: 시스템의 모든 메모리와 하드웨어 리소스에 대한 완전한 접근 권한을 가짐, 특권 명령을 실행 가능.
-> 하드웨어 제어, 시스템 자원 관리, 프로세스 스케줄링 등 커널 기능이 실행되는 모드
- 커널 모드는 CPU의 특권 레벨 중 가장 낮은 레벨인 Ring 0에서 실행 -> 모든 명령어 실행 가능한 권한
- 커널 모드에서 실행되는 프로그램은 하나의 가상 메모리 영역 공유하여 서로 접근이 가능하다.
User Mode
: 사용자 모드에서는 사용자 애플리케이션이 실행된다. 시스템의 나머지 부분을 보호하기 위해서 제한된 접근 권한을 가짐, 특권 명령을 실행할 수 없음.
각 응용 프로그램은 독립적인 프로세스로 실행됨
- User mode는 CPU의 특권 레벨 중 가장 높은 레벨인 Ring 3에서 실행 -> 제한된 권한, 특권 명령을 실행할 수 없음.
- user mode에서 실행되는 프로세스는 각자의 가상 메모리 영역을 가지고 다른 프로세스와 격리되어 있다. -> 한 프로세스에서 오류가 발생해도 다른 프로세스나 운영체제에 영향을 미치지 않는다.
예를 들어, 사용자 프로그램은 파일을 읽거나 쓰려면, OS의 파일 시스템 서비스를 요청해야 한다.
이렇게 user mode와 kernel mode를 분리함으로써 시스템의 보안과 안정성을 크게 향상시킬 수 있다.
User mode에서 Kernel mode로 전환할 때 일반적으로 시스템 콜을 통해 이루어진다!
: 사용자 모드에서 커널 모드로 전환하기 위해 필요한 인터페이스
파일 조작 (예: open, read, write)
프로세스 관리 (예: fork, exec, exit)
메모리 관리 (예: mmap, munmap)
기타 다양한 운영체제 기능
인터럽트와 시스템 콜의 차이가 무엇인가?
Interrupt
: 하드웨어나 소프트웨어 이벤트에 의해 발생. 주로 비동기적으로 cpu 흐름을 변경하여 특정 이벤트 처리
- 주로 시스템이 즉각적으로 처리해야 하는 상황 => 입출력 장치 관리, 타이머 이벤트 처리, 하드웨어 오류 처리, 전원 관리, 프로세스 간 통신 등등
System call
: 사용자 프로그램이 운영체제의 기능을 요청할 때 사용됨. 커널 모드로 안전하게 전환하여 시스템 자원을 사용할 수 있게끔
Register
- CPU 내부에 위치
- 매우 빠름, CPU가 가장 빠르게 접근할 수 있는 저장 공간
- but 용량이 매우 작음
- CPU의 연산을 직접 지원하기 위해 사용
- 주로 명령어 실행 중 중간 결과를 저장
- 주소 계산, 데이터 이동 등 다양한 단기 작업에 사용
예시) PC, SP(Stack Pointer), 범용 레지스터 등
context switch가 발생하면 운영체제는 각 프로세스의 PCB를 이용해서 현재 실행중인 프로세스의 레지스터 상태를 저장하고, 다음 실행할 프로세스의 레지스터 상태를 복원한다.
이를 통해 여러 프로세스가 CPU를 공유하면서도 각 프로세스가 자신의 작업을 이어나갈 수 있게 된다.
Memory
- CPU 외부에 위치(일반적으로 메인 보드에 장착)
- 레지스터에 비해 느리다.
- 용량이 큼
- 실행 중인 프로그램과 그 데이터를 저장
- 운영체제, 사용자 프로그램이 사용하는 작업 공간 제공
- 프로세스 간의 데이터 공유 및 저장
예시) RAM(Random Access Memory), ROM(Read-Only Memory)
프로그램을 실행하려면 먼저 실행 파일을 디스크에서 읽어서 메모리로 로드한다.
실행 파일의 각 세그먼트(코드, 데이터, 스택 등)를 메모리의 적절한 위치에 배치한다.
Cache
: CPU와 RAM 사이에 위치한 고속 메모리
캐시는 cpu내부에 위치하며 메모리 접근 시간을 줄이고 성능 향상을 위해 사용된다.
레지스터와 캐시의 비교
: 레지스터는 CPU의 가장 빠른 기억장치로, 명령어 실행 시 직접 접근하므로 지연 시간이 거의 없다.
캐시는 레지스터보다는 느리지만 RAM 보다는 훨씬 빠르다. -> 데이터 접근 시간을 줄이기 위해 사용된다.
레지스터는 CPU의 연산 및 명령어 수행 시에 사용된다. 연산 중간 결과를 저장하거나 특정 명령어 실행에 필요한 데이터를 일시적으로 저장한다.
캐시는 자주 사용되는 데이터나 명령어를 저장하여 메모리 접근 시간을 줄인다. -> CPU와 메인 메모리 사이의 중간 저장소 역할
: 사용자 프로그램의 함수 호출, 지역 변수 저장, 함수 매개 변수 전달을 위해 사용됨
LIFO(Last In First Out) 방식으로 작동한다. -> 가장 마지막 스택에 추가된 데이터가 가장 먼저 나가게 된다.
스택은 높은 메모리 주소에서 낮은 메모리 주소 방향으로 성장한다. 새로운 데이터가 추가될 때마다 스택 포인터(Stack Pointer: 가장 최근에 추가된 데이터의 주소)가 낮은 주소로 이동한다.
push
: 새로운 데이터가 User stack에 추가되면 스택 포인터는 낮은 메모리 주소로 이동하고 데이터는 그 위치에 저장된다.
pop
: 가장 최근에 저장된 데이터, 가장 낮은 주소의 데이터가 먼저 제거. 스택에 데이터가 제거되면 스택 포인터는 높은 주소로 이동
궁금증_ 힙과 스택은 왜 서로 다른 방향으로 성장하는가?
서로 반대 방향으로 성장하면 두 메모리 영역이 충돌할 가능성이 줄어든다. 힙은 위로, 스택은 아래로 성장하기 때문에 만나기 전까지는 독립적으로 확장할 수 있다.그리고 stack-heap collision 을 막기 위한 여러 매커니즘이 사용된다고 한다! 예시 - 가상 메모리 사용하여 물리적 메모리 한계 극복하기
-> but
주소 공간에 여러 쓰레드가 공존할 때는 이런 식으로 주소 공간을 나누게 되면 동작하지 않는다고 한다.
: 운영체제의 커널 모드에서 사용되는 스택
-> 시스템 콜이나 인터럽트 처리와 같은 커널 모드 작업을 처리하는 동안 사용
-> 중간에 끼어들거나 분리될 수 없는, 즉 한번에 완전히 수행되거나 전혀 수행되지 않는 연산
=> 주로 멀티 쓰레딩 환경에서 데이터 일관성을 유지하고 동기화를 위해 사용된다.
=> 핀토스에서 예를 들면 sema_down, sema_up 과 같이 세마포어의 값을 증가 감소시키는 연산은 원자적으로 수행되어야 한다.
각 메모리 주소 32bit로 표현 가능 vs. 64 bit로 표현 가능
cpu는 메모리에서 값을 읽어올 때 한번에 4byte(32 bit), 8byte(64bit)를 읽어온다.
32 bit OS
: 32 bit register -> (예: EAX, EBX, ECX 등).
64 bit OS
: 64 bit register -> (예: RAX, RBX, RCX 등). 32 bit 레지스터도 사용할 수 있지만, 상위 32비트는 무시됨
Segmentation Fault?
- 컴퓨터 프로그램이 허용되지 않은 메모리 영역에 접근하려고 할 때 발생하는 오류
운영체제는 각 프로그램이 자신의 메모리 공간만 접근하도록 제한 - 다른 프로그램의 메모리나 운영체제 자체의 메모리를 침범하지 못하게.예시)
int* ptr = (int*) 0x00000000; *ptr = 1;
여기서 ptr은 int형 포인터로 선언되고, 0x00000000 주소로 초기화 -> NULL 포인터 - 유효하지 않은 메모리 주소
운영체제에 의해 보호됨. 일반적인 사용자 프로그램이 이 주소에 접근하려고 하면 오류 발생포인터를 사용할 때는 항상 유효한 메모리 주소로 초기화해야 한다. -> 동적 메모리 할당을 통해서 가능하다.
그리고 항상 NULL 여부를 확인해서 유효성을 검증해주자.int* ptr = (int*) malloc(sizeof(int)); if (ptr != NULL) { *ptr = 1; }