What a happens when a program runs?
프로그램은 실행 중일 때 명령들을 실행한다.
1. Fetch : processor가 memory에서 instruction을 가져오면
2. Decode : 해당 명령이 어떤 명령인지 확인하는 decode를 한다.
3. Execute : 실행한다. (ex. 숫자 추가, memoery 접근, condition 확인,...)
4. next instruction : processor가 다음 명령으로 이동
Operating System (OS)
OS는
program 실행을 쉽게 하고
program에서 memory 공유를 허용하고
program이 devices들과 interact 하는 것을 허용한다.
OS is in charge of making sure the system operates correctly and efficiently.
OS는 system이 효율적으로 올바르게 작동하는지 확인하는 역할이다.
OS에 대해서 좀 더 알아보자. ( 사실상 요약정리.. )
OS는 컴퓨터 HW 바로 위에 설치되어 사용자 및 다른 SW와 HW를 연결하는 SW 계층
- 좁은 의미의 OS : 운영체제의 핵심 부분으로 memory에 상주하는 부분이다. 흔히 Kernel(커널)이라고 부른다.
- 넓은 의미의 OS : Kernel 뿐 아니라 각종 주변 시스템 utility를 포함하는 개념이다. 항상 memory에 올려져 있는 것이 아닌 필요할 때 올라가는 별도의 program이다.
- ** utility : 시스템 소프트웨어 중 컴퓨터를 관리하거나, 정상적으로 돌아가도록 유지하는 소프트웨어
OS에서 중요한 기능 중 하나는 Virtualization(가상화)이다.
Virtualization은 OS가 processor나 memory, Disk와 같은 물리적 자원을 잘 사용할 수 있도록 해준다.
OS를 Virtual machine이라고 부르기도 한다.
가상화로 여러 program이 동시에 실행되고, 동시에 공유 memory에 접근하거나 주변 devices들에 접근할 수 있다.
OS가 CPU, memory, Disk 등 자원들도 관리하기 때문에 resource manager라고 불리기도 한다.
OS는 여러 system call을 제공한다. system call은 운영체제 kernel이 제공하는 서비스에 대해, 응용 program의 요청에 따라 kernel에 접근하기 위한 interface이다.
Virtualization
OS가 processor, memory, disk와 같은 physical resource를 virtual form으로 변환한다.
virtual form은 more general, powerful, easy-to-use 하다는 장점이 있다.
그래서 OS를 virtual machine이라고 부르기도 한다.
System call
System call을 통해 user는 OS에 수행할 작업을 지시할 수 있다.
OS는 일부 interface를 제공한다. (APIs, standard library)
일반적인 OS는 program 실행, memory 접근, devices 접근과 같은 수백개의 system call을 전문으로 한다.
The OS is a resource manager.
OS는 CPU, memory, dick와 같은 resource를 관리하기 때문에 resource manager이다.
OS에서 허용하는 것은 다음과 같다.
CPU 공유를 통한 program 실행
memory 공유를 통한 program이 자신의 instruction과 data에 동시 접근
disk 공유를 통한 program의 device 접근
Virtualizing the CPU
system에는 많은 가상 CPU가 있다.
1개의 cpu를 무한한 cpu가 있는 것처럼 보이게 해 준다.
CPU 가상화를 통해 한 번에 여러 program을 실행할 수 있도록 해준다.
example(cpu.c) : Code That Loops and Prints
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | # include <stdio.h> # include <stdlib.h> # include <sys/time.h> # include <assert.h> # include "common.h" int main (int argc, char *argv[]) { 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; } | cs |
Spin(1)로 1초 대기 후 argv[1]을 화면에 반복해서 출력
Execution result 1.
Ctrl+c를 누르지 않는 이상 영원히 실행된다.
prompt> gcc -o cpu cpu.c -Wall
prompt> ./cpu "A"
A
A
A
^C
prompt>
Execution result 2.
하나의 processor에서 위의 예시 코드를 4번 동시에 실행시켰을 때, 4개의 program이 동시에 실행되는 것처럼 보인다.
prompt> ./cpu A & ; ./cpu B & ; ./cpu C & ; ./cpu D &
[1] 7353
[2] 7354
[3] 7355
[4] 7356
A
B
D
C
A
B
D
C
A
C
B
D
...
result 1과 result 2를 통해 실제로는 하나의 processor에서 동작하기 때문에 마치 processor가 무한히 있는 것처럼 느껴지는 착각을 느낄 수 있다. >>> CPU 가상화
Virtualizing Memory
physical memory는 byte배열이다.
program은 모든 data 구조를 memory에 저장한다.
read memory (load) : data에 access 할 수 있는 address 지정
write memory (store) : 지정된 주소에 쓸 data 지정
example. A program that Accesses Memory (mem.c)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | # include <unisted.h> # include <stdio.h> # include <stdlib.h> # include "common.h" int main (int argc, char *argv[]) { int *p = malloc(sizeof(int)); // a1 : allocate some memory 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; } | cs |
output 1.
prompt> ./mem
(2134) memory address of p: 00200000
(2134) p: 1
(2134) p: 2
(2134) p: 3
(2134) p: 4
(2134) p: 5
^C
The newly allocated memory is at address 00200000.
It updates the value and prints out the result.
output2. >>> running mem.c multiple times
prompt> ./mem &; ./mem &
[1] 24113
[2] 24114
(24113) memory address of p: 00200000
(24114) memory address of p: 00200000
(24113) p: 1
(24114) p: 1
(24114) p: 2
(24113) p: 2
(24113) p: 3
(24114) p: 3
...
서로 다른 2개의 process가 같은 주소값(0x00200000)을 출력하고 있다.
이것은 마치 각 process가 물리적 memory를 공유하지 않고, 고유한 memory를 가지고 있는 것처럼 보인다.
정리하자면...
이것은 OS가 memory를 가상화했기 때문이다.
각각의 process는 자체 개인 가상 address 공간에 access한다.
- OS가 address 공간을 physical memory에 매핑한다.
- 실행 중인 하나의 programm 안의 memory 참조는 다른 process의 address 공간에 영향을 주지 않는다.
- physical memory는 OS에서 관리하는 공유 resource이다.
The problem of Concurrency
OS는 처음에 하나의 process를 실행하고, 그다음에는 다른 process를 실행하면서 동시에 많은 것을 juggling 하고 있다.
현대의 multi-thread program도 동시성 문제를 보인다.
example. A Multi-threaded Program (thread.c)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | # 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; } | cs |
- The main program creates two threads.
- Thread : a function running within the same memory space. Each thread start running in a routine called worker().
- worker() : increments a counter
main에서 Pthread_create로 2개의 thread를 생성한다.
그럼 동시에 2개 이상의 thread가 한 memory 공간에서 활성화될 수 있다.
생성된 2개의 thread는 worker()에서 loop를 돌면서 counter값을 증가시킨다.
output 1. loops: 1000
prompt> gcc -o thread thread.c -Wall -pthread
prompt> ./thread 1000
Initial value : 0
Final value : 2000
output 2. loops: 100000
prompt> ./thread 100000
Initial value : 0
Final value : 143012 // ????
prompt> ./thread 100000
Initial value : 0
Final value : 137298 // ???
loops determines how many times each of the two workers will increment the shared counter in a loop.
output 2를 보면 예상한 값과는 다른 결과나 나온다. 또한 실행 시마다 결과가 달라진다.
>>> 세부적으로 실행된 명령어를 보자.
Why is this happening?
- Increment a shared counter >>> take three instructions.
- Load the value of the counter from memory into register.
- Increment it
- Store it back into memory
- These three instructions do not execute atomically. >>> Problem of concurrency happen.
총 3가지의 명령이 실행됐는데, 이러한 명령어들이 atomically 하게 실행되지 않아 concurrency 문제가 발생하였다.
** atomically : memory에 접근할 때, 모든 thread에서 순서가 지켜지는...
Persistence
Device는 system memory를 DRAM과 같은 저장소에 휘발성으로 저장한다.
data를 지속적으로 저장하기 위해서는 HW와 SW가 필요하다.
HW는 I/O 장치 형태로 제공 (ex. HDD, SSD), 이런 disk를 관리하는 OS의 SW를 file system이라 한다. file system이 사용자가 작성한 모든 file을 저장한다.
example. Create a file (/tmp/file) that contains the string "hello world"
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | # 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; } | cs |
open(), write() and close() system calls are routed to the part of OS called the file system, which handles the requests
OS는 disk에 쓰기 작업을 하기 위해 새로운 data가 상주할 위치를 파악한다. 그 후에 기본 storage device에 대한 I/O 요청을 실행한다.
file system은 쓰기 중 system 충돌을 처리한다. Journaling or copy-on-write, carefully ordering writes to disk와 같은 방법으로...
Design Goals
OS는 CPU, memory, disk와 같은 physical resource를 관리하고 가상화한다.
그리고 concurrency와 같은 문제를 해결하고, file을 persistency 하게 저장할 수 있도록 해준다.
이렇게 하기 위해서는
abstraction, overhead를 줄여 performance를 높이기, isolation을 통한 protection, 높은 reliability 등이 필요하다.
부산대학교 안성용 교수님의 강의를 복습하기 위한 글입니다.
복습용 글이기에 정확하지 않을 수 있다는 점 유의하시길 바랍니다.
'CS > 운영체제' 카테고리의 다른 글
Mechanism : Limited Direct Execution (0) | 2023.05.14 |
---|---|
Processes (0) | 2023.04.12 |