fork 방식의 서버 프로그래밍에서 SIGCHLD 핸들러 수행시 부모프로세스가 멈춰버립니다.
제목대로 일반적인 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 하는 문장
}
에러 코드가 EINTR 일때
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.
***************************************/
Signal handler에서 cout 쓰면 안됩니다.
지금 문제가 그것 때문인 것 같지는 않지만, 일단 signal handler에서는 cout 못씁니다. 심지어 printf도 못씁니다. 썼다간 정상 동작을 보장할 수 없습니다. (이게 매우 무서운 말인데, 개발중에는 잘 돌다가 고객이 손을 대는 순간 터진다는 뜻입니다.)
Signal handler에 사용할 수 있는 함수는 매우 제한되어 있습니다. Stevens의 Advanced Programming in the UNIX Environment를 참조하시길...
여기에도 있군요. (제일 아래로 내리세요.)
http://www.opengroup.org/onlinepubs/009695399/functions/xsh_chap02_04.html
댓글 달기