[OS] SystemStructure & Program Execution 2

Doodung·2022년 2월 6일
0

OS - 운영체제

목록 보기
5/15
post-thumbnail

☑️동기식 입출력과 비동기식 입출력

  • 동기식 입출력 (synchronous I/O)

    I/O 장치까지 가서 보고 뭐가 적혀있는지 확인하고 읽어와서 그거 보고 작업했다 하면 sync read.

    I/O 장치에서 쓰라고 한 일을 눈으로 확인하고 와서 그 다음일을 하면 sync write.

    • sync : 시간적으로 서로 맞추는것
    • I/O 요청 후 입출력 작업이 완료된 후에야 제어가 사용자 프로그램에 넘어감
    • 구현방법 1
      • I/O가 끝날때까지 CPU를 낭비시킴
      • 매시점 하나의 I/O만 일어날 수 있음 → I/O 장치도 낭비
    • 구현방법 2 → 보통 이 방식으로 구현
      • I/O가 완료될 때까지 해당 프로그램에서 CPU를 빼앗음
      • I/O 처리를 기다리는 줄에 그 프로그램을 줄 세움
      • 다른 프로그램에게 CPU를 줌
      • CPU와 I/O 장치가 낭비가 안된다.
  • 비동기식 입출력(asynchronous I/O)

    • I/O가 시작된 후 입출력 작업이 끝나기를 기다리지 않고 제어가 사용자 프로그램에 즉시 넘어감
  • 두 경우 모두 I/O의 완료는 인터럽트로 알려줌

I/O는 커널을 통해서만 가능하다.

  • SYNC I/O

사용자 프로그램이 I/O 요청을 운영체제 커널에 하게 되면 그 I/O 장치의 디바이스 드라이버를 거치고, 실제 하드웨어를 통해 읽거나 쓰거나를 한다.

이 I/O 작업은 오래걸리는 작업이다. 그래서 시간이 흘러야지만 I/O끝난게 도착을 하고, 그걸 보고 사용자가 다음 작업을 한다.

  • ASYNC I/O

사용자 프로그램이 I/O 요청을 운영체제 커널에 해서 I/O가 진행되는데, 그걸 기다리지 않고 요청만 해놓고 바로 cpu 제어권을 얻어서 다른 작업들을 하는 것.

I/O가 끝났다고 알려주는 것은 인터럽트가 알려줌.

실제로 이 두개의 I/O가어떤 의미를 가지는가?

예를들어 내가 하나의 프로그램을 짰는데 디스크에서 뭘 읽어와야 한다. 보통 디스크에서 읽어온 다음에 그 결과를 보고 그 다음 작업을 하게 프로그램을 짠다.
그래서 read는 보통 I/O 작업을 하는 동안은 기다려야 하기 때문에 SYNC만 한다.

그치만 I/O 요청을 했지만 그 결과를 보지 않고 읽어온 데이터와 상관 없이 그냥 할 수 있는 작업들이 있을 것이다. 그래서 읽어오는 동안 이러한 작업을 하게끔 프로그램을 짤 수 있을 것이다.
I/O가 끝났다고 알려주면 그거 보고 위의 SYNC 작업을 하면 된다. 이런 식으로 read 지만 async 적으로 가능 하다.

write는 async가 자연스럽다. storage에 데이터를 쓰라고 하고 결과에 상관 없이 다른 작업을 하면 된다. 하지만 sync 적으로도 하고 싶을 때가 있다. 정말 storage에 잘 쓰였는지 확인 하고 프로그램을 실행할 때.
그럴 때는 sync write를 하는 것이다. 구현하기 나름.

어떤 프로그램이 I/O 요청을 하게 되면 그건 오래걸리는 작업인데, 그 오래걸리는 작업 동안 CPU를 가지고 있으면서 기다리면 CPU가 낭비가 될것이다. 그래서 보통 이 요청을 한 다음에 CPU를 다른 프로세스에게 CPU를 넘겨주게 된다.


DMA (Direct Memory Access)

원래는 메모리에 접근할 수 있는 장치는 cpu 밖에 없다. I/O 장치들이 CPU와 통신할 필요가 있을 때 인터럽트 걸고, CPU가 I/O 작업을 처리 하는데 , I/O 장치들은 워낙 다양하다.
키보드 입력 하나 하나를 인터럽트 걸어서 카피해온다면 CPU는 인터럽트를 너무 많이 당한다. 인터럽트를 당하면 사용자 프로그램이 사용하던걸 멈추고 운영체제가 CPU 가져가게 되는데
오버헤드가 너무 큰 작업이다.

그래서 DMA도 메모리를 직접 접근할 수 있게 만들어서 작은 일들(1byte의 키보드 등등.. )은 블럭정도의 데이터가 쌓이면 그게 끝났다고 CPU에게 인터럽트를 걸어 알려준다.
그럼 CPU가 인터럽트를 당하는 빈도가 적어지고 효율적으로 일할 수 있게 되는 것이다.

  • 빠른 입출력 장치를 메모리에 가까운 속도로 처리하기 위해 사용
  • CPU의 중재 없이 device controller가 device를 buffer storage의 내용을 메모리에 block 단위로 직접 전송
  • 바이트 단위가 아니라 block 단위로 인터럽트를 발생 시킴

서로 다른 입출력 명령어

  1. I/O를 수행하는 special instruction에 의해 → 일반적인 I/O 방식 (좌측 그림)

    CPU에서 실행할 수 있는 기계어에는 메모리에만 접근하는 instruction(load store)이 있고, I/O에 접근하는 instruction이 있다.
    메모리 주소가 있듯이 I/O디바이스도 주소가 있다.

  2. Memorry Mapped I/O에 의해
    I/O 디바이스 들에게 메모리 주소에 연장 주소를 붙인다. 그래서 메모리 접근하는 instruction을 통해서 I/O 디바이스에 접근할 수 있게 한다.


저장 장치의 계층 구조

맨 위에 CPU가 있고 CPU 안에 Register가 있다. Main Memory는 DRAM으로 구성되고, Secondary storage(하드디스크, 마그네틱 테잎 등)이 있다.
하드디스크 대신 플래쉬 메모리가 사용되기도 하고 구성이 달라지고 있다.

  • 특징
    1. 위로 갈수록 보통 속도가 빠른 매체를 사용하고, 대신에 단위 공간당 가격이 비싸기 때문에 위로 갈수록 용량이 적다.

      CPU가 instruction을 처리 할 때 빠르게는 1clock당 instuction 1개를 처리하게 되는데, DRAM 접근할 때는 10~100clock cycle까지 걸리니까 오래걸린다.
      그러한 속도 차이를 완충하기 위해서 cache메모리를 두고, 레지스터로 읽어들이고 하는 것이다.
      그러나 cache메모리는 메인 메모리보다 용량이 작기 때문에 모든 내용을 담지 못한다. 그래서 당장 필요한것만 밑에서 위로 올려서 쓰는데, 그런것이 cashing이다.
      빠른 매체로 정보를 읽어 들여서 쓰는 것. 보통 캐싱은 재사용을 목적으로 한다.

      • Cashing : coping information into faster storage system

      처음 불러들일땐 밑에서 위로 올려야 하는데, 같은걸 두번 요청할때 이미 위에 읽어온 데이터가 있으면 그것을 사용하는 것이다.

    2. 휘발성 매체냐 아니냐

      하드디스크나 테잎은 비휘발성 매체이다 → 전원이 꺼져도 내용이 사라지지 않는다. (핑크색)
      반면 DRAM이나 캐시메모리로 사용하는 SRAM이나 CPU안의 레지스터는 휘발성 매체이다. (연두색)
      요즘은 메인 메모리도 비휘발성으로 할 수 있는게 등장하기도 한다.

    3. CPU에서 직접 접근할수 있냐

      Primary → 직접 접근. byte 단위로 접근 가능해야 한다. DRAM의 경우 byte 단위로 메모리 주소를 지정하기 때문에 CPU접근이 가능하다.
      Secondary → 직접 접근해서 처리 못함. 하드디스크의 경우 byte 단위가 아니라 섹터단위이기 때문에 CPU가 직접 접근이 불가하다.



☑️프로그램의 실행(메모리 load)

프로그램이라는 것은 실행파일 형태로 하드디스크에 저장되어 있다. (파일시스템에 파일 형태로 저장되어 있다.)
그런 프로그램이 실행되게 되면 메모리에 올라가서 프로세스가 된다.
물리적인 메모리에 바로 올라가는게 아니라 중간에 Virtual Memory(가상 메모리)를 거치게 된다.

어떤 프로그램이 실행되면 그 프로그램의 address space(메모리 주소 공간)이 생기게 된다.
A라는 프로그램을 실행하게 되면 프로그램 A의 독자적인 주소공간이 0번지부터 만들어지게 되고, B라는 프로그램을 실행하게 되면 프로그램B의 독자적인 주소공간이 0번지부터 만들어지게 되는 것.

이런 주소 공간은 각 프로그램마다 만들어지는 것. code, data, stack 영역으로 구성된다.

  • code : 프로그램 기계어 코드 (CPU에서 실행 할)
  • data : 변수 등 자료구조
  • stack : 함수를 호출하거나 리턴할 때 데이터를 쌓았다 꺼내는 용도

이것을 물리적인 메모리에 올려서 실행시킨다. 커널은 부팅을 하고 나면 메모리에 항상 상주하여 올라가고 있지만,
Virtual Memory는 프로그램을 실행 하면 주소공간이 생겼다가 프로그램을 종료 시키면 사라진다.
이 주소 공간을 물리적인 메모리에 통째로 다 올려놓는 것이 아니라(그러면 메모리 낭비) 주소공간에서 당장 필요한 부분만 올려놓고 나머진 올리지 않는다.
올리지 않은 것들은 디스크의 swap area라는 곳에 내려놓게 된다.
또한, 물리적 메모리에서 나중에 사용되지 않으면 쫓아내게 된다. 보관해야되는 것이 있으면 냅두고

Virtual memory들은 실제로 어느 부분에 연속적으로 할당되는 것이 아니라, 쪼개져가지고 어떤것은 물리적 메모리에 있고, 어떤것은 swap 메모리에 있다.
보통 Virtual memory 기법을 메인 메모리의 연장 공간으로 하드디스크를 사용하는 스와핑으로 말하기도 하지만,
사실 Virtual memory는 각 프로그램마다 가지고 있는 독자적인 공간을 말한다.

그림을 보면 하드 디스크가 파일 시스템에도 그려져 있고, Swap area 쪽에도 그려져 있는데 이 두가지는 용도가 다르다.
파일 시스템에 있는 것은 전원이 나가더라도 파일은 내용이 유지되어야 하기 때문에 그 용도로 사용하고,
swap area 쪽은 전원이 나가면 의미가 없어진다. 프로세스가 종료가 되기 때문에. 메모리에 있는 내용도 사라지기 때문에 여기에 있는 내용도 사실 의미가 없는 정보가 된다.
swap area는 메모리 용량의 한계로 메모리 연장 공간으로써 사용하는 것이다. 용도가 다르기 때문에 관리하는 방법도 다르다. (나중에 disk storage 관리 부분에서 설명할 것이다.)

물리적인 메모리도 제일 아래부터 0번지로 위로 갈수록 주소가 증가한다. 가상 메모리에서 예를들어 1000번진데 물리적인 메모리에서는 3000번지로 주소 번호가 바뀌어야 한다.
그것을 Address translation(주소 변환)이라고 한다. 메모리 주소 변환을 해주는 계층이 있다. 그것은 운영체제가 할 수 있는 것은 아니고, 주소 변환해주는 하드웨어 장치가 필요하다.
(이 메모리 주소 변환 과정은 이후에 자세히 설명)


커널 주소 공간의 대응

운영체제 커널도 하나의 프로그램이기 때문에 code, data, stack 이런 식의 주소 공간으로 구성되어 있다.

  • code
    운영체제는 자원을 효율적으로 관리하는 일을 하고, 사용자에게 편리한 인터페이스를 제공한다. 이에 관련된 코드들이 들어있을 것이다.
    운영체제라는 것은 인터럽트가 들어오면 CPU를 얻게 되므로 인터럽트마다 무슨 일을 해야하는지 운영체제 커널에 함수 형태로 구현이 되어 있을 것이다.

  • data
    커널의 데이터 영역에는 운영체제에서 사용하는 자료구조들이 정의가 되어 있다. 운영체제는 하드웨어를 직접 관리하고 통제하므로 하드웨어 종류마다 자료구조를 하나씩 만들어서 관리를 할 것이다. 그림은 그것을 추상적으로 그려놓은 것
    운영체제는 프로세스들을 관리한다. (현재 실행중인 프로그램) 각 프로그램들이 독자적인 주소공간을 가지고 있지만 그것을 관리하기 위한 자료구조가 필요하다.
    그것을 PCB라고 부른다. (process control block) 그래서 프로세스마다 PCB가 하나씩 만들어져서 관리.

  • stack
    운영체제도 함수 구조로 코드가 짜여져 있기 때문에 코드를 호출하거나 리턴할 때 스택에 쌓이게 된다.
    운영체제의 코드는 여러 사용자 프로그램들이 요청에 따라서 쓸 수 있다. A라는 프로그램이 시스템 콜을 사용하여 운영체제 커널의 코드를 불러서 실행 할 수 있다.
    그래서 어떤 사용자 프로그램이 커널의 코드를 실행중인가에 따라서 커널 스택을 따로 둔다.
    즉, 사용자 프로그램(프로세스)마다 커널 스택을 따로 둔다.


사용자 프로그램이 사용하는 함수

함수(function)

  • 사용자 정의 함수
    • 자신의 프로그램에서 정의한 함수
  • 라이브러리 함수
    • 자신의 프로그램에서 정의하지 않고 갖다 쓴 함수
    • 자신의 프로그램의 실행 파일에 포함되어 있다.
  • 커널 함수
    • 운영체제 프로그램의 함수
    • 커널 함수의 호출 = 시스템 콜

👀사용자 정의 함수나 라이브러리 함수는 실행 파일 안에 포함되어 있다.
코드 자체도 프로그램 안에 포함되어 있는 것. 호출을 하더라도 이 내부에서 JUMP를 하며 실행이 된다.


👀커널 함수는 프로그램에서 시스템 콜을 통해 가져다 쓸 수 있는데,
이 함수는 커널 코드 안에 들어있는 함수이다.
그래서 내 프로그램 안에는 이 함수의 정의가 없이 호출만 하게 된다.

프로그램이 커널 함수를 호출하면 시스템 콜로 호출해야 하고, 점프가 불가능하다.
영역 자체가 다르기 때문에. (점프는 논리적 주소상에서의 점프이다.)

그래서 커널 함수를 호출할때는 시스템 콜로 인터럽트 라인을 셋팅 해서 CPU제어권이 넘어가게해서
커널 함수를 호출하는 것이다.


프로그램의 실행

A라는 프로그램이 시작 돼서 종료 될때까지의 모습이다. 이 그림은 타이머 인터럽트 등등.. 이 생략되고 프로그램 A가 CPU를 가지고 있을때만의 그림이다.

프로그램이 직접 CPU를 잡고 있으면 user mode에 있다고 한다. 이 프로그램이 user mode안에서 자신이 정의한 사용자 정의 함수를 사용할 수도 있을 것이다.
그러다 system call을 하게 되면 그 프로그램의 주소공간의 코드가 아니라 운영체제 커널 주소 공간에 있는 코드가 실행되게 된다. 그것이 kernel mode에서 CPU가 동작 되는 것이다.
kernel mode 가 끝나게 되면 다시 A라는 프로그램한테 CPU 제어권이 넘어오고, 본인의 주소공간에 있는 코드를 실행 할 것이다.

이런 식으로 프로그램이 실행되기 때문에, 프로그램은 죽을때까지 유저모드, 커널모드,유저모드, 커널모드,유저모드, 커널모드.... 를 반복하며 실행하다
언젠가는 프로그램이 종료가 되는 것이다.

profile
반가워요!

0개의 댓글