운영체제 강의 노트 - System Structure & Program Execution 2

조해빈·2023년 5월 10일
0

OS

목록 보기
2/9

LECTURE is here

KOCW 온라인에서 제공되는 이화여대 반효경 교수님의 OS 강의에 대한 정리 요약

  • 노션에 기록했듯 CSAPP과 함께 천천히 병행
  • 연관 게시글은 강의 진행 순서대로 정렬되어 있지 않고, 내 필요에 따라 강의의 주제를 선택해 듣는다.
  • 온라인 상의 타인들이 올려놓은 연관 자료 역시 함께 참고한다.

I/O에 대한 동기식 입출력, 비동기식 입출력

동기식 입출력(snychronous I/O), 비동기식 입출력(asynchronus I/O)는 어디에서 언급되는지에 따라 의미가 조금씩 다르다고 한다. 뒤에 배울 프로세스에 나올 synchronus와 지금의 synchronous도 조금 다르다고 하셨다.

우선 그냥 보통 생각하는 동기식, 비동기식을 생각하면 될 것 같다. 간단히 말해서 동기식은 I/O를 기다리고, 비동기식은 기다리지 않고 작업하는 것이다.

동기식(snychronous)

입출력에서 동기식은 I/O 요청 후 입출력 작업이 완료된 후에야 사용자 프로그램으로 제어가 넘어가는 것을 말한다.

구현 방법 1

I/O 끝날때까지 CPU가 낭비된다.
그 뿐만 아니라 매 시점 하나의 I/O만 일어날 수 있다.

구현 방법 2

I/O 완료될 때까지 해당 프로그램에게서 CPU를 빼앗고, I/O 처리를 기다리는 줄에 그 프로그램을 줄 세운다.
다른 프로그램에게 CPU를 준다.

비동기식(snychronous)

입출력에서 비동기식은 반대로 I/O 작업이 끝나기를 기다리지 않고, 사용자 프로그램에 제어가 즉시 넘어간다. I/O가 진행이 되든 말든 해당 프로세스는 I/O와 무관한 작업을 수행한다.

I/O 장치 여럿이서 동시에 실행될 수 있다. (요청을 던져놓고, 또 다른것 실행하다가 요청을 또 던지고…)
I/O 기다리지 않고 다른 작업을 하다가 인터럽트로 알려준다.

보통 write는 비동기식이 자연스러우나, 값을 제대로 확인 하고 싶으면 동기식으로 사용할 수도 있고, 상황에 따라 다르다.

인터럽트, DMA

인터럽트

원래 메모리는 CPU만 접근할 수 있으나, 워낙 I/O 장치도 다양하고 많으며, 작은 일마다 빈번히 인터럽트가 들어오기에 CPU의 효율성이 떨어진다.
그렇기 때문에 DMA를 사용하며, local buffer에 데이터가 어느정도 쌓이면 전달한다.

DMA(Direct Memory Access)

빠른 입출력 장치(빈번하게 I/O발생)를 메모리에 가까운 속도로 처리하기 위해 사용한다. CPU의 중재 없이 buffer storage의 내용을 메모리에 block 단위로 직접 전송한다.

서로 다른 입출력 명령어

일반적인 메모리 구조

메모리와 각 장치들의 주소가 별개이다. 로드/스토어와 같이 메모리에 접근하는 instruction, 별개의 I/O 장치에 접근해야하는 special instruction 으로 나누어진다. 위와 같이 일반적인 메모리 구조에서는 메모리 접근하는 부분과 I/O장치에 접근하는 부분의 인스트럭션이 별개로 나누어져있다.

Memory Mapped I/O

  • I/O장치에 메모리 주소를 매겨서 사용하는 방법
  • Memory Mapped I/O에 의해 통제된다.

Memory Mapped I/O의 경우 I/O장치들에 메모리주소를 매겨서 메모리 접근하는 인스트럭션을 통해 접근 가능하다. 메모리 주소에 연장 주소를 붙이는 것이다. 예시로 메모리 주소영역에 값을 더하면 I/O장치의 주소가 나올 수 있다.

결론적으로 입출력 명령어는 일반적인 메모리 구조에서는 I/O를 수행하는 special instruction에 의해, 
Memory Mapped I/O는 Memory Mapped I/O에 의해 통제된다.

저장장치 계층 구조

Primary(Executable) vs Secondary

맨위에 사실상 CPU가 있다고 생각하고 그림을 살펴보자.

Primary(Executable) (연두색)

보통 휘발성
요즘은 새로운 것들이 생겨나서, 메인 메모리도 비휘발성인 경우들이 있다.(2014년 강의임)
CPU에 직접 접근(byte 단위 접근 가능해야 가능)이 가능하다(Execuatable 하다). 예시로 DRAM(메인메모리)은 바이트 단위 접근이 되지만, 하드디스크는 안된다.

Secondary (분홍색)

비휘발성
CPU에 직접 접근 불가

캐시메모리

cpu와 메모리는 속도 차이가 상당히 많이난다. (대략적으로) cpu가 1클락 당 1개의 명령어를 처리한다면, 메모리는 10~100클락의 싸이클을 가진다. 이렇게 큰 속도 차이를 완충해주는 것이 캐시메모리이다. 캐시메모리는 용량이 적지만 빠르다. 메인 메모리보다 용량이 적고 많은 것을 담지는 못하지만, 당장 필요한 것만 메모리에 올려서 쓴다. 이전 데이터를 담아두었다가 필요할때 빠르게 재사용하는 것이다.

다시 말해 이전 데이터를 복사한 임시 저장소인 것이다. 필요할 때 빠르게 꺼내쓰므로 cpu와 메모리의 속도 차이를 완화해준다. 용량이 적기 때문에 모든 것을 담아 둘 수는 없으나, 빈번히 사용되는 필요한 정보를 선별적으로 저장하여 재사용하는 캐싱 기법을 통해 시스템의 성능을 높일 수 있다.

이곳은 많은 것을 담지 못하므로, 메모리처럼 꽉 차면 또 무언가를 쫓아내야 한다. 그런 것을 관리하는 것이 caching을 할 때 중요한 사안이다.

CPU에서 직접 접근할 수 있는 매체를 말하며, Executable (실행 가능하다)라고 부른다. 즉, 해당 저장소는 바이트 단위로 CPU 접근이 가능하므로 Primary라고 부르며 전원이 꺼지면 데이터가 사라지는 휘발성 특징을 갖는다.

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

프로그램은 File System에 실행 파일 형태로 저장되어 있고, 이를 실행하면 메모리에 올라가 프로세스가 된다. 정확히 말하면, 물리적인 메모리에 프로그램이 바로 올라가는 것이 아니라 가상 메모리 단계를 추가로 거친다. 이때 독자적인 메모리 주소 공간이 형성되는데, 이 공간에는 Code, Data, Stack 영역이 있다.

📌 code

cpu를 실행할 기계어 코드를 담고 있다. 시스템콜, 인터럽트 처리 코드, 자원 관리를 위한 코드, 편리한 서비스 제공을 위한 코드 등의 기계어 코드를 컴파일하여 기계어로 만들고 디스어셈블을 하여 어셈블리어로 바꾸어 code 영역에 넣는다.

📌 data

전역변수, static 변수의 할당의 위해 존재하는 공간이다. 하드웨어/프로세스를 관리하기 위한 자료구조이다.
(디스크, PCB…)

📌 stack

지역변수가 저장되는 공간이다. 함수가 호출될 때 호출된 함수의 수행을 마치고 복귀할 주소 및 데이터를 임시로 저장한다.

그 다음 가상 메모리에서 물리적인 메모리로 프로그램이 올라가는데, 메모리 낭비를 방지하기 위해 프로그램 중 당장 실행에 필요한 부분만 올라가고, 그렇지 않은 부분은 디스크 중 메모리의 연장 공간으로 사용되는 스왑 영역에 내려 놓는다. 즉, 주소 공간을 쪼개서 어떤 부분은 메모리에 있고, 어떤 부분은 스왑 영역에 있게 된다.

어떤 사용자 프로그램이 커널의 코드를 실행중인가에 따라서, 사용자 프로그램마다 커널 스택을 따로 둔다.

📌 heap

위 도식화된 그림에는 없지만, 힙은 프로그래머의 동적 할당을 위해 존재하는 공간이다.

📌 disk

위 그림에서 스왑 영역과 File System 두 디스크가 존재하는데, 스왑 영역은 메모리의 연장 공간으로서 휘발성 특징을 갖고 있고, File System은 비휘발성 용도로 사용한다.

커널 주소 공간의 내용

📌 code

CPU, 메모리 등의 효율적으로 자원을 관리하기 위한 부분과 사용자에게 편리한 인터페이스를 제공하기 위한 부분이 주를 이루고 있다. 이 밖에도 커널의 코드는 시스템 콜 및 인터럽트를 처리하기 위한 부분을 포함한다.

📌 data

각종 자원을 관리하기 위한 자료 구조가 저장된다. CPU나 메모리와 같은 하드웨어 자원을 관리하기 위한 자료 구조뿐 아니라 프로세스를 관리하기 위한 자료 구조도 커널의 데이터 영역에 유지된다. 커널의 데이터 영역 내에는 각 프로세스의 상태, CPU 사용 정보, 메모리 사용 정보 등을 유지하기 위한 PCB를 두고 있다.

📌 stack

프로그램의 스택 영역과 마찬가지로 함수 호출 시의 복귀 주소를 저장하기 위한 용도로 사용된다. 하지만 커널의 스택은 일반 사용자 프로그램의 스택과 달리 현재 수행 중인 프로세스마다 별도의 스택을 두어 관리한다.

이는 일반 사용자 프로그램이 자기 주소 영역 내부의 함수를 호출하면 자신의 스택에 복귀 주소를 저장하지만, 프로세스가 특권 명령을 수행하려고 커널에 정의된 시스템 콜을 호출하고 시스템 콜 내부에서 다른 함수를 호출하는 경우에는 복귀 주소가 커널 내의 주소가 되므로 커널의 스택 영역에 저장되어야 하기 때문이다.

즉, 프로그램이 자기 자신의 코드 내에서 함수 호출 및 복귀 주소를 유지하기 위해서는 자기 주소 공간 내의 스택을 사용하고, 시스템 콜이나 인터럽트 등으로 운영 체제의 코드가 실행되는 중에 함수 호출이 발생할 경우 커널 스택을 사용한다.

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

사용자 정의 함수

자신의 프로그램에서 정의한 함수

라이브러리 함수

자신의 프로그램에서 정의하지 않고 갖다 쓴 함수
자신의 프로그램의 실행 파일에 포함되어 있다

커널 함수

운영체제 프로그램의 함수
커널 함수의 호출 = 시스템 콜

사용자 정의 함수나 라이브러리 함수는 프로그램 안에 들어있지만, 커널 함수는 그렇지 않다. 논리적 메모리 주소에서 사실상 점프를 하는데, 커널은 주소를 바로 점플 불가능해서 시스템콜을 해야 한다.

프로그램의 실행

✅ 결국에는 유저모드 - 커널모드를 반복하며 실행된다. 중간에 사용자정의 함수, 라이브러리 함수가 호출되면서 시스템콜을 하면 운영체제로 전환된다.

위 그림은 사용자 프로그램 입장에서 본 실행 및 종료 과정이다.

프로세스 A가 CPU에서 실행되고 있다고 하면, 이는 자신의 주소 공간에 정의된 코드를 실행하는 것과 커널의 시스템 콜 함수를 실행하는 것으로 나누어 볼 수 있다. 전자를 사용자 모드에서의 실행 상태라고 하고, 후자를 커널 모드에서의 실행 상태라고 한다.

이때 한 가지 주의할 점은 비록 시스템 콜을 통해 실행되는 것이 프로세스 A의 코드가 아닌 운영 체제 커널의 코드이지만, 시스템 콜이 수행되는 동안 "커널이 실행 상태에 있다"고 하지 않고 "프로세스 A가 커널 모드에서 실행 상태에 있다"고 표현하는 것이 맞다. 프로세스 A 입장에서는 CPU를 운영 체제 커널에 빼앗긴 것으로 생각할 수 있지만, 사실 프로세스 A가 해야 할 일을 시스템 콜을 통해 커널이 대행하고 있는 것이기 때문이다.

profile
UE5 공부하는 블로그로 거처를 옮겼습니다!

0개의 댓글