이전에 ISR을 너무 길게 짜면 안 된다는 발언에 대한 디펜스. ISR을 너무 길게 짜면 안 된다는 발언을 한 적이 있는 데 그럼 할 일이 많은 ISR은 어쩌라는 말이냐! 라는 항의에 너무 무책임 한 듯 하여, 이런 긴 ISR에 대한 처리를 어떻게 하면 좋을 까 하는 데에 대한 얘기입니다요.
대부분 이런 Interrupt는 휴대전화가 그 좋은(?) 예이죠. 친구랑 얘기하던 중에도 전화가 오면 그거부터 받고 짧은 얘기면 곧바로 얘기를 모두 끝내고, 길고 중요하지 않은 얘기면 이따가 다시 전화할께라던가 하죠. 요 길고 중요하지 않아 나중에 처리하겠다고 하는 부분이 바로 문제의 그 부위 입니다. ㅋ
Deferred Procedure Call이나, Asynchronous Procedure Call, 또는 Linux에서 말하는 Bottom half라는 거 들어보신 적이 있으실랑가 모르겠네요. 그다지 주목 받지도 못하고, 아름답지도 못한 이런 수상한 편법은 실은 정말로 아름다운 방법으로 구현 가능하게 되어 있답니다. 이런 아름다운 얘기를 하게 되어 약간은 긁적긁적이지만, 재미있는 여행이라고 생각해 주시면 영광굴비입니다.
용어를 한번 정리하자면, Bottom Half = APC + DPC라고 보시면 되요. APC는 지금 처리하지 말고, 나중에 Task Level로 처리할 수 있도록 처리하는 부분이고요, DPC는 APC를 직접 처리하는 부분이라고 보시면 무리 없어요. 보통은 APC는 API로, DPC는 Task로 구성된다고 보심 됩니다요. 일단은 ISR을 너무 길게 짜면 System에 문제가 있을 테니, ISR중에 너무 긴 녀석은 두 가지 Stage로 나누어 생각해 보도록 하죠. 지금 당장 처리해야 하는 부분과 나중에 Task Level로 처리해도 되는 부분. 이건 ISR을 만들 때 각자 잘 생각해서 나누셔야 해요.
이번에 다루려는 건 이미 다루었던 clk_register (key_polling, 100) 같은 녀석을 다뤄볼까 합니다. Callback Function을 등록해 놓았는데, 이 녀석을 그냥 IRQ level에서 실행하려고 보니까, 이런 녀석이 한두개가 아니더란 말이죠. 그럼 이 녀석을 Task Level에서 실행할 수 있도록 해줬으면 좋겠는데..라고 생각이 들 때 사용하는 게 이런 Bottom half라는 거죠 머.
이번에도 이용하는 건 Queue인데요, call back function을 APC를 처리할 DPC task의 Queue Command로 보내서 처리하면 되는 것이지요. 먼저, ISR routine에서 또또또 Queue를 이용해서 DPC를 처리할 Task의 Queue에 Callback function을 넣어주는 거죠.
queue_apc() 라는 APC API가 있다고 치죠 머. Clock Tick ISR이 계속 진행 되면서 Timer 등록 된 녀석 중에 expire 된 녀석이 있을 경우에! 곧바로 Clock Tick ISR에서 직접 Call back을 실행하지 않고요, queue_apc ()를 이용해서 DPC를 담당하는 Task에 callback을 넘겨 준다고 생각해 보죠.
queue_apc(key_polling);
send_signal (DPC_WORK_SIG);
이렇게 넣어주면 queue_apc는 key_polling() 함수를 DPC를 담당하는 Task (이제부터 DPC_task()라고 부를게요) DPC_task()의 Queue에 함수를 넣어두는 거에요. ISR이 끝나고 Scheduling이 될 때, DPC_task에게 순서가 왔을 때, Queue에 넣어진 Callback function들을 하나씩 꺼내서 실행하는 구조로 만들면 되겠사옵니다. 여기서 주의할 점은 DPC_task의 Priority는 꽤나 높아야 하겠죠. 아무래도 나중에 처리한다고 해도, Interrupt에 의해서 처리 되었었어야 되는 걸 처리하는 거니까, 빨리 처리하는 게 신상에 좋을 거에요. 그리고, 이런 Callback 함수를 처리할 때는 Interrupt도 안 걸리게 하는 게 좋겠죠.
void DPC_task()
{
while (1)
{
wait_signal ( DPC_WORK_SIG );
clear_signal ( DPC_WORK_SIG );
Interrupt_Lock( );
while( (queue_get())() ); /* Queue가 비워지지 않는 한 get한 함수를 처리 */
Interrupt_free( );
}
} /* END rex_dpc_task *
어때요. DPC 차암~ 쉽죠 잉?
DPC나, APC, 그리고 Bottom Half
친절한 임베디드 개발자 되기 강좌 글 전체 리스트 (링크) -
댓글