Ch1.7 The Operating System Manages the Hardware

Park Choong Ho·2021년 8월 13일
1

1.7 The Operating System Manages the Hardware

hello 프로그램 예제로 다시 돌아 오겠습니다. 쉘이 로드하고 hello 프로그램을 돌릴때 그리고 프로그램 메시지를 출력할 때, 프로그램은 키보드, 디스플레이, 디스크, 메인메모리 등에 직접적으로 접근하지 않습니다. 오히려, 이러한 장치들은 operating system이 제공하는 서비스위에 존재합니다. 운영체제는 아래 그림과 같이 프로그램과 하드웨어 중간에 존재하는 층으로 생각하시면 됩니다. 프로그램이 하드웨어에 접근하려면 반드시 운영체제를 거쳐야 합니다.

Figure1.10

운영체제는 크게 2가지 목적을 가지고 있습니다. 하나는 프로그램이 하드웨어를 오용하지 않도록 보호하는 목적입니다. 다른 하나는 복잡하고 다른 저레벨 하드웨어 장치를 다루는데 있어 간단하고 통일된 매커니즘을 프로그램에 제공하기 위함입니다. 운영체제는 이러한 목적을 추상화를 통해 달성하는데 아래 그림에 보이는 process, virtual memory, files가 바로 그 추상화들입니다. 그림에서 볼 수 있듯이, 파일은 I/O device의 추상화이며 가상 메모리는 main memoryI/O device의 추상화이며 프로세스는 processor, main memory 그리고 I/O device의 추상화입니다. 이를 하나씩 살펴보도록 하겠습니다.

Figure1.11

1.7.1 Processes

hello 프로그램이 현대 시스템에서 돌아갈때, 운영체제는 그 프로그램만이 시스템에서 돌아가는 유일한 프로그램인 것처럼 느끼게 합니다. 프르그램이 마치 프로세서, 메인메모리 그리고 입출력장치에 배타적인 사용 권한을 가진것처럼 보입니다. 프로세서는 프로그램 인스트럭션을 한줄 한줄 실행하는데, 아무런 간섭이 없는 것처럼 보입니다. 그리고 프로그램의 코드와 데이터는 시스템 메모리상의 유일한 객체인 것 같습니다. 이러한 착시들은 프로세스라는 개념에 의해서 제공됩니다. 프로세스는 컴퓨터 과학에 있어 가장 중요하고 성공적인 개념중 하나입니다.

프로세스running program을 운영체제가 추상화한 것입니다. 여러개의 프로세스들은 시스템상에서 concurrently하게 동작하며 각각의 프로세스들은 하드웨어에 대한 배타적인 사용을 하는 것처럼 보입니다. Concurrently하다는 의미는 프로세스의 인스트럭션들이 진행되는 동안 다른 프로세스의 인터럭션들이 중간에 끼어든다는 의미입니다. 대부분 시스템에서, 대개 프로세스를 돌리는 CPU보다 더 많은 프로세스들이 있습니다. 새로운 멀티 코어 프로세서들이 여러개의 프로그램을 동시에 돌릴 수 있는 반면에, 전통적인 시스템들은 한번에 하나의 프로그램만 동작시킬 수 있었습니다. 각각의 경우, 하나의 CPU가 프로세스를 바꿔가면서, 여러개의 프로세스들을 concurently하게 실행할 수 있습니다. 운영체제가 이러한 사이사이의 교환을 context switching이라는 매커니즘을 통해 수행합니다. 이야기를 단순화하기 위해, 우리는 하나의 CPU만 가진 uniprocessor를 기반으로 얘기해 가겠습니다. 1.9.2에서 multiprocessor관련된 얘기로 돌아오도록 하겠습니다.

운영체제는 프로세스가 실행되는데 필요한 상태 정보들을 계속 체크하고 있습니다. 이러한 상태를 context라 하는데 context에는 PC, 레지스터 파일, 메인메모리 안에 내용등의 여러가지 정보들이 포함됩니다. 어떤 시간이든 간에, uniprocessor 시스템은 오직 하나의 프로세스에 대한 코드만 실행할 수 있습니다. 운영체제가 제어권을 현재 프로세스에서 다른 새로운 프로세스로 넘겨주는 것을 정할 때, 운영체제는 현재 프로세스의 context를 저장하고 새로운 프로세스의 context를 불러온 다음 제어권을 새로운 프로세스에게 넘겨 줌으로써 context switching을 수행합니다. 새로운 프로세스는 정확히 이전에 중단된 지점부터 다시 시작합니다. 아래 그림이 예시인 hellocontext switching을 보여줍니다.

Figure1.12

2개의 concurrent한 프로세스들이 있습니다. 하나는 쉘 프로세스고 다른 하나는 hello 프로세스입니다. 처음에, shell 프로세스는 입력값을 기다리면서 혼자 돌고 있습니다. hello 프로그램을 실행시키라는 명령을 내리면, 쉘은 운영체제의 제어권을 넘기는 system call이라 알려진 특별한 함수를 호출함으로써 명령을 수행합니다. 운영 체제는 쉘의 context를 저장하고 hello 프로세스와 해당 프로세스의 context를 생성합니다. 그 다음 hello 프로세스에 제어권을 넘겨줍니다. hello 프로그램이 종료되고 나면, 운영체제는 쉘 프로세스의 context를 다시 불러오고 쉘에 제어권을 다시 넘겨줍니다. 그러면 쉘은 다시 명령어를 입력받기를 기다립니다.

위 그림에서 확인할 수 있듯이, 한 프로세스에서 다른 프로세스로 넘어가는 것은 kernel에 의해서 관리됩니다. 커널은 메모리에 항상 상주하고 있는 운영체제 코드의 일부분입니다. 프로그램이 파일을 읽거나 쓰는 등의 운영체제에 의한 동작을 요청할 때, 프로그램은 특별한 system call 인스트럭션을 실행함으로써 커널에 제어권을 넘겨줍니다. 커널은 요청받은 동작을 수행하고 다시 프로그램에 반환합니다. 커널이 별도의 프로세스가 아님을 명심하시기 바랍니다. 대신, 커널은 시스템이 프로세스들을 관리하는데 사용하는 코드와 데이터 구조의 집합입니다.

프로세스 추상화를 구현하는 것은 저 레벨 하드웨어와 운영체제 소프트웨어 모두의 긴밀한 협력이 필요합니다. 이것이 어떻게 동작하고 어떻게 프로그램이 만들어지고 프로세스들을 어떻게 관리하는지는 8장에서 살펴보도록 하겠습니다.

1.7.2 Threads

보통은 프로세스를 하나의 제어흐름을 갖는다고 생각하는데, 현대 시스템에서 프로세스는 스레드라 불리는 여러 실행흐름으로 구성되어 있습니다. 각 스레드는 자신이 속한 프로세스의 context에서 실행되며, 같은 코드와 글로벌 변수를 공유합니다. 스레드의 중요성은 날이 갈수록 커지고 있습니다. 네트워크의 concurrency 부합, 멀티 프로세스보다 멀티 스레드가 데이터를 공유하기 쉽고, 그리고 스레드가 전형적으로 프로세스보다 효율적입니다. 멀티스레딩은 또한 여러개의 프로세스가 존재할 때 프로그램을 빠르게 동작하게하는 한기지 방법이 됩니다. 1.9.2에서 다시 살펴보도록 하겠습니다. 스레드를 활용하는 프로그램을 어떻게 작성하는지를 포함해서 concurrency에 대한 기초적인 개념을 12장에서 배울 예정입니다.

1.7.3 Virtual Memory

Virtual Memory는 각각의 프로세스가 메인메모리에 독점적인 사용을 가지고 있는 것과 같은 착각을 일으키게하는 추상화입니다. 각각의 프로세스는 동일한 형태의 메모리를 가지고 있는데 이를 virtual address space라 합니다. 리눅스 프로세스의 가상 주소 공간은 아래 그림과 같이 이루어져 있습니다.

Figure1.13

리눅스에서, 가상 주소 공간은 가장 위쪽은 모든 프로세스가 가지고 있는 운영체제의 코드와 데이터가 들어가 있습니다. 더 아래 공간은 유저 프로세스에 의해 정의된 코드와 데이터가 들어 있습니다. 위 그림상 주소가 아래에서 위로 점점 증가한다는 사실을 확인하시기 바랍니다.

각 프로세스에서 확인되는 가상주소 공간은 특정한 목적을 가진 여러개의 공간으로 이루어져 있습니다. 자세한 사항은 나중에 살펴보겠지만 간단하게 전체를 개괄해보도록 하겠습니다. 시작은 가장 아래 주소부터 시작합니다.

  • Program code and data: Code는 모든 프로세스에서 고정된 같은 주소에서부터 시작합니다. 그 다음을 데이터 영역이 따라가고 있습니다.데이터에는 globa C 변수값이 담겨있습니다. 코드와 데이터 영역은 executable object 파일의 내용을 바탕으로 초기화됩니다. 7장에서 링킹과 로딩을 배울 때 이 부분을 더 자세히 살펴보겠습니다.
  • Heap: 코드와 데이터 영역 다음에는 런타임 heap영역이 자리합니다. 코드와 데이터 영역과 다르게(이 둘은 프로세스가 시작하면서 그 크기가 고정됩니다.) 힙은 malloc이나 free같은 C 표준 라이브러리 콜 결과에 따라 런타임에서 동적으로 확장과 수축을 반복합니다. 우리는 9장에서 가상메모리를 어떻게 관리하는지를 배우면서 힙에 대해 더 자세히 살펴보겠습니다.
  • Shard libraries: 가상주소 공간 중간쯤에 shard libraries(c standard librarymath library등) 의 코드와 데이터를 담고 있는 영역이 있습니다. shard library 개념은 어렵지만 정말 강력한 힘을 가진 아이디어입니다. 7장에서 dynamic 링킹을 배우면서 어떻게 동작하는지 배우겠습니다.
  • Stack: 유저 가상 메모리 공간 가장 위쪽에는 컴파일러가 함수 콜을 수행할때 활용하는 user stack이 있습니다. 힙처럼, user stack은 런타임동안 확장과 수축을 반복합니다. 특히, 우리가 함수를 호출할 때마다, 스택이 증가하며 함수로부터 반환할 때마다 수축합니다. 컴파일러가 스택을 어떻게 사용하는지 3장에서 살펴보겠습니다.
  • Kernel virtual memory: 가장 주소 공간의 가장 위에는 커널이 있습니다. 프로그램은 이 영역의 내용들을 읽고 쓸 수 없게 되어 있습니다. 또한 커널 코드에 정의된 함수들을 직접적으로 호출할 수 없습니다. 대신에, 커널에게 이러한 동작들을 수행하게끔 합니다.

가상 메모리가 동작하기 위해서는 운영체제 소프트웨어와 하드웨어간의 정교한 상호작용이 요구됩니다. 여기에는 프로세서에 의해 생성된 모든 주소에 대한 하드웨어(프로세서)의 변환 작업도 포함됩니다. 가장 기본적인 개념은 프로세스 가상메모리의 내용을 디스크에 저장하고 메인메모리를 디스크의 캐시처럼 사용하는 것입니다. 9장은 이러한 동작이 어떻게 일어나는지 그리고 현대 시스템에서 이러한 동작이 왜 그렇게 중요한지를 살펴볼 것입니다.

1.7.4 Files

file은 바이트의 연속입니다. 정말 그 이상 그 이하도 아닙니다. 모든 입출력 장치(디스크, 키보드, 디스플레이, 네트워크)들은 파일로서 모델링되어 있습니다. 컴퓨터 시스템 상에서의 모든 input, output은 파일을 읽고 씀으로 인해 수행됩니다. 이러한 작업은 Unix I/O라 알려진 시스템 콜의 작은 집합을 활용합니다.

이 간단하고 멋진 파일 아이디어는 굉장히 강력합니다. 왜냐하면, 프로그램에게 시스템에 포함된 여러 입출력 장치에 대한 동일한 시각을 제공하기 때문입니다. 예를 들어, 디스크 파일에 있는 내용을 다루는 프로그래머들은 특정 디스크 기술을 몰라도 됩니다. 게다가, 같은 프로그램이 디스크 기술이 다른 시스템에서 동작할 수 있습니다. UNIX I/O는 10장에서 살펴보겠습니다.

profile
백엔드 개발자 디디라고합니다.

1개의 댓글

comment-user-thumbnail
2021년 8월 18일

교수님 글 잘봤습니다!

답글 달기