프로그램의 실행
1. 프로세서가 메모리부터 명령어를 가져오는 것 (fetch)
2. 어떤 명령어인지 확인(decode)
3. 실행함(ex. 숫자 더하기, 메모리 접근, 상태 확인, 함수(jump to function) 등등) (execute)
4. 다음 명령어로 프로세서가 이동
Fetch -> Decode -> Execute => File
--> 해당 작업이 너무 오래 걸리므로 병렬화함
ex)
_
F| D | E |
| F | D | E
| | F | D E
ㅡ 」
-- 은 펄스
OS의 역할
1. 프로그램 실행을 쉽게함
2. 프로그램이 메모리 공유를 할 수 있게함
3. 프로그램이 디바이스(하드웨어)와 상호작용할 수 없게 함
=> 시스템이 정확하고 효율적으로 작동하는지 확인함
가상화
- OS는 물리적인 자원(프로세서, 메모리, 디스크 등등) 을 가상의 형태로 변환함
- 가상의 형태는 보다 더 general, powerful하고 더 쉽게 사용 가능
=> OS를 가상 머신이라고 부를수도있음
System Call
: 사용자가 OS에게 수행할 작업을 지시함
=> 하드웨어를 접근하는 유일한 방법은 system call을 os와 주고받는 방법밖에 없다
- OS는 API, standard library와 같은 인터페이스를 제공함
OS는 다음과 같은 경우 (수백개의) system call을 함
- 프로그램 실행
- 메모리 접근
- 디바이스 접근
다음과 같이 system call이 이루어짐
APP -> OS <- DEVICE
화살표는 system call
OS의 리소스 관리
: OS는 CPU, memory, disk와 같은 자원을 관리함
- 다수의 프로그램을 동시에 실행 -> Sharing CPU
- 다수의 프로그램이 동시에 자신의 명령어와 데이터에 접근 -> Sharing memory
- 다수의 프로그램이 동시에 장치에 접근할때 -> Sharing disk
CPU 가상화 (Virtualizing the CPU)
- 시스템은 굉장히 많은 수의 가상화된 CPU를 가지고 있음
- 한개의 CPU(single core)를 마치 무한한 수의 CPU가 있는것처럼 만듦
- 여러 프로그램이 동시에 실행되는것처럼 만듦
#include <stdio.h>
#include <stdlib.h>
#include <sys/time.h>
#include <assert.h>
#include "common.h"
int
main(int argc, char *argv[]) // argc 는 프로그램을 실행할 때 지정해 준 명령행 옵션의 개수가 저장되는 곳, argv는 프로그램을 실행할 때 지정해 준 명령행 옵션의 문자열들이 실제로 저장되는 배열(vector)
{
if (argc != 2) {
fprintf(stderr, "usage: cpu <string>\n");
exit(1);
}
char *str = argv[1];
while (1) {
Spin(1); // Repeatedly checks the time and returns once it has run for a second
printf("%s\n", str);
}
return 0;
}
argv의 첫번째 값(argv[0])은 실행 경로이다. 따라서 argc가 1인 경우는 실행경로만 포함되고 코드에서 아무 정보도 전달하지 않았다는 뜻
메모리 가상화 (Virtualizing Memory)
- 물리적인 메모리는 byte들의 배열이다
- 프로그램은 모든 데이터 구조들을 메모리에 저장함
=>
- read memory (load) : 데이터에 접근할 수 있는 주소 지정
- write memory (store) : 지정된 주소에 쓸 데이터 지정
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include "common.h"
int
main(int argc, char *argv[])
{
// malloc : 동적 할당
int *p = malloc(sizeof(int)); // a1: allocate some memory
// assert : fault
assert(p != NULL);
printf("(%d) address of p: %08x\n",
getpid(), (unsigned) p); // a2: print out the address of the memory
*p = 0; // a3: put zero into the first slot of the memory
while (1) {
Spin(1);
*p = *p + 1;
printf("(%d) p: %d\n", getpid(), *p); // a4
}
return 0;
}
- 각 프로세스는 각각의 가상 주소 공간에 접근한다
=> OS는 주소 공간을 물리적 메모리에 매핑. 실행 중인 프로그램 내의 메모리 참조는 다른 프로세스의 주소 공간에 영향을 주지 않음. 물리적 메모리는 OS에 의해 관리되는 공유 리소스이다
동시성 문제(The problem of Concurrency)
- OS는 동시에 많은 일들을 한번에 수행함. 먼저 한 프로세스를 실행하고 다른 프로세스를 실행
=> 현대의 멀티쓰레드 프로그램들도 동시성 문제를 맞이함
Concurrency Example
#include <stdio.h>
#include <stdlib.h>
#include "common.h"
volatile int counter = 0;
int loops;
void *worker(void *arg) {
int i;
for (i = 0; i < loops; i++) {
counter++;
}
return NULL;
}
int
main(int argc, char *argv[])
{
if (argc != 2) {
fprintf(stderr, "usage: threads <value>\n");
exit(1);
}
loops = atoi(argv[1]);
pthread_t p1, p2;
printf("Initial value : %d\n", counter);
Pthread_create(&p1, NULL, worker, NULL);
Pthread_create(&p2, NULL, worker, NULL);
Pthread_join(p1, NULL);
Pthread_join(p2, NULL);
printf("Final value : %d\n", counter);
return 0;
}
=> 100000번 루프를 돌았을때 기댓값은 200000이다. 하지만 실행할때마다 값이 달라지는것을 볼 수 있음
코드 진행 과정은 1. 메모리로부터 counter값을 받아서 레지스터에 저장함(load) 2. 증가 3. 메모리 store
=> 이 과정들은 개별적으로 실행되는게 아님. 따라서 동시성의 문제(Problem of concurrency) 가 발생함.
Persistence
- DRAM과 같은 디바이스들은 volatile로 값을 저장함
=> volatile이란?
- volatile 변수를 참조할 경우 레지스터에 로드된 값을 사용하지 않고 매번 메모리를 참조한다.
- 그만큼 실행 시간이 오래 걸리기에 딜레이를 만들어줄 수 있다. 또한, 실제 메모리 값을 계속 참조하므로 값이 계속 변경된다.
- 하드웨어와 소프트웨어는 데이터를 지속적으로 저장해야만한다.
하드웨어 : I/O 장치(하드디스크, SSD)
소프트웨어 : 파일 시스템은 디스크를 관리함, 파일 시스템은 사용자가 만든 모든 파일을 저장해야함
#include <stdio.h>
#include <unistd.h>
#include <assert.h>
#include <fcntl.h>
#include <sys/types.h>
int
main(int argc, char *argv[])
{
int fd = open("/tmp/file", O_WRONLY | O_CREAT | O_TRUNC, S_IRWXU);
assert(fd > -1);
int rc = write(fd, "hello world\n", 13);
assert(rc == 13);
close(fd);
return 0;
}
=> 정상적인 fd 값 4 이상, 다 차면 -1, unsigned int(양수), int(최상위 비트값 -1)
OS와 Disk
- OS는 디스크 어디에 새로운 데이터가 저장될지 파악함
- 저장 장치에 I/O 요청을 함
OS와 파일 시스템
- 파일 시스템은 write 작업 중에 발생한 시스템 충돌을 처리함
- 저널링, copy-on-write(말 그대로 리소스 수정시, 이전 리소스의 복사본 값으로 처리한다는 뜻)
OS의 목표
추상화
- 시스템을 편리하고 쉽게 사용할 수 있도록 합니다.
high performance 제공
- OS의 오버헤드를 최소화
- OS는 과도한 오버헤드를 발생시키지 않개 가상화를 해야함
실행 프로그램 보호
- Isolation : 문제가 다른 프로그램과 OS에 영향이 가지 않아야하
=> 각각의 프로그램은 OS가 뭘 해줬는지 알수없어야함. 프로그램 입장에서는 실행될때 OS로 인해서 무언가가 영향이 받지않음.
높은 신뢰성을 가져야 함
- OS가 실행 중 멈추면 안 됨
그외에는 에너지효율, 보안 등등
OS의 역사 정확히는 컴퓨터와 OS의 역사
1G(1945~1955)
- 진공관(vacuum tube)과 플러그판(plugboard)들로 이루어짐
- OS 없음
- 프로그래밍 언어 없음
- 어셈블리어조차도 없음
EX) ENIAC(1946)
2G(1955~1965)
- 트랜지스터(transistor)를 가진 대형 컴퓨터(mainframes)
=> 트랜지스터 : 펄스값을 증폭시키거나 감소시켜주는 것
- batch system
=> 한번 실행시 오직 하나의 작업(프로세스?)만 가능
EX) 카드 리더기, 테이프 드라이브, 라인 프린터
- OS는 메모리에 있으며 라이브러리 마냥 그저 transfers a control함
- 입출력의 병목 현상으로 인해 CPU의 사용률이 낮음
- OS는 최상위 레이어에 위치
3G (1965~1980)
- Time-sharing system
- 응답 시간(response time)을 개선함
- 3G OS의 형태
=>
- 정교한 CPU 스케줄링
- 가상 메모리 및 스와핑
- 파일 시스템
- 동기화(Synchronization)
- 프로세스간 통신
- 상호작용하는 쉘
EX) MIT CTSS (1961), Multics (1965), Unix (1969)
4G (1980~)
- 구조적인 개선이 일어남
=> 마이크로프로세서(작고 더 빨라짐), 스토리지(더 크고 더 빨라짐), PC, CPU작업은 I/O 디바이스로 오프로드(다른 기기로 전달하여 처리한 후 결과를 반환)
- 현대의 OS 기능
=> GUI, Multimedia, Internet&Web, Mobile, Networked, Distributed, Virtualization