본문으로 건너뛰기

버그 제보

이상한 점을 알려 주시면 고칠게요. 지금 보고 있는 페이지와 브라우저 정보가 함께 담겨요.

페이지:

Computer Science

컴퓨터 시스템 구조와 인터럽트

작성 2026. 10. 21.추가
목차17개 항목
출처와 참고

「시스템 콜과 라이브러리 함수」·「man 페이지」 절은 팔만코딩경(42Seoul 커뮤니티 위키)의 「write는 라이브러리 함수가 아니다? 커널과 시스템 콜에 대해」, 「man」, 「[unix] 나만의 manpage 작성하기」를 바탕으로 보강했다. 동기식 입출력의 구현 방식과 커널 주소 공간은 고무오리 CS 스터디의 KOCW 반효경 「운영체제」 강의 정리를 바탕으로 보강했다. 「인터럽트」 절의 동기·비동기 구분과 예외 분류, 「모드 비트」 절의 슈퍼바이저 플래그와 시스템 콜 관찰 방법은 강민철의 「혼자 공부하는 컴퓨터 구조+운영체제」 강의(한빛미디어)를 바탕으로 보강했다. 「장치 제어기와 장치 드라이버」 절의 드라이버 추상화 설명은 CrashCourse의 「Crash Course Computer Science」 강의를 바탕으로 보강했다. 「시스템 콜은 번호로 구분한다」 절은 fru1tworld/cs-basic 모의 면접 질문 목록을 점검해 빠진 개념을 보강했다. 「커널 입출력 서브시스템」 절과 블로킹·논블로킹·비동기 비교는 고려사이버대학교 「운영체제이론 및 실습」(2024-1학기) 강의 내용으로 보강했다.

운영체제가 어떻게 하드웨어를 통제하는지 이해하려면 먼저 컴퓨터 시스템의 구성 요소와, 이들이 인터럽트로 CPU와 소통하는 방식을 알아야 한다. 현대 운영체제는 "인터럽트에 의해 구동된다"고 할 만큼 인터럽트가 핵심이다.

구성 요소 한눈에 보기

요소역할
CPU · 레지스터메모리의 명령어를 순서대로 실행. 레지스터는 메모리보다 빠른 저장 공간
모드 비트지금 CPU에서 실행 중인 것이 OS인지 사용자 프로그램인지 구분
인터럽트 라인키보드 입력·디스크 완료 같은 외부 이벤트를 CPU에 알림
타이머한 프로그램이 CPU를 독점하지 못하게 함
장치 제어기 + 로컬 버퍼각 I/O 장치를 관리하는 작은 CPU와 그 데이터 버퍼
DMA 제어기CPU 대신 로컬 버퍼의 데이터를 메모리로 직접 복사
메모리 제어기CPU와 DMA가 동시에 메모리에 접근할 때 충돌 조정

디스크는 저장 장치이면서 동시에 입출력 장치이기도 하다.

모드 비트 (Mode bit)

사용자 프로그램의 잘못된 동작이 다른 프로그램이나 OS를 망가뜨리지 않도록 하드웨어가 두 가지 실행 모드를 지원한다.

  • 1 사용자 모드: 사용자 프로그램 실행
  • 0 커널 모드(모니터·시스템 모드): OS 코드 실행

보안에 영향을 주는 명령은 특권 명령으로 지정되어 커널 모드에서만 실행할 수 있다. 사용자 모드에서 특권 명령을 실행하려 하면 CPU가 예외를 일으켜 명령이 실행되지 않는다. 교재의 CPU 모델에서는 플래그 레지스터의 슈퍼바이저 플래그가 이 모드 비트 역할을 하고, 실제 x86에서는 CS 레지스터에 담긴 현재 특권 수준(CPL, 링 0~3)으로 표시한다. 인터럽트나 예외가 발생하면 하드웨어가 모드 비트를 0으로 바꾸고, OS는 사용자 프로그램에 CPU를 넘기기 직전에 다시 1로 바꾼다. 이중 모드가 왜 필요한지와 시스템 호출의 입문 설명은 운영체제 개요에 있고, 여기서는 그 메커니즘을 하드웨어 요소 단위로 본다.

타이머 (Timer)

무한 루프 같은 이유로 한 프로그램이 CPU를 계속 쥐고 있는 것을 막는다. OS는 CPU를 넘겨줄 때 타이머에 아주 짧은 시간을 설정하고, 시간이 다 되면 타이머 인터럽트로 제어권을 되찾는다. 시분할 시스템의 기반이다.

장치 제어기와 장치 드라이버

  • 장치 제어기(device controller): 각 I/O 장치를 통제하는 일종의 작은 CPU(하드웨어). 제어·상태 레지스터와 로컬 버퍼를 가진다. 실제 I/O는 장치와 로컬 버퍼 사이에서 일어나고, 끝나면 인터럽트로 CPU에 알린다.
  • 장치 드라이버(device driver): OS 코드 중 장치별 처리 루틴(소프트웨어). 제조사·모델마다 다른 하드웨어 세부를 드라이버가 감추고 OS는 표준화된 입출력 API만 내놓는다. 그래서 응용 프로그램은 "출력하라"는 같은 호출로 어떤 프린터에든 출력하고, 장치 모델별 코드를 직접 짜지 않는다. 드라이버가 들어가는 입출력 계층 전체는 아래 「커널 입출력 서브시스템」에 있다.

인터럽트

인터럽트가 들어오면 CPU는 현재 명령어를 마친 뒤 PC와 레지스터를 저장하고 인터럽트 처리 루틴으로 넘어간다. 하드웨어 쪽 처리 순서는 입출력 구조에 있다.

종류발생 원인
하드웨어 인터럽트장치 제어기, 타이머 등 하드웨어
트랩(소프트웨어 인터럽트): 예외0으로 나누기, 잘못된 메모리 접근 등 프로그램 오류
트랩: 시스템 콜사용자 프로그램이 커널 함수를 호출

CPU 쪽에서 보면 명령어를 실행하다 그 명령어 때문에 생기는 예외는 동기 인터럽트, 실행 중인 명령어와 상관없는 시점에 장치가 보내는 하드웨어 인터럽트는 비동기 인터럽트다. 예외는 처리한 뒤 어디서 다시 시작하는지에 따라 다시 나뉜다. 이때의 트랩은 위 표보다 좁은 뜻이다.

예외처리 뒤 재개 위치예
폴트(fault)예외를 낸 명령어를 다시 실행페이지 폴트. 페이지를 메모리에 올린 뒤 같은 명령어를 재시도한다
트랩(trap)예외를 낸 명령어의 다음 명령어디버거 중단점, 시스템 콜 명령
중단(abort)재개하지 못하고 프로그램을 끝낸다복구할 수 없는 하드웨어 오류
  • 인터럽트 벡터: 인터럽트 종류별로 처리 루틴의 주소를 담은 테이블
  • 인터럽트 처리 루틴(ISR, 인터럽트 핸들러): 실제로 인터럽트를 처리하는 커널 함수

인터럽트 우선순위(데이지 체인, 마스크 레지스터)와 DMA·IOP도 같은 노트에 있다.

시스템 콜과 입출력

모든 입출력 명령은 특권 명령이다. 그렇다면 사용자 프로그램은 어떻게 I/O를 할까?

  1. 사용자 프로그램이 시스템 콜로 OS에 I/O를 요청한다.
  2. 트랩이 발생해 인터럽트 벡터의 해당 위치로 이동하고, 모드가 커널 모드로 바뀐다.
  3. OS가 올바른 요청인지 확인한 뒤 I/O를 수행한다.
  4. 완료되면 제어권을 시스템 콜 다음 명령으로 돌려준다.

시스템 콜은 번호로 구분한다

커널 진입 명령은 하나뿐이므로, 어떤 시스템 콜인지는 함수 이름이 아니라 번호로 알린다. 커널은 시스템 콜 번호를 인덱스로 하는 표(Linux의 sys_call_table)를 두고, 트랩이 들어오면 정해진 레지스터의 번호로 처리 함수를 찾아 부른다.

Linux 아키텍처진입 명령번호인자반환값
x86-64syscallraxrdi, rsi, rdx, r10, r8, r9rax
ARM64svc #0x8x0~x5x0
  • 같은 시스템 콜도 아키텍처마다 번호가 다르다. x86-64 Linux에서 read는 0, write는 1이다. C에서는 <sys/syscall.h>의 SYS_write 같은 상수로 쓰고, 전체 표는 커널 소스의 syscall_64.tbl에 있다. 과거 32비트 x86은 int 0x80 소프트웨어 인터럽트로 진입했다.
  • 실패하면 커널은 음수 오류 번호(-ENOENT 등)를 돌려주고, libc 래퍼가 이를 errno에 넣은 뒤 -1을 반환한다.
  • 래퍼 함수가 없는 시스템 콜도 syscall(SYS_gettid)처럼 번호로 직접 부를 수 있다.
  • 한번 정한 번호는 바꾸지 않는다. 바꾸면 이미 컴파일된 프로그램이 엉뚱한 시스템 콜을 부르게 되므로, 사용자 공간 ABI를 깨지 않는다는 Linux 원칙의 한 예다.

동기식 vs 비동기식 입출력

  • 동기식(synchronous): I/O가 끝날 때까지 기다렸다가 제어가 사용자 프로그램으로 넘어간다.
  • 비동기식(asynchronous): I/O를 시작만 시켜 두고 즉시 제어가 돌아와 다른 일을 한다.

어느 쪽이든 I/O가 끝나면 장치 제어기가 인터럽트로 알린다. 동기식은 기다리는 동안 CPU를 어떻게 쓰느냐에 따라 두 가지로 구현할 수 있다.

동기식 구현기다리는 동안문제
요청한 프로그램이 CPU를 쥔 채 기다림CPU가 아무 일도 하지 않는다CPU가 낭비되고, 매 순간 I/O를 하나만 진행할 수 있다
요청한 프로그램을 blocked로 두고 CPU를 다른 프로그램에 줌다른 프로그램이 CPU를 쓴다없음. 여러 장치의 I/O가 동시에 진행되므로 현대 OS가 쓰는 방식이다

비동기식에서도 I/O 결과를 써야 하는 지점에 이르면 결국 기다려야 하지만, 결과가 필요 없는 쓰기 요청 같은 경우에는 기다리지 않고 바로 다음 일을 한다.

응용 프로그램이 시스템 콜에서 보는 동작으로 나누면 세 가지다.

방식데이터가 아직 없을 때
블로킹커널이 프로세스를 대기 큐로 옮기고, 끝나면 준비 큐로 돌려놓는다. 프로그램이 가장 단순하다
논블로킹기다리지 않고 지금까지 된 만큼(없으면 EAGAIN)을 바로 돌려준다. 다시 시도하는 코드를 직접 짠다
비동기요청만 걸어 두고 바로 돌아오며, 완료되면 커널이 알려 준다

논블로킹 fd 여러 개를 한꺼번에 감시하는 select·poll·epoll은 UNIX 파일 입출력에, 애플리케이션 수준의 비동기·논블로킹 구분은 동시성·병렬·비동기·논블로킹과 동시성 모델에 있다.

DMA (Direct Memory Access)

바이트마다 인터럽트를 받아 CPU가 데이터를 옮기면 오버헤드가 너무 크다. DMA 제어기는 장치 버퍼의 내용을 CPU 개입 없이 블록 단위로 메모리에 복사하고, 블록 전송이 끝났을 때 한 번만 인터럽트를 건다. 버스 요청·승인 절차와 입출력 프로세서(IOP)까지 포함한 하드웨어 동작은 입출력 구조에 있다.

I/O 장치에 주소를 붙이는 두 방식(격리형 I/O와 메모리 맵 I/O)의 비교, 폴링·인터럽트·DMA 중 무엇을 고를지도 입출력 구조를 참고한다.

커널 입출력 서브시스템

입출력 장치는 CPU보다 훨씬 느리고 종류마다 데이터 단위와 제어 방법이 다르다. 운영체제의 입출력 시스템은 이 차이를 숨겨 응용 프로그램이 디스크든 키보드든 open·read·write·close로 다루게 하고, 느린 장치 때문에 CPU가 노는 시간을 줄인다. 그래서 장치에 공통인 부분과 장치마다 다른 부분을 층으로 나눈다.

flowchart TB
  app["응용 프로그램<br>read(fd, buf, n)"]
  kio["커널 입출력 서브시스템<br>VFS, 페이지 캐시, 입출력 스케줄러, 버퍼, 권한 검사"]
  drv["장치 드라이버<br>장치별 코드: driver_read(), driver_write(), ioctl 처리"]
  ctl["장치 컨트롤러<br>상태·제어·데이터 레지스터, 내부 버퍼"]
  dev["장치<br>디스크, 키보드, NIC ..."]
  app ---|"시스템 콜 경계"| kio --- drv ---|"하드웨어 경계"| ctl --- dev
  • 커널 입출력 서브시스템은 모든 장치에 공통인 일(같은 API, 스케줄링, 버퍼와 캐시, 오류 처리, 보호)을 한다.
  • 장치 드라이버는 서브시스템이 정한 함수 표(open, read, write, ioctl …)를 장치에 맞게 채운다. 위쪽 코드는 장치를 몰라도 되고, 새 장치를 지원하려면 드라이버만 더하면 된다.
  • 장치 컨트롤러는 CPU가 레지스터에 쓴 비트대로 장치를 움직이고, 기계적인 타이밍과 신호 형식을 처리한다.

장치의 세 분류

분류단위와 접근예Linux에서
블록 장치고정 크기 블록, 임의 위치 접근(seek)디스크, SSDls -l /dev에서 b. 파일 시스템이 그 위에 올라간다
문자 장치바이트 스트림, 대개 순차 접근키보드, 마우스, 직렬 포트, 터미널ls -l /dev에서 c
네트워크 장치패킷, 소켓 인터페이스NIC/dev에 없고 ip link로 본다

블록 장치만 페이지 캐시와 입출력 스케줄러를 거치고, 문자 장치는 대개 드라이버로 바로 간다. 표준 연산으로 표현하기 어려운 장치별 기능(터미널 속도, 디스크 형상 질의)은 ioctl이라는 비상구로 넘긴다.

서브시스템이 제공하는 서비스

서비스하는 일
입출력 스케줄링요청 순서를 바꿔 장치 효율과 공정성을 높인다. 디스크는 디스크 관리와 스케줄링
버퍼링장치와 응용 사이에서 데이터를 잠시 담아 속도·전송 단위 차이를 흡수한다(입출력 버퍼링)
캐싱자주 쓰는 데이터의 사본을 메모리에 두어 장치 접근 자체를 줄인다. 파일 데이터는 페이지 캐시(파일 시스템 구현)
스풀링한 번에 한 작업만 받는 느린 장치(프린터) 앞에 디스크를 큰 대기열로 둔다. 여러 프로세스가 동시에 출력해도 섞이지 않고 차례로 나간다
장치 예약테이프처럼 공유할 수 없는 장치를 한 프로세스에 독점 할당한다
오류 처리일시적 오류는 재시도하고, 실패하면 errno로 알린다
보호입출력 명령은 특권 명령이라 사용자 프로그램은 시스템 콜로만 장치에 닿는다

버퍼는 데이터의 유일한 사본일 수 있고 캐시는 원본이 따로 있는 사본이다. 페이지 캐시는 두 역할을 함께 한다. write()가 돌아오면 커널이 그 시점의 내용을 자기 버퍼로 복사해 두었으므로 응용은 버퍼를 곧바로 다시 써도 되지만, 디스크에 실제로 써졌다는 뜻은 아니다. 그것이 필요하면 fsync를 부른다.

입출력 성능을 높이는 원칙

실제 프로그램은 대부분 계산보다 입출력을 기다리는 시간이 길다. 그래서 연산을 다듬는 것보다 입출력 경로를 줄이는 쪽이 효과가 크다.

원칙예
문맥 교환을 줄인다작은 write 여러 번 대신 버퍼에 모아 한 번에(stdio 버퍼, writev)
메모리 복사를 줄인다파일을 소켓으로 보낼 때 read+write(사용자 공간을 거쳐 복사가 늘어남) 대신 sendfile로 커널 안에서 바로 보낸다
인터럽트 빈도를 줄인다큰 블록 전송, NIC의 인터럽트 결합(coalescing), 부하가 높을 때 폴링
CPU 대신 하드웨어가 옮기게 한다DMA, 체크섬·TCP 분할을 NIC에 맡기는 오프로드
병목을 옮겨 가며 균형을 맞춘다CPU, 메모리, 버스, 장치 중 하나만 빠르게 해도 전체는 가장 느린 곳에 맞춰진다

저장장치 계층과 프로그램 실행

  • Primary(레지스터, 캐시, 메인 메모리): CPU가 바이트 단위로 직접 접근해 실행할 수 있다.
  • Secondary(디스크 등): 섹터 단위로 접근하며, 실행하려면 메모리로 올려야 한다.
  • 위로 갈수록 빠르고 비싸며 용량이 작다(메모리 계층 구조).

실행 파일은 실행되면 자신만의 주소 공간(code, data, stack)을 가진 프로세스가 된다. 당장 필요한 부분만 물리 메모리에 올리고 나머지는 디스크의 스왑 영역에 둔다. 커널도 하나의 프로그램이라 자기 주소 공간을 가지며, 모든 프로세스가 이를 공유한다.

커널 영역담는 것
code시스템 콜 처리, 인터럽트 처리, 자원 관리(CPU·메모리·장치) 코드
data하드웨어 자원을 관리하는 자료구조와, 프로세스마다 하나씩 있는 PCB(Process Control Block) (프로세스와 스레드)
stack프로세스마다 따로 둔 커널 스택. 어느 프로세스든 시스템 콜로 커널 코드를 실행할 수 있으므로, 커널 안의 함수 호출 기록도 프로세스별로 쌓아야 한다

프로그램이 쓰는 함수

  • 사용자 정의 함수, 라이브러리 함수: 프로세스 자신의 주소 공간에서 실행 (사용자 모드)
  • 커널 함수: OS의 함수. 호출하는 것이 곧 시스템 콜 (커널 모드)

프로그램은 실행되는 동안 사용자 모드와 커널 모드를 계속 오간다.

시스템 콜과 라이브러리 함수

커널은 하드웨어를 직접 다룰 수 있는 유일한 프로그램이다. 디스크·네트워크·화면에 무언가를 하려면 프로그램은 커널에 부탁해야 하고, 그 창구가 시스템 콜이다. C에서 read, write, open, fork, execve, unlink는 시스템 콜을 감싼 얇은 함수라 일반 함수처럼 부른다.

구분시스템 콜라이브러리 함수
실행 위치커널 모드로 전환해 커널 코드 실행사용자 모드, 프로세스 자신의 주소 공간
비용모드 전환이 있어 상대적으로 비싸다일반 함수 호출
예write, read, fork, mmapprintf(내부에서 write 호출), strlen(시스템 콜 없음), malloc(필요할 때만 brk·mmap)
매뉴얼man 2man 3

라이브러리 함수는 시스템 콜을 쓸 수도, 안 쓸 수도 있다. printf가 매번 write를 부르지 않고 버퍼에 모았다가 한 번에 부르는 것처럼, 라이브러리는 시스템 콜 횟수를 줄이는 역할도 한다(UNIX 파일 입출력). 프로그램이 부르는 시스템 콜은 strace(Linux)·dtruss(macOS)로 볼 수 있다. strace ls나 한 줄짜리 hello world를 추적해 보면 execve, mmap, openat, read, write 같은 시스템 콜이 수십 번 지나간다. 단순해 보이는 프로그램도 사용자 모드와 커널 모드를 쉴 새 없이 오간다는 것을 눈으로 확인하는 가장 빠른 방법이다. 자세한 사용법은 C 디버깅 도구에 있다.

범주UNIXWindows
프로세스 제어fork, exec, wait, exitCreateProcess, WaitForSingleObject, ExitProcess
파일 관리open, read, write, closeCreateFile, ReadFile, WriteFile, CloseHandle
장치 관리ioctl, read, writeSetConsoleMode, ReadConsole, WriteConsole
정보 관리getpid, alarm, sleepGetCurrentProcessId, SetTimer, Sleep
통신pipe, shm_open, mmapCreatePipe, CreateFileMapping, MapViewOfFile
보호chmod, umask, chownSetFileSecurity, SetSecurityDescriptorGroup

man 페이지

UNIX 계열의 매뉴얼은 섹션으로 나뉜다. 이름이 같은 항목이 여러 섹션에 있을 수 있어서, man printf는 셸 명령(섹션 1)을 보여 주고 C 함수는 man 3 printf로 봐야 한다. 시스템 콜 write는 man 2 write다.

섹션내용예
1사용자 명령ls, chmod
2시스템 콜open, fork
3라이브러리 함수printf, strlen
4장치 파일 (/dev)null, tty
5설정 파일 형식passwd, crontab
6게임
7개요·규약signal, ascii, regex
8관리자 명령mount, useradd
  • 문서는 NAME, SYNOPSIS(원형·헤더), DESCRIPTION, RETURN VALUE, ERRORS, EXAMPLES, SEE ALSO 순으로 구성된다. 함수를 쓸 때는 RETURN VALUE와 ERRORS를 꼭 확인한다.
  • man -k 키워드(= apropos)로 검색하고, man -f 이름(= whatis)으로 어느 섹션에 있는지 본다. 같은 이름의 섹션을 찾는 순서는 시스템마다 설정이 다르다.
  • 매뉴얼 파일은 /usr/share/man/man<섹션>/에 roff 형식을 gzip으로 압축해 둔다. 직접 쓴 페이지는 man -l 파일로 바로 볼 수 있다. 문법은 man 7 groff_man에 있다(.TH 제목, .SH 절, .B 굵게).

관련 노트: 운영체제 개요, UNIX 파일 입출력과 파일 디스크립터, UNIX 시그널, 운영체제의 시간과 타이머, 입출력 구조와 시스템 버스, 디스크 관리와 스케줄링, 동시성 모델

원본 기록9

댓글

GitHub 계정으로 댓글을 남길 수 있어요.