pintos operating system homework

11
2015326목요일 자율과제 Pintos 설치 분석 주제를 들어가기 앞서 안녕하세요. 컴퓨터공학과 3학년 이기찬입니다. 이번 운영체제 자율과제를 수행하면서 정말 많이 려웠고 여러 가지에 대해 많이 배웠습니다. 하지만 과제에서 원하는 내용을 전부 이해하지는 못하여서 제가 이해할 있고 있는 범위 내에서 과제를 수행했습니다. 운영체제 시스템프로그래밍기 초라는 과목에서 간단한 명령어를 수행하고 익혔던 수업에서 벗어나 새로운 운영체제를 설치하고 행해보는 데에만 거의 대부분의 시간이 소요되었습니다. 익숙치 못한 과정이기도 하고 스스로 리눅스 대해 공부하고자 했던 적이 없었기에 Oracle Virtual Box에만 우분투를 올리는 데에도 수많은 설치 과정을 겪어 자신에게 너무 실망하고 과제를 포기할까 고민도 많이 했습니다. 하지만 과정을 완벽히 실행하겠다는 욕심을 접고 최대한 따라가기만 하더라도 제가 배울 있는 것이 많겠구나 라는 생각이 들어 이렇게 열심히 쫓아가 보려합니다. 이미 익숙하게 리눅스를 다룰줄 아는 친구들 혹은 코딩을 아주 잘하는 친구들과는 다르게 초보적인 지식으로 어떻게든 담을 넘어보려 노력하였습니다. 1. 리눅스, 핀토스 설치 및 실행 리눅스를 설치한 Virtual Box입니다. virtual box 위에 ubuntu.iso얹어 설치 과정을 진행하고 해상도 문제를 해결하기 위해 VBoxGuestAdditions.isoCD/DVDinsert추가 설치 과정 마칩니다. 우분투를 정상 설치했음에도 CD/DVD인식할 없다거나 아직 insert하지않 1

Upload: gichan-lee

Post on 11-Apr-2017

727 views

Category:

Software


5 download

TRANSCRIPT

Page 1: PINTOS Operating system homework

2015년 3월 26일 목요일

자율과제 Pintos 설치 및 분석

주제를 들어가기 앞서

안녕하세요. 컴퓨터공학과 3학년 이기찬입니다. 이번 운영체제 자율과제를 수행하면서 정말 많이 어려웠고 여러 가지에 대해 많이 배웠습니다. 하지만 과제에서 원하는 내용을 전부 이해하지는 못하여서 제가 이해할 수 있고 할 수 있는 범위 내에서 과제를 수행했습니다. 운영체제 전 시스템프로그래밍기초라는 과목에서 간단한 명령어를 수행하고 익혔던 수업에서 벗어나 새로운 운영체제를 설치하고 실행해보는 데에만 거의 대부분의 시간이 소요되었습니다. 익숙치 못한 과정이기도 하고 스스로 리눅스에 대해 공부하고자 했던 적이 없었기에 Oracle Virtual Box에만 우분투를 올리는 데에도 수많은 재설치 과정을 겪어 제 자신에게 너무 실망하고 과제를 포기할까 고민도 많이 했습니다. 하지만 이 과정을 완벽히 실행하겠다는 욕심을 접고 최대한 따라가기만 하더라도 제가 배울 수 있는 것이 많겠구나 라는 생각이 들어 이렇게 열심히 쫓아가 보려합니다. 이미 익숙하게 리눅스를 다룰줄 아는 친구들 혹은 코딩을 아주 잘하는 친구들과는 다르게 초보적인 지식으로 어떻게든 담을 넘어보려 노력하였습니다.

1. 리눅스, 핀토스 설치 및 실행

리눅스를 설치한 Virtual Box입니다. virtual box 위에 ubuntu.iso를 얹어 설치 과정을 진행하고 그 후 해상도 문제를 해결하기 위해 VBoxGuestAdditions.iso를 CD/DVD에 insert해 추가 설치 과정을 마칩니다. 이 때 우분투를 정상 설치했음에도 CD/DVD를 인식할 수 없다거나 아직 insert하지않

�1

Page 2: PINTOS Operating system homework

2015년 3월 26일 목요일았음에도 CD/DVD 안에 이미 들어가 있어 강제로 꺼내기를 실행하여도 꺼내지지 않아 재설치 하는 등 쉽지 않았습니다.

에디션까지 설치를 완료하면 드디어 보이는 ubuntu의 모습설치과정 중 문제점과 해결 방법

terminal을 실행하여 Virtual Box와 맥 OS간의 공유폴더를 마운트한 후 그 곳에 bochs 에뮬레이터와 pintOS를 설치하였습니다. bochs 에뮬레이터까지는 설치가 순조롭게 진행되었지만 pintOS부터 애를 먹었습니다. make 명령어의 make[1]: warning: clock skew detected. your build may be incomplete. 오류로 인해 이 make 오류가 왜 발생했는지 찾아야 했고 ubuntu의 바이오스와 pintOS의 시간이 맞지않을 것이다 등 온갖 추측을 세워가며 그 문제를 해결하기 위해 발버둥쳐야했습니다.

�2

Page 3: PINTOS Operating system homework

2015년 3월 26일 목요일http://ingorae.tistory.com/358의 글을 참조해가며 결국 문제를 해결했더니 다른 문제에 봉착했습니다. pintos 설치 후 ~/.bashrc를 수정했음에도 PPT의 설치과정의 설명대로 pintos가 실행되지 않아 pintos : command not found를 출력하는 것이었습니다. PPT의 단편적인 설명만으로는 이 오류가 왜 출력되고 있고 뭘 잘못해서 이렇게 되었는지 전혀 알 수가 없었습니다. 여러 가지 시도를 한 끝에 bashrc의 상대경로 설정이 작동하지 않는다는 것을 알게되었고 그로 인해 절대경로를 입력하여 pintOS를 실행하게 되었습니다. 아직까지 어떻게 해결하는지 알지못해 절대경로로 실행중입니다. 맨땅에서 하나하나 시작하다보니 조금은 익숙해지면서도 이 과제를 무사히 다 할 수 있을지 벌써부터 걱정하기 시작했습니다.

구글링을 하게 한 수많은 오류들(build에서 시작하지 않아서 생기는 오류, bochs가 제대로 설치되지않아 재설치를 해야했던 오류(xp libraries를 받아서 재설치) 등)

최종bochs실행화면

�3

Page 4: PINTOS Operating system homework

2015년 3월 26일 목요일

2. 커널 소스 수정 및 pintos 디버깅 시작

시작하기에 앞서 gdb kernel.o를 실행하고 target명령을 통해 bochs에 출력을 시작합니다. 그 후에 alarm-wait.c의 코드를 수정하여 thread_print_stats();를 추가, 컴파일 후 gdb에서 c를 입력하면

이렇게 bochs emulator 상에서 alarm multiple의 실행을 확인할 수 있습니다.

�4

Page 5: PINTOS Operating system homework

2015년 3월 26일 목요일

3. Pintos 디버깅

Pintos의 구성요소, 기본 구조 및 동작 방식과 Pintos 부팅과정

Pintos는 가장 기본 기능인 threads를 중심으로 user program, virtual memory, 그리고 file system을 추가하는 구조를 갖는다. 그래서 소스 코드의 분포 역시 기본적인 것들은 threads 디렉토리에 다 있고, 그 밖의 것들은 각각의 디렉토리에 존재하면서 threads에 있는 소스 코드를 참조하는 식으로 되어있다. 그리고 Pintos를 구성하는 데는 필요하지만 과제 항목에는 포함되어있지 않은 기본 library나 device는 별도의 디렉토리에 존재한다. 즉, 기본적인 구조와 틀은 이미 threads의 init.c에 모두 존재하고 과제를 수행하면서 기능을 더 추가하거나 수정하는 구조로 되어있어 커널의 기본 틀을 고칠 일은 없는 구조로 구성되어있다. (http://lacti.me/2008/04/01/pintos-internal-structure/) 조사를 통해 screenshot의 init.c의 메인함수를 분석해야 기본구조와 동작방식을 확인할 수 있음을 알게 되었고 GDB를 통한 분석을 위해 GDB 사용법을 익히기로 하였습니다.

GDB 사용법

- target remote localhost:1234 : gdb 프롬프트에서 gdb와 bochs를 접속시키는 명령

�5

Page 6: PINTOS Operating system homework

2015년 3월 26일 목요일- c : Bochs가 지정된 파일(alarm-multiple.c)의 수행을 계속 진행한다.(continue)

- l : 옵션에 따라 실행중인 프로그램의 소스를 다양한 방법으로 볼 수 있다.(list)( list 10, list [function], list [file]:[function] 등)

�6

Page 7: PINTOS Operating system homework

2015년 3월 26일 목요일list 명령을 통해 alarm-multiple을 확인해본 결과 수많은 init함수를 통해 쓰레드, 메모리, 페이지 테이블, 세그먼트, 인터럽트 등 수많은 OS의 기초정보들을 초기화하고 마지막으로 printf ("Boot complete.\n”)을 출력하는 것을 확인함으로써, 내부의 각 함수에 대해 잘 알지 못하지만 kernel.o가 부팅 과정이고 그 위의 인자로 run과 alarm-multiple이 주어짐을 확인할 수 있었다.

- b : breakpoints를 설정해 프로그램을 어디까지 실행할지 지정하는 break명령

- r : run 명령어는 GDB가 프로그램을 실행시켜 이상이 발생했을때의 파일과 지점을 출력해준다.하지만 run 명령은 remote target을 지원하지 않으므로 alarm-multiple을 실행해볼 수는 없다.

- p : 디버깅 도중 내부 존재하는 어떠한 값을 출력할 때 사용하는 명령은 print명령

- s와 n : continue의 경우 breakpoint를 건 그 지점까지 곧바로 실행하는 것을 확인했다. 이와는 반대로 내부에서 한 행씩 차례차례 실행하기를 원할 경우 사용하는 것이 step과 next이다. 이 경우 그 함수 내부로 진입(*step into*)하여 디버깅을 진행할 것인지, 아니면 그냥 지나쳐(*step over*) 갈 것인지 선택할 수 있다. 전자의 진입(step into)의 명령은 step이고, 후자의 진행(step over)의 명령은 next이다.

- finish : 함수 내부에서 끝나는 지점으로 이동하기 위해서는 사용하는 finish 명령어

- bt : 스택의 개요를 보여준다. backtrace명령어는 현재 활성화된 서브루틴을 위해 스택 프레임을 출력한다.

- mem : 메모리를 초기화하고 속성을 정의하는 명령어, 메모리기반타겟팅을 한다.

�7

Page 8: PINTOS Operating system homework

2015년 3월 26일 목요일다른 과목의 과제를 하며 틈틈이 공부를 해 프로젝트를 시도했지만 분석 과정중 나오는 수많은 init들과 flow를 익히기 위해서 다시 시스템프로그래밍기초를 공부하느라 시간이 많이 모자랐습니다. 정보를 검색하던 중 서울대 팀프로젝트 파일이 저희의 과제와 유사하다는 것을 알았고 그를 통해 공부하면서 알아갔으며, kernel.o 부팅과정과 run alarm-multiple을 인자로 주어 프로그램을 실행하는 전체 시퀀스를 이해하는데 도움이 되었습니다. 제가 분석을 하더라도 제출된 과제를 분석한 것이 될 것 같아 참조하여 공부했던 분석을 보고서에 첨부하려 합니다.

출처 : file:///Users/gichan/Documents/2011os_prj1_%EC%9A%B0%EB%A6%AC%20%ED%95%80%ED%86%A0%EC%8A%A4%EB%8A%94%20%ED%8C%90%EB%A7%A4%EC%9A%A9%20(1).docx

Analysis1. Pintos boot sequence

Pintos에서 가장 먼저 실행되는 요소는 Loader (threads/loader.S)이다. PC BIOS가 loader를 메모리로 올린다. loader는 CPU를 초기화하고 Pintos의 나머지 부분을 메모리로 올리며 시작 부분으로 jump하는 역할을 수행한다.loader가 CPU를 초기화할 때 가장 중요시해야 하는 것은 A20 line을 켜는 것이다. 예전에는 주소를 20bit로만 표현했었는데 점점 32~64bit로 발전하면서 호환성에 문제가 생겨났다. 그것을 해결하기 위해 21번째 bit를 켜고 끌 수 있는 기능이 생겨났다. A20 line을 켜지 않으면 메모리 주소 21번째 bit는 0으로 고정된 것처럼 오작동한다. 다시 말해서 0x100000은 0x0로 인식되기 때문에 홀수 단위의 MB 주소를 사용할 수 없게 된다. 그러므로 반드시 A20 line을 켜주어야 한다.다음으로 BIOS에 PC의 메모리 크기 정보를 요청한다. BIOS는 최대 64MB의 메모리를 인식할 수 있으므로 Pintos도 64MB만큼만 지원할 수 있다. 숨겨진 메모리들은 부팅이 완료된 후 읽을 수 있다.다음으로 loader는 basic page table을 생성한다. page table은 64MB의 가상메모리를 각각의 실제 물리적인 주소로 매핑시킨다. 그리고 메모리의 시작 부분을 LOADER_PHYS_BASE로 매핑한다. page table이 만들어진 후에 protected mode를 활성화시키기 위해 control register와 segment register를 load한다.마지막으로 disk로부터 kernel을 불러온다. 이 과정을 위해 IDE 디스크 컨트롤러를 직접적으로 프로그래밍한다. 이를 위해서는 kernel은 첫번째 IDE 디스크의 두번째 섹터부터 저장되어 있으며, BIOS가 IDE 디스크 컨트롤러를 설치해놨다는 가정이 필요하다. 디스크로부터 KERNEL_LOAD_PAGES 페이지를 가상메모리로 읽어온다. 그 후 컴파일된 kernel 이미지의 시작 부분으로 jump한다.그 후 threads/kernel.lds.S의 linker script를 이용하여 kernel이 assembly

module(threads/start.S)로 시작되도록 정렬한다. 마지막으로 assembly module이 main()을 호출한다.main()이 실행된 후에는 ram_init()을 통해 RAM을 초기화한다. ram_init()는 kernel의

BSS segment들을 초기화하고 머신의 메모리 크기를 읽어와 ram_pages 변수에 저장한다. 그 다음 read_command_line()을 통해 kernel command line을 각각의 arguments로 쪼갠 후 parse_option()을 통해 option들을 읽어들인다.그 다음 thread_init()은 thread system을 초기화한다. 그 후 console을 초기화하고

startup 메시지를 출력한다. 다음으로 아래 함수들이 차례로 호출된다.palloc_init() : kernel의 page allocator를 설정한다.malloc_init() : 임의 크기의 메모리 블록 할당을 다루는 allocator를 초기화한다.paging_init() : page table을 초기화한다.intr_init() : CPU의 IDT(interrupt descriptor table)을 초기화한다.timer_init() & kbd_init() : timer와 keyboard interrupt handling을 준비한다.

�8

Page 9: PINTOS Operating system homework

2015년 3월 26일 목요일input_init() : serial input과 keyboard input을 하나의 stream으로 병합하기 위한 초

기화 수행thread_start() : scheduler를 실행하여 idle thread를 생성한 후 interrupt를 활성화한다.timer_calibrate() : 정확한 short delays로 timer를 조정한다.

위의 과정이 모두 완료되면 부팅은 성공한 것이다. 그 후 run_actions()가 호출되어 command line에 입력된 action을 수행한다.

2. Pintos architecture and behavior▶ ThreadsPintos 스레드 구조는 threads/thread.h 파일 안의 struct thread에 정의되어 있다. 모든 thread 구조체는 메모리 내에서 각자의 페이지(4KB)를 차지하고 있다. 앞부분에는 struct thread의 멤버가 저장되고, 남은 부분은 kernel stack이 차지하는데 뒤쪽에서부터 쌓인다. kernel stack을 위한 메모리 공간도 보장되어야 하므로, struct thread의 크기를 1KB 미만으로 잡는 것이 좋다. 또한 kernel stack을 무한정 확장시킬 수 없으므로 비정적 지역변수로서 큰 자료구조나 배열을 선언하는 것은 좋지 않다. 동적 메모리 할당을 이용하는 것이 바람직하다.

�- tid_t tid : Thread Identifier(tid)라 불린다. 각각의 thread는 고유한 tid를 갖게 된다. 1부터 차례대로 정수 값을 갖게 된다.

- enum thread_status status : thread의 상태를 나타내는 열거형 값들이다.

�- char name[16] : thread의 이름

�9

Page 10: PINTOS Operating system homework

2015년 3월 26일 목요일- uint8_t *stack : 평소에는 CPU의 stack pointer register가 thread의 stack-top을 가리키고 있지만, CPU가 다른 thread를 처리 중일 때는 이 변수가 stack-top을 기억하고 있는다. 다른 register들의 값은 stack에 저장된다.

- int priority : thread의 우선순위. 0~63까지의 값을 가지며 숫자가 클수록 우선순위가 높다. 최초의 Pintos는 thread의 우선순위를 무시하지만 별도로 우선순위 스케줄링을 구현할 수 있다.

- struct list_elem elem : list element라 불린다. thread를 doubly linked list로 올릴 때 사용한다. doubly linked list는 ready_list(실행을 기다리는 thread의 리스트) 혹은 semaphore를 기다리는 thread의 리스트가 될 수 있다. struct list_elem allelem은 모든 threads list의 list element이다.

- unsigned magic : 스택 오버플로우를 검사하는데 쓰인다. 항상 THREAD_MAGIC 값으로 설정되어 있는데, 스택 오버플로우가 발생하면 이 값이 바뀐다. thread_current()가 이 값을 검사하여 스택 오버플로우를 감지한다.

▶ Memory AllocationPintos는 page 단위와 block 단위의 memory allocator를 제공한다. Page Allocator는 threads/palloc.h에 정의되어 있으며 page 단위로 메모리를 할당한다. page allocator는 메모리를 kernel pool과 user pool로 분리한다. 기본적으로 각 pool은 시스템 메모리의 절반씩을 차지하지만 경우에 따라서 조절할 수 있다. 기본적으로 user pool은 user process를 위해 사용되고 kernel pool은 그 이외의 목적으로 사용되어야 하나, 아직까지는 kernel pool만 사용하도록 설계되어있다. 각 pool은 bitmap을 사용해 메모리를 할당한다. 하나의 page는 하나의 bit를 갖고 그 값이 false이면 아직 할당되지 않은 page라는 뜻이다. page가 할당되면 해당 bit는 true로 바뀐다.(first-fit allocation strategy) Page Allocator는 페이지를 분할하여 할당한다. 예를 들어 n개의 page 할당을 요청 받았을 때, 연속적인 n개의 페이지를 할당하는 것이 아니라 여기저기 나눠져 있는 n개의 페이지를 할당하는 것이다. 사용 중인 페이지들이 중간에 섞여 있기 때문에 n개의 연속적인 페이지를 할당하는 것은 불가능하다.

Block Allocator는 threads/malloc.h에 정의되어 있으며 블록 단위로 메모리를 할당한다. Block Allocator는 kernel pool로부터 메모리 블록을 할당한다. 메모리를 할당할 때 두 가지 방법을 사용하는데 첫번째는 1KB 이하의 작은 블록(page 크기의 1/4)에 적용된다. 작은 블록들을 여러 개 할당한 후 하나의 page로 모아 사용한다. 두번째 방법은 1KB 이상의 큰 블록을 할당하는데 사용된다.

�10

Page 11: PINTOS Operating system homework

2015년 3월 26일 목요일

▶ Virtual Address32bit virtual address는 20bit의 page number와 12bit의 page offset으로 나눠진다.

�Pintos의 virtual memory는 user virtual memory와 kernel virtual memory로 나뉜다. 둘의 경계는 PHYS_BASE이고 이는 kernel virtual memory의 base address이기도 하다. 기본적으로 0xc0000000(3GB)를 가진다. user virtual memory의 범위는 0번 주소에서 PHYS_BASE까지이다. kernel virtual memory는 PHYS_BASE에서 시작하여 나머지 메모리 영역을 모두 차지한다. 32bit로 주소를 표현하므로 최대 4GB까지 지원한다. x86 아키텍처는 물리 메모리(physical memory)로의 직접적인 접근을 허락하지 않는다. 그러므로 Pintos는 kernel virtual memory를 물리 메모리와 1:1 매핑시킨다. 즉, virtual address PHYS_BASE는 물리 메모리 0번과 매핑된다.

▶ Interrupt Handling☞ Internal interrupt : CPU instruction으로부터 직접적으로 일어나는 interrupt이다. system call이나 divied-by-0 오류 등이 internal interrupt를 발생시킨다. CPU instruction으로부터 발생하므로 synchronous하다.

☞ External interrupt : CPU 외부의 하드웨어 장비로부터 발생하는 interrupt이다. 그러므로 asynchronous하다.

Pintos는 위 두 interrupt를 다루기 위한 공통된 구조를 사용한다. interrupt가 발생하면 CPU는 중요한 상태 정보를 스택에 저장하고 interrupt handler routine으로 jump한다. x86 아키텍처는 256개의 interrupt를 지원하며 각각의 handler는 IDT(interrupt descriptor table)로 정의되어 있다.Pintos에서는 threads/interrupt.c 파일의 intr_init()가 IDT를 초기화한다. IDT는 각 interrupt로의 entry point를 갖고 있다. CPU는 interrupt number를 외부로 공개하지 않기 때문에 entry point는 interrupt number를 스택에 저장해 놓는다. 그 후 intr_entry()는 아직 스택에 저장되지 않는 register 들을 저장한 후 intr_handler()를 호출한다. intr_handler()는 각 interrupt에 맞는 적절한 함수를 호출한다. intr_handler()가 리턴된 후에 threads/intr-stubs.S의 어셈블리 코드는 이전에 저장해 두었던 CPU register의 값들을 복원하고 interrupt handling을 마친다.

�11