운영체제 기본/장치 제어와 입출력: 두 판 사이의 차이
예외·인터럽트 벡터와 IDT 게이트를 통한 처리기 진입 설명 |
용어 통일: 시스템 콜, 컨텍스트 스위치와 첫 등장 영문 병기 |
||
| 1번째 줄: | 1번째 줄: | ||
[[운영체제 기본/보호|이전: 보호와 시스템 | [[운영체제 기본/보호|이전: 보호와 시스템 콜]] · [[운영체제 기본|목차]] · [[운영체제 기본/프로세스와 스레드|다음: 프로세스와 스레드]] | ||
장치와 소프트웨어의 상호작용에는 세 가지 일이 있다. '''제어 명령을 전달하는 일, 데이터를 옮기는 일, 사건이나 완료를 확인하는 일'''이다. 장치 레지스터 접근, DMA, 인터럽트는 각각 이 과정의 다른 부분을 담당한다. DMA로 데이터를 전송하면서 완료는 인터럽트로 통지받을 수도 있고, 같은 DMA 전송의 완료를 폴링으로 확인할 수도 있다. | 장치와 소프트웨어의 상호작용에는 세 가지 일이 있다. '''제어 명령을 전달하는 일, 데이터를 옮기는 일, 사건이나 완료를 확인하는 일'''이다. 장치 레지스터 접근, DMA, 인터럽트는 각각 이 과정의 다른 부분을 담당한다. DMA로 데이터를 전송하면서 완료는 인터럽트로 통지받을 수도 있고, 같은 DMA 전송의 완료를 폴링으로 확인할 수도 있다. | ||
| 38번째 줄: | 38번째 줄: | ||
=== 인터럽트와 동기적 예외 === | === 인터럽트와 동기적 예외 === | ||
장치 인터럽트는 비동기적 사건인 반면, [[운영체제 기본/보호#보호 위반과 예외|메모리 접근 예외]]는 현재 명령의 실행과 연결된다. 시스템 | 장치 인터럽트는 비동기적 사건인 반면, [[운영체제 기본/보호#보호 위반과 예외|메모리 접근 예외]]는 현재 명령의 실행과 연결된다. 시스템 콜(System Call)도 프로그램이 정해진 진입 명령으로 요청하는 동기적 사건이다. 트랩이라는 용어의 범위는 아키텍처마다 다르며, RISC-V에서는 인터럽트와 예외에 따른 제어 이전을 모두 트랩이라고 부른다. 같은 용어를 쓴다고 발생 원인까지 같은 것은 아니다.<ref name="riscv-terms">[https://docs.riscv.org/reference/isa/v20240411/unpriv/intro.html#_exceptions_traps_and_interrupts RISC-V Instruction Set Manual: Exceptions, Traps, and Interrupts].</ref> | ||
=== 처리 진입점과 IDT === | === 처리 진입점과 IDT === | ||
[[운영체제 기본/보호#보호 위반과 예외|보호]]에서 본 예외와 장치 인터럽트는 발생 원인이 다르지만, 사건의 종류에 맞는 처리 코드로 실행을 넘겨야 한다. 이를 '''사건 번호인 벡터(vector)와 처리 진입점의 대응 관계'''로 생각할 수 있다. | [[운영체제 기본/보호#보호 위반과 예외|보호]]에서 본 예외와 장치 인터럽트는 발생 원인이 다르지만, 사건의 종류에 맞는 처리 코드로 실행을 넘겨야 한다. 이를 '''사건 번호인 벡터(vector)와 처리 진입점의 대응 관계'''로 생각할 수 있다. | ||
x86-64의 전통적인 처리 방식에서는 운영체제가 '''인터럽트 서술자 테이블(Interrupt Descriptor Table, IDT)'''을 초기화하고 CPU에 그 위치를 등록한다. CPU는 예외나 인터럽트의 벡터를 인덱스로 사용해 IDT 항목을 선택한다. 예를 들어 #PF는 14번, #GP는 13번 항목을 선택하여 해당 게이트가 가리키는 커널 진입 코드로 넘어간다. 각 항목은 단순한 함수 주소가 아니라, 처리 코드의 주소와 | x86-64의 전통적인 처리 방식에서는 운영체제가 '''인터럽트 서술자 테이블(Interrupt Descriptor Table, IDT)'''을 초기화하고 CPU에 그 위치를 등록한다. CPU는 예외나 인터럽트의 벡터를 인덱스로 사용해 IDT 항목을 선택한다. 예를 들어 #PF는 14번, #GP는 13번 항목을 선택하여 해당 게이트가 가리키는 커널 진입 코드로 넘어간다. 각 항목은 단순한 함수 주소가 아니라, 처리 코드의 주소와 커널 진입에 필요한 속성을 담은 '''게이트(gate)'''다. 여기에는 대상 코드 영역, 스택 선택 정보, 게이트 유형과 접근 제한 등이 포함된다.<ref name="intel-idt">Intel, [https://www.intel.com/content/dam/www/public/us/en/documents/manuals/64-ia-32-architectures-software-developer-vol-3a-part-1-manual.pdf Intel 64 and IA-32 Architectures Software Developer’s Manual, Volume 3A], 2016년 9월, 6.10~6.14절 및 5.8.8절.</ref> | ||
<pre> | <pre> | ||
| 53번째 줄: | 53번째 줄: | ||
게이트의 '''서술자 특권 수준(Descriptor Privilege Level, DPL)'''은 <code>INT n</code> 같은 소프트웨어 명령으로 그 진입점을 호출할 수 있는지를 제한한다. 하드웨어 인터럽트나 CPU가 검출한 예외에는 이 게이트 DPL 검사를 적용하지 않는다.<ref name="intel-idt" /> | 게이트의 '''서술자 특권 수준(Descriptor Privilege Level, DPL)'''은 <code>INT n</code> 같은 소프트웨어 명령으로 그 진입점을 호출할 수 있는지를 제한한다. 하드웨어 인터럽트나 CPU가 검출한 예외에는 이 게이트 DPL 검사를 적용하지 않는다.<ref name="intel-idt" /> | ||
진입할 때 CPU가 복귀에 필요한 일부 상태를 저장하고, 운영체제의 진입 코드가 필요한 상태를 추가로 보존한다. 처리가 끝나면 소프트웨어가 상태를 복원하고 <code>IRETQ</code> 같은 복귀 명령으로 실행을 이어 간다. 이 진입과 복귀 자체가 다른 프로세스로의 | 진입할 때 CPU가 복귀에 필요한 일부 상태를 저장하고, 운영체제의 진입 코드가 필요한 상태를 추가로 보존한다. 처리가 끝나면 소프트웨어가 상태를 복원하고 <code>IRETQ</code> 같은 복귀 명령으로 실행을 이어 간다. 이 진입과 복귀 자체가 다른 프로세스로의 컨텍스트 스위치(Context Switch)를 뜻하지는 않는다. 다른 프로세스를 실행할지는 스케줄러의 별도 결정이다.<ref name="intel-idt" /> | ||
구체적인 x86 진입 코드는 [[운영체제 기본/Linux의 키보드 입력/IRQ 진입과 디스패치#IDT에서 C 진입점까지|키보드 IRQ 각론]]으로 이어진다. IDT는 모든 CPU의 공통 구조는 아니다. 또한 x86-64의 <code>SYSCALL</code>은 별도로 설정된 진입 주소를 사용하므로, 모든 시스템 | 구체적인 x86 진입 코드는 [[운영체제 기본/Linux의 키보드 입력/IRQ 진입과 디스패치#IDT에서 C 진입점까지|키보드 IRQ 각론]]으로 이어진다. IDT는 모든 CPU의 공통 구조는 아니다. 또한 x86-64의 <code>SYSCALL</code>은 별도로 설정된 진입 주소를 사용하므로, 모든 시스템 콜이 IDT를 거친다고 설명해서는 안 된다.<ref name="intel-idt" /> | ||
=== 진입, 상태 보존, 복귀 === | === 진입, 상태 보존, 복귀 === | ||
CPU는 인터럽트를 받아들일 때 복귀 위치와 이전 실행 권한 등 필요한 최소 상태를 보존하고 설정된 처리기 진입점으로 이동한다. 진입 코드는 나머지 레지스터와 처리기에 필요한 스택을 준비한다. 하드웨어와 소프트웨어가 맡는 범위는 아키텍처마다 다르다. 사용자 코드 실행 중이었다면 커널 처리를 위한 권한 전환도 이루어지며, 진입점과 권한을 함께 통제해 보호 경계를 유지한다.<ref name="xv6-entry">Cox 외, [https://pdos.csail.mit.edu/6.1810/2025/xv6/book-riscv-rev5.pdf xv6], 2025, 4.1·4.2절.</ref> | CPU는 인터럽트를 받아들일 때 복귀 위치와 이전 실행 권한 등 필요한 최소 상태를 보존하고 설정된 처리기 진입점으로 이동한다. 진입 코드는 나머지 레지스터와 처리기에 필요한 스택을 준비한다. 하드웨어와 소프트웨어가 맡는 범위는 아키텍처마다 다르다. 사용자 코드 실행 중이었다면 커널 처리를 위한 권한 전환도 이루어지며, 진입점과 권한을 함께 통제해 보호 경계를 유지한다.<ref name="xv6-entry">Cox 외, [https://pdos.csail.mit.edu/6.1810/2025/xv6/book-riscv-rev5.pdf xv6], 2025, 4.1·4.2절.</ref> | ||
원래 실행을 재개할 때는 저장 상태를 복원하고 복귀 절차를 거친다. 처리기가 잠깐 실행된 것과 다른 스레드로 | 원래 실행을 재개할 때는 저장 상태를 복원하고 복귀 절차를 거친다. 처리기가 잠깐 실행된 것과 다른 스레드로 컨텍스트 스위치를 한 것은 다르다. 실행 대상을 바꾸는 결정은 스케줄러가 별도로 수행한다.<ref name="xv6-switch">Cox 외, [https://pdos.csail.mit.edu/6.1810/2025/xv6/book-riscv-rev5.pdf xv6], 8.1·8.2절.</ref> | ||
=== 입출력 완료와 깨우기 === | === 입출력 완료와 깨우기 === | ||
| 73번째 줄: | 73번째 줄: | ||
=== 타이머 인터럽트와 선점 === | === 타이머 인터럽트와 선점 === | ||
시스템 | 시스템 콜마다 실행 중인 작업을 양보시킨다고 가정하자. 다음 C 반복문에는 시스템 콜 자체가 없다. | ||
<pre> | <pre> | ||
| 80번째 줄: | 80번째 줄: | ||
</pre> | </pre> | ||
시스템 | 시스템 콜을 유일한 양보 지점으로 삼았다면 커널은 다음 양보 지점을 기다려도 제어권을 돌려받지 못한다. '''시스템 콜마다 양보하게 하는 것과, 시스템 콜을 하지 않는 코드에서도 CPU를 회수하는 것은 다르다.''' 일반적인 선점형 시분할은 사용자 코드와 독립적으로 만료를 알리는 타이머를 사용한다. 커널이 통지 시각을 설정하면 반복문이 계속되어도 인터럽트로 다시 제어권을 얻을 수 있다.<ref name="riscv-timer">[https://docs.riscv.org/reference/isa/v20260120/priv/machine.html RISC-V Instruction Set Manual: Machine-Level ISA], 타이머와 인터럽트 레지스터.</ref> | ||
타이머 통지는 시간 계산과 선점 판단의 기회다. 다른 실행 가능 작업이 없거나 현재 작업을 유지할 이유가 있으면 그대로 복귀한다. 또한 인터럽트 진입을 잠시 막는 것과 스레드 선점을 미루는 것은 다른 제약이며, 안전한 시점까지 | 타이머 통지는 시간 계산과 선점 판단의 기회다. 다른 실행 가능 작업이 없거나 현재 작업을 유지할 이유가 있으면 그대로 복귀한다. 또한 인터럽트 진입을 잠시 막는 것과 스레드 선점을 미루는 것은 다른 제약이며, 안전한 시점까지 컨텍스트 스위치가 지연될 수 있다.<ref name="preempt-safe">[https://docs.kernel.org/locking/preempt-locking.html Linux 커널 문서: Proper Locking Under a Preemptible Kernel].</ref> | ||
=== 통지 빈도와 처리 비용 === | === 통지 빈도와 처리 비용 === | ||
통지나 | 인터럽트 통지나 컨텍스트 스위치를 자주 하면 응답 기회는 늘지만 처리기·스케줄러 실행과 캐시 교란 비용도 늘 수 있다. 여러 사건을 묶으면 처리량은 좋아질 수 있지만 먼저 도착한 사건은 기다려야 한다. 처리량과 개별 요청의 지연 시간을 함께 고려해야 한다.<ref name="switch-cache">Mogul·Borg, [https://decdoc.itsx.net/WRL/WRL-TN-16.pdf The Effect of Context Switches on Cache Performance], 1990.</ref> | ||
고정 주기의 타이머만 가능한 것은 아니다. 다음 필요한 시각에 한 번 울리는 원샷 타이머를 재설정할 수 있다. 틱리스 설계는 불필요한 주기적 통지를 줄이는 것이며, 선점 자체를 포기하는 방식은 아니다.<ref name="dynamic-ticks">[https://docs.kernel.org/timers/highres.html High resolution timers and dynamic ticks], [https://docs.kernel.org/timers/no_hz.html NO_HZ].</ref> | 고정 주기의 타이머만 가능한 것은 아니다. 다음 필요한 시각에 한 번 울리는 원샷 타이머를 재설정할 수 있다. 틱리스 설계는 불필요한 주기적 통지를 줄이는 것이며, 선점 자체를 포기하는 방식은 아니다.<ref name="dynamic-ticks">[https://docs.kernel.org/timers/highres.html High resolution timers and dynamic ticks], [https://docs.kernel.org/timers/no_hz.html NO_HZ].</ref> | ||
2026년 10월 31일 (토) 08:32 판
이전: 보호와 시스템 콜 · 목차 · 다음: 프로세스와 스레드
장치와 소프트웨어의 상호작용에는 세 가지 일이 있다. 제어 명령을 전달하는 일, 데이터를 옮기는 일, 사건이나 완료를 확인하는 일이다. 장치 레지스터 접근, DMA, 인터럽트는 각각 이 과정의 다른 부분을 담당한다. DMA로 데이터를 전송하면서 완료는 인터럽트로 통지받을 수도 있고, 같은 DMA 전송의 완료를 폴링으로 확인할 수도 있다.
장치 제어와 데이터 전송
레지스터를 통한 장치 제어
장치는 소프트웨어가 읽거나 쓸 수 있는 레지스터를 제공한다. 제어 레지스터에는 시작이나 중지 명령을 쓰고, 상태 레지스터에서는 준비 상태나 오류를 확인한다. 데이터 레지스터를 통해 실제 데이터를 주고받는 장치도 있다. 드라이버는 장치 규약과 운영체제 요청을 연결한다.
메모리 매핑 입출력인 MMIO(Memory-Mapped Input/Output)는 CPU의 주소 공간 일부를 장치 접근에 사용한다. 포트 입출력은 일반 메모리와 구별되는 포트 주소 공간을 사용하며, x86에서는 별도의 입출력 명령으로 접근한다. 두 방식은 레지스터에 접근하는 방법의 차이다. 데이터 전송을 CPU가 담당할지 다른 하드웨어에 맡길지와는 별개의 구분이다.[1]
장치 레지스터는 읽기가 상태를 바꾸거나 쓰기가 동작을 시작할 수 있다. 따라서 일반 변수처럼 접근을 생략하거나 순서를 바꿔도 된다고 가정할 수 없다. 쓰기 명령의 실행이 끝났더라도 실제 장치에 쓰기가 도착했는지는 별도로 판단해야 하는 하드웨어도 있다.[1]
DMA와 버퍼의 소유권
CPU가 데이터 레지스터를 읽어 메모리에 저장하는 방식에서는 CPU 명령 실행이 데이터 이동을 담당한다. i8042 입력에서 CPU가 컨트롤러의 데이터 레지스터를 읽는 것도 이쪽에 해당한다. 인터럽트가 읽을 시점을 알려주더라도 그 전송이 DMA가 되지는 않는다.
직접 메모리 접근인 DMA(Direct Memory Access)에서는 장치 자체의 전송 엔진이나 별도의 DMA 컨트롤러가 메모리 접근을 수행한다. CPU가 전송되는 데이터마다 읽기와 쓰기 명령을 실행할 필요는 없다. 주변장치 대신 별도 컨트롤러가 전송하는 구조도 있고, 장치가 직접 버스 접근을 시작하는 버스 마스터 구조도 있다.[2][3]
DMA에서도 드라이버는 버퍼를 준비하고 장치가 사용할 주소와 길이와 방향을 설정하며 전송을 제출한다. 전송 중인 버퍼를 덮어쓰거나 해제하면 데이터가 손상될 수 있으므로, CPU와 장치가 언제 데이터를 변경해도 되는지 소유권 규약이 필요하다. 버퍼와 주소 매핑은 전송이 끝날 때까지 유효해야 하며, 완료와 필요한 동기화를 확인한 뒤 재사용한다.[2]
DMA는 데이터 이동 방식이고 폴링과 인터럽트는 상태를 확인하거나 사건을 알리는 방식이다. DMA 완료를 폴링으로 확인할 수도 있다. 네트워크 수신과 저장장치 블록 전송은 대표적인 용도지만, 모든 전송에 DMA가 필요한 것은 아니다.
주소와 일관성과 전송 비용
CPU의 가상 주소, CPU의 물리 주소, 장치의 DMA 주소는 구별해야 한다. 단순한 시스템에서는 DMA 주소와 물리 주소가 같지만 일반적으로 이를 가정할 수 없다. 입출력 메모리 관리 장치인 IOMMU(Input-Output Memory Management Unit)는 장치의 주소를 물리 메모리로 변환하고, 설정에 따라 접근 가능한 영역을 제한하는 데에도 쓰인다. 모든 시스템에 IOMMU가 있는 것은 아니다.[3]
CPU 캐시와 장치가 보는 메모리 사이에는 동기화가 필요할 수 있다. 하드웨어가 캐시 일관성을 제공하더라도 접근 순서까지 자동으로 보장하지는 않는다. 전송 요청을 기록한 디스크립터에서 주소와 길이보다 유효 표시가 먼저 보이면 장치가 미완성 작업을 실행할 수 있다. 메모리 배리어는 이런 공개 순서를 보장하는 데 사용한다.[3]
DMA는 CPU의 반복적인 복사를 줄이지만 설정과 완료 처리, 주소 매핑, 캐시 동기화 비용이 든다. 장치는 버스와 메모리 대역폭을 소비하고 다른 작업과 경합한다. 임시 버퍼 때문에 추가 복사가 필요할 수도 있다. 버퍼 부족과 요청을 모으는 정책은 대기 시간에도 영향을 준다. 작은 전송은 CPU가 직접 처리하는 편이 단순할 수 있으며, DMA 사용 여부는 처리량뿐 아니라 지연과 자원 사용까지 고려해야 한다.
사건 통지와 인터럽트
실행 흐름과 독립적인 사건의 통지
키보드 입력, 네트워크 패킷 도착, 저장장치의 입출력 완료는 실행 중인 프로그램의 명령 순서와 독립적으로 발생한다. 하드웨어 인터럽트는 사건을 CPU에 알리고 운영체제가 처리할 기회를 만드는 통지 수단이다. 키보드의 입력 바이트를 읽거나 패킷을 처리하는 일, 완료된 요청을 확인하는 일은 통지를 받은 뒤 각 처리기가 수행한다. 스케줄링과 선점은 그 활용 중 하나이며, 모든 인터럽트가 CPU 양보를 목적으로 발생하지는 않는다.
폴링은 장치 상태를 반복해서 확인한다. 자주 확인하면 응답은 빨라지지만 사건이 없을 때도 CPU 시간을 쓰고, 확인 간격을 늘리면 처리가 늦어진다. 인터럽트는 이런 반복 확인을 줄일 수 있으나 사건이 빈번하면 진입·복귀 비용이 커진다. 인터럽트로 처리를 시작한 뒤 폴링으로 여러 결과를 묶어 처리하는 설계도 사용한다.[4]
인터럽트와 동기적 예외
장치 인터럽트는 비동기적 사건인 반면, 메모리 접근 예외는 현재 명령의 실행과 연결된다. 시스템 콜(System Call)도 프로그램이 정해진 진입 명령으로 요청하는 동기적 사건이다. 트랩이라는 용어의 범위는 아키텍처마다 다르며, RISC-V에서는 인터럽트와 예외에 따른 제어 이전을 모두 트랩이라고 부른다. 같은 용어를 쓴다고 발생 원인까지 같은 것은 아니다.[5]
처리 진입점과 IDT
보호에서 본 예외와 장치 인터럽트는 발생 원인이 다르지만, 사건의 종류에 맞는 처리 코드로 실행을 넘겨야 한다. 이를 사건 번호인 벡터(vector)와 처리 진입점의 대응 관계로 생각할 수 있다.
x86-64의 전통적인 처리 방식에서는 운영체제가 인터럽트 서술자 테이블(Interrupt Descriptor Table, IDT)을 초기화하고 CPU에 그 위치를 등록한다. CPU는 예외나 인터럽트의 벡터를 인덱스로 사용해 IDT 항목을 선택한다. 예를 들어 #PF는 14번, #GP는 13번 항목을 선택하여 해당 게이트가 가리키는 커널 진입 코드로 넘어간다. 각 항목은 단순한 함수 주소가 아니라, 처리 코드의 주소와 커널 진입에 필요한 속성을 담은 게이트(gate)다. 여기에는 대상 코드 영역, 스택 선택 정보, 게이트 유형과 접근 제한 등이 포함된다.[6]
잘못된 페이지 접근 → #PF(14) → IDT[14] → 커널의 페이지 폴트 진입 코드 일반 보호 예외 → #GP(13) → IDT[13] → 커널의 일반 보호 예외 진입 코드 장치의 인터럽트 → 벡터 v → IDT[v] → 커널의 인터럽트 진입 코드
게이트의 서술자 특권 수준(Descriptor Privilege Level, DPL)은 INT n 같은 소프트웨어 명령으로 그 진입점을 호출할 수 있는지를 제한한다. 하드웨어 인터럽트나 CPU가 검출한 예외에는 이 게이트 DPL 검사를 적용하지 않는다.[6]
진입할 때 CPU가 복귀에 필요한 일부 상태를 저장하고, 운영체제의 진입 코드가 필요한 상태를 추가로 보존한다. 처리가 끝나면 소프트웨어가 상태를 복원하고 IRETQ 같은 복귀 명령으로 실행을 이어 간다. 이 진입과 복귀 자체가 다른 프로세스로의 컨텍스트 스위치(Context Switch)를 뜻하지는 않는다. 다른 프로세스를 실행할지는 스케줄러의 별도 결정이다.[6]
구체적인 x86 진입 코드는 키보드 IRQ 각론으로 이어진다. IDT는 모든 CPU의 공통 구조는 아니다. 또한 x86-64의 SYSCALL은 별도로 설정된 진입 주소를 사용하므로, 모든 시스템 콜이 IDT를 거친다고 설명해서는 안 된다.[6]
진입, 상태 보존, 복귀
CPU는 인터럽트를 받아들일 때 복귀 위치와 이전 실행 권한 등 필요한 최소 상태를 보존하고 설정된 처리기 진입점으로 이동한다. 진입 코드는 나머지 레지스터와 처리기에 필요한 스택을 준비한다. 하드웨어와 소프트웨어가 맡는 범위는 아키텍처마다 다르다. 사용자 코드 실행 중이었다면 커널 처리를 위한 권한 전환도 이루어지며, 진입점과 권한을 함께 통제해 보호 경계를 유지한다.[7]
원래 실행을 재개할 때는 저장 상태를 복원하고 복귀 절차를 거친다. 처리기가 잠깐 실행된 것과 다른 스레드로 컨텍스트 스위치를 한 것은 다르다. 실행 대상을 바꾸는 결정은 스케줄러가 별도로 수행한다.[8]
입출력 완료와 깨우기
입출력을 기다리는 스레드는 대기 상태로 두고 다른 작업에 CPU를 줄 수 있다. 장치가 완료 인터럽트를 보내면 드라이버가 요청의 결과를 확인하고 해당 요청을 기다리던 스레드를 깨운다. 깨우기는 실행 가능 상태로 바꾸는 일이지, 인터럽트 처리기 안에서 사용자 프로그램을 호출하는 일이 아니다.[9]
입출력 대기 → 완료 통지 → 결과 확인과 깨우기
→ 실행 가능 상태 → 스케줄러 선택 → 실행 재개
장치 인터럽트는 다른 작업이 실행 중일 때도 도착한다. 따라서 인터럽트에서 복귀하는 대상과 방금 깨어난 스레드가 같다고 볼 수 없다. DMA 전송이라면 완료 확인 뒤 버퍼를 사용할 수 있는 상태로 동기화하는 일도 필요하다.
타이머 인터럽트와 선점
시스템 콜마다 실행 중인 작업을 양보시킨다고 가정하자. 다음 C 반복문에는 시스템 콜 자체가 없다.
while (1) {
}
시스템 콜을 유일한 양보 지점으로 삼았다면 커널은 다음 양보 지점을 기다려도 제어권을 돌려받지 못한다. 시스템 콜마다 양보하게 하는 것과, 시스템 콜을 하지 않는 코드에서도 CPU를 회수하는 것은 다르다. 일반적인 선점형 시분할은 사용자 코드와 독립적으로 만료를 알리는 타이머를 사용한다. 커널이 통지 시각을 설정하면 반복문이 계속되어도 인터럽트로 다시 제어권을 얻을 수 있다.[10]
타이머 통지는 시간 계산과 선점 판단의 기회다. 다른 실행 가능 작업이 없거나 현재 작업을 유지할 이유가 있으면 그대로 복귀한다. 또한 인터럽트 진입을 잠시 막는 것과 스레드 선점을 미루는 것은 다른 제약이며, 안전한 시점까지 컨텍스트 스위치가 지연될 수 있다.[11]
통지 빈도와 처리 비용
인터럽트 통지나 컨텍스트 스위치를 자주 하면 응답 기회는 늘지만 처리기·스케줄러 실행과 캐시 교란 비용도 늘 수 있다. 여러 사건을 묶으면 처리량은 좋아질 수 있지만 먼저 도착한 사건은 기다려야 한다. 처리량과 개별 요청의 지연 시간을 함께 고려해야 한다.[12]
고정 주기의 타이머만 가능한 것은 아니다. 다음 필요한 시각에 한 번 울리는 원샷 타이머를 재설정할 수 있다. 틱리스 설계는 불필요한 주기적 통지를 줄이는 것이며, 선점 자체를 포기하는 방식은 아니다.[13]
실시간 제어와 RTOS
장치 제어에서는 결과뿐 아니라 도착 시점도 중요하다. 실시간 운영체제(RTOS: Real-Time Operating System)는 평균 처리량보다 응답 시간의 상한과 예측 가능성을 우선한다. 인터럽트를 막은 구간, 처리기 실행, 스케줄링과 잠금 대기뿐 아니라 버스 경쟁과 DMA 완료 대기도 실제 출력의 마감시간에 영향을 준다. 높은 우선순위나 RTOS 도입만으로 마감시간이 보장되지는 않으며, 주어진 작업량과 하드웨어 조건에서 최악의 실행·대기 시간을 검토해야 한다.[14]
관련 문서
- 운영체제 기본/파일과 입출력: 장치 위에서 제공하는 파일·버퍼링·영속성.
- 운영체제 기본/Linux의 키보드 입력: i8042의 IRQ부터 입력 이벤트와 프로세스 깨우기까지.
참고 문헌
- ↑ 1.0 1.1 Linux Kernel Documentation, Bus-Independent Device Accesses.
- ↑ 2.0 2.1 Linux Kernel Documentation, DMA Engine API Guide.
- ↑ 3.0 3.1 3.2 Linux Kernel Documentation, Dynamic DMA mapping Guide.
- ↑ Linux 커널 문서: NAPI, 인터럽트와 폴링의 결합.
- ↑ RISC-V Instruction Set Manual: Exceptions, Traps, and Interrupts.
- ↑ 6.0 6.1 6.2 6.3 Intel, Intel 64 and IA-32 Architectures Software Developer’s Manual, Volume 3A, 2016년 9월, 6.10~6.14절 및 5.8.8절.
- ↑ Cox 외, xv6, 2025, 4.1·4.2절.
- ↑ Cox 외, xv6, 8.1·8.2절.
- ↑ Cox 외, xv6, 6.1·9.1절.
- ↑ RISC-V Instruction Set Manual: Machine-Level ISA, 타이머와 인터럽트 레지스터.
- ↑ Linux 커널 문서: Proper Locking Under a Preemptible Kernel.
- ↑ Mogul·Borg, The Effect of Context Switches on Cache Performance, 1990.
- ↑ High resolution timers and dynamic ticks, NO_HZ.
- ↑ QNX Neutrino 7.1: Scheduling latency, 인터럽트 지연과 스케줄링 지연의 구분.