우리가 OS를 공부하기 이전에 꼭 알아야 하는 하드웨어들에 대해 알아보겠습니다.
- Computer System Architecture
- Interrupt Mechanism
- Hardware Protection
Computer System Archtecture
컴퓨터에서 온갖 연산과 제어동작을 실행시켜 주는 부분으로 CPU가 존재합니다. 컴퓨터의 메모리에는 프로그램을 수행할 때 필요한 instruction들이 저장되어 있습니다. (instruciton의 sequence가 코드입니다.) input과 output 디바이스도 컴퓨터 시스템에서 빼놓을 수 없는 부분입니다.
위에서 언급한 CPU, 메모리, I/O device는 컴퓨터의 핵심적인 3가지의 Component입니다. 이 3가지 요소들이 따로 존재하면 컴퓨터로써의 동작을 하지 못하기 때문에 연결이 필요한데 이때 연결성을 제공해 주는 요소를 Data Path라고 합니다.
우리가 사용하는 컴퓨터는 대부분 폰 노이만 구조를 따릅니다. 이 구조를 Stored program 컴퓨터라고도 부르는데 데이터를 저장하는 것뿐만 아니라 데이터를 조작하는 코드까지 포함된 구조입니다. 폰 노이만 머신에는 메모리가 필요합니다. 메모리에는 데이터만 들어가는 것이 아니라 instruction의 시퀀스도 저장됩니다. 이 저장된 시퀀스들을 알고리즘 순서에 맞게 실행시켜 주는 장치도 필요한데 이게 CPU입니다.
CPU가 메모리에서 instrction과 데이터를 가져오려면 위에서 언급한 Data Path가 필요하게 됩니다. CPU안에는 여러 하드웨어 서킷이 들어있습니다. 산술연산을 해주는 Arithmetic Unit과 컴퓨터가 Boolean Logic을 할 수 있도록 도와주는 Logic Unit 존재하는데 이 두 개의 competition unit을 묶어서 Arthmetic and Logic Unit(ALU)이라고 부릅니다.
CPU가 연산을 하다 보면 그 연산의 중간 값을 저장해 놓거나 아니면 메인 메모리에서 가져온 데이터를 담아둘 메모리가 필요합니다. 이런 역할을 register라는 CPU의 작은 메모리가 하게됩니다. 대표적인 register로는 데이터를 담아두는 data register와 수행할 instructions의 주소, 데이터 주소 등을 담는 역할을 하는 Address register 그리고 메인 메모리에서 가져온 instruction을 담아둘 instruction register가 존재합니다.
마지막으로 알고리즘 순서대로 instructions을 수행시키려면 다음 instruction을 시퀀싱 해 주는 서킷이 필요한데 이것을 프로그램 카운터 레지스터(program counter register)라고 합니다. 이 프로그램 카운터 레지스터에는 fetch 할 다음 instruction의 주소를 저장하고 있습니다.
실제 컴퓨터를 간단하게 도식화한 사진입니다. disk와 disk controller가 연결되는 등 각종 I/O 디바이스들에 서킷들이 존재합니다. 이 서킷들이 바로 IBM에서 개발한 I/O 컨트롤러입니다. 이 서킷들은 CPU가 직접 I/O 디바이스를 조작하지 않아도 CPU의 일을 대신 수행해 주는 역할을 합니다. (operation을 실행시키고 끝나면 interrupt를 보내는 역할)
메모리 컨트롤러는 메모리 access throughput을 높이기 위해 병렬성을 주는 등 하드웨어적으로 메모리 access 효율을 높여주는 역할을 합니다. 이 컴퓨터 시스템들을 구성하는 각 서킷들은 Data Path나 System Interconnect으로 연결되어 있습니다.
Bit란 binary digit의 약자로 컴퓨터가 표현할 수 있는 가장 작은 단위입니다. 모든 Bit들을 직접 다 access 할 수 없기에 컴퓨터는 8개의 Bit를 하나의 Byte로 묶어서 저장합니다. 메인 메모리에서 주소가 부여되는 단위가 Byte입니다.
그다음 Word라는 단위가 존재하는데 이 단위는 가변적인 단위로 컴퓨터의 CPU를 설계하는 사람이 결정을 합니다. CPU에서 Word 사이즈가 결정되면 그것에 맞게 다른 하드웨어 로직들이 함께 설계됩니다. Word는 컴퓨터 하드웨어가 주고받는 데이터의 단위이며 CPU에서 일어나는 연산이 Word 단위로 일어납니다.(우리가 사용하는 정수나 실수를 표현하는 수단)
Data Path에 대해서 더 알아보겠습니다.
Data Path는 컴퓨터 아키텍처에서 System Interconnect 또는 System Bus라고 부릅니다. 폰 노이만 머신에서는 데이터와
Instructions이 모두 메인 메모리에 저장되어 있습니다. 그래서 CPU가 어떤 프로그램을 수행시키려면 메인 메모리에서 CPU로 fetch 되는 과정이 필요합니다. 이때 System Bus가 사용이 됩니다. System Bus는 3개의 서브 Bus로 나눠져 있습니다.
- Address Bus : CPU에서 읽거나 쓰고자 하는 데이터나 Instructions의 주소를 전달해 주는 역할로 단방향 Bus입니다.
- Control Bus: CPU가 메모리에게 Instruction을 fetch 한다는 명령을 보낼 때 이 명령을 실어 나르는 Bus가 Control Bus입니다.
- Data Bus: 2번 과정을 통해 메인 메모리는 데이터를 CPU로 보내야 한다는 것을 인지하게 되었고 그 데이터가 몇 번째 주소에 있는지 알게 되었습니다. 데이터나 Instruction을 CPU에게 보내는 경로가 Data Bus입니다.
폰 노이만 머신에는 CPU와 메모리를 연결시키는 Data Path를 System Bus라고 부르며 이 안에는 Address Bust와 Data Bus 그리고 Control Bus가 있다고 생각하시면 됩니다.
CPU가 메모리에서 데이터를 읽어오는 과정을 load라고 합니다. 반대로 CPU 레지스터에서 메모리로 데이터를 보내는 동작을 store 또는 write라고 합니다. 이러한 동작을 하려면 CPU가 Address Bus를 통해 작업하고 싶은 데이터 주소를 실어 보내고 그다음에 메모리에게 Control Bus를 통해 store 동작인지 write 동작인지 알려줍니다. 메모리는 Address Bus로 지정된 주소에 Control Bus로 전달된 동작을 Data Bus를 통해 수행하게 됩니다.
CPU가 데이터를 쓰거나 읽는 동작을 Bust Transaction이라고 부릅니다. 두 개의 Transaction이 존재하는데 한 개는 Read Transaction이고 하나는 Wrtie Transaction입니다. Bus를 주체적으로 사용하는 것은 CPU입니다. 우리가 쓰는 스마트폰이나 컴퓨터는 1개의 CPU가 아닌 4개, 8개, 16개 등이 있을 수 있습니다. 이렇게 많은 CPU가 동시에 Bus를 장악하면 동시에 Data Bus로 여러 CPU 시그널들이 전송되니 엉터리 값이 전송됩니다.
그래서 System Bus는 하나의 CPU가 독점적으로 사용해야 되는데 이러한 조정을 Bus arbitration이라고 합니다. Bus arbitration에는 여러 방법이 있는데 그중 Centralized Bus arbitration 방법에 대해서 알아보겠습니다. 이 방법은 System Bus에 Bus arbiter이라는 별도의 서킷이 붙어 있습니다. CPU들이 Bus를 원하면 Bus arbiter에게 Bus request 시그널을 보냅니다. 그러면 Bus arbiter는 여러 CPU 중 하나만 선정해서 Bus grant 시그널을 보내 Bus를 허용합니다. Bus를 다 사용하고 나면 CPU는 다른 CPU가 Bus를 사용할 수 있도록 Bus arbiter에게 Bus release 시그널을 보냅니다.
CPU처럼 주체적으로 BusTransaction을 컨트롤할 수 있는 장치를 Bus Master라고 부릅니다. 이러한 Bus Master가 Bus Transaction을 했을 때 response만 할 수 있는 장치를 Bus Slave (메모리 컨트롤러, 메모리 등)라고 부릅니다.
I/O 컨트롤러 같은 경우 Bus Slave일까요? 아니면 Bus Master일까요?
I/O 컨트롤러는 CPU와 I/O 디바이스 사이에 존재하는 서킷으로 CPU처럼 레지스터가 존재합니다.
- CPU가 출력을 원하는 데이터를 저장하는 output 레지스터
- CPU에 전달하기 위한 데이터를 잠시 저장하는 input 레지스터
- CPU가 부여한 명령(input, output)을 기억하는 control 레지스터
- 현재 I/O의 상태를 저장하는 Status 레지스터
CPU가 출력하고자 하는 byte를 interconnect를 이용하여 I/O 컨트롤러에 있는 output 레지스터에 저장한 후 다시 CPU가 control 레지스터에 출력 command를 저장합니다. 그다음 I/O 컨트롤러는 자체적으로 출력 연산을 시도합니다. 연산 성공 시 CPU로 interrupt를 걸게 됩니다.
출력 operation은 크게 polling I/O와 Interrupt I/O로 나뉘게 됩니다. polling I/O는 CPU가 전적으로 I/O operation을 전담합니다. CPU가 loop를 한 번 돈 후 I/O 컨트롤러에게 작업이 완료되었는지 물어보는 것을 반복하게 됩니다. 이 완료 여부를 CPU는 status 레지스터를 확인하는 것을 통해 알 수 있습니다. (CPU가 스스로 clock cycle을 사용하여 I/O operation을 책임지는 것이 polling I/O)
이 polling I/O 방식은 CPU가 clock cycle을 사용하면서 유용한 작업을 하지 못하고 loop만 도는 상태로 비효율적인 방법입니다. 이런 비효율적인 부분을 제거하기 위해서 나온 방법이 Interrupt-driven I/O 방식입니다. Interrupt-driven I/O는 CPU가 출력한 데이터와 원하는 operation을 전송하고 다른 작업을 진행하게 됩니다. I/O 컨트롤러가 Interrupt를 보내 CPU를 깨우는 방식으로 진행됩니다.
- CPU가 값을 주면 저장하고, CPU가 값을 읽기를 원하면 제공해 주는 Bus slave로 볼 수 있습니다.
I/O 컨트롤러는 Bus Slave의 일종으로 CPU가 메모리와 같이 읽고 쓰는 동작을 진행합니다. 이때 각각의 레지스터에 접근하려면 주소가 필요합니다. 컴퓨터 시스템에는 많은 I/O 컨트롤러가 존재하며 많은 I/O 컨트롤러들은 각자의 레지스터를 가지고 있기 때문에 이런 레지스터들을 주소로 구별해야 하는 문제가 발생합니다. 이 문제는 2가지 방법으로 해결할 수 있습니다.
- Memory-mapped I/O: 물리적 메모리가 사용하는 주소 공간의 ROM이나 RAM이 꼽혀있지 않은 빈 공간을 I/O 레지스터와 매핑하여 사용합니다.
- Port-mapped I/O: 물리적 메모리의 주소 공간과 완전히 별개의 I/O address 스페이스를 만들어서 저장하는 방법입니다.
Bus 관점에서 I/O 컨트롤러가 Bus Master가 되는 경우가 있을까요?
이 경우를 Direct Memory Access(DMA)라고 이야기합니다. polling I/O나 Interrupt I/O 같은 경우 바이트 단위의 데이터를 I/O 레지스터에 보내거나 읽는 연산이었습니다. 그런데 입출력 데이터가 몇 Byte가 아니고 큰 데이터 block이라면 이걸 byte 단위로 transfer 한다는 건 굉장히 큰 오버헤드가 발생하는 operation이 됩니다.
이런 큰 데이터가 입출력되는 경우 효율을 높이기 위해 고안된 기법이 DMA입니다. 이 기법도 CPU가 Initiation을 하게 됩니다. CPU가 1k 메모리 block을 디스크로 출력하는 명령을 한 번 생각해 봅시다.
- 디스크 컨트롤러에게 출력할 1k 데이터 block의 시작 주소를 전달
- 데이터 block 사이즈가 1k라는 걸 알려줍니다
- 현재 operation이 출력이라 것을 알려줍니다
이렇게 3가지 정보를 디스크 컨트롤러가 알게 된다면 직접 메모리를 액세스 해서 CPU를 경유하지 않고 byte 단위로 데이터를 얻어온 다음, block으로 만들어서 block을 디스크 드라이브로 출력하는 연산을 할 수 있게 됩니다.(CPU가 하던 메모리 access를 디스크 드라이브가 직접 함으로 Bus Master의 역할을 한다고 볼 수 있습니다.)
Interrupt Mechanism
Interrupt Mechanism은 하드웨어가 Interrupt를 발생하는 주체이면 Hardware Interrupt로 소프트웨어가 Interrupt를 발생하는 주체이면 Software Interrupt로 나눠집니다.
- Hardware Interrupt : 시그널을 통해 처리할 일을 들어오면 하던 일을 멈추고 먼저 처리 (Asyncronous 한 핸들링을 촉발, CPU는 이 Interrupt가 언제 발생할지 모름, 하던 일은 Program Counter에 기록됩니다.)
- Software Interrupt : 하던 일이 지정한 위치까지 진행되면 멈추고 다른 일을 진행 (Syncronous 한 핸들링, CPU가 Interrupt가 언제 발생할지 앎)
대표적인 Hardware Interrupt는 I/O 컨트롤러가 CPU에게 I/O operation의 종료를 알리는 행동이 있습니다. Software Interrupt의 경우 Interrupt가 여러 개의 instruction으로 구현되는 것을 의미합니다. CPU의 관점에서 외부로 들어오는 시그널이 전혀 없는 경우입니다.
- 유저가 에러 처리로 임의의 순간에 Software Interrupt를 발생시키는 경우 (작성한 코드를 훑어보면 어디서 Interrupt가 발생하는지 알 수 있음)
- 프로그램을 수행하다 도저히 진행할 수 없어 Software Interrupt를 발생시키는 경우 (어떤 수를 0으로 나눌 때 등, 이때 핸들링 제어권이 OS에게 넘어가 Software Interrupt을 발생시킨 부분을 terminate)
Hardware Interrupt가 발생하면 내가 수행하던 코드 진행을 멈추고 Program Counter에 기록한 후 Interrupt를 발생시킨 source의 IRQ number를 얻습니다. 그다음 IRQ number를 가지고 수행시켜야 될 핸들러 코드를 찾습니다. OS의 특정 영역에 array가 존재하는데 이 array의 index는 IRQ number로 되어 있습니다. 이것을 통해 각 element를 access 하면 그곳에 Interrupt 핸들링을 해야 하는 주소가 들어있습니다.
이렇게 Interrupt 핸들러 주소를 담고 있는 array, 즉 테이블을 Interrupt vector table이라고 합니다. Interrupt 수행이 다 끝나면 마지막에 return 문이 있습니다. 이를 통해 중단된 코드로 돌아가게 됩니다.
Prorection Mechanisms
컴퓨터 시스템의 핵심적인 요소들은 CPU, 메모리, I/O 디바이스로 이 세 항목에 대한 보안이 필요합니다. 이런 보안을 설정하려면 하드웨어적인 Basic Mechanisms이 필요합니다.
Dual Mode Operation
Dual mode는 2가지 모드가 존재한다는 의미입니다. 이 모드를 가지는 주체는 CPU로 모든 접근을 허용하는 모드와 그렇지 않은 모드 이렇게 2가지로 나뉩니다. 이렇게 Protection을 제공해 줄 때 CPU가 100% 신뢰할 수 있는 대상이 필요한데 그것이 바로 OS입니다. Protection은 OS의 동작은 100% 안전하다는 것을 전제로 깔고 들어갑니다. 그 반면에 사용자의 코드는로 시스템의 데이터가 망가지는 결과를 초래할 수 있으니 통제된 모드로만 수행될 수 있게 됩니다.
- Kernel mode : 모든 접근이 허용되는 모드
- User mode : 굉장히 통제된 모드
마이크로 프로세서는 동작을 하기 전에 현재 모드가 User mode인지 Kernel mode인지 구별을 해야 합니다. 어떻게 구별하냐면 마이크로 프로세서 내부 레지스터 중 한 bit을 할애해서 mode를 표현함으로써 구별하게 됩니다. (CPU의 레지스터 중 Status register)
이 레지스터를 Process Status Register(PSR)이라고 부릅니다. 이 레지스터에는 마이크로 프로세서의 연산의 결과 등 여러 상태를 표현하는 다양한 정보들이 bit으로 인코딩 되어 존재합니다. 이것을 프로세서의 status word라고 부르는데 그 owrd 중 한 bit가 mode를 표현하는 데 사용됩니다.
이전에 배운 MMU에서는 베이스 레지스터와 바운드 레지스터가 존재했었는데 이 레지스터 들은 사용자들이 임의로 읽거나 쓰면 안 되는 레지스터였습니다. 그래서 이 레지스터는 커널 모드에서만 수정이 일어납니다. 이런 레지스터를 읽거나 쓰는 instructions을 Privileged Instructions이라고 부릅니다.
Privileged Instructions는 커널 모드에서만 실행될 수 있기 때문에 사용자가 Privileged Instructions을 실행할 시 마이크로 프로세서가 mode bit을 읽고 user mode임을 확인하여 software interrupt를 발생시킵니다.
커널 모드를 0, 유저 모드를 1이라고 가정해 봅시다. 다음과 같이 각 모드를 변경하는 State transition이 일어나는데 유저 모드에서 커널 모드로 넘어가는 것은 하드웨어적인 메커니즘이 지원하고 있습니다. Interrupt를 사용하여 넘어가며 Return from Interrupt를 통해 커널에서 다시 유저로 돌아갈 수 있습니다.
Interrupt는 Hardware Interrupt와 Software Interrupt 이렇게 2가지가 존재하는데 유저 모드에서 커널 모드로 이동하는 Interrupt는 둘 다 사용합니다.
- Hardware Interrupt의 경우 Interrupt 시그널이 도착하면 Interrupt request number를 얻어 Interrupt vector table을 찾아보고 Interrupt의 서비스 루트로 점프해서 수행한 다음 return 합니다.
- Software Interrupt의 경우 Instructions을 수행하다 Interrupt Instructions 을 발견하면 CPU가 시그널을 받진 않았지만 프로그래머가 주는 IRQ number를 Instruction의 operand로 받아 Interrupt vector table을 찾아보고 서비스 루틴으로 이동하게 됩니다.
이렇게 2가지 방법이 존재하는데 하드웨어적인 방법은 지금 수행하는 유저 코드와 asynchronous 하게 모드 변경이 이루어지는 것이고 소프트웨어적인 방법은 유저가 자신의 필요에 의해 커널 함수를 수행시키는 것입니다.
유저가 자신의 필요에 의해서 Software Interrupt를 걸어주는 것을 System call이라고 부릅니다. 유저 코드에서 유저 function을 호출하는 것을 function call이라고 부릅니다. System call 도 일종의 function call이지만 Operating system function을 부른다는 차이점이 존재합니다.
- Protection : 메모리 같은 경우 어떤 특정 영역만 access 할 수 있게 하거나 특정 영역만 access 하게 만듦으로써 가능하고 I/O 디바이스나 CPU의 경우 Privileged Instruction을 이용하여 가능하게 합니다. I/O Protection은 I/O 컨트롤러에 있는 레지스터에 대한 access를 막으면 됩니다.
- CPU Protection은 어떤 유저 코드가 CPU를 혼자 독점하지 못하게 하는 것입니다. 타이머를 사용하여 일정 시간 사용하면 Interrupt를 걸어주는 방식입니다.
'CS > Operating System' 카테고리의 다른 글
6. 리소스(Resource) (0) | 2023.10.24 |
---|---|
5. 멀티 쓰레딩(Multi Threading) (2) | 2023.10.17 |
4. Process (creation, termination, context switching) (0) | 2023.10.12 |
3. 스택(Stack) (2) | 2023.10.01 |
1. OS의 발전 과정 (0) | 2023.09.23 |