[유혁 교수의 운영체제] 4.컴퓨터 구조

컴퓨터 구조

이 장의 목표

  • 운영체제에서 알아야 할 HW 이슈 정리
    • 컴퓨터 시스템의 전체적인 구성
    • 장치의 성능 비교
    • I/O의 동작원리

버스 구조

  • 단일 버스 - 초창기 컴퓨터
    • 시스템 버스에 여러가지 디바이스들이 연결
      • 버스 : High speed serial connection
      • CPU, Memory, I/O의 속도가 비슷했던 초창기에 사용
    • CPU, Memory, I/O의 속도 격차 증가 -> 병목 현상 발생

장치의 속도 비교

  • 속도 격차
    • CPU > Memory » I/O 로 속도의 격차가 커짐
      • CPU와 메모리 속도: GHz vs usec
        • CPU는 0.5nsec 정도의 속도로 동작
        • 메모리는 usec 정도의 속도로 동작하며, 서버용 하드웨어는 몇십 nsec으로 동작하기도 함
      • 메모리와 disk: usec vs msec
        • disk는 msec 속도로 동작 (CPU와 약 100만 배의 차이)
      • Disk와 네트워크
        • 네트워크는 1bit를 보낼때마다 걸리는 시간 기준
      • I/O 장치 - CD, Keyboard
        • 특정 단위(1Mb)를 읽고 쓰는 데 걸리는 시간 기준
    • 이로 인한 병목현상 발생
      • 여러 격차의 속도를 갖는 것들이 하나의 경로를 통해 다닌다면, 전체적인 속도는 가장 느린 것의 속도에 맞춰짐

usec(Microsecond) vs msec(Millisecond) vs nsec(Nanosecond) vs GHz

  • 1000usec = 1msec = 0.001sec
  • 1e-9nsec = 1usec
  • 1Hz : 1초에 1한번 진동
  • 1GHz = 1,000,000,000Hz (1초에 10억번 진동)
  • 1GHz = 1/1,000,000,000sec = 0.00000001sec = 0.01usec

Bottleneck

  • 병목현상
    • 같은 버스에 연결된 디바이스들 간의 속도 차이로 인해 발생
    • 빠른 디바이스가 처리하는 양 만큼을 느린 디바이스가 처리하지 못함 -> 빠른 디바이스 stall (기다려야 함)
    • 전체 시스템 속도는 느린 디바이스의 속도로 제한됨
    • ex) CPU는 초당 5 단위의 일을 처리할 수 있지만 메모리가 초당 3 단위의 일만을 전달해 줄 수 있다고 할 때, 전체적인 시스템 속도는 메모리의 속도로 제한된다.

버스 이중화

  • 계층적 버스 구성 (2tier)
    • System Bus와 I/O 버스 두 계층으로 분리
      • 유명한 I/O 버스 : SCSI, PCI 버스…
      • System Bus는 I/O 버스에 비해 유명한 것들이 없는데, 회사마다 Secure specific하기 때문이다. 그에 비해 I/O 버스는 이름이 알려지거나 표준화되어있는 이유는 다양한 종류의 CPU 같은 디바이스들과 호환이 되어야하기 때문이다.
      • 버스를 둘로 분리함으로써 CPU와 Memory가 I/O 디바이스를 기다리는 일을 방지한다.
      • System Bus와 I/O 버스를 연결해주는 것은 Bridge Bus Interface이다.
        • I/O Device에서 Memory로 데이터를 보낼 때, I/O Bus를 거친 데이터가 Bridge 측에서 대기하다가 때가 되면 System Bus를 통해 Memory로 보낸다.
    • 속도 격차로 인한 병목 현상 해결
    • 접근 빈도가 적고, 처리 속도가 느린 장치들은 시스템 버스에 직접 연결하지 않음(I/O 버스를 거쳐 연결됨) -> stall 방지

이벤트 처리 기법 : Interrupt

  • 비동기적 이벤트를 처리하기 위한 기법
    • 비동기적 이벤트를 쉽게 표현하면, 특정 프로세스의 수행 순서와 상관없는 시점에서 이벤트가 독립적으로 발생하는 것을 말한다.
    • 예: 네트워크 패킷 도착 이벤트, I/O 요청
    • 하드웨어 레벨에서 Interrupt를 발생시켜야 한다. 이 때 발생된 Interrupt를 CPU는 반드시 처리해줘야 한다.
  • 인터럽트 처리 순서
    1. 현재 실행 상태(state=CPU의 state)를 저장
    2. ISR(interrupt service routine)로 점프
      • ISR 수행 중에는 자신보다 우선순위가 낮거나 같은 Interrupt를 처리할 수 없으므로 왠만하면 ISR 중에는 자신보다 우선순위가 낮거나 같은 Interrupt를 disable시켜놓는다.
    3. 저장한 실행 상태(state)를 복원
      • 전의 실행된 상태로 다시 돌아오지 못하는 경우도 존재
    4. 인터럽트로 중단된 시점부터 다시 시작
  • 인터럽트에는 우선순위가 있으며, H/W 장치마다 다르게 설정됨
    • 낮은 우선순위인 인터럽트는 mask out이 되고, 높은 우선 순위인 인터럽트는 인터럽트 안에서 또다시 인터럽트로서 수행된다.
    • 우선순위는 이미 정적으로 결정되어있다. disk 인터럽트 보다는 네트워크 인터럽트가 더 높은 우선순위를 갖는다. 네트워크 인터럽트는 당장 처리하지 않으면 패킷이 폐기되기 때문이다. 우선순위가 가장 높은 인터럽트는 강제 종료, shut down sequence로, non-maskable interrupt(mask out이 절대 되지 않는 인터럽트)이다.
    • 만약 현재 ISR 중에 있는 인터럽트보다 더 우선순위가 높은 인터럽트가 발생하면 이를 바로 처리해줘야하므로, ISR이 완료되고 Interrupt가 발생한 지점으로 돌아가는 데 걸리는 시간(위의 처리 순서에서 2, 3 번에 해당하는 과정)은 undeterministic인 것이다.

  • Note
    • ISR은 짧아야 함
      • 너무 길면 다른 인터럽트들이 제시간에 처리되지 못함 ex) 1usec
        • ISR 수행 중에는 다른 Interrupt는 물론이고 다른 프로세스를 수행하지 못한다.
        • D-Dos 공격 : 너무 많은 패킷을 짧은 시간 안에 보내면 ISR 수행을 하느라 CPU가 마비되는 상황
      • Timesharing 역시 timer interrupt의 도움으로 가능하게 됨
        • 이전 Timesharing에서 생긴 이슈 : “어떻게 CPU가 이미 프로세스를 수행하고 있는 중에 타이머에 반응을 할 수가 있느냐” => 외부적인 도움이 필요하거나 주기적인 인터럽트가 발생됨으로써 이를 해결할 수가 있다.
        • 1초에 100번 정도로 주기적으로 인터럽트가 발생할 때마다, 현재 수행중인 프로그램이 다른 프로그램에게 CPU를 넘겨준다.
    • 컴퓨터를 사용하는 도중에 키보드에 무언가를 입력했는데 화면에 글자가 뜨지 않거나, 영상이 자꾸 끊어지는 등의 문제들 중 다수가 인터럽트를 제때에 처리하지 못했기 때문일 수 있다.

Q : Timer 인터럽트 없이 Timesharing이 가능한가요? A : 인터럽트 기능을 사용하지 않고서는 프로그램 내에서 CPU가 몇 개의 instruction을 수행할 때마다 주기적으로 시간을 확인하게 하도록 하거나, 프로그램이 수시로 타이머를 확인하는 수 밖에 없다. 비효율적일 수 밖에 없으므로 대부분 인터럽트를 통해 이를 수행한다.

Q : multiprogramming은 인터럽트가 필요 없나요? A : multiprogramming에서는 어떤 프로그램이 I/O를 하면 그 루틴을 보고서 커널이 다른 프로그램을 수행한다. 따라서 이것은 비동기적인 이벤트에 대한 처리도 아닐 뿐더러, 강제적으로 CPU를 뺏는 인터럽트와 달리 voluntary switching이다.

Q : 인터럽트가 기존에 실행하던 프로그램을 종료하는 명령을 수행한다면, ISR 이후에는 어느 시점으로 돌아가나요? A : 이미 프로그램이 종료되었으므로 어느 시점으로 돌아가지 않을 것이다.

Q : mask out된 인터럽트는 자신의 차례가 오면 수행이 되는건가요? A : 순번이 올 때까지 데이터가 폐기되지 않았다면, 수행된다. 패킷과 같은 경우에는 특정 시간이 지나도록 처리가 되지 않으면 폐기된다.

Q : 차례를 기다리는 인터럽트들은 어디에 저장되나요? A : 특정 버퍼에 저장된다. 다만 이 버퍼도 용량이 정해져있기 때문에 너무 인터럽트가 많아지면 데이터가 폐기될 수 있다.

Q : 인터럽트 처리 순서의 “2. ISR(interrupt service routine)로 점프”에서 ‘점프’는 ‘스위칭’과 같은 것을 의미하나요? A : 엄밀히 말하면 둘은 다르다. 프로그램 카운터가 ISR 루틴으로 뛰어가는 것을 의미한다.

이벤트 처리 기법: Trap

  • 동기적인 이벤트를 처리하기 위한 기법
    • 예: 시스템 콜, divide by zero, null pointer error
      • 시스템콜 : 프로그램은 시스템 콜 라이브러리를 통해 커널에 진입하는데, 이 라이브러리가 바로 시스템 콜에 해당하는 trap을 걸어주는 역할을 한다. 이 과정을 통해서 CPU는 유저모드에서 커널 모드로 전환된다.
    • 동기적 - 현재 수행하고 있는 프로그램에 의해 발생
    • Trap handler에 의해 처리
    • Trap service routine이 있기 때문에 인터럽트와 유사하지만, 인터럽트와 달리 실행 상태(state)를 저장/복원하지 않음
      • 트랩은 동기적인 이벤트이므로 상태를 굳이 저장하지 않아도 상태가 이미 유지되고 있기 때문이다.
      • 인터럽트는 트랩에 비해서 ISR이 아예 다른 context에서 수행될 수도 있다. 따라서 state 저장이 필수이다.
  • 트랩 처리 순서
    1. state 저장 없이 Trap Service Routine으로 점프
      • 시스템 콜을 예시로 들면 Trap Service Routine에서는 CPU가 커널 모드로 커널 함수를 수행하는 것을 의미한다.
    2. 트랩의 발생 지점으로 다시 돌아감

Q : 커널 모드에 있는 도중에 인터럽트가 발생하면 어떻게 되나요? A : 계속 커널 모드가 유지된다. 인터럽트를 처리하는 모드가 애초에 커널 모드일 가능성이 크기 때문이다.

Q : 트랩은 우선순위가 있나요? A : 트랩은 동기적인 이벤트이므로 별도의 우선순위 없이 발생하는 순서대로 처리한다.

Q : 트랩과 인터럽트가 동시에 발생하면 둘 중에 무엇이 먼저 처리되나요? A : 인터럽트가 먼저 처리된다.

Q : 프로그램이 호출하는 I/O는 비동기적인 이벤트가 아닌가요? A : 그렇다. 프로그램이 I/O를 호출할 때는 동기적인 이벤트라고 할 수 있으며, 오히려 I/O가 완료되는 시점(I/O compliete)이 비동기적이라고 할 수 있다.

Q : multiprogramming에서 I/O로 인한 프로그램 스위칭 과정을 인터럽트를 통해 설명해주실 수 있나요? A : 어떤 job이 I/O를 호출할 경우, job은 스스로 CPU를 다른 job에게 넘겨준다. 그리고 I/O complete가 발생하면 이에 따라 인터럽트가 발생하고, 이것을 수행중인 다른 job이 잡아 처리하게 된다.

Q : ISR은 수행 중간에 중단될 수 있나요? A : ISR은 수행 중간에 절대 wait되지 않는다.

Q : Trap Service Routine은 deterministic한가요? A : deterministic을 어떻게 해석하느냐에 따라 다른데, 시스템 콜도 아주 단순한 함수부터, 아주 복잡한 함수까지 다양한 함수가 있으므로 수행 시간이 늘 같진 못하다는 점에서 deterministic하지는 않다. 그러나, 커널 함수를 실행하고 리턴하면 다시 프로그램으로 돌아간다는 이 일련의 과정은 정해져있다는 점에서 바라보면 deterministic하다고 할 수 있다.

I/O device model

  • Device registers
    • 하드웨어 장치는 장치를 제어하는 controller가 있음
    • Controller는 대부분 4종류의 레지스터를 가짐
    • Control register, Status register, Input register, Output register
      • Control register : 읽거나 쓰라는 명령 정보
      • Status register : 디바이스의 상태를 나타내는 정보 ex) 데이터가 들어있거나 비어있는 상태
      • Input register : 디바이스에서 Controller로 들어온 데이터
      • Output register : Controller에서 디바이스로 내보내는 데이터
    • 커널이 Control register에다가 명령을 주면 Controller가 이 명령에 따라 동작을 한다.
    • 레지스터들은 메인 메모리의 일부 영역에 매핑
      • 매핑된 영역의 주소만 알면, CPU에서 접근 가능
  • I/O = CPU가 register에 읽고 쓰는 동작

I/O 처리 기법 : Polling

  • Loop(timed-delay loop)안에서 특정 이벤트의 도착 여부를 계속 확인하는 방법
    • 이벤트가 들어왔을때 Status register에 정보가 적힌다면, loop을 통해 계속해서 Status register을 읽으면서 이벤트가 도착했는지를 확인하는 것이다.
  • 인터럽트와 반대되는 개념
    • 인터럽트는 이벤트가 발생했을 시에만 그것을 받아 확인
    • 폴링은 지속적으로 이벤트의 발생 여부를 확인
      • 프로그램 수행 중에 어떤 간격을 두고 계속해서 이벤트 발생 여부를 확인한다.
  • 장치가 매우 빠른 경우, 폴링은 event 처리 기법으로 적당함
    • 왜? CPU 상태를 저장하는 과정에서 오버헤드가 발생하는 인터럽트와 달리, 폴링은 프로그램 수행을 하던 context 자체에서 trap을 통해 커널로 진입하여 디바이스를 읽어온다. 따라서 오버헤드가 발생하지 않는다.
    • 느린 장치인 경우? 반복적인 이벤트 확인 과정이 오버헤드를 발생시킬 수가 있다.
  • 이벤트 도착 시간이 길 경우, 폴링은 CPU time을 낭비
    • 컴퓨터 시스템에서 CPU time은 매우 귀중한 자원!!
  • 폴링 뒤에 programmed I/O(PIO) 수행
    • CPU가 I/O를 위한 데이터 이동을 담당

Direct Memory Access (DMA)

  • 폴링을 사용할 경우, 모든 동작은 CPU에 의해 진행
  • DMA는 I/O를 위한 별도의 장치
    • 아주 단순한 기능을 하는 로직이다.
    • CPU 대신 장치의 상태 확인 및 데이터 이동에 사용하는데 사용
    • DMA를 사용하는 경우, I/O를 호출한 프로세스는 sleep함
      • 프로세스가 sleep하는 동안, DMA가 I/O(메모리<->장치)를 대신해준다.

DMA와 CPU

  • DMA(engine)라는 특정 목적 프로세서가 있음 (DMA는 하나의 로직을 칭하기도 하고, engine을 생략한 용어로서 프로세서를 칭하기도 함)
    • CPU와 DMA 간 통신으로 I/O를 수행
      • CPU가 DMA에게 I/O를 요청하면 DMA는 CPU를 대신하여 I/O 장치와 메인 메모리 사이 데이터 전송을 수행
      • CPU와 DMA가 동시에 동작할 수가 있으므로, CPU는 I/O가 수행되는 동안 다른 작업을 수행할 수 있음
    • CPU는 I/O가 진행하는 동안 다른 일을 수행
  • DMA는 Bus Stealing을 사용하여 동작
    • 여러 디바이스는 하나의 Bus에 데이터를 실을 수는 있지만 동시에 두 개 이상의 데이터를 실을 수는 없다.
    • 그런데 CPU와 DMA가 둘 다 데이터를 읽고 쓸 수 있는 상황이다. 즉, CPU 이외에 Bus Transaction을 initiate함으로써 읽고 쓰겠다는 명령을 Bus에 실을 수 있는 디바이스가 하나 더 생긴 셈이다.
    • CPU 측에서 보내는 데이터와 DMA 측에서 보내는 데이터가 충돌하지 않을 수 있는 방법으로써 Bus Stealing이 고안되었다.
    • Bus Stealing은 양측이 충돌할 때 항상 CPU가 이기는 것을 말한다. 이를 따르면, CPU가 메모리에 접근하고 있지 않을 때에만, Bus를 사용하고 있지 않을 때에만 잠깐 DMA가 이를 사용하여 I/O를 수행한다.
  • DMA가 동작을 마치면 CPU에 Interrupt 발생시킴
    • “I/O가 시작되면 프로세스가 sleep하고 I/O가 완료되면 프로세스가 깨어난다”
      • 프로세스가 I/O를 할때 DMA에 명령을 하고 Sleep을 한다.
      • DMA가 I/O를 하는 동안 Timesharing에 의해서 다른 프로세스가 동작한다.
      • I/O가 완료되면 interrupt가 발생하고, 이를 다른 프로세스가 받아서 처리함으로써 기존에 sleep 상태에 있던 프로세스가 깨어나게 된다.

DMA-Read

IDE Disk Controller에 read을 요청했을 때의 동작 과정

  1. CPU는 DMA Controller 초기화하고 전송모드를 DMA_MODE_READ로 설정
    • Control Register에 해당 정보를 작성함
  2. CPU는 DMA Controller에게 Buffer의 주소(X), 크기(C)를 알려줌
    • I/O를 요청한 프로세스가 Sleep하기 시작
  3. DMA controller는 Disk Controller에게 데이터 전송을 요청
  4. Disk Controller는 매번 byte 단위로 읽어 오는 데이터를 DMA Controller에게 전송
    • byte 단위는 1byte가 아니라 I/O 버스에서 한번에 전송 가능한 단위를 기준으로 한다. ex) 32byte
  5. DMA Controller는 읽어오는 데이터를 주소 x의 버퍼에 기록
    • 매 전송마다 C 값을 감소시키고, C=0이 될 때까지 읽어옴
    • 이 과정까지 계속해서 CPU는 다른 프로세스를 수행하고 있음
  6. C 값이 0이되면, 전송이 완료된 것이므로, DMA Controller는 전송이 완료됐음을 인터럽트를 통해 CPU에 알림

DMA vs Polling: 어느 것이 나은가

얼핏 생각하면 DMA를 사용하면 I/O를 하는 동안에 CPU는 다른 작업을 할 수 있으므로 DMA를 사용하는 쪽이 훨씬 효율적인 것처럼 느껴진다. 그렇지만,

  • DMA - 추가적인 hardware가 필요

  • 성능 - DMA를 최대로 활용하기 위해서는 적당한 parallelism이 필요

    • 한 프로그램이 Sleep할 때, 다른 프로그램이 작업될 수 있는, 혹은 다른 작업이 필요한 환경에서 활용성 극대화

    • 만약 굳이 다른 작업을 할 필요가 없는 환경이라면? ex) 하나의 작업에만 집중해야 하는 임베디드 시스템

      • 휴대전화 camera pixel을 읽어들이려고 할 때 DMA가 필요한가?

        현재의 휴대 전화에서는 다양한 어플리케이션이 동시에 동작하고 있다. 충분히 parallelism이 필요한 환경이므로 DMA를 사용하는 편이 좋다. 휴대전화가 지금보다 단순한 기능만을 제공할 때에는 필요가 없었을 것이다.

I/O Device Access 기법: I/O Instruction

우리는 I/O 장치를 사용하기 위해 I/O 장치 내부의 Register을 사용한다. 이 Register를 사용하는 방법은 두 가지이며, 그 중 한 가지는 I/O Instruction이다.

  • CPU는 I/O Instruction을 수행
    • I/O Instruction : I/O를 하기 위한 특정 명령어
  • I/O Instruction은 해당 장치 레지스터의 값을 읽고 씀으로써 장치와 통신함
    • 예: Intel의 I/O 명령어
      • in, out, ins, outs …

I/O Device Access 기법: Memory Mapped I/O

  • 장치 레지스터들을 메모리 공간에 매핑하여 사용
    • 장치 레지스터들을 I/O 장치라기보다, 메모리 공간의 일부로 취급
    • 장치 레지스터가 메모리 주소에 매핑이 되면, 레지스터들은 주소 공간의 일부로 접근
  • CPU는 일반적인 명령어를 사용하여 I/O 작업을 수행
    • 예: mov, and, or, xor

Comments