๐ ๊ณต๋ถํ ๋ด์ฉ
๐ป - Thread.c ์ฝ๋ ๋ถ์
1. ASSERT
// ASSERT(์กฐ๊ฑด์) ํจ์๋ assert์ ์ง์ ํ ์กฐ๊ฑด์์ด ๊ฑฐ์ง์ด๋ฉด ํ๋ก๊ทธ๋จ์ ์ค๋จํ๋ฉฐ ์ฐธ์ด๋ฉด ํ๋ก๊ทธ๋จ์ ๊ณ์ ์คํํ๋ค.
ASSERT(intr_get_level () === INTR_OFF); // => intr_get_level() ํจ์๋ ํ์ฌ interrupt๊ฐ disabled์ธ์ง, enabled(INTR_OFF)์ธ์ง ํ์ธํ disabled(INTR_ON)์ผ ๊ฒฝ์ฐ ๊ณ์ ์งํ
์ด ํจ์๋ก ํด๋น ๊ฒฐ๊ณผ๊ฐ ์ ๋๋ก ์งํ๋๋์ง ํ ์คํธ ํ ์ ์๋ค.
๋๊ธฐํ ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๋ ๊ฐ์ฅ ์ ์ข๊ณ ๋ฌด์ํ ๋ฐฉ๋ฒ์ interrupts๋ฅผ ๋ฌด๋ ฅํ(disabled) ์ํค๋ ๊ฒ์ด๋ค.
2. GDT
static uint64_t gdt[3] = { 0, 0x00af9a000000ffff, 0x00cf92000000ffff };
Global descriptor table๋ก CPU ์ ์ฒด์ ๋ฉ๋ชจ๋ฆฌ ์ธ๊ทธ๋จผํธ๋ฅผ ๋งํ๋ค.
์ธ๊ทธ ๋จผํธ ์์ญ์ ๋ํ ๋ฐ์ดํฐ๋ฅผ ์ผ์ ํ ๋์คํฌ๋ฆฝํฐ ํ์์ผ๋ก ๊ธฐ์ ํ๊ณ ์ด๋ฅผ ํ๋์ ํ ์ด๋ธ์ ๋ชจ์๋๊ณ ์ ํ๋ ๊ฒ
3. lock_init(), list_init()
/* List of processes in THREAD_READY state, that is, processes
that are ready to run but not actually running. */
static struct list ready_list;
/* Lock used by allocate_tid(). */
static struct lock tid_lock;
/* Thread destruction requests */
static struct list destruction_req;
/* Init the globla thread context */
lock_init (&tid_lock);
list_init (&ready_list);
list_init (&destruction_req);
// ๋๊ธฐํ๋ฅผ ์ํ lock๊ณผ scheduling์ ์ํ listdml ์ด๊ธฐํ
์ฐ๋ ๋๋ค์ ๊ธฐ๋ณธ์ ์ผ๋ก ํ๋ก์ธ์ค์ ํ ๋น๋ ๋ฉ๋ชจ๋ฆฌ ๊ณต๊ฐ์ ๊ณต์ ํ๊ธฐ์ ๋๊ธฐํ ์ด์๊ฐ ๋ฐ์ํ๋๋ฐ ์ด๋ฅผ ์กฐ์ ํ๊ธฐ ์ํ ํจ์๋ค์ด (lock_init()) /threads/synch.c์ ๊ตฌํ๋์ด ์์
์ธ๋งํฌ์ด๋ lock์ด๋ ๋๊ธฐ์ค์ธ threads์ list๋ฅผ ์ ์ง, ๋ณด์ ํ๋๋ฐ ์ด๊ฒ์ list.c์ ๊ตฌํ๋์ด ์์.
๐ก - lock์ binary semaphore๋ผ๊ณ ๋ ํ๋๋ฐ 0 ํน์ 1์ ๊ฐ๋ง์ ๊ฐ์ง๊ธฐ ๋๋ฌธ์ด๋ค. ์ด๊ณณ์์๋ lock์ด๋ผ๊ณ ํํ๋์์ง๋ง ์ผ๋ฐ์ ์ผ๋ก Mutex๋ผ๊ณ ํ๋ค. ๋ค๋ง Mutex๋ Semaphore์ ์ ํ๋ ๋ฒ์ ผ์ด ์๋ ๋ ๋ฆฝ์ ์ธ ๊ธฐ๋ฒ์ผ๋ก ๋ด์ผํ๋ค. ์๋ํ๋ฉด Mutex๋ Semaphore ๋ ๊ฐ๊ฐ์ ์ฅ๋จ์ ์ ๊ฐ์ง๊ณ ์๋ ์๋ฒฝํ์ง ๋ชปํ ์ํธ๋ฐฐ์ ๊ธฐ๋ฒ์ด๊ธฐ ๋๋ฌธ์ด๋ค. ๋์ ๊ฐ์ฅ ํฐ ์ฐจ์ด์ ์ ํ์ฅ์ค(๊ณต์ ์์)์ผ๋ก ๊ฐ๋ ํค์ ๊ฐ์๊ฐ 1๊ฐ๋ ๋ณต์๊ฐ๋์ด๋ค.
https://worthpreading.tistory.com/90 → ์ธ๋งํฌ์ด์ ๋ฎคํ ์ค์ ์ฐจ์ด
๐ lock_init()
/* Initializes LOCK. A lock can be held by at most a single
thread at any given time. Our locks are not "recursive", that
is, it is an error for the thread currently holding a lock to
try to acquire that lock.
A lock is a specialization of a semaphore with an initial
value of 1. The difference between a lock and such a
semaphore is twofold. First, a semaphore can have a value
greater than 1, but a lock can only be owned by a single
thread at a time. Second, a semaphore does not have an owner,
meaning that one thread can "down" the semaphore and then
another one "up" it, but with a lock the same thread must both
acquire and release it. When these restrictions prove
onerous, it's a good sign that a semaphore should be used,
instead of a lock. */
void
lock_init (struct lock *lock) {
ASSERT (lock != NULL);
lock->holder = NULL;
sema_init (&lock->semaphore, 1);
}
lock์ ๋ฎคํ ์ค์ ๊ฐ๋ ๊ณผ ๊ฐ๊น๊ณ ๋ฎคํ ์ค๋ ํค๊ฐ ํ๋์ด๊ธฐ ๋๋ฌธ์ ํค๋ฅผ ๋ฐํํ์ง ์์ผ๋ฉด ์ฌ์ฉํ ์๊ฐ ์๋ค.
์ธ๋งํฌ์ด๋ ํค๋ฅผ ์์ ํ๋ ์ฌ๋์ด ์๊ณ ๊ฐฏ์?๋ก ์ ์ฉํ๊ธฐ ๋๋ฌธ์ ํ๋์ ํค๋ฅผ ์ฌ์ฉํ๋ ๊ฒ์ด ๋ฒ๊ฑฐ์ธ๋ lock ๋์ ์ ์ธ๋งํฌ์ด๋ฅผ ์ฌ์ฉํ๋ ๊ฒ์ด ์ข์ ์ ์๋ค.
๐ list_init()
/* List element. */
struct list_elem {
struct list_elem *prev; /* Previous list element. */
struct list_elem *next; /* Next list element. */
};
/* List. */
struct list {
struct list_elem head; /* List head. */
struct list_elem tail; /* List tail. */
};
/* Initializes LIST as an empty list. */
void
list_init (struct list *list) {
ASSERT (list != NULL);
list->head.prev = NULL;
list->head.next = &list->tail;
list->tail.prev = &list->head;
list->tail.next = NULL;
}
list → ์ฐ๊ฒฐ ๊ตฌ์กฐ์ฒด ๋ฆฌ์คํธ๋ก ๊ตฌํ.
4.Thread ์ด๊ธฐํ
/* Initial thread, the thread running init.c:main(). */
static struct thread *initial_thread;
/* Set up a thread structure for the running thread. */
initial_thread = running_thread ();
init_thread (initial_thread, "main", PRI_DEFAULT);
initial_thread->status = THREAD_RUNNING;
initial_thread->tid = allocate_tid ();
running_thread()
/* Returns the running thread.
* Read the CPU's stack pointer `rsp', and then round that
* down to the start of a page. Since `struct thread' is
* always at the beginning of a page and the stack pointer is
* somewhere in the middle, this locates the curent thread. */
#define running_thread() ((struct thread *) (pg_round_down (rrsp ())))
ํ์ฌ ์คํ์ค์ธ thread์ ํฌ์ธํฐ๋ฅผ ๋ฐํํ๋ค.
init_thread()
//์ธ์๋ก ๋ฐ๋ ๊ฐ
initial_thread = running_thread ();
init_thread (initial_thread, "main", PRI_DEFAULT);
/* Does basic initialization of T as a blocked thread named
NAME. */
static void
init_thread (struct thread *t, const char *name, int priority) {
ASSERT (t != NULL);
ASSERT (PRI_MIN <= priority && priority <= PRI_MAX);
ASSERT (name != NULL);
memset (t, 0, sizeof *t);
t->status = THREAD_BLOCKED;
strlcpy (t->name, name, sizeof t->name);
t->tf.rsp = (uint64_t) t + PGSIZE - sizeof (void *);
t->priority = priority;
t->magic = THREAD_MAGIC;
}
/* Random value for struct thread's `magic' member.
Used to detect stack overflow. See the big comment (thread ๊ตฌ์กฐ์ฒด ์ค๋ช
)
at the top of thread.h for details. */
#define THREAD_MAGIC 0xcd6abf4b
thread_start()
/* Starts preemptive thread scheduling by enabling interrupts.
Also creates the idle thread. */
void thread_start(void)
{
/* Create the idle thread. */
struct semaphore idle_started;
sema_init(&idle_started, 0);
thread_create("idle", PRI_MIN, idle, &idle_started);
/* Start preemptive thread scheduling. */
intr_enable();
/* Wait for the idle thread to initialize idle_thread. */
sema_down(&idle_started);
}
idle_started → ํ๋์ cํ๋ก๊ทธ๋จ์์ ํ๋์ mainํจ์ ์์์ ์ฌ๋ฌ ํจ์ํธ์ถ๋ค์ด ์ด๋ฃจ์ด์ง๋ ๊ฒ์ฒ๋ผ, pintos kernel ์์์ ์ฌ๋ฌ thread๋ค์ด ๋์์ ์คํ๋ ์ ์๋๋ก ํ๋ ๋จ ํ๋์ main thread์ด๋ค.
thread_create๋ฅผ ํ๋ ์๊ฐ idle thread๊ฐ ์์ฑ๋๊ณ ๋์์ idle ํจ์๊ฐ ์คํ๋๋ค. idle thread๋ ์ฌ๊ธฐ์ ํ๋ฒ schedule์ ๋ฐ๊ณ ๋ฐ๋ก sema_up์ ํ์ฌ ๋ง์ง๋ง์ sema_down์ ํ์ด์ฃผ์ด thread_start๊ฐ ์์ ์ ๋๋ด๊ณ run_action()์ด ์คํ ๋ ์ ์๋๋ก ํด์ฃผ๊ณ idle ์์ ์ block๋๋ค.
idle thread๋ pintos์์ ์คํ๊ฐ๋ฅํ thread๊ฐ ํ๋๋ ์์ ๋ wake ๋์ด ์๋ํ๋๋ฐ, ์ด๊ฒ์ CPU๊ฐ ๋ฌด์กฐ๊ฑด ํ๋์ thread๋ ์คํํ๊ณ ์๋ ์ํ๋ฅผ ๋ง๋ค๊ธฐ ์ํจ์ด๋ค
Thread์ ์งํ ํ๋ฆ
thread_init → thread ์คํ (THREAD_RUNNING) → thread ์ข ๋ฃ(THREAD_DYING)
→ thread ์ค๋น์ํ(THREAD_READY)
- thread๊ฐ ์ค๋น์ํ ์ผ๋ Thread_Ready์ ๋ค์ด๊ฐ๊ณ READY_QUEUE์์ ๊ด๋ฆฌํ๋ค.
- ready_queue์์ ๋ฃ๊ณ ๋นผ๋ ์์๋ฅผ ์ ํ๋ ๊ฒ์ด scheduling์ ๊ธฐ๋ณธ ์์ ์ด๋ค.
- pintos๋ round-robin์ผ๋ก ๊ตฌํ๋์ด ์์
thread_create()
- ์๋ก์ด thread๋ฅผ ๋ง๋ค๊ณ ์คํํ๋ค. return ๊ฐ์ผ๋ก ์์ฑ/์คํ๋ thread ํฌ์ธํฐ๋ฅผ ๋๊ฒจ์ค๋ค.
/* Creates a new kernel thread named NAME with the given initial
PRIORITY, which executes FUNCTION passing AUX as the argument,
and adds it to the ready queue. Returns the thread identifier
for the new thread, or TID_ERROR if creation fails.
If thread_start() has been called, then the new thread may be
scheduled before thread_create() returns. It could even exit
before thread_create() returns. Contrariwise, the original
thread may run for any amount of time before the new thread is
scheduled. Use a semaphore or some other form of
synchronization if you need to ensure ordering.
The code provided sets the new thread's `priority' member to
PRIORITY, but no actual priority scheduling is implemented.
Priority scheduling is the goal of Problem 1-3. */
tid_t thread_create(const char *name, int priority, thread_func *function, void *aux)
{
struct thread *t;
tid_t tid;
ASSERT(function != NULL);
/* Allocate thread. */
t = palloc_get_page(PAL_ZERO);
if (t == NULL)
return TID_ERROR;
/* Initialize thread. */
init_thread(t, name, priority);
tid = t->tid = allocate_tid();
/* Call the kernel_thread if it scheduled.
* Note) rdi is 1st argument, and rsi is 2nd argument. */
t->tf.rip = (uintptr_t)kernel_thread;
t->tf.R.rdi = (uint64_t)function;
t->tf.R.rsi = (uint64_t)aux;
t->tf.ds = SEL_KDSEG;
t->tf.es = SEL_KDSEG;
t->tf.ss = SEL_KDSEG;
t->tf.cs = SEL_KCSEG;
t->tf.eflags = FLAG_IF;
/* Add to run queue. */
thread_unblock(t);
return tid;
}
thread_yield()
- CPU๋ฅผ ์ ์ ์ค์ผ์ฅด๋ฌ์๊ฒ ์๋ณดํ๋ค. ์ฌ๊ธฐ์ ์ค์ผ์ฅด๋ฌ๊ฐ ์ด๋ค ์ฐ๋ ๋๋ฅผ ๋จผ์ ์คํ์ํฌ์ง ๊ณ ๋ฅธ๋ค.
- ์คํ์ค์ธ ์ฐ๋ ๋๋ sleep๋์ง ์๊ณ ๋ค์ ready list์ ํฌํจ๋๋ค.
void thread_yield(void)
{
struct thread *curr = thread_current();
enum intr_level old_level;
ASSERT(!intr_context());
old_level = intr_disable();
if (curr != idle_thread)
list_push_back(&ready_list, &curr->elem);
do_schedule(THREAD_READY);
intr_set_level(old_level);
}
๐ญ ๋๋ ์ & ๋ฐฐ์ด ์
์ฌ๋ฌ๋ฒ ์ฝ๋ค๋ณด๋๊น ๋์ถฉ ๊ตฌ์กฐ๊ฐ ์ด๋ ์ ๋ ์กํ๋ค. ๊ตฌ์กฐ๋ ์ด๋์ ๋ ํ์ ํ๋๋ฐ ์ค์ง์ ์ผ๋ก ์ฝ๋๋ฅผ ๊ตฌํํ๋ ๊ฒ์ ๋ณ๊ฐ์ธ ๊ฒ ๊ฐ๊ณ , ์์ง ํ ์ผ์ด ํ์ฐ์ธ๋ฏ ํ๋ค. ์ด๋ฒ ์ฃผ๋ง๊น์ง alarm์ ๊ตฌํ ์๋ฃ๋ฅผ ๋ชฉํ๋ก ์ก๊ณ ์ ์งํด์ผ๊ฒ ๋ค.
๊ฐ์์ค๋ฌ์ด ๋น์ ์์นจ ์ด๋์ ์๊ฐ๋ ค๋ค๊ฐ ๋น ๋ง์ผ๋ฉด์ ๊ฑธ์ด๊ฐ๋ค ์๋๋ฐ ์๊ฐ๋ณด๋ค ์ด๋์ด ์๋จ
๋น๋ถ๊ฐ์ ๋น ์์์ ์ด๋์ ๋ชป๊ฐ๋ ๋ชป๊ฐ๋ ๋งํผ ๋ ๊ณต๋ถ๋ฅผ ๋ง์ด ํด์ผํ ๋ฏ ํ๋ค.
๐ฅ ๋ด์ผ ๊ณต๋ถํ ๋ด์ฉ
- thread_init() ์์ :
- sleep queue ๊ตฌ์กฐ ์ ์ธ ๋ฐ ์ด๊ธฐํ
- timer_sleep() ์์ :
- seelp queue์ thread ์ฝ์
- timer_interrup() ์์ :
- ๋ชจ๋ tick๋ค์ sleep queue์์ wake up ํ๋์ง ๊ฒ์ฌํ๊ณ wake up ํจ์๋ก wake up ์ํค๊ธฐ
๐๏ธ ํฌ์คํ
https://spongecake.tistory.com/94
[BFS] - G5_13549_์จ๋ฐ๊ผญ์ง 3
https://www.acmicpc.net/problem/13549 13549๋ฒ: ์จ๋ฐ๊ผญ์ง 3 ์๋น์ด๋ ๋์๊ณผ ์จ๋ฐ๊ผญ์ง์ ํ๊ณ ์๋ค. ์๋น์ด๋ ํ์ฌ ์ N(0 ≤ N ≤ 100,000)์ ์๊ณ , ๋์์ ์ K(0 ≤ K ≤ 100,000)์ ์๋ค. ์๋น์ด๋ ๊ฑท๊ฑฐ๋ ์๊ฐ์ด
spongecake.tistory.com
ใ ๋๋ ๋ค๋ฅธ ๋ฌด์๋ณด๋ค ์ธ๊ฐ์ด ์๊ธฐ ์์ ์ ์ฌ๋ํ๋ค๋ ๊ฒ์ ๋๋ผ๊ณ ๋ ํ๋ค. ๊ทธ๋ผ์๋ ์ธ๊ฐ์ ์์ ์ ๋ํด์๋งํผ์ ์์ ์ ํ๋จ๋ณด๋ค ๋ค๋ฅธ ์ฌ๋์ ํ๋จ์ ๋ ์ ๋ขฐํ๋ค. (...) ์ด๋ป๊ฒ ๋์ ์๊ฐ์ด ์๋๋ผ ๋ค๋ฅธ ์ฌ๋์ ํ๋จ์ ๋ ์ ๋ขฐํ ์ ์๋ค๋ ๋ง์ธ๊ฐ! ใ
-๋ง๋ฅด์ฟ ์ค ์์ฐ๋ ๋ฆฌ์ฐ์ค, ๋ช ์๋ก, 12.4
๋ค๋ฅธ ์ฌ๋์ ๋ฐ์์ด ์ค์ ๋์ ์๊ฒฌ๋งํผ ์ค์ํ์ง ์๋ค๋ ๊ฒ์ ์๋ค๋ฉด ์ฐ๋ฆฌ๋ ๋ ๋ง์ด ํ๋ณตํด์ง ์ ์๋ค.
์ด๋ค ์ผ์ ๋ํ ๋ค๋ฅธ ์ฌ๋์ ์๊ฐ์ ์๊ธฐ ์ํด ๋๋ฌด ๋ง์ ์๊ฐ์ ์๋นํ์ง ๋ง๋ผ. ๋ด๊ฐ ์๊ฐํ๋ ๊ฒ์ด ๋ฌด์์ธ์ง ์๊ธฐ ์ํด ๋ ธ๋ ฅํ๋ผ.
๊ฒฐ๊ณผ์ ๋ํด ์๊ฐํ๋ ๋์ ๊ฐ์ ธ์ฌ ํจ๊ณผ์ ๋ํด, ๊ทธ๋ฆฌ๊ณ ๊ทธ๊ฒ์ด ์ณ์ ์ผ์ธ์ง ์๋์ง์ ๋ํด ๋ ์๊ฐํ๋ผ.
ใ ์ฌ๋์ ์ง๋ฐฐํ๋ ์์น์ ๋์ฌ๋ณด๋ผ. ํนํ ์งํ๋ก์ด ์ฌ๋๋ค์ ์์น์ ๋ณด๋ผ. ๊ทธ๋ค์ด ๋ฌด์์ผ๋ก๋ถํฐ ๋ฉ๋ฆฌ ๋ฌ์๋๋ ค ํ๊ณ ๋ฌด์์ ์ถ๊ตฌํ๋์ง์ ๋ํด์. ใ
-๋ง๋ฅด์ฟ ์ค ์์ฐ๋ ๋ฆฌ์ฐ์ค, ๋ช ์๋ก, 4.38
๊ทธ ๋๊ฐ ๋์๋ , ๋๊ตฐ๊ฐ๋ฅผ ์ ํํ์ฌ ๊ทธ๋ค์ด ํ๋ ์ผ์ ์ง์ผ๋ณด์ ํน์ ๊ทธ๋ค์ด ํ์ง ์์ ์ผ์ ์ง์ผ๋ด๋ ์ข๋ค. ๊ทธ๋ค์ด ํ ๊ฒ์ฒ๋ผ ์ฐ๋ฆฌ๋ ์ต์ ์ ๋คํ๋ ๊ฒ์ด๋ค.