운영체제 개요
1. CPU 가상화
2. 메모리 가상화
3. 병행성
4. 영속성
프로그램이 실행될 때 어떤 일이 일어날까?
프로세서는 다음 명령어로, 또 그 다음 명령어로 프로그램이 완전히 종료될 때까지 실행을 계속한다
- 명령어를 실행(execute)한다.
- 프로세서는 명령어를 초당 수백만 번 (요즘은 수십억 번) 반입(fetch)
- 명령어를 해석(decode)하고 (즉, 무슨 명령어인지 파악하고),
- 명령어를 실행(execute)한다(즉, 두 수를 더하고, 메모리에 접근하고, 조건을 검사하고, 함수로 분기하는 등의 정해진 일을 한다).
운영체제(operating system, OS)
시스템을 사용하기 편리하면서 정확하고 올바르게 동작시킬 책임
- CPU, 메모리, 디스크와 같은 물리 자원을 가상화(virtualize)한다.
- 운영체제는 병행성과 관련된 복잡한 문제를 처리한다.
- 파일을 영속적으로 저장하여 아주 오랜 기간, 안전한 상태에 있게 한다.
운영체제 목표
1. 성능 == 오버헤드를 최소화(minimize the overhead)
“가상화”와 “사용하기 쉬운 시스템을 만드는것”은 의미가 있지만 반드시 해야 하는 것은 아니다.
가상화 및 다른 운영체제 기능을 과도한 오버헤드 없이 제공해야 한다.
오버헤드
시간 (더 많은 명령어) 과 공간 (메모리 또는 디스크)의 형태로 나타난다.
시간이나 공간 혹은 둘 다 최소로 하는 해결책을 찾을 것이다.
2. 고립(isolation) == 응용 프로그램 간의 보호, 운영체제와 응용 프로그램 간의 보호
보호는 운영체제의 원칙 중 하나인 고립(isolation) 원칙의 핵심이다.
프로세스를 다른 프로세스로부터 고립시키는 일은 보호의 핵심
운영체제가 해야 하는 일 중 많은 부분의 근간이 된다.
다수 프로그램들이 동시에 실행되기 때문에
운영체제는 한 프로그램의 악의 적인 또는 의도치 않은 행위가 다른 프로그램에게 피해를 주지 않는다는 것을 보장해야 한다.
당연히 응용 프로그램이 운영체제에게 악영향을 주면 안된다.(운영체제에게 해를 가하면 모든 프로그램에게 영향을 주기 때문이다).
3. 신뢰성
운영체제는 계속 실행되어야 한다. 운영체제가 실패하면 그 위에서 실행되는 모든 응용 프로그램도 실패하게 된다
- 프로그램을 쉽게 실행하고 (심지어 동시에 여러 발의 프로그램을 실행시킬 수도 있음)
- 프로그램 간의 메모리 공유를 가능케 하고
- 장치와 상호작용을 가능
- 흥미로운 일을 할 수 있게 하는 소프트웨어
운영체제는 앞에서 언급한 일을 하기 위하여 가상화(virtualization)라고 불리는 기법을 사용한다
프로세서, 메모리, 또는 디스크와 같은 물리적 (physical) 인 자원을 이용
일반적이고, 박력하고, 사용이 편리한 가상(virtual) 형태의 자원을 생성한다.
운영체제는 사용자에게 API를 제공 == 시스템 콜을 제공
응용 프로그램이 사용 가능한, 시스템 콜을 제공
- 사용자 프로그램의 프로그램 실행
- 메모리 할당, 접근
- 파일 접근
- 장치 접근
1. CPU 가상화
동시에 많은 수의 프로그램을 실행시키는 것
- 많은 프로그램들이 CPU를 공유
- 프로그램들이 동시에 실행될 수 있게 함
- 프로그램들이 각자 명령어와 데이터를 접근할 수 있게 함
- 프로그램들이 디스크 등의 장치를 공유할 수 있게 함
자원을 어떻게 가상화시키는가?
가상화 효과를 얻기 위하여 운영체제가 구현하는 기법과 정책은 무엇인가?
운영체제는 이들을 어떻게 효율적으로 구현하는가?
어떤 하드웨어 지원이 필요한가?
하드웨어의 도움을 받아
운영체제가
시스템에 매우 많은 수의 가상 CPU가 존재하는 듯한
환상(illusion)을 만들어 냄
운영체제 == 자원 관리자(resource manager)
효율적으로, 공정하게, 이들 자원을 관리하는 것이 운영체제의 역할
CPU, 메모리, 디스크
자원 관리자로서의 운영체제의 역할
다수의 프로그램을 동시에 실행시킬 수 있는 기능
새로운 종류의 문제를 발생
특정 순간에 두 개의 프로그램이 실행되기를 원한다면,
누가 실행되어야 하는가 ?-> 이 질문은 운영체제의 정책 (policy)에 달려있다.
2. 메모리 가상화
물리 메모리(physical memory)
바이트의 배열
메모리는 프로그램이 실행되는 동안 항상 접근된다.
프로그램은 실행 중에 자신의 모든 자료 구조를 메모리에 유지
명령어 역시 메모리에 존재
명령어를 반입할 때마다 메모리가 접근된다
메모리를 읽기 -> 데이터에 주소(address)를 명시
메모리에 쓰기 -> 주소와 데이터를 명시
메모리를 할당 받는다
할당 받은 메모리의 주소를 출력, 새로 할당된 메모리의 주소는 00200000
새로 할당 받은 메모리의 첫 슬롯에 숫자 0 을 넣는다
루프로 진입하여 1초 대기 후, 변수 p가 가리키는 주소에 저장되어 있는 값을 1 증가 시킴
출력할 때마다 실행 중인 프로그램의 프로세스 식별자 (PID)라 불리는 값이 함께 출력
프로그램들은 같은 주소에 메모리를 할당 받지만 (00200000),
각각이 독립적으로 00200000 번지의 값을 갱신한다.
각 프로그램은 물리 메모리를 다른 프로그램과 공유하는 것이 아니라
각자 자신의 메모리를 가지고 있는 것처럼 보인다.
운영체제가 메모리 가상화(virtualizing memory)를 하기 때문에 이런 현상이 생긴다.
각 프로세스는 자신만의 가상 주소 공간(virtual address space), 때로 그냥 주소 공간(address space)이라고 불림)
운영체제는 이 가상 주소 공간을 컴퓨터의 물리 메모리로 매핑 (mapping)한다.
하나의 프로그램이 수행하는 각종 메모리 연산은, 다른 프로그램의 주소 공간에 영향을 주지 않는다.
실행 중인 프로그램의 입장에서는, 자기 자신만의 물리 메모리를 가진다.
실제로는물리 메모리는 공유 자원이고, 운영체제에 의해 관리된다.
3. 병행성(concurrency)
프로그램이 한 번에 많은 일을 하려 할 때 -> 병행성 문제 발생 -> 운영체제 자체에서 발생
멀티 쓰레드 프로그램 -> 병행성 문제 발생
counter 변수의 최종 값은 얼마가 될까?
각 쓰레드 100,000 -> 두 개니까... 예상 200,000
메인 프로그램은 Pthread_create()를 사용하여 두 개의 쓰레드를 생성.
쓰레드를 동일한 메모리 공간에서 함께 실행 중인 여러 개의 함수.
각 쓰레드는 worker()라는 루틴을 실행
worker() 루틴은 loops번 만큼 루프를 반복하면서 카운터 값을 증가시킨다.
loops 값은 각 쓰레드가 카운터를 증가시키는 횟수
뭐가 문제지? --> 병행성(concurrency) 문제 -> 원자성
예상하지 못한 결과의 원인은 명령어가 한 번에 하나씩만 실행된다는 것과 관련 있다.
앞 프로그램의 핵심 부분인 counter를 증가시키는 부분은 세 개의 명령어로 이루어진다.
- counter 값을 메모리에서 레지스터로 탑재 명령어
- 레지스터를 1 증가시킴 명령어
- 레지스터의 값을 다시 메모리에 저장하는 명령어
3개의 명령어가 원자적(atomically)으로 (한 번에 3개가 모두) 실행되지 않기 때문에
이상한 일이 발생할 수 있다.
4. 영속성(persistence)
- 데이터를 영속적으로 저장하는 방법은 무엇인가?
- 파일 시스템은 데이터를 영속적으로 관리하는 운영체제의 일부분이다.
- 올바르게 일하기 위해서는 어떤 기법이 필요할까?
- 작업 성능을 높이기 위해서 어떤 기법과 정책이 필요한가?
- 하드웨어와 소프트웨어가 실패하더라도 올바르게 동작하려면 어떻게 해야 하는가?
DRAM과 같은 장치는 데이터를 휘발성(volatile) 방식으로 저장하기 때문에 메모리의 데이터는 쉽게 손실될 수 있다.
데이터를 영속적으로 저장할 수 있는 하드웨어와 소프트웨어가 필요하다.
하드웨어는 입력/출력(input/output) 혹은 I/O 장치 형태로 제공
- 전원 공급이 끊어지거나
- 시스템이 갑자기 고장나면 (crash) 메모리의 모든 데이터는 사라진다.
파일 시스템(File system)
디스크를 관리하는 운영체제 소프트웨어
- 요청을 처리
- 사용자에게 에러 코드 반환
사용자가 생성한 파일(ile)을 시스템의 디스크에 저장
CPU나 메모리 가상화와는 달리 운영체제는 프로그램 별로 가상 디스크를 따로 생성하지 않는다.
- 에디터 (예, Emacs7 )를 사용하여 C 파일을 생성하고 편집한다 (emacs -nw main.c).
- 편집이 끝나면 소스 코드를 컴파일한다 (예, gcc -o main main.c).
- 컴파일이 완료되면 새로 생성된 실행 파일을 실행할 수 있다 (예, ./main).
- 파일이 여러 다른 프로세스 사이에서 공유된다.
- 우선, emacs라는 편집기가 컴파일러가 사용할 파일을 생성한다.
- 컴파일러는 입력 파일을 사용하여 새로운 실행 파일을 생성한다
- 실행 파일이 실행
운영체제를 세 번 == 시스템 콜 (system call)
- open() 콜은 파일을 생성하고 연다.
- write() 콜은 파일에 데이터를 쓴다.
- close() 콜은 단순히 파일을 닫는다. 프로그램이 더 이상 해당 파일을 사용하지않는다.
시스템 콜 (system call)은 파일 시스템(File system)로 전달된다
새 데이터가 디스크의 어디에 저장될지 결정
파일 시스템이 관리하는 다양한 자료 구조를 통하여 데이터의 상태를 추적
저장 장치로부터, 기존 자료 구조를 읽거나 갱신해야 한다.
당신을 대신하여 장치가 무언가 하게 하는 일은 복잡한 작업이다.
저수준의 장치 인터페이스와 그 시맨틱에 대한 깊은 이해가 필요하다.
'학습 기록 (Learning Logs) > Today I Learned' 카테고리의 다른 글
비관적락, 낙관적락 (0) | 2025.03.25 |
---|---|
레디스 pub/sub (0) | 2025.03.24 |
머신러닝 (0) | 2025.03.21 |
✨ Operating Systems: Three Easy Pieces ✨ - 프로세스 (0) | 2025.03.19 |
✨ Operating Systems: Three Easy Pieces ✨ - 가상화 (0) | 2025.03.19 |