race cindition을 피하기 위해 하드웨어 지원을 통한 Busy Waiting
TSL(Test and Set Lock)
TSL R, A 명령어를 사용한다.
메모리 A에서 레지스터 R로 값을 읽고 동시에 A에 0이 아닌 값을 저장한다.
프로그램 코드상 동시에 일어나기 때문에 스케쥴러에 의해 코드가 실행되다 cpu사용권을 넘겨주는 일은 발생하지 않는다.
실행 횟수 | cpu | memory |
초기 | 0 | 0 |
실행 1 | 0 | 1 |
실행 2 | 1 | 1 |
0번 프로세스와 1번 프로세스가 있다고 가정한다면
0번 프로세스가 메모리의 0을 읽는다. 그럼 cpu의 레지스터엔 0이 읽히고 메모리는 1이된다.
그리고 1번 프로세스로 넘어가서 다시 메모리를 읽으려한다면 메모리엔 실제 값인 0이 아닌 1의 값을 읽게된다.
메모리의 값이 0이면 풀려있고 1이면 잠겨있는 뜻이다.
소스코드(Busy waiting)
enter_region:
TSL REGISTER, LOCK //메모리에서 레지스터로 값을 읽어오고 1을 기록한다.
CMP REGISTER, #0 //레지스터로 읽어온 값이 0인지 비교한다.
JNE enter_region //레지스터의 값이 0이 아니라면 line 2로 jump한다
RET|return to caller; // critical region으로 진입.
leave_region:
MOVELOCK, #0 //critical region을 빠져나오는 프로세스는 Lock을 풀어준다
RET|return to caller
이 코드는 어셈블리언어수준의 코드이다.
만약 3개의 프로세스가 LOCK메모리에 접근한다면 실제 메모리에 기록되어있는 0을 가져오는 프로세스는 하나다.
또한 JNE enter_region의 코드에서 line2로 jump하기 때문에 Loop를 돌게되고 이게 busy waiting이다.
mutex(non-busy-watiing)
busy-wating과의 비교
mutex_lock:
TSL REGISTER, MUTEX //register로 값을 읽고, 1을 기록한다.
CMP REGISTER, #0 //register의 값이 0인지 비교한다.
JZE ok //compare결과 0이면 ok로 jump한다.
CALL thread_yield //0이 아닐경우 cpu제어권을 포기한다.
JMP mutex_lock //cpu제어권이 돌아오면line2로 jump한다
ok: RET| return to caller; //critical region에 진입한다.
mutex_unlock:
MOVE MUTEX, #0 // critical region에서 빠져나오면서 메모리에 0을 기록한다.
RET| return to caller
cpu제어권을 자발적으로 포기하므로 cpu를 사용하며 무작정 wating하는 방식보다 부하가 덜하다.