do_exit 함수를 분석 중에 이해가 안되는 부분이 발생하였으나 도저히 해결이 안되어
고수님 분들께 자문을 구하고 싶습니다.
다음 코드는 저희 팀이 분석중인 4.16커널의 do_exit() 함수의 일부입니다.
저희가 적은 주석도 적혀있습니다. (모기향 책의 내용과 코드가 동일합니다.)
모기향 책 638쪽 코드 5-23의 2번 라인입니다.
/*
* We're taking recursive faults here in do_exit. Safest is to just
* leave this task alone and wait for reboot.
*/
//IMRT : 현재 태스크가 종료작업이 다른 곳에서 진행중인지 확인한다.
// - 동시에 종료작업이 수행되는 경우가 무엇인가??????????????
// 하나의 task가 동시에 kernel context에서 수행될 수 있는것인가?
if (unlikely(tsk->flags & PF_EXITING)) {
pr_alert("Fixing recursive fault but reboot is needed!\n"); /*
* We can do this unlocked here. The futex code uses
* this flag just to verify whether the pi state
* cleanup has been done or not. In the worst case it
* loops once more. We pretend that the cleanup was
* done as there is no way to return. Either the
* OWNER_DIED bit is set by now or we push the blocked
* task into the wait for ever nirwana as well.
*/
tsk->flags |= PF_EXITPIDONE;
// 중복되는 do_exit() 호출을 막기 위해 상태 변경하고
set_current_state(TASK_UNINTERRUPTIBLE);
// IMRT : 다른 태스크가 수행할 수 있도록 schedule() 호출함.
schedule();
}
// PF_EXITING 설정하여 해당 태스크가 종료중임을 알린다.
// 만약 태스크가 스레드이고 스레드에 pending 시그널이 있다면 대신 처리해줄 스레드를 스레드 그룹에서 찾아 해당 시그러널을 모두 처리하도록 설정한다.
exit_signals(tsk); /* sets PF_EXITING */
궁금한 것 첫 번째는 이 if문이 참이되는 상황이 발생하는가? 입니다.
if문의 조건부에서 tsk->flags가 PF_EXITING이 활성화 되는 곳은 if문 바깥은
exit_signals()함수가 유일합니다. 또한 이 함수는 do_exit()에서만 호출됩니다.
그렇다면 이 do_exit()함수가 두 번이상 호출되어야 이 if문을 들어오게 되는데,
이러한 "가능성"이 존재하는 지를 모르겠습니다.
예상한 가능성은 다음과 같습니다.
1. 싱글코어에서 한 태스크가 동작 중에 다른 태스크에서 kill 시그널을 날려
do_exit()을 다시 수행하게 된다. 즉, do_exit() 함수를 수행 하는 중에
kill 시그널이 들어와서 exit 시스템 콜을 호출하여 소프트웨어 인터럽트가
다시 발생하여 do_exit()을 다시 호출 (우선순위 전도로 인한 발생일 수도 있고)
2. 멀티코어에서 do_exit()을 동작하는 중에 다른 태스크에서 kill 시그널을 날려
1번과 같이 exit 시스템콜에 의한 kill
3. do_exit()이 동작 중에 높은 우선순위 태스크에 의해 선점당하였는데 해당 태스크에서
kill 시그널 전송하여 1번처럼 다시 exit 시스템콜 수행
do_exit()이 처리되고 있는 상황은 이미 kernel mode에서 수행되고 있을 터인데,
do_exit()의 중간에서 스케줄링 되었을 때, 해당 context를 저장하고 있을 텐데,
시그널에 의해 새로이 context가 갱신 될 수 있는지 모르겠습니다.
그 가능성이 아니면 도저히 위 if문의 조건을 충족시킬 수 있는 상황이 발생하는지 모르겠습니다.
두 번째 궁금한 것은, 위와 같이 signal이 발생한다면, signal을 발생하지 않게 만들지 않은 이유는
무엇일까요?
저 if문을 수행할 때'만' TASK_UNINTERRUPTIBLE로 만들어 스케줄링 해버립니다.
즉, 스케줄링도, 인터럽트도 받지 않는 좀비가 되어버리는 것이죠.
중복해서 do_exit()을 수행 시, 왜 좀비로 만들어버리는가? 이미 처리중인 do_exit()을 마무리하고
프로세스를 종료하면 되지 않는가?
만약 그것이 이점이 있다면 왜 진작 처음 do_exit()을 수행하는 중에 UNINTERRUPTIBLE로 만들어
시그널을 받지 않게 만들지 않았는가? TASK_KILLABLE로 만들기라도 하면 좋겠는데,
도저히 그런 부분을 찾을 수가 없네요...
이러한 궁금증이 생깁니다.
do_exit()함수는 마지막에서 do_task_dead()함수를 수행하며(모기향 책과는 조금 다름)
do_task_dead()에서 tsk->state를 TASK_DEAD로 바꾸고, 스케줄링합니다(모기향 책에서는 do_exit()에서 수행)
그렇다면, 위 if문을 충족시키는 경우는 do_exit()함수 안에서 다른 함수를 거쳐서
do_exit()을 재귀호출하게 되는 경우 밖에 없는 것 같은데, 이게 발생할 수 있는 사례인가요?
do_exit()을 하는 도중에 memory 요청을 했는데 fault가 나서 do_exit(SIGSEGV)가 발생한다거나...
답변 기다리겠습니다.
답변해주시면 정말 감사드리겠습니다.
댓글 2
-
구본규
2019.07.15 19:40
-
코딩의노예
2019.07.16 23:19
답변 감사합니다.
저희도 페이지폴트 등 상황에서 발생하지 않을까? 라는 예상을 하긴 했는데
페이지 폴트시에 die() 혹은 do_exit()함수를 호출하게 되나요?
찾아본다고 찾아보긴 했는데 따라가보니까 die나 do_exit함수를 호출하는 부분을 못찾아서..
정상 종료 시에 실패했다면, 나중에 처리할 것이 아니라 스케줄링과 인터럽트를 막아버리는 것이 어떤 철학에 의해선지 이해가 가지 않아서 질문 드렸습니다 ㅜㅜ
다른 상황에서 프로세스(pid 1번 등)에 의해서 처리를 해주지 않고, 현재 상태를 그대로 유지하고 재부팅까지 남겨둔다는 것이요..
.
저는 분석하지 않은 함수라 대략 훑어보고 답변 달아봅니다.
Q1. 저런 상황이 나올 수 있나?
-> 있죠. do_exit() 처리 중에 재진입을 막지 않았으니까요. 사례는 말씀하신 것 같이 do_exit() 수행 중에 exit_mm() -> mm_release() 등에서 페이지 폴트가 난 케이스도 해당되겠네요.
Q2. 왜 저렇게 처리하나?
-> 정상 종료하려다가 실패했으니까요. 주석을 보니 막아놔도 한 번 더 진입할 수도 있다고 하는데, 안 막아두면 계속 실패를 반복하겠죠. 원래 do_exit() 종료 후 리턴하지 않으니 더이상 스케줄링 되지 않도록 막아둬도 되겠네요.