fork 방식의 서버 프로그래밍에서 SIGCHLD 핸들러 수행시 부모프로세스가 멈춰버립니다.

cokeman의 이미지

제목대로 일반적인 fork 방식의 서버 프로그램입니다.
클라이언트로부터의 request가 오면 fork하여 child를 하나 만들고 요구사항을 처리하고
부모는 다른 클라이언트로의 접속요청을 대기합니다.
child가 요구 사항을 처리하고 종료하면 SIGCHLD를 발생하고 부모 프로세스는 이 시그널을 인지하면
waitpid를 통하여 child의 리턴값을 처리하고 다시 돌아갑니다.

테스트를 해보니 클라이언트로부터 빠르게 자주 접속 요청이 들어오면 시그널 핸들러인
void sig_child(int signo)의 while ((pid = waitpid(-1, &stat, WNOHANG)) >0 ) 문에서
멈춰버립니다. 처음에는 어디에서 멈추는 지를 몰라서 각 라인별로 printf로 한참을 찾다가
나중에 strace를 통하여 추적을 해보니 그 위치더군요. 너무 빨리 접속해서 그런가 해서
클라이언트의 접속을 1초 단위로 해도 같은 현상이 발생합니다. 또한 count를 해보니 특별히 정해진 횟수
에서 멈추는 것은 아니고 실행할때마다 다릅니다.

pid를 watch해보니까 이미 종료된 프로세스 번호가 pid로 리턴되어있었습니다. (ps로는 없는 child pid더 군요)
제 생각에는 이미 이 세상에 없는 pid를 계속 기다리느라고 block된 것 같은데요.
어떤 이유에서 이런 일이 발생하고 해결방안은 없는지요?
(운영체제는 Linux 2.4.20입니다.)

고수님들 부탁드립니다. ^^

(아래에 소스를 첨부합니다. 넘 길어서 중간중간 필요없는 부분은 빼고 뼈대만 나열했습니다. 이해바랍니다.)

int main(int argc, char **argv)

{
int listenfd, connfd;
pid_t childpid;
socklen_t clilen;
struct sockaddr *cliaddr;
void sig_child(int);

static struct sigaction act;
act.sa_handler=sig_child;
act.sa_flags|=SA_NODEFER|SA_RESTART;

sigaction(SIGCHLD, &act, NULL);
clilen = sizeof(struct sockaddr);
cliaddr = (struct sockaddr*)malloc(clilen);

for (;;) {
if ((connfd=accept(listenfd, cliaddr, &clilen)) <0 ) {
if (errno == EINTR) {
::close(connfd);
cout << "[ener] EINTR Accept Error" << endl;
continue;
}
else {
cout << "[ener] Accept Error!! (err no : " << errno << ")" <<
strerror(errno) << endl;
exit(1);
}
} else {
cout << "[ener] Accept OK.. (connfd : " << connfd << ")" << endl;
}
if ((childpid=fork()) == 0) {
::close(listenfd);
child_main(connfd);
exit(0);
}
::close(connfd);
}
}

void sig_child(int signo)
{
pid_t pid;
int stat;

while ((pid = waitpid(-1, &stat, WNOHANG)) >0 ) // 문제의 위치
cout << "child " << pid << " terminated" << endl;
return;
}

void child_main(int connfd)
{
... 생략.... 단순히 클라이언트로부터의 요청을 echo 하는 문장
}

[/][/][/]
prether의 이미지

accept()가 -1을 리턴하고 에러 코드가 EINTR 일때
보통 바로 continue하는데
아래 코드를 보니

if (errno == EINTR) {
::close(connfd);
cout << "[ener] EINTR Accept Error" << endl;
continue;
}

close(connfd)를 왜 하죠?
close(-1)이걸 실행한 결과가 되는데요.

[/]

/***************************************
Being the one is just like being in love.
***************************************/

jick의 이미지

지금 문제가 그것 때문인 것 같지는 않지만, 일단 signal handler에서는 cout 못씁니다. 심지어 printf도 못씁니다. 썼다간 정상 동작을 보장할 수 없습니다. (이게 매우 무서운 말인데, 개발중에는 잘 돌다가 고객이 손을 대는 순간 터진다는 뜻입니다.)

Signal handler에 사용할 수 있는 함수는 매우 제한되어 있습니다. Stevens의 Advanced Programming in the UNIX Environment를 참조하시길...

여기에도 있군요. (제일 아래로 내리세요.)
http://www.opengroup.org/onlinepubs/009695399/functions/xsh_chap02_04.html

댓글 달기

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
이것은 자동으로 스팸을 올리는 것을 막기 위해서 제공됩니다.