평소에 user level이 아닌곳에서 주로 작업을 하다보니 가끔 시그널을 사용할 때마다 헷갈리는 일이 생긴다.. ㅠ_ㅠ
뭐.. 잘 사용안하기 때문에 까먹는 것도 있지만.. 이 시그널이라는 놈이 막상 문서로 공부를 하다보면 좀 애매한 부분이 많아서.. -_-;; 평소에 익숙한 커널코드에서 어떻게 동작하는지 파악을 하고 평소에 애매하게 생각되는 부분을 확실하게 정리하기로 했다.
우선 시그널을 발생시키는 부분부터 알아보면, 특정 프로세스에게 signal을 전달 하게 될 경우 signal은 수신 대상 프로세스의 task structure에 signal을 저장하는 queue로 전달되고 저장 되면서 전달 작업은 모두 종료하게 된다.
그리고 시그널의 실행을 알아보면, 프로세스는 시스템콜, 인터럽트 등등 여러가지 이유로 Kernel모드에 진입했다가 유저모드로 복귀하기전에 do_signal 커널API를 실행하게 되며 do_signal()는 task structure의 queue에서 signal을 하나 꺼낸 다음 signal에 대한 처리를 수행한다.
signal 처리를 수행 하는 방법은 프로세스가 user모드와 kernel모드로 변경되는 과정에서 프로세스의 실행에 관련된 모든 정보를 저장하는 pt_regs의 정보를 조작하는 방법으로 수행이 된다.
kernel모드에서 user모드로 복귀하는 과정을 살펴 보면 kernel모드로 진입하면서 저장해둔 pt_regs정보를 CPU레지스터에 복원하면서 프로세스는 kernel모드에 진입하기 직전상태에 이어서 계속적으로 로직을 수행할수 있는 것이다.
그리고 do_signal()은 user모드로 복귀하기 직전에 수행이 되면서 user모드로 복귀하자마자 signal handler가 호출될수 있도록 pt_regs정보를 수정하게 되며 이로 인하여 signal handler는 자연스럽게 프로세스가 복원되야하는 지점에서 호출된 함수처럼 동작하고 return을 하면서 원래 프로세스가 수행하려던 로직으로 복귀를 하게된다.
일단 signal이 동작하는 방식은 대충 이해했으니.. 이 정보를 기반으로 pending, block, ignore의 동작 방식을 정리해보자.
pending
task structure의 queue에 signal이 달려있으나 아직 do_signal()이 signal를 안 꺼낸 상태(즉 signal을 받았으나 아직 실행전)를 의미하며 만약 동일한 signal이 또 발생시 queue를 검사해서 동일한 signal이 존재하면 pending상태로 보고 queue에 넣은 과정을 생략하고 폐기하게 된다. queue에는 동일 signal이 1개만 pending될 수 있다.
block
task structure의 queue에 있는 signal을 실행하기 위해서 꺼내는 과정 중 block 비트가 켜진 시그널은 queue에 그냥 두고 block 비트가 없는 시그널중에 가장 앞에있는 것을 꺼낸다. 결과적으로 block이 풀릴때 까지 해당signal은 queue에 머물수 밖에 없으며 이로 인하여 pending상태가 유지된다.
ignore
task structure의 queue에서 signal을 꺼내고 ignore bit가 켜진 시그널이면 그냥 버린다.
시그널 handler가 수행중이라면 ?
시그널 handler가 실행되기 직전에 do_signal()에 의해서 해당 signal에 상태를 block으로 바꾸고 user모드로 진입하기 때문에 handler가 실행중일때 동일한 시그널이 들어오면 자연스럽게 Block과 동일하게 동작을 하며 이로인하여 동일한 시그널은 1개만 pending이 되는것이다.
* 만약 SA_NODEFER옵션이 있을 경우 handler 실행 전에 block으로 바꾸는 행위는 하지 않음. handler가 수행중일 지라도 동일한 시그널이 치고 들어올수 있음.
댓글 없음:
댓글 쓰기