[크래프톤 정글 3기] 11/28(화) TIL

ClassBinu·2023년 11월 28일
0

크래프톤 정글 3기 TIL

목록 보기
46/120

알람 못 듣고 기절
08:56 입실
핀토스 priority 완성하기

Pintos


synch.c

sema_down()

void
sema_down (struct semaphore *sema) {
	enum intr_level old_level;

	ASSERT (sema != NULL);
	ASSERT (!intr_context ());

	old_level = intr_disable (); // 인터럽트 막기
	while (sema->value == 0) { // 세마포어 0이면 접근 대기
		list_push_back (&sema->waiters, &thread_current ()->elem); // 세마포어 waiters에 들어가서 대기
		thread_block (); // 스레드 블락으로 다른 스레드로 자원 넘기기
	}
	sema->value--; // 자원 접근 가능하면 세마포어 값 1 내리기(이 시점에 블록 풀려있음)
	intr_set_level (old_level); // 인터럽트 받는 걸로 변경
}

자원 접근 될 때까지 기다린다! 느낌

sema_try_down()

bool
sema_try_down (struct semaphore *sema) {
	enum intr_level old_level;
	bool success;

	ASSERT (sema != NULL);

	old_level = intr_disable (); // 원자적 실행 위해 인터럽트 비활성화
	if (sema->value > 0) // 세마 값이 0보다 커서 자원 접근 가능하면
	{
		sema->value--; // 세마 값 1 감소
		success = true; // 결과값 성공 할당
	}
	else
		success = false; // 접근 불가하면 결과값 실패 할당
	intr_set_level (old_level); // 인터럽트 활성화

	return success; // 결과 반환
}

자원 접근 '한 번 시도해보자~' 안되면 말고 느낌

sema_up()

void
sema_up (struct semaphore *sema) {
	enum intr_level old_level;

	ASSERT (sema != NULL);

	old_level = intr_disable (); // 인터럽트 비활성화
	if (!list_empty (&sema->waiters))
		thread_unblock (list_entry (list_pop_front (&sema->waiters),
					struct thread, elem)); // 웨이터가 비어있지 않으면 웨이터 큐에서 스레드를 깨움.
	sema->value++; // 세마 값 올리기
	intr_set_level (old_level); // 인터럽트 활성화
}

lock_init()

void
lock_init (struct lock *lock) {
	ASSERT (lock != NULL);

	lock->holder = NULL; // 락을 쥐고 있는 스레드가 없음.
	sema_init (&lock->semaphore, 1); // 세마포어를 1로 초기화
}

세마포어로 락을 구현함!
뮤텍스를 쓰지 않고 세마포어로 락을 구현한 이유는 세마포어가 더 유연함. 카운팅 세마포어와 이진 세마포터 키워드 기억하기!

lock_acquire()

void
lock_acquire (struct lock *lock) {
	ASSERT (lock != NULL);
	ASSERT (!intr_context ());
	ASSERT (!lock_held_by_current_thread (lock));

	sema_down (&lock->semaphore); // 해당 락의 세마포어에 접근해서 값을 낮춤.
	lock->holder = thread_current (); // 해당 락의 홀더를 현재 스레드로 변경
}

코드는 프로그램일 뿐. 코드가 인스턴스화되면 실행 흐름이 된다. sema_down에서 스레드가 blocked이 되면 이 실행 흐름은 중지되고, 풀리면 다시 lock->holder 부분이 실행된다.

lock_try_acquire()

bool
lock_try_acquire (struct lock *lock) {
	bool success;

	ASSERT (lock != NULL);
	ASSERT (!lock_held_by_current_thread (lock));

	success = sema_try_down (&lock->semaphore);
	if (success)
		lock->holder = thread_current ();
	return success;
}

sema_try_down()를 호출하는 함수

lock_release()

void
lock_release (struct lock *lock) {
	ASSERT (lock != NULL);
	ASSERT (lock_held_by_current_thread (lock));

	lock->holder = NULL; // 홀더 해제(락 반환)
	sema_up (&lock->semaphore); // 세마 업
}

cond_init()

조건 변수 초기화

void
cond_init (struct condition *cond) {
	ASSERT (cond != NULL);

	list_init (&cond->waiters);
}

조건변수 내부에는 waiters 리스트가 있음.

cond_wait()

스레드를 특정 조건이 될때까지 기다리게 하는 함수

void
cond_wait (struct condition *cond, struct lock *lock) {
	struct semaphore_elem waiter; // waiter 구조체 초기화

	ASSERT (cond != NULL);
	ASSERT (lock != NULL);
	ASSERT (!intr_context ());
	ASSERT (lock_held_by_current_thread (lock));

	sema_init (&waiter.semaphore, 0); // 세마포어 값 0으로 초기화
	list_push_back (&cond->waiters, &waiter.elem); // 해당 조건변수의 waiters리스트에 elem 포인터 삽입.
	lock_release (lock); // 락을 해제
	sema_down (&waiter.semaphore); // 세마포어 값 1감소
	lock_acquire (lock); // 락 취득
}

초기화 되지 않았는데 어떻게 &waiter.elem을 넣는거야?
여기서는 값이 중요한 게 아니다. 초기화 되지 않아서 값이 존재하지 않지만 주소를 넣는 거니까 상관 없다!

이 코드는 조건변수와 락을 넘겨주면, 그걸 바탕으로 해당 조건변수의 waiters에 대기할 스레드를 넣어주는 기능을 함.

cond_signal()

wait에 의해 대기 중인 스레드를 깨우는 것

void
cond_signal (struct condition *cond, struct lock *lock UNUSED) {
	ASSERT (cond != NULL);
	ASSERT (lock != NULL);
	ASSERT (!intr_context ());
	ASSERT (lock_held_by_current_thread (lock));

	if (!list_empty (&cond->waiters)) // 조건 변수 안의 waiters가 비어있지 않다면
		sema_up (&list_entry (list_pop_front (&cond->waiters),
					struct semaphore_elem, elem)->semaphore); // 세마포어 값 증가
}

cond_broadcast()

waiters의 전체 스레드 깨우기

void
cond_broadcast (struct condition *cond, struct lock *lock) {
	ASSERT (cond != NULL);
	ASSERT (lock != NULL);

	while (!list_empty (&cond->waiters))
		cond_signal (cond, lock);
}

전체 구조체 도식화해서 확인
condition 이해 어려웠지만 80% 정도 이해한 듯..?


모니터

뮤텍스, 세마포어와 같은 동기화 기법은 고수준으로 추상화한 것

모니터는 여러 메서드의 집합
이 메서드들이 상호 배제를 자동으로 제공
한 번에 하나의 스레드만 모니터 내부 코드 실행 가능

조건 변수는 스레드가 대기할 수 있는 큐

메서드가 호출되면, 해당 메서드는 자동 잠금이 된다.

모든 모니터는 하나의 lock 변수와 하나의 conditional variable을 가진다.

단순히 시그널, 웨이트와 같은 함수로 스레드를 컨트롤 하는게 아니라
자동화된 조건 변수를 통해 좀더 세밀하고 안정적으로 스레드를 컨트롤

모니터는 뮤텍스, 세마포어와 별개의 개념이 아니라
이 두개가 수동 기어라면 모니터는 상호배제와 실행순서제어를 자동으로 해주는 도구같은 개념

모니터 이해한 거 대충 그림 그려봤는데 정확한지는 모르겠음

아닌 거 같음 -_-;
이상한데.. 아시는 분 댓글 좀..


Priority Donation

핵심 개념은 락을 가진 하위 스레드를 임시적으로 우선 순위를 올려서 빨리 끝낸 다음에 락을 반환시키는 것

Multiple Donation

하나의 스레드가 여러 다른 스레드로부터 우선 순위 기부 받음
만약 A가 락을 보유하고 있는데 우선 순위가 높은 B와 C가 락을 기다리는 상황에서,
A는 B와 C중에 가장 높은 우선 순위로 상승

Nested Donation

우선순위 기부가 여러 레벨에 걸쳐서 중첩적으로 일어나는 것

기부는 내일 다시 정리하기!

0개의 댓글