본문 바로가기
Context와 AAPCS

꽤나, 중요한 얘기인데도, 어쩐지 들여다 보질 않아서, 잘 모르는 것들이 있어요. 늘 봐야지 하는 생각은 드는데, 귀찮은 생각도 들기도 하고, 어렵기도 한 거 같기도 하고 해서 다음에 보지 뭐 하는 것들이 있는데, 그것이 Context라는 거지요.
Context라는 거, 앞에서도 많이 들어 보았지요. Context라는 걸 제대로 이해를 해야 함수가 불린다거나, Context Switching을 한다거나 하는 의미를 제대로 알 텐데 어쩐지 이런 녀석을 제대로 다루는 것도 본적이 별로 없고... 해서! 제대로 한번 다뤄 보겠어요. Context라는 거 우리 ARM Register Set에서 처음 보았었었었드랬죠. Context는 현재의 CPU에 대한 모든 정보를 말하고요, 그 모든 정보는 Register들의 현재 값들을 말하는 거죠. 자, 이제 Context라는 것의 의미를 한번 보는 게 좋을 거 같군요.

자, 요놈이 바로 Context인 거에요. 요놈이 바로 Context 라구요라고 두번 외치고 싶네요. 이 Register들은 현재 System이 동작하는데 필요한 모든 정보를 담고 있어요. R0~R12 까지는 CPU가 연습장으로 사용하고 있는 중이고요. R13은 Stack Pointer로서 현재 CPU가 사용하고 있는 Stack의 주소 중에서도, 마지막 Push한 Data가 있는 곳의 주소를 가리키고 있고요. R14는 Linked Register로서 어디서 왔는지, 어디로 돌아가야 하는지를 나타내주는 정보이고요, 그리고 PC는 당연히 현재 실행하고 있는 주소 정보이고요, 마지막으로 CPSR은 현재 CPU의 상태에 대한 주소를 나타내지요. 우리가 뭔가 다른 일을 CPU한테 시키고 싶고, 언젠가 다시 지금 멈춰진 하던 일을 CPU에게 이어서 시키고 싶다면 이 Register값들을 어딘가에 저장하고, 다시 복원만 잘 해주면 되는 거지요. 그러니까 뭐가 되었든 Register 값들만 잘 보존시키면 되는 거지요. 이런 일의 대표적인 예는 Function Call인데요, 어떻게 해서 그런 일이 벌어지는지 한번 볼까요?

뭐 간단한 함수 콜을 예를 들어서 한번 해보져 머.

void ContextDaughter();

void ContextMother (void)
{
     나불나불나불~
     ①
     ContextDaughter ();
     궁시렁궁시렁~
}

ContextMother가 ContextDaughter를 호출하는 구조의 예지요. 나불나불나불~을 하다가 ContextDaughter()를 호출하기 바로 직전 ①에서의 Register의 모양을 다음과 같다고 볼까요?

오 이런 모양이로군요. 현재의 Context를 해석해 볼까요? R0~R12까지의 CPU의 연습장은 여러 가지 값으로 채워져 있고요. 현재 SP (Stack Pointer)는 0x1000을 가리키고 있어요. 그리고 ContextMother가 실행된 후에 돌아가야 할 곳은 0x2000이고요, 현재 실행되고 있는 ContextDaughter() 바로 직전의 PC값은 0x1500인 거지요. 자, 이 상태에서 ContextDaughter()로 마음의 준비 없이 진입하게 되면 어떤 일이 벌어질까요? R0~R12는 ContextDaughter 마음대로 주물럭 거릴 테고, R13은 역시 철없는 ContextDuaghter가 Data를 Push/ pop한 만큼 증가되거나, 감소될 거구요. R14는 만질 수도 있고, 안만질 수도 있고요. 물론 PC (R15)는 branch를 했으니까 값이 바뀌겠죠.

아! 어쩌면 좋을까요?

void ContextMother (void)
{
    나불나불나불~
    ①
    ContextDaughter ();
    ②
    궁시렁궁시렁~
}

아이디어는 ①번과 ②번 사이에 ContextDaughter가 불리워 졌어도, ContextMother()는 ContextDaughter()가 불렸었는지 안 불렸었는지, 모르는 게 최곤거죠. 그러면 어쩌면 좋을까요! 그렇습니다. Context를 ContextDaughter가 불리는 순간 (①) 어딘가에 저장해 놓았다가, ②번 순간에 다시 복원시키면!

void ContextMother (void)
{
   나불나불나불~
    /* ContextDaughter() 가 있던 자리 */
    궁시렁궁시렁~
}

이렇게 보이겠죠. 이것이 바로 Context의 정체인 것인 거죠. Context만 잘 보관해 두면 언제든지 아까 하던 작업을 계속 진행 할 수 있어요.

뭐, 일단은 이걸 실현하려면, ContextDaughter()가 불리는 순간에 R0~R12까지의 Register는 어떻게 처리할 것이냐 하면. ContextDaughter()는 자기가 구현될 때 어떤 Register들을 가지고 작업을 할건지, Compile 단계에서 결정이 된답니다. 결정이 된다는 건 어떤걸 저장해 두어야 하는지를 알고 있는 거겠죠. 그러니까, ContextDaughter()가 호출되면 호출되자마자, ContextDuaghter()가 연습장으로 쓸 Register들을 Stack에 Push를 한답니다. 그러니까 만약에 R3, R4, R5를 작업용도로 사용한다고 결정이 났다면, ContextDaughter()함수의 초입에는 push R3, R4, R5를 해주는 코드가 삽입되는 거죠. 그러면 자연스럽게 SP (R13)은 Stack의 용도에 맞게 사용이 되고요, 그리고, R14, R15를 알아보기 위하여, 주소를 다음과 같이 써보면, - Thumb mode 가정이에요 -

void ContextMother (void)
{
0x1500 | 나불나불나불~
0x1502 | /* ContextDaughter() 가 있던 자리 */
0x1504 | 궁시렁궁시렁~
}

R14는 나불나불나불 다음에 궁시렁궁시렁이 곧바로 실행되어야 한다는 걸 반영하여, 궁시렁궁시렁의 값인 0x1504를 R14에 넣어줍니다. 그러고 ContextDaughter()도 다른 함수를 또 부를지 모르니까 R14도 Stack에 같이 Push해 주는 거죠. 여기서 CPSR은 Backup 안 해두느냐. 뭐, Function Call의 경우 ContextDaughter()가 CPSR을 Flag field를 제외한 나머지 값들을 수정하는 routine이 있어서는 안되죠. 그래서 이런 모든 것들을 어떻게 Backup해 두느냐 하면, ContextDaughter()에 진입하자 마자, push {R3, R4, R5, R14}를 해두고요. ContextDaughter()를 다 수행하자마자 pop{R3, R4, R5, pc}을 해서 이전과 같은
Context를 유지할 수 있도록 해 준답니다. 아주, 간단하죠. 이런걸 유식한 말들로 좀 정리를 해주면,

1) Callee (호출된 함수)에서 Register값을 변경하면 Caller함수에서 Register값이 변경되어 문제가 발생하죠.
2) 그러니까, Callee (호출된 함수)는 Caller로 복귀할 때 원래의 환경을 깨끗하게~ 그~대~로~ 복구 시켜야 하지요.
3) 실은 이렇게 깨끗하게 완전 복구 시켜주면 참 좋겠지만, Scratch Register들 (R0~R3, R12)의 경우에는 Callee가 마음대로 변경 및 수정, 훼손에 대한 권리를 갖고 있어서, Caller로 복귀 시에는 망가져 있을 수도 있습니다요. 그러니까, 주의해서 사용해야 해요. 왜냐하면 항상 전부다 깨끗하게 백업/복원을 계속하면, Function Call의 Performance에 영향을 미치니까, 어느 정도의 Flexibility를 둔거죠 머. 게다가 그전에 살펴 보았던, AAPCS라는 것에 의하여 R0~R3은 Return값과 Callee 함수에 대한 Passing Parameter를 넘겨줄 수 있으니, 당연히 만질 수 있어야겠죠.

4) 그러니까, Function Call의 경우를 따져 보면, - Scratch Register를 훼손하였으나, 다른 함수를 호출하지 않는 경우에는 (이런 함수를 Leaf 함수라고 해요) Stack에 뭔가 Backup할 필요도 없고요, 돌아올 때, MOV pc, lr을 이용해서 곧바로 돌아올 수 있어요. - Scratch Register를 훼손하였고, 그 함수가 또 다른 함수를 불렀을 경우에는 lr을 stack에 넣어주고, 돌아올 때는 stack에 있은 lr을 pop하여 pc에 넣어주면 되지요.

뭐, 이런 겁니다.
Context Concept을 잘 이해 하시면, Exception이 발생한 순간이나, Function Call이나, Scheduler에 의한 Context Switching이나 모두 매 한가지 의미라는 걸 아시게 될 거에요. 또 다른 대표적인 예인 RTOS에서 Context Switching에 대해서라면, 이것이야 말로 맑고 깨끗하고 완벽한 Context의 Backup과 복원의 예에요.그건 RTOS 팩토리에서 한번 제법 더 자세히 보시죠.

 
Linked at 임베디드 시스템 개발자 되기 .. at 2009/07/20 21:56

... er_initial_staqckheap 5) Software 비네팅 (Vinetting) ⓐ Context와 AAPCS ⓑ Pointer와 배열은 소녀시대와 원더걸스, 그리고 이중 포인터 ... more

친절한 임베디드 시스템 개발자 되기 강좌 글 전체 리스트 (링크) -



댓글





친절한 임베디드 개발자 되기 강좌 글 전체 리스트 (링크) -