[질문] SIGCHLD 처리에 대한 질문입니다.

minij의 이미지

안녕하세요.
리눅스에서 코딩을 처음 하는 초보입니다.
지금 간단한 리모트 쿼리에 간단한 응답을 날리는 동시 접속 서버 프로그램을 작성중입니다.

리퍼런스 책과 이곳 게시판을 참조해서 작업중인데,
Child signal을 처리해 주지 않으면 child 프로세스가 좀비 상태로 남는다는 말을 들었습니다.
이거 저거 참고해서 SIGCHLD를 처리하고 작업을 일단 마무리 했는데,
이 처리 부분에서 가끔 시간이 매우 많이 걸리는 현상이 일어납니다.
다음은 소스 코드입니다.

/************************************************************************************************
   child_signal_handler
************************************************************************************************/

static void child_signal_handler(int sig)
{
	int stat;
	pid_t pid_child;
	
	/* Process dead child */
	do 
	{
		pid_child = waitpid(-1, &stat, WNOHANG);
	} while (pid_child != -1);
	
	/* Reset child signal handler */
	set_child_signal_handler();
}


/************************************************************************************************
   set_child_signal_handler
************************************************************************************************/

int set_child_signal_handler(void)
{
	int res;
	struct sigaction actsig;
	
	/* Setting */
	actsig.sa_handler = child_signal_handler;
	sigfillset(&(actsig.sa_mask));
	actsig.sa_flags = 0;
	
	/* Set child signal handler */
	res = sigaction(SIGCHLD, &actsig, NULL);
	
	return res;
}


/************************************************************************************************
   main
************************************************************************************************/

int main(int argc, int *argv[])
{
	/* Initialize Globals */
	initialize_globals();
	
	/* Set shild signal handler */
	if (set_child_signal_handler() == -1)
	{
		terminate_server("Setting child signal handler failure !");
	}
	
	/* Ignore SIGPIPE signal */
	signal(SIGPIPE, SIG_IGN);
	
	/* Run main server */
	run_server();
	
	/* Will not be processed */
	return 0;
}

보시다시피 메인에서 SIGCHLD 시그널 핸들러를 설정하고, SIGPIPE를 무시하고, 바로 서버를 돌립니다.
서버 프로그램은 포크로 자식을 분기하는 일반적인 루틴입니다.

테스트 중에 한 클라이언트가 종료하고 난 후에,
서버 프로그램의 CPU 점유율이 99%로 올라가는 현상을 발견했습니다.
디버깅을 해보니 child_signal_handler의 while 문에서 많은 시간이 흘러가고 있었고, 경우에 따라서 몇 분씩 걸릴 때도 있었습니다.

여기 저기서 보고 만든 것이라, 제가 한 방법이 틀릴 수도 있을 것입니다.
작은 힌트에서부터 커다란 도움까지 모두 환영합니다.
도와 주세요. ㅠ.ㅠ

yoocj9의 이미지

child가 두개이상 생기게 되면,
waitpid()가 0을 리턴하는 경우가 생기게 되죠.

그러면? do while 이...

minij의 이미지

yoocj9 wrote:
child가 두개이상 생기게 되면,
waitpid()가 0을 리턴하는 경우가 생기게 되죠.

그러면? do while 이...

그럼 0을 리턴해도 Child Process는 정상 종료되는 건가요?

Aim high !

jinyeong의 이미지

while 문의 ret 값 체크를 > 0 로 바꿔 주십시오.

Quote:
그럼 0을 리턴해도 Child Process는 정상 종료되는 건가요?

man waitpid의 WNOHANG와 return value 0를 참조하시길..

I thought what I'd do was,
I'd pretend I was one of those deaf-mutes.. or should I?

최종호의 이미지

코드 상으로는 별 문제 없어 보이는데요,,
로그를 찍어보세요.. ^^
Posix계열 signal handling은 reliable 하니까
다시 SIGCHLD를 설정해주실 필요는 없을 것 같고요.

Solaris쪽 waitpid() man페이지를 보니까

Quote:

RETURN VALUES
If waitpid() returns because the status of a child process
is available, it returns a value equal to the process ID of
the child process for which status is reported. If wait-
pid() returns due to the delivery of a signal to the calling
process, -1 is returned and errno is set to EINTR. If wait-
pid() was invoked with WNOHANG set in options, it has at
least one child process specified by pid for which status is
not available, and status is not available for any process
specified by pid, then 0 is returned. Otherwise, -1 is
returned and errno is set to indicate the error.

같은 부분이 보이네요.
child는 있는데 status가 available하지 못하면 0이 리턴될 수도 있다고 하네요.

테스트해 보니까 child process가 STOP 되는 경우
(해당 process에 kill -STOP 으로 STOP시그널을 주었습니다.)
waitpid()가 0을 리턴하면서 무한루프에 빠지는경우가 발생하네요.
WUNTRACED 옵션을 주어서 상태를 얻어올 수 있는데
그 다음번 waitpid() 시에 곧바로 0이 리턴되어서 해결책은 안되는 듯 싶습니다.
waitpid() 가 0을 리턴했을때도 signal handler를 빠져나가는 것도 방법일 듯 싶은데,
실제로 exit()한 녀석들(status가 available한)과 STOP된 녀석들이 waitpid()시에 동시에 있을 때
STOP된 녀석을 맨 마지막에 wait해 줄지가 보장이 되어있지 않고,
STOP된 녀석을 wait한 후에 0이나 해당 프로세스 ID를 리턴한 후
(WUNTRACED 옵션에 따라서 틀림), 다음번에 waitpid를 호출했을 때
그 다음 대기하고 있는 child process를 wait해 줄지, 아니면 STOP된
녀석이 계속 wait되어 0이 리턴될지에 대한 내용이 스펙에 기술이 안되어 있는 문제가 있습니다.

Posix 쪽에서는

Quote:

The wait() and waitpid() functions shall obtain status information pertaining to one of the caller's child processes. Various options permit status information to be obtained for child processes that have terminated or stopped. If status information is available for two or more child processes, the order in which their status is reported is unspecified.

라고해서 status가 available한 경우에 그 순서는 정해져있지 않다고 하고,
status가 available한 process와 available하지 않은 process간의 순서에
대한 사항은 명확히 나와있지 않습니다.

테스트 해 본 결과로는 STOP된 프로세스는 맨 마지막에 wait가 되었습니다.
status 에 NULL을 주어서 상태를 얻어오지 않아도 역시 0을 리턴했습니다.

또 interrupt에 의해서도 0을 리턴하는 것 같은데,
현재 코드에서는 모든 시그널을 마스킹 해 놓으셨으니까
이 경우는 아닌 듯 싶고요.

minij의 이미지

답변주신 최종호님, 웃는남자님, yoocj9님 감사합니다.
덕분에 이 문제를 해결할 수 있었습니다.

이제 또 다른 테스트에 들어가야지요. ^^
또 모를 때, 질문드리겠습니다.

행복하시구, 좋은 하루가 되시기를 !

Aim high !

minij의 이미지

최종호 wrote:

Posix계열 signal handling은 reliable 하니까
다시 SIGCHLD를 설정해주실 필요는 없을 것 같고요.

왜 이걸 다시 설정해야 하는지 무지 궁금한 부분이었는데... Reliability 문제였군요.
여러 가지로 테스트까지 해주시고 신경 써 주셔서 감사합니다. ^^

Aim high !

댓글 달기

Filtered HTML

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

BBCode

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param>
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

Textile

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • You can use Textile markup to format text.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Markdown

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • Quick Tips:
    • Two or more spaces at a line's end = Line break
    • Double returns = Paragraph
    • *Single asterisks* or _single underscores_ = Emphasis
    • **Double** or __double__ = Strong
    • This is [a link](http://the.link.example.com "The optional title text")
    For complete details on the Markdown syntax, see the Markdown documentation and Markdown Extra documentation for tables, footnotes, and more.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Plain text

  • HTML 태그를 사용할 수 없습니다.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 줄과 단락은 자동으로 분리됩니다.
댓글 첨부 파일
이 댓글에 이미지나 파일을 업로드 합니다.
파일 크기는 8 MB보다 작아야 합니다.
허용할 파일 형식: txt pdf doc xls gif jpg jpeg mp3 png rar zip.
CAPTCHA
이것은 자동으로 스팸을 올리는 것을 막기 위해서 제공됩니다.