Arm Linux Kernel Hacks

rousalome.egloos.com

포토로그 Kernel Crash


통계 위젯 (화이트)

593
557
422264


[라즈베리파이] 동기화 - 스핀락(spinlock): 스핀락 획득 두 번째 시도 9. 커널 동기화(spinlock/mutex)

스핀락 획득 두 번째 시도
이전 시간에 스핀락을 누군가 획득했는데 다른 프로세스가 다시 스핀락을 획득할 때 동작을 알아봤습니다. 이번에는 다른 프로세스가 같은 스핀락 획득을 시도하는 상황을 그리면서 분석하겠습니다. 

다시 분석할 _raw_spin_lock() 함수 코드를 같이 봅시다.
1  80704b60 <_raw_spin_lock>:
2  80704b60: e1a0c00d  mov ip, sp
3  80704b64: e92dd800  push {fp, ip, lr, pc}
4  80704b68: e24cb004  sub fp, ip, #4
5  80704b6c: e52de004  push {lr} ; (str lr, [sp, #-4]!)
6  80704b70: ebe82672  bl 8010e540 <__gnu_mcount_nc>
7  80704b74: f590f000  pldw [r0]
8  80704b78: e1903f9f  ldrex r3, [r0]
9  80704b7c: e2832801  add r2, r3, #65536 ; 0x10000
10 80704b80: e1801f92  strex r1, r2, [r0]
11 80704b84: e3310000  teq r1, #0
12 80704b88: 1afffffa  bne 80704b78 <_raw_spin_lock+0x18>
13 80704b8c: e1a02823  lsr r2, r3, #16
14 80704b90: e6ff3073  uxth r3, r3
15 80704b94: e1530002  cmp r3, r2
16 80704b98: 0a000003  beq 80704bac <_raw_spin_lock+0x4c>
17 80704b9c: e320f002  wfe
18 80704ba0: e1d030b0  ldrh r3, [r0]
19 80704ba4: e1530002  cmp r3, r2
20 80704ba8: 1afffffb  bne 80704b9c <_raw_spin_lock+0x3c>
21 80704bac: f57ff05b  dmb ish
22 80704bb0: e89da800  ldm sp, {fp, sp, pc}

r0는 0xb93b4a78이고 이 메모리 공간에 0x00030001 값이 있습니다. r3은 0x00040001로 변경됩니다. r3에 ticket owner와 next를 모두 저장한 겁니다. next가 owner보다 2만큼 크니 누군가 스핀락을 획득했고 다른 모듈이 스핀락을 기다리는 상태입니다.

다음 8번째 줄 코드를 보겠습니다.
9  80704b7c: e2832801  add r2, r3, #65536 ; 0x10000

r3에 0x10000을 더합니다. 이는 ticket에 next를 +1만큼 더하는 동작입니다. ticket의 자료구조 패턴을 활용한 명령어입니다. 결과 r2는 0x00040001이 됩니다.

다음은 10번째 줄 코드입니다.
10 80704b80: e1801f92  strex r1, r2, [r0]

r2에는 ticket next만 +1만큼 더한 결괏값인 0x00040001을 저장하고 있습니다. 이 값을 r0 메모리 공간에 저장합니다. 스핀락 next 값을 +1만큼 증감시키는 겁니다. 만약 r0 메모리 공간에 0x00040001이 제대로 저장됐으면 r1은 1로 변경됩니다.

다음은 11~12번 줄 코드입니다. 
8  80704b78: e1903f9f  ldrex r3, [r0]
9  80704b7c: e2832801  add r2, r3, #65536 ; 0x10000
...
11 80704b84: e3310000  teq r1, #0
12 80704b88: 1afffffa  bne 80704b78 <_raw_spin_lock+0x18>

만약 0x00040001이 r0 메모리 공간에 저장을 못 하면 80704b78 코드 주소로 이동해서 다시 스핀락 변수를 읽습니다.

이번에는 _raw_spin_lock 함수의 후반부 코드를 보겠습니다.
13 80704b8c: e1a02823  lsr r2, r3, #16
14 80704b90: e6ff3073  uxth r3, r3
15 80704b94: e1530002  cmp r3, r2
16 80704b98: 0a000003  beq 80704bac <_raw_spin_lock+0x4c>
17 80704b9c: e320f002  wfe
18 80704ba0: e1d030b0  ldrh r3, [r0]
19 80704ba4: e1530002  cmp r3, r2
20 80704ba8: 1afffffb  bne 80704b9c <_raw_spin_lock+0x3c>

13번 줄 코드를 보면 r2와 r3 레지스터로 연산을 합니다.
8  80704b78: e1903f9f  ldrex r3, [r0]
...
13 80704b8c: e1a02823  lsr r2, r3, #16

r2는 ticket에서 next 값을 +1만큼 증감한 0x00040001입니다. r3는 처음 스핀락 값을 저장하고 있으니 0x00030001입니다. 

r3가 0x00030001인데 이 값을 왼쪽으로 16만큼 왼쪽으로 비트 Shift 연산을 하는 동작입니다.

연산 결과는 다음과 같습니다. 
0x00030001(r3)
+lsr
=============
0x00000003(r2)

r3에는 next와 owner가 포함된 ticket 값이 있습니다. 0x00030001입니다. 이 중에 next 값을 r2에 저장하는 동작입니다.

다음 14번째 줄 코드를 보겠습니다.
14 80704b90: e6ff3073  uxth r3, r3

uxth명령어는 이전 절에서 배웠으니 넘어가겠습니다. 연산 결과는 다음과 같습니다. 이 어셈블리 명령어는 next와 owner 중 owner를 r3에 저장하는 임무를 수행합니다.
0x00010001(r3)
uxth
================
     0x0001

13~14줄 명령어를 같이 늘어놓고 해석하면 r3(0x00030001) 있는 next를 r2에 저장하고 owner를 r3에 저장하는 동작입니다.
13 80704b8c: e1a02823  lsr r2, r3, #16
14 80704b90: e6ff3073  uxth r3, r3

0x0003 | 0001(r3)
 next     owner
  r2        r3

다음 15번째 줄 코드를 분석할 차례입니다.
15 80704b94: e1530002  cmp r3, r2
16 80704b98: 0a000003  beq 80704bac <_raw_spin_lock+0x4c>
...
21 80704bac: f57ff05b  dmb ish

r3(owner)와 r2(next) 값을 비교해서 16번 줄 코드와 같이 두 값이 같지 않으니 18번 줄 코드를 실행합니다.
18 80704ba0: e1d030b0  ldrh r3, [r0]
19 80704ba4: e1530002  cmp r3, r2
20 80704ba8: 1afffffb  bne 80704b9c <_raw_spin_lock+0x3c>

이번에는 r0 메모리값을 r3로 읽습니다. ldrh 명령어는 32비트 중 하위 16비트만 읽습니다. 이 명령어로 스핀락의 owner를 읽습니다. r3은 메모리 공간에 있는 스핀락 owner 값이고 r2는 _raw_spin_lock 함수를 처음 실행했을 때 next인 0x3를 저장하고 있습니다. 18번 줄 코드에서 r3이 0x3가 될 때까지 다음 코드17~20줄 어셈블리 코드를 무한히 수행합니다. 이를 busy-waiting이라고 합니다.
17 80704b9c: e320f002  wfe
18 80704ba0: e1d030b0  ldrh r3, [r0]
19 80704ba4: e1530002  cmp r3, r2
20 80704ba8: 1afffffb  bne 80704b9c <_raw_spin_lock+0x3c>

이 어셈블리 코드의 의미를 생각해 봅시다. _raw_spin_lock() 함수에 진입하기 전 이미 누군가 스핀락을 획득한 상태였습니다. 17~20번째 줄 코드는 스핀락을 해제할 때까지 기다리는 코드 흐름이라 볼 수 있습니다.

이미 스핀락을 누군가 획득하고 있고 다른 모듈에서 스핀락을 기다리고 있습니다. 
스핀락을 두 번 해제하면 owner가 +1만큼 두 번 증가하니 18번 줄 코드에서는 r3은 0x3가 될 겁니다. 이때 스핀락 전역 변수는 0x0004|0003가 됩니다. next와 owner가 같으니 스핀락을 얻게 됩니다. 

이 흐름을 그림으로 정리하면 다음과 같습니다.
 
여전히 [1] 단계에서 스핀락을 획득한 프로세스는 임계영역에서 코드를 실행 중입니다. 이때 2개의 프로세스가 스핀락을 얻기 위해 계속 기다리고 있습니다.

다음 시간에 [1] 단계에서 획득한 스핀락을 해제하면 어떤 동작을 하는지 알아보겠습니다.


#Reference 시스템 콜


Reference(워크큐)
워크큐(Workqueue) Overview


덧글

댓글 입력 영역