Reputation: 47
I have a problem with zombie-processes. When I close the connection from the client side, zombie childs don't die. If I close the connection from the server side, everything is OK. There is no zombie child. I'm using the code below
Any Help ??
#define SERV_PORT 1051
#define LISTENQ 1024
void sig_chld(int signo)
{
pid_t pid;
int stat;
while ( (pid = waitpid(-1, &stat, WNOHANG)) > 0)
printf("child %d terminated\n", pid);
return;
}
void *pThread_TCP(void *ptr)
{
int listenfd, connfd;
pid_t childpid;
socklen_t clilen;
struct sockaddr_in cliaddr, servaddr;
void sig_chld(int);
unsigned char pData[255];
unsigned short n;
listenfd = socket(AF_INET, SOCK_STREAM, 0);
bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_port = htons(SERV_PORT);
struct ifreq ifr;
memset(&ifr, 0, sizeof(struct ifreq));
snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "usb0");
ioctl(listenfd, SIOCGIFINDEX, &ifr);
setsockopt(listenfd, SOL_SOCKET, SO_BINDTODEVICE, (void*)&ifr, sizeof(ifr));
bind(listenfd, (struct sockaddr *) &servaddr, sizeof(servaddr));
listen(listenfd, LISTENQ);
signal(SIGCHLD, sig_chld); /* must call waitpid() */
for ( ; ; ) {
clilen = sizeof(cliaddr);
if ( (connfd = accept(listenfd, (struct sockaddr *) &cliaddr, &clilen)) < 0)
{
if (errno == EINTR)
continue; /* back to for() */
}
if ( (childpid = fork()) == 0)
{ /* child process */
while(1)
{
n = recvfrom(connfd,pData,100,0,(struct sockaddr *)&cliaddr,&clilen);
if(n>0)
{
if(pData[0] == '^') break;
sendto(connfd,"OK\r\n",4,0,(struct sockaddr *)&cliaddr,sizeof(cliaddr));
}
}
close(listenfd); /* close listening socket */
exit(0);
}
close(connfd); /* parent closes connected socket */
}
}
Upvotes: 1
Views: 3608
Reputation: 12668
A zombie process consumes only the entry in the process table. The kernel maintains it to allow for the parent process' wait(2)
system call (and family) to be aware that there's actually a process to be waited for and don't fail about calling wait()
without having subprocesses walking around. Those walking dead processes are there to ensure kernel data consistency and, as such, you cannot kill them (even as root user) The only way to ensure that a living parent doesn't have this bunch of zombies around is to do one wait(2)
call for each fork()
it has done before (which you don't do at all). As in your code the thread is going to die just after closing the file descriptor, you have a chance to do a waitpid(pid_of_child, ...);
there, so you'll wait for the proper child. See waitpid(2)
for more info about this system call. This approach will have a non-visible drawback (your thread will last until the child dies). The reason this works normally with processes (the non-need to do wait()
in the parent process) is that you are not dying the parent (the parent lives after the thread dies) and so, the fork()/wait()
relationship maintains. When a parent dies, the kernel makes init (process with id == 1
) the parent of your process, and init(8)
is always making wait(2)
s for the orphaned children in the system.
By just adding the following code after
...
close(connfd); /* parent closes connected socket */
int retcode; /* return code of child process */
waitpid(childpid, &retcode, 0);
} /* for loop */
or, as you are not going to check how did the child terminate
...
close(connfd); /* parent closes connected socket */
waitpid(childpid, 0, 0);
} /* for loop */
This has another drawback, is that you are going to wait for the child to terminate and will not get into the accept(2)
system call before your child terminates, which can be not what you want. If you want to avoid creating child zombie processes, there's another alternative (but it has some other drawbacks) is to ignore the SIGCHLD
signal in the whole process, which makes the kernel not create those zombies (legacy way to do, there are other ways to avoid zombie children) or you can have a new thread just making the needed wait()
s and dispatch the returned values from the children to the proper place, once they die.
Upvotes: 2
Reputation: 204698
Do you really mean "zombie process" (Z
state in ps
)? Those are already dead, but they haven't been reaped by their parent yet.
recvfrom
/sendto
are useless on SOCK_STREAM
sockets, and actually using values other than NULL
/0 can fail on some implementations.pData[0]
is faulty. A client sending "^A\r\n^B\r\n"
could legitimately be received as "^"
followed by "A\r\n^B\r\n"
or as "^A\r\n^"
followed by "B\r\n"
or any other split."OK\r\n"
.recvfrom
returns <0
, as it does in an error state like trying to read from a shutdown socket, you loop forever in while(1)
. This is not a zombie - it's a running process, although one burning CPU for no use.SIGCHLD
nor call wait
/waitpid
/etc. This means that when children exit, they aren't reaped, and this will result in actual zombie processes.Upvotes: 0