728x90

우선 PintOS 는 2004년 스탠포드 대학에서 만들어진 교육용 운영체제라고 한다.

해당 과정은 KAIST 에서 진행하는 KAIST PintOS 과정으로 진행된다.

https://casys-kaist.github.io/pintos-kaist/

처음에는 너무 방대한 양의 이론들과 자료들이 넘쳐나서.. 뭐부터 공부해야할지 갈피가 안잡혔는데
우선 코치님들이 PintOS 바이블이라고 설명해주신 Gitbook -> KAIST 강의 -> 한양대 강의 자료를 보고 프로젝트 진행의 갈피를 잡을 수 있었다.

 


1. PintOS 에서의 GDB 사용법

  • -g 옵션을 사용해서 컴파일 해야한다.
    • 적절한 정보를 포함한 컴파일이 가능하다.

 

  • GDB 실행
    • gdb [file]

 

  • gdb로 인자 넘기기
    • (gdb) set args argument
  • continue(c) : breakpoint를 만날 때까지 프로그램 실행
  • step(s) : 함수 내부로 진입
  • next(n) : 함수 건너뜀
  • finish : 함수 종료 및 중지
  • return [value] : 함수 실행을 취소하고 [value] 로 돌아감
  • list : 현재 실행 중인 부분의 소스코드 출력
  • list [number] : [number] 번째 줄 출력
  • list [function] : [function]의 소스 코드 출력
  • set listsize [n] : list 명령어를 쳤을때 보여지는 라인 수 조정

 

  • whatis [var] : 변수 타입 출력
  • print(p) [var] : 변수 출력

 

  • 원하는 곳에 중단점 설정
    • break(b) [number]
    • break(b) [func]
    • break(b) [file:func]
    • break(b) [file:number]
    • info break
    • delete [number] : 이전에 설정해둔 중단점 제거

 

 

 

PintOS 디버그 방법

  • terminal 1
>>> pintos --gdb -- -q run alarm-multiple

 

(새로운 터미널을 연다.)

  • terminal 2
>>> gdb --tui kernel.o # gdb 진입 >>> target remote localhost:1234

 

 

2. 테스트 방법

>>> cd ./pintos-kaist/threads
>>> make

>>> pintos -- -q run alarm-single # pintos -- -q run [테스트 명]
>>> pintos -- -q run alarm-multiple
  • -q 옵션 : 프로그램이 호출된 이후 종료된다.
  • 빌드 파일이 꼬여서 안되는 경우가 많으니, make 전에 make clean 을 습관화하자! 🤣

 

 

3. QEMU

  • What is qemu?

Qemu 는 PC 환경을 위한 하나의 프로세스 애뮬레이터로, 프로세스 뿐만 아니라 각종 주변기기까지 애뮬레이터한다.

하나의 가상 컴퓨터를 구축해주는 소프트웨어로, 가상화 솔루션의 하나라고 보면 된다.

Guest 운영체제가 하드웨어 자원에 대한 접근을 요청할 때

  1. 해당 요청을 Qemu로 보낸다.
  2. 요청을 받은 Qemu는 해당 명령을 변환한다.
  3. 하드웨어로 해당 명령을 전달해서 요청을 처리한다.

 

 


Project 1. Introduction

🎯 Goal : 최소한으로 작동하는 스레드 시스템 구현, 동기화 문제 이해

  • 주로 threads 디렉토리에서 작업하며, devices 디렉토리를 작업하게 될 수도 있다.
  • threads 컴파일은 디렉토리에서 이루어져야한다.

 

 


1. 스레드 이해하기

  • 이미 구현되어 있는 사항
    • 스레드 생성
    • 스레드 완료
    • 스레드 전환을 위한 스케줄러
    • 동기화 프리미티브(semaphores, locks, condition variables, and optimization barreier)
  • thread_creat()
    • 새로운 컨텍스트를 생성한다.
    • 컨텍스트에서 실행할 함수를 thread_create() 의 인수로제공한다.
    • 주어진 시간에 하나의 스레드가 실행되고 나머지는 비활성화된다.
    • 함수 리턴 → 스레드 종료
  • 스케줄러는 다음에 실행할 스레드를 결정한다.
  • 만약 실행 준비된 스레드 존재 ❌ → idle() 스레드가 실행된다.
  • synchronization primitives는 하나의 스레드가 또다른 스레드의 작업 수행을 위해 대기해야할 때 컨텍스트 스위칭을 할 수 있다.
  • thread_launch() _ by. treads/tread.c (⭐️ 이해할 필요는 없음)
    • 컨텍스트 스위칭의 메커니즘이 구현되어있다.
    • 현재 실행중인 스레드의 상태를 저장하고 전환하려는 스레드의 상태를 복원한다.

📢 gdb 이용해서 컨텍스트 스위치를 통해 추적하라.

  • schedule() 에 중단점을 설정해서 어떤 과정이 발생되는 지를 파악하라.
  • 한 스레드가 iret 을 실행할 때(do_iret()) 또다른 스레드가 작동된다.
  • 핀토스에서 각 스레드는 4kb미만의 작은 고정 크기의 실행 스택이 할당된다.
    • 스택 할당의 대안으로 page allocator와 block allocator를 사용할 수 있다. (ref)

 

 


2. 소스 파일

  • ⭑ : 현재 프로젝트랑 관련있는 파일들
  • threads
threads
├── Make.vars
├── Makefile
├── init.c # 커널 초기화(main()을 포함한다.) ⭑
├── interrupt.c # 기본 인터럽트 처리
├── intr-stubs.S # 저수준 인터럽트 처리
├── kernel.lds.S # 커널 연결 링커 스크립트
├── loader.S # 커널 로더
├── malloc.c # malloc 구현
├── mmu.c # 페이지 테이블 작업(lab 1 이후)
├── palloc.c # page allocator
├── start.S
├── synch.c
├── targets.mk
└── thread.c # 기본 스레드 ⭑

 

  • devices
devices
├── disk.c
├── input.c # 입력 레이어
├── intq.c # 인터럽트 큐
├── kbd.c # 키보드 드라이버
├── serial.c
├── targets.mk 
├── timer.c # 초당 100회 작동하는 시스템 타이머 ⭑
└── vga.c # VGA 디스플레이 드라이버 - 텍스트 쓰기 담당(x)

 

  • lib
    • lib/user 의 경우 프로젝트 2에서 사용해야한다.(커널의 일부는 아니다.)
lib
├── arithmetic.c
├── debug.c
├── kernel
│   ├── bitmap.c # 비트맵 구현(프로젝트 1 이외, 원한다면 사용)
│   ├── console.c
│   ├── debug.c # 디버깅 지원
│   ├── hash.c # 해시 테이블 구현(프로젝트 3)
│   ├── list.c # 이중 연결 리스트 구현(주석 훑어보는 것 추천) ⭑ 
│   └── targets.mk
├── random.c # 난수 생성
├── stdio.c
├── stdlib.c
├── string.c
├── targets.mk
└── user
    ├── console.c
    ├── debug.c
    ├── entry.c
    ├── syscall.c
    └── user.lds

 

 


3. 동기화

  • 모든 동기화 문제는 인터럽트를 끄면 해결할 수 있다.
  • 인터럽트가 꺼져 있는 동안에는 동시성이 없으므로 경쟁 조건이 발생할 가능성이 없다.
  • But, 해당 방법으로 해결하지말고 세마포어, 락, 상태 변수를 이용해서 해결해라!

 

  • threads/synch.c 를 읽어라
  • synch.c 인터럽트를 비활성화해서 구현한다.
  • 커널 스레드와 인터럽트 처리기 공유되는 데이터를 조정한다.
  • 인터럽트 핸들러는 sleep 할 수 없기 때문에, lock을 할 수가 없다.
    → 인터럽트를 꺼서 커널 스레드와 인터럽트 핸들러 간에 공유되는 데이터를 보호한다.

 

  • 인터럽트를 끄면 인터럽트 핸들링 latency가 매우 증가할 수 있다.

 

  • synch.c 동기화 프리미티브는 인터럽트를 비활성화하면서 구현된다.

 

  • busy waiting 이 없어야한다. → thread_yield() 는 busy waiting 의 형태이다.

 


🙋 synchronization primitives?

Synchronization primitives는 플랫폼에 의해 제공되는 단순한 소프트웨어 메커니즘이다. 이것의 목적은 스레드나 프로세스의 동기화이다. 일반적으로 저수준의 메커니즘이다. (e.g. atomic operations, memory barriers, spinlocks, context switches etc).

ref. primitives

  • Our pintos project : semaphores, locks, condition variables, optimization barreier

 

 


 

지옥의 핀토스 주간.. 시작,,,ㅠㅠㅠㅠ 아자 👊🏻

끝의 끝까지 하면 기회는 반드시 온다.

 

728x90

'🌱 Dev Diary > 📄 TIL' 카테고리의 다른 글

Project 1. Priority Scheduling 구현 (1)  (1) 2022.12.26
Project 1. Alarm Clock 구현  (0) 2022.12.24
Team Session - File Descriptor(FD)  (1) 2022.12.11
Malloc Lab 구현  (0) 2022.12.08
Team Session - Data Segment  (0) 2022.12.08