kill로 시그널 보낸 후 다시 시그널을 받아오는 부분이 안됩니다ㅠㅠ
글쓴이: s7mile / 작성시간: 금, 2015/11/06 - 3:35오후
리눅스를 공부한지 얼마 안된 초보자입니다ㅠㅠ
kill(ABC, SIGUSR1); printf("dddd"); signal(SIGUSR2, sig_handler); pause();
pid를 ABC로 가진 곳에 SIGUSR1을 보냈습니다
그리고 ABC에서는 다시 SIGUSR2를 kill로 보내는데요
위 코드의 SIGUSR1 시그널을 보낸 후 뒷코드(출력문조차ㅠㅠ)가 실행이 안되고
사용자 정의 시그널2라고 출력만 됩니다
SIGUSR2를 받긴 하는것같은데 왜 뒷코드들은 실행이 안되는걸까요?
Forums:
printf() 까지 가기도 전에 ABC 가
printf() 까지 가기도 전에 ABC 가 SIGUSR2 시그널을 보내면 어떻게 되는지 따져보세요.
printf()는 사용자 영역에서(운영체제영역이
printf()는 사용자 영역에서(운영체제영역이 아닌) 버퍼링을 행합니다.
이는 단일 프로세스 프로그램에서는 좋은데, 다중 프로그램(2개 이상)이 동작하는 것을 모니터링하는데 어려움을 줍니다.
무슨 얘기냐면, printf() 호출한다고 즉시, 출력하는게 아니고, 나름(프로그래머 의지와 관계없이) 효율을 위해 버퍼링을 하기 때문입니다.
* 빈차로 왔다갔다? 휘발유 절약하는 융통성(효율을 위해) 발휘해서, 시간은 다소 지연되지만 몇명 더 태우고 출발? 이런 격으로 보시면 되겠지요.
그래서, 가급적이면, 다중 프로그램(한개 이상의 프로세스가 협동으로 동작하는 프로그램)을 하는 사람들은 printf()를 사용하지 않고, 사용자 영역 버퍼링 없이 즉시 출력하는 write()를 즐겨 사용합니다.
그런데, write()를 사용하려면, 문자열 버퍼상에 먼저 포맷팅을 하는 snprintf()또는 유사 함수 등을 써야 해서 귀찮아 집니다.(사람에 따라 다르지만요.)
이때, 다음 처럼 사용자 영역에서 출력을 버퍼링하게 하는 stdout 의 버퍼를 없애 주면, printf()를 호출해도 버퍼링 없이 즉시, 출력됩니다.
즉, snprintf() + write()한 효과라고 볼 수 있겠지요.
setbuf(stdout, NULL);
위 문장을 프로그램 시작 부분에(적어도 printf() 호출 전에 한번! 저 같으면 프로그램 시작하자마자...ㅎ) 두고 다시 실행해 보세요.
----------------------
아울러, signal() 즉, 시그널 핸들러에 대한 등록은 프로그램 시작할때 하는게 적절합니다.
즉,
// 시그널 핸들러 달기
signal(SIGUSR2, sig_handler); // 시그널을 등록만 하는 것임, 즉, 언제라도 SIGUSR2가 오면 sig_handler를 호출하겠음
setbuf(stdout, NULL); // 출력을 버퍼링 하지 않고 즉시 출력하겠음
kill(ABC, SIGUSR1); // ABC라는 프로세스에게 SIGUSR1이라는 시그널을 보내라.
printf("dddd");
pause();
--------------
그리고 나서도 또 문제가 있는데요.
1) 이 프로그램이 kill()하는 시점에 ABC라는 프로세스가 아직 동작하고 있지 않다면 어떻게 될까요?(다른 프로세스로 인해 바쁘면 충분히, 발생할 수 있습니다. 아울러, 손이 느린 사람인 경우도 ㅎ)
2) 시그널은 신뢰성이 없어서(unreliable), ABC라는 프로세스에게 간다고 보장할 수 없습니다. 예를 들어, 운영체제가 바쁘거나 하면, 해당 시그널을 전달하지 않고, 버릴 수도 있습니다. 통상적으로는 운영체제가 배달을 하긴 하지만, 더 중요한 사유로, 바쁘면 버릴 수도 있다는 얘기지요. 참고로, 시그널은 운영체제가 중계해주는 통신 메카니즘입니다. 시그널은 반드시 운영체제를 경유한다는 얘기입니다.
그럼 빈번하게 잃어 버리느냐? 그렇지는 않습니다. 아주 가끔. 그러나, 이런 가능성 조차도 고려해 주어야 한다는......
1), 2) 문제는 반대편 프로세스에게도 해당하는 문제입니다. 입장바꿔서....
자, 그렇다면 어찌해야 할까요?
1)을 보장하기 위한 여러가지 동기화 메카니즘을 동원합니다.(학습할 때는 sleep()도 좋습니다. 단, 실전에서 쓰면 안되고요.)
2)를 보장하기 위해서 시그널을 사용하려면 신뢰성있는(reliable signal)을 사용합니다. sigaction() 참조.
----- 고 찰 ---------
* 시그널을 보내는 측은 그 시점을 알고 있지만,(코드에 써 있으니), 받는 측은 언제 받을지 알 수 없습니다. 그래서, 어떤 프로세스가 시그널을 받게 되면 어떻게 되는가를 반드시 알아야 합니다. 시그널을 받으면, 하던일을 중단하고(그게 무엇이던 간에)그에 해당하는 처리 루틴(시그널핸들러)을 실행하게 됩니다. 핸들러는 사용자/프로그래머가 안달아도 기본 핸들러가 동작하게 됩니다. 그리고, 시그널에 따라, 수신한 프로세스가 종료되기도 하고, 등등 처리가 다양합니다. 또, 프로그래머가 인위적으로 받은 시그널을 무시해(maskable) 버릴 수도 있고, 무시할 수 없는(non-maskable) 것도 있습니다. 시그널에 따라, 적절한 핸들러를 고려해야 합니다. 말 그대로, 시그널은 언제 올지 모르니까요. 이에 대한 처리를 게을리하면, 프로그램이 언제 어떻게 될지 모른다는 얘기와 같지요. 세심한 배려가 필요합니다.
아울러, 모든 통신프로그램들은 이와 유사한 성질을 가진다고 보면 됩니다.
* 내친 김에, 시그널은 쓰레드(thread) 개념이 나오기 전부터 존재하던 것입니다. 한 프로세스내에 다수 개의 쓰레드들이 존재할 때, 시그널을 보내면 어느 쓰레드가 받게 될까요? 알 수 없다면, 어떻게 처리하게 해야할까요?
* 멀티쓰레드에서 시그널처리가 중요한 이유는 무엇일까요?
* 시그널 처리가 없는 프로그램들이(혹시 그런 프로그램이 있다면,) 세상에 어떤 영향을 미칠까요?
와 바로 해결되었습니다!
순서만 바꿨는데도 해결되니 신기하네요!
리눅스는 참 신기하고 어려운 분야군요..
답변자님이 적어주신 고찰과 고려사항도 열심히 공부하겠습니다
감사합니다!^_^
댓글 달기