변수의 기록

(OS) 유저 모드와 커널 모드, 그리고 인터럽트와 시스템 콜 (예제 있음) 본문

CS지식/운영체제 (Operating System)

(OS) 유저 모드와 커널 모드, 그리고 인터럽트와 시스템 콜 (예제 있음)

불광동 물주먹 2025. 4. 13. 15:09

유저 모드와 커널 모드, 그리고 인터럽트와 시스템 콜

1. 유저 모드 vs 커널 모드

모던 운영체제는 CPU의 실행 권한을 **유저 모드(User Mode)**와 **커널 모드(Kernel Mode)**로 나눔.
이유는 운영체제와 시스템 자원을 보호하기 위함임.

유저 모드

  • 애플리케이션(사용자 프로그램)이 실행되는 환경
  • CPU는 제한된 명령어만 사용 가능
  • 직접 하드웨어 접근 불가 (예: 디스크, 메모리 제어, 네트워크 등)
  • 문제가 발생하더라도 시스템 전체에는 영향을 미치지 않음

커널 모드

  • 운영체제의 핵심인 커널이 실행되는 환경
  • CPU는 모든 명령어 실행 가능 (하드웨어 제어 포함)
  • 시스템 자원 관리 (메모리, CPU 스케줄링, 파일 시스템 등)
  • 모든 프로세스의 보호와 보안을 책임짐

2. 모드 전환 (유저 모드 → 커널 모드)

유저 모드에서 커널 모드로 전환되는 시점은 다음 두 가지 경우임

  1. 시스템 콜 호출
  2. 인터럽트 발생

전환 시 CPU는 현재 작업 중인 프로그램 상태(레지스터, PC 등)를 저장한 뒤 커널 코드로 진입함.
작업이 완료되면 다시 상태를 복원하고 유저 모드로 돌아감.


3. 시스템 콜 (System Call)

시스템 콜은 유저 프로그램이 OS 커널에 기능을 요청할 때 사용하는 인터페이스.
즉, 유저 모드에서 직접 하드웨어를 제어하지 않고 커널에게 "요청"하는 방식임.

예시 (주요 시스템 콜 종류)

 

시스템 콜 종류  설명
read() / write() 파일 입출력
open() / close() 파일 열기, 닫기
fork() / exec() 프로세스 생성, 프로그램 실행
wait() / exit() 프로세스 대기, 종료
socket() / bind() / accept() 네트워크 소켓 처리
kill() 시그널 전송

 

* 근데 개발을 할때 os 시스템 콜을 우리는 직접 쓴 적이 있을까?

그동안 개발을 하면서 다음과 같은 작업을 수도 없이 해왔을 것임:

  • 파일을 열고 읽거나 저장하고
  • 네트워크로 데이터를 보내고 받고
  • 새로운 쓰레드나 프로세스를 만들고 관리하고

그런데 여기서 한 가지 질문.

"우리는 직접 시스템 콜을 호출한 적이 있는가?"

예를 들어, open(), read(), write() 같은 시스템 콜을 직접 코드에 적은 적이 있었는가?

아마 대부분은 없음.

그렇다면 또 이런 질문이 생김.

"우리는 시스템 콜을 직접 호출한 적이 없는데, 어떻게 그런 기능들을 사용해온 걸까?"

답은 이렇다.

→ 우리가 사용하는 **고수준 언어(Java, Python 등)**는 시스템 콜을 직접 노출하지 않음.
대신, **내부적으로 시스템 콜을 래핑(wrapping)**해서 제공하는 API나 라이브러리를 통해 간접적으로 사용하게 해줌.

 

예:

FileInputStream fis = new FileInputStream("data.txt");

이 한 줄이 실제로는 커널에게:

  • "data.txt" 파일을 열어줘 (open 시스템 콜)
  • "내용을 읽어와줘 (read 시스템 콜)"
    같은 요청을 보내는 것과 같음. 단지 그걸 우리가 직접 쓰지 않았을 뿐.

결국 우리가 시스템 콜을 직접 호출하지 않아도, 항상 그 기능을 '간접적으로' 사용하고 있었던 것임.

 


4. 인터럽트 (Interrupt)

인터럽트는 예기치 않은 사건을 CPU에 알리는 메커니즘임.
인터럽트가 발생하면 CPU는 현재 작업을 중단하고 커널 모드로 전환하여 해당 인터럽트에 대응함.

인터럽트 종류

 

구분 예시 설명
하드웨어 인터럽트 키보드 입력, 마우스 클릭, 네트워크 패킷 수신 등 장치가 CPU에 직접 신호를 보냄
소프트웨어 인터럽트 시스템 콜, 예외 상황 프로그램 내부에서 명시적으로 발생시키거나 오류 발생 시

인터럽트 처리 흐름

  1. 장치 또는 소프트웨어가 인터럽트 신호 발생
  2. CPU는 현재 상태 저장 (컨텍스트 스위칭)
  3. 커널 모드 진입
  4. 해당 인터럽트에 등록된 인터럽트 핸들러 실행
  5. 처리 완료 후 원래 유저 모드로 복귀

예:

  • 사용자가 마우스 클릭 → 마우스 컨트롤러가 인터럽트 발생
  • CPU가 이를 받아 마우스 인터럽트 핸들러 실행
  • OS가 클릭 좌표 확인, 클릭된 아이콘 실행

 - 아래 파일 read 흐름 

사진 출처 - 유튜브 쉬운코드

1. 유저모드 : t1 파일 요청 (system call) 

     ->  * 시스템 콜 발생 시 CPU는 자동으로 커널 모드 진입함
2. 커널모드 : t1 cpu 상태 저장 , ssd로 파일 읽을 준비 , t1 wating , t2  running   

    ->  *  이때 I/O 작업은 비동기 처리, 즉 SSD가 읽는 동안 CPU는 다른 일 함
3. 유저모드 : 인터럽트 발생 (이유 : 파일 ssd에서 다 읽음)   

    ->  * 이건 **디스크 컨트롤러(하드웨어 장치)**가 인터럽트를 발생시킴 
4. 커널모드 : t2 상태 저장 , t1 ready , t2 cpu 상태 복원  
5. 유저모드 : 인터럽트 발생 (이유 : 타임슬라이스 cpu점유 시간 다씀)

     ->*  이건 타이머 인터럽트로, OS가 강제로 CPU 점유를 회수
6. 커널모드 : t2 cpu 상태 저장, t2 ready , t1 running , t1 cpu 상태 복원 

 

 

5. 왜 커널 모드를 만들어서 보호하는가?

운영체제는 전체 시스템 자원을 통제하고 관리함.
이걸 모든 프로그램이 직접 접근할 수 있게 허용하면 보안 위협, 시스템 충돌이 발생할 수 있음.

커널 모드는 이런 위험을 차단하고, 엄격한 통제 하에 커널만이 민감한 작업을 수행하도록 설계됨.
유저 프로그램은 오직 시스템 콜을 통해서만 커널과 통신할 수 있음.


6. 정리

  • 일반 애플리케이션은 유저 모드에서 동작
  • 시스템 자원이 필요하거나 하드웨어 사건이 발생하면 커널 모드 진입
  • 인터럽트는 예상치 못한 사건을 CPU에 알리는 신호
  • 시스템 콜은 유저 프로그램이 커널에 기능을 요청하는 방법
  • Java, Python 등은 내부적으로 시스템 콜을 감싼 API를 통해 커널 기능 사용
  • 커널 모드는 시스템 보호와 안정성을 위한 핵심 기제임