Shell lab Trace 08 은 부모 프로세스가 sigint 를 수신하면 foreground 로 실행 중인 job 을 종료하는 문제입니다.
myintp 의 소스 코드를 열어보면 다음과 같습니다.
/*
* myintp.c - Sends a SIGINT to its parent (the shell)
*
* A correctly written shell will echo the SIGINT back to the child.
*/
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <signal.h>
#include <stdlib.h>
#include "config.h"
void sigalrm_handler()
{
exit(0);
}
int main()
{
signal(SIGALRM, sigalrm_handler);
alarm(JOB_TIMEOUT);
if (kill(getppid(), SIGINT) < 0) {
perror("kill");
exit(1);
}
while(1);
exit(0);
}
주석을 읽어보면 shell 에 sigint 를 보낸다라고 적혀있습니다. 실제로 소스코드를 보면 main 함수에서 sigalrm을 sigalrm handler 로 제어하겠다고 signal 함수를 이용했고, alarm(job_timeout) 을 호출합니다. 그리고 부모 프로세스에 sigint signal을 송신합니다. 그 이후 while(1)에서 무한반복문을 돌며 myintp는 계속 running됩니다.
myintp의 kill(getppid(), sigint) 에 의해 sigint를 수신한 부모 프로세스 tiny shell이 foreground job 을 종료시키려면 어떻게 해야할까요?
프로세스를 종료하는 방법에는 프로세스가 exit을 만나 종료하거나 kill 명령어를 통해 shell 에서 실행 중인 프로세스를 직접 종료시키거나 또는 kernel 의 인터럽트와 예외적인 제어흐름의 추상화인 signal 을 통해 종료시키는 것이 있었습니다.
지금은 forground job 이 실행 중일 때, tiny shell 이 sigint 를 받으면 foreground job을 종료시켜야 하는 경우이니 3번째 방법인 signal을 이용해야 합니다.
먼저 tsh.c 의 main 함수부를 보면 다음과 같이 sigint 를 sigint_handler로 다루겠다고 명시되어 있습니다.
따라서 sigint_handler 를 적절하게 정의해주어야 합니다. sigint_handler 를 정의해줄 때 무엇을 고려해주어야 할까요?
sigint_handler in Trace08
- foreground job
- foreground job 에 종료 시그널 보내기
이 두가지를 고려하여 sigint_handler 를 다음과 같이 구현할 수 있습니다.
kill(pid, SIGINT)에 의해서 tiny shell 이 sigint 를 수신하면, tiny shell이 foreground job 에 sigint 를 송신하고 따라서 foreground job 은 종료됩니다. foreground job 은 부모 프로세스인 tiny shell 의 자식 프로세스이니 foreground job 이 종료되는 순간, kernel 은 tiny shell(부모 프로세스)에 sigchld 시그널을 송신합니다. 그렇다면 tiny shell 은 무엇을 해주어야 할까요? 바로 좀비가 된 자식 프로세스의 제거와 job list에서 자식 프로세스를 제거해주어야합니다. sigchld 가 수신되었을 때 코드 작성자가 이러한 명령을 내려줄 수 있는 함수가 sigchld_handler 이므로 sigchld_handler를 잘 구현해봅시다.
sigchld_handler in Trace 08
- 종료되어 좀비가 된 자식 프로세스를 정리
- Trace 08 이 원하는 출력 방식으로 출력
- job list 에서 foreground 로 실행되던 job 의 pid 제거
- 단, signal 에 의해 죽은 프로세스들에 대해서만 수행되어야 함. 그렇지 않으면 다른 백그라운드 프로세스들이나 정상 종료된 프로세스들에 대해서도 2번이 수행됨.
위 사항들을 구현한 sigchld_handler 의 코드는 다음과 같습니다.
추가로, WTERMSIG는 어떤 시그널로 종료되었는지를 리턴합니다.
그리고 수정해주어야할 부분이 하나 더 있습니다.
이전 Trace 에서 foreground 와 background로 프로세스를 실행시키는 작업을 했습니다. 그 때 eval 함수에 이런 부분이 있었습니다.
Foreground 로 job 이 등록될 경우, foreground job 이 종료되기 전까지 부모 프로세스(tiny shell)을 suspend 시켰다가 종료 이후 foreground job 을 제거하고, joblist 에서 foreground job 의 process id 를 제거했습니다. 즉, 자식 프로세스의 제거가 이 부분내에서 이루어졌지만, Trace 08 에서 부모 프로세스(tiny shell) 의 자식 프로세스가 종료될 때마다 발생하는 sigchld 에 대한 sigchld_handler에서 waitpid 를 통해 자식 프로세스를 모두 제거해주므로, 이 eval 함수 내의 부분을 수정해줄 필요가 있습니다. 그렇지 않으면 같은 프로세스가 종료되기까지 부모 프로세스를 정지시키고 자식 프로세스를 제거하는 작업을 중복해서 두 번 수행하므로 문제가 생길 수 있음을 예상할 수 있습니다.
따라서 eval 함수 내의 부분을 다음과 같이 수정해주어야 합니다.
먼저 앞서 언급한 if(!bg) 분기문을 살펴봅시다. foreground job 을 실행시키고 있을 때 부모 프로세스인 tiny shell은 반복문을 수행하게 됩니다. pid 에는 fork에 의해서 foreground job 으로 실행 중인 자식 프로세스의 id 가 저장되어 있을 것이고 fgpid(jobs)를 통해 현재 실행 중인 job 들 중 foreground job 의 process id 를 얻어와서 sigsuspend를 수행합니다. foreground job 이 종료되면 sigchld_handler의 deletejob 에 의해 jobs의 fgpid(jobs) 결과가 pid 가 아닐 것입니다. 즉 foreground job 이 실행되고 있는 동안에 sigsuspend를 수행하게 되는 것입니다.
Trace 10은 background 로 실행 중이던 자식 프로세스가 종료되었을 때 적절한 처리를 요구하는 문제입니다.
background process 가 종료되면 부모 프로세스인 tiny shell에는 kernel 이 sigchld 를 송신합니다. trace 08에서 이미 적절하게 종료된 자식 프로세스를 reaping 하고 job list를 갱신하도록 sigchld handler를 구현했으므로 자동으로 통과됩니다.
Trace 11은 자식 프로세스가 자신에게 sigint 를 보냈을 때, tiny shell 이 적절하게 동작하도록 (joblist 를 갱신하고, 적절한 출력문구를 출력) 하는 문제입니다.
먼저 trace11.txt를 열어보며 myints 를 실행한다는 것을 알 수 있습니다. myint 에 대해 알아보기 위해 myints.c 파일을 열어보도록 하겠습니다.
주석을 보면 알 수 있듯, myints 는 kill(getpid(), SIGINT) (trace08 의 getppid, trace11은 getpid로 차이가 있습니다.) 를 통해 자신에게 sigint 시그널을 보냅니다. sigint 시그널은 프로세스를 종료시키므로 이 시그널을 받은 자식 프로세스 자신은 종료가 될 것이고, 그러면 부모 프로세스(tiny shell)에는 sigchld 시그널이 갈 것임을 예상할 수 있습니다.
아까 trace08 에서 sigchld를 처리하는 sigchld_handler를 정의할 때 WIFSIGNALED를 통해 시그널에 의해 종료된 모든 프로세스에 대해 처리해주었으므로, Trace 11은 자동으로 처리된 것이나 마찬가지입니다.
'시스템 프로그래밍' 카테고리의 다른 글
[System Programming] 동적 메모리 할당 (Dynamic Memory Allocate) (0) | 2022.11.26 |
---|---|
[Shell lab] Trace 09, Trace 12 (0) | 2022.11.24 |
[System Programming] Shell (1) | 2022.11.22 |
[Shell Lab] Trace07 (0) | 2022.11.15 |
[Shell Lab] Trace05, Trace 06 (0) | 2022.11.15 |