Main goal
- FIFO scheduling을 priority scheduling으로 수정할 것
- ready list를 thread priority로 정렬
- wait list 정렬 - synchronization primitives로
- 선점 구현
- 선점은 운영 체제가 현재 실행 중인 태스크(프로세스 또는 스레드)를 일시 중단하고, 더 높은 우선순위의 태스크에 CPU 사용권을 넘겨주는 과정입니다. 이는 시스템이 더 긴급하거나 중요한 작업을 빠르게 처리할 수 있도록 해줍니다.
- 선점 구현 방법
- 타이머 인터럽트 사용: 대부분의 현대 운영 체제는 타이머 인터럽트를 사용하여 정기적으로 현재 실행 중인 태스크의 상태를 확인합니다. 타이머 인터럽트가 발생하면, 운영 체제는 CPU를 점유하고 있는 태스크를 중단시킬 수 있는지 결정합니다.
- 우선순위 체크: 운영 체제는 실행 대기 중인 태스크들의 우선순위를 검토합니다. 현재 실행 중인 태스크보다 높은 우선순위를 가진 태스크가 있으면, 운영 체제는 현재 태스크를 중단하고 더 높은 우선순위의 태스크에 CPU를 할당합니다.
- 컨텍스트 스위치: 선점이 결정되면, 운영 체제는 컨텍스트 스위치를 수행합니다. 이는 현재 태스크의 상태(컨텍스트)를 저장하고, 새로 실행할 태스크의 컨텍스트를 복원하는 과정을 포함합니다.
- 자원 관리: 선점이 일어나면, 운영 체제는 필요한 자원(메모리, 입출력 장치 등)을 새로운 태스크에 재할당해야 할 수도 있습니다. 이는 자원의 효율적 관리와 빠른 접근을 보장하기 위해 필요합니다.
- 선점 지점preemption point : 쓰레드가 ready list에 포함될 때(타이머 인터럽트가 호출될 때마다가 아님)
- Files to modify
- threads/thread.*
- threads/synch.*
고려해야 할 세 가지
- ready list에서 run할 쓰레드를 고를 때, 가장 우선순위가 높은 것을 골라야 함
- 선점preemption
- 뉴 쓰레드를 ready list에 삽입할 때, running 쓰레드와 우선순위를 비교해야 함
- 새로 삽입된 쓰레드가 currently running 쓰레드보다 우선순위가 높다면 스케줄링 수행
- 락: 세마포어, 조건변수
- 락(또는 조건 변수)을 기다리는 쓰레드 집합에서 쓰레드를 선택할 때, 가장 높은 우선순위를 가진 스레드를 선택하라
우선순위 in pintos
- Priority ranges from PRI_MIN (=0) to PRI_MAX (=63).
- 숫자가 높을 수록 우선순위가 높음
- Default is PRI_DEFAULT (=31)
- 핀토스는 thread_create()에 의해 생성될 때 초기 우선순위를 설정한다
- Existing functions
- void thread_set_priority (int new_priority)
- 현재 쓰레드의 우선순위를 new_priority로 변경하라
- int thread_get_priority (void)
- 현재 쓰레드의 우선순위를 리턴하라
- void thread_set_priority (int new_priority)
Implementation of Priority Scheduling
tie_t thread_create(const char *name, int priority, thread_func *function, void *aux)
- 업데이트 포인트
- 우선순위 순서대로 준비 목록에 쓰레드를 삽입하라. (확장성이 없다는 점을 유의하라)
- 스레드가 준비 목록에 추가될 때, 새 스레드의 우선순위와 현재 스레드의 우선순위를 비교하라.
- 새 스레드의 우선순위가 더 높다면 schedule()을 호출하라(현재 스레드가 CPU를 양보함)
- pintos/threads/thread.c
tid_t thread_create (const char *name, int priority, thread_func *function, void *aux)
{
...
thread_unblock (t);
/*
compare the priorities of the currently running thread and the newly inserted one.
Yield the CPU if the newly arriving thread has higher priority
*/
return tid;
}
Others to modify
void thread_unblock(struct thread *t)
// when the thread is unblocked, it is inserted to ready_list in the priority order
void thread_yield(void)
// The current thread yields CPU and it is inserted to ready_list in priority order
void thread_set_priority(int new_priority)
// set priority of the current thread
// reorder the ready_list
Hint: thread_unblock
- When unblocking a thread, use list_insert_ordered instead of list_push_back
- pintos/threads/thread.c
void thread_unblock (struct thread *t)
{
...
// list_push_back (&ready_list, &t->elem); --> delete
list_insert_ordered(&ready_list, &t->elem, cmp_priority, NULL); // --> add
t->status = THREAD_READY;
intr_set_level (old_level);
}
우선순위는 무시하고 FIFO mechanism으로 작동하는 pintos
B D C 순으로 lock을 얻음 (request 먼저 한 순서대로)
우선순위 기반 락/언락 매커니즘
D C B 순으로 lock을 얻음 (priority 순서대로)
Semahpore in pintos
- sema_init()
- 세마포어 초기화
- sema_down()
- 세마포어 요청
- If it acquired the semaphore, value를 1만큼 감소
- sema_up()
- semaphore를 release하고 value를 1만큼 증가
- semahpore를 release한다는 게 뭔 말?
- "세마포어를 release(릴리스)한다"는 말은 멀티스레드 프로그래밍에서 세마포어의 값(리소스의 수를 나타냄)을 증가시켜서, 다른 스레드들이 해당 리소스를 사용할 수 있도록 만드는 작업을 의미합니다. 세마포어는 주로 리소스 접근을 제한하는 데 사용되며, 리소스를 안전하게 공유할 수 있도록 돕습니다.
스레드가 리소스 작업을 마치고 나서 세마포어를 릴리스하면, 세마포어의 카운트가 증가합니다. 이렇게 카운트가 증가함으로써, 대기 중인 다른 스레드들 중 하나가 리소스를 얻을 수 있는 기회를 가지게 되고, 그 스레드는 세마포어를 'acquire(획득)'하여 리소스에 접근하게 됩니다.
- "세마포어를 release(릴리스)한다"는 말은 멀티스레드 프로그래밍에서 세마포어의 값(리소스의 수를 나타냄)을 증가시켜서, 다른 스레드들이 해당 리소스를 사용할 수 있도록 만드는 작업을 의미합니다. 세마포어는 주로 리소스 접근을 제한하는 데 사용되며, 리소스를 안전하게 공유할 수 있도록 돕습니다.
조건변수 in pintos
- cond_init()
- CV data structure 초기화
- cond_wait()
- CV에 의한 시그널을 기다림
- cond_signal()
- 우선순위가 가장 높은 쓰레드에게 시그널을 보냄, CV에서 기다리는 쓰레드 중에
- cond_broadcast()
- 모든 쓰레드에게 시그널을 보냄, CV에서 기다리는 쓰레드 중에
Priority Inversion
생략!
Priority Donation
Nested Donation
Multiple Donation
Data Structure for Multiple Donation
Data Structure for Nested Donation
Implementation of Priority Donation
- init_thread(*쓰레드, *쓰레드 이름, 우선순위)
- priority donation을 위한 data structure 초기화
- lock_acquire(*락)
- 락이 불가능하면, 락의 주소를 저장(사용가능할 때까지 대기하기 위해)
- 현재 우선순위를 저장하고 이 스레드에게 우선순위를 기부 받은 스레드들을 리스트에서 관리(?)
- 우선순위 기부
- lock_release(*락)
- 락이 릴리즈될때, donation list에서 락을 hold한 스레드를 제거하고 우선순위를 다시 set
- thread_set_priority(new 우선순위)
- donation을 고려한 우선순위 set
결과
backtrace 하는법
https://web.stanford.edu/class/cs140/projects/pintos/pintos_10.html
Pintos Projects: Debugging Tools
If you notice strange behavior while using GDB, there are three possibilities: a bug in your modified Pintos, a bug in Bochs's interface to GDB or in GDB itself, or a bug in the original Pintos code. The first and second are quite likely, and you should se
web.stanford.edu
스탠포드 대학교에서 알려주는 디버깅 도구들..중에 backtrace항목을 보았는데, 봐도 뭔 소린지 잘 모르겠다.
커널이 패닉 상태가 되면, "백트레이스"를 출력합니다. 즉, 패닉 시점에 실행 중이었던 함수 내의 주소 목록을 요약하여 프로그램이 어떻게 현재 상태에 이르렀는지 보여줍니다. 코드 내의 어떤 지점에서든 debug_backtrace() 함수를 호출하여 백트레이스를 출력할 수 있으며, 이 함수는 <debug.h>에 프로토타입으로 선언되어 있습니다. 또한 <debug.h>에 선언된 debug_backtrace_all() 함수는 모든 스레드의 백트레이스를 출력합니다.
백트레이스의 주소는 원시 16진수 숫자로 나열되며, 이는 해석하기 어렵습니다. 우리는 이러한 주소를 함수 이름과 소스 파일 줄 번호로 변환해주는 backtrace 도구를 제공합니다. 첫 번째 인수로 kernel.o의 이름과 백트레이스를 구성하는 16진수 숫자들(0x 접두사 포함)을 나머지 인수로 입력하면 각 주소에 해당하는 함수 이름과 소스 파일 줄 번호를 출력합니다.
백트레이스의 번역된 형태가 엉망이거나 말이 되지 않는 경우(예를 들어, 함수 A가 함수 B 위에 나열되어 있지만 B가 A를 호출하지 않는 경우), 커널 스레드의 스택이 손상되었다는 좋은 징후입니다. 왜냐하면 백트레이스는 스택에서 추출되기 때문입니다. 또 다른 가능성은 backtrace에 전달한 kernel.o가 백트레이스를 생성한 커널과 동일하지 않을 수 있기 때문입니다.
컴파일러 최적화로 인해 때때로 백트레이스가 혼란스러울 수 있습니다. 함수가 마지막 행동으로 다른 함수를 호출하는 경우(테일 콜), 호출 함수는 백트레이스에 전혀 나타나지 않을 수 있습니다. 마찬가지로 함수 A가 반환하지 않는 함수 B를 호출할 때, 컴파일러는 관련 없는 함수 C가 A 대신 백트레이스에 나타나도록 최적화할 수 있습니다. 함수 C는 단순히 메모리상에서 A 바로 뒤에 있는 함수입니다. 스레드 프로젝트에서 이는 일반적으로 테스트 실패에 대한 백트레이스에서 볼 수 있습니다.
'크래프톤 정글 일지' 카테고리의 다른 글
github default branch 변경 (0) | 2024.05.15 |
---|---|
보이어-무어 알고리즘 (0) | 2024.05.14 |
[PintOS] 이 OS, 핀토스는 어디가 시작점인가 (0) | 2024.05.13 |
[PintOS] Project 1 - Threads - Alarm clock (0) | 2024.05.11 |
[PintOS] 카이스트 전산학과 권영진 교수님 OS 특강 (0) | 2024.05.11 |