Properly reaping children from a fork()

2003-02-24 Thread G-der
I wasn't sure which group to send this too but -hackers seemed more 
appropriate than -questions.  I've started to play with sockets 
under FreeBSD and have created a very simple server.  All it does is 
listens (on port 2525 by default) and when it receives a connection 
fork()s.  The only purpose it is accept a string, print it to stdout along 
with the number of bytes received.

This is a first attempt for me but I seem to have problems when it comes 
to ensuring that all the children exit like they should.  What happens is 
that each child process remains in a zombied state (as seen through ps).  
Also if you check sockstat you can see that each zombied process still has 
a connection open.

I'm not sure if the problem is how I am trying to close the socket or if 
it is with my signal handling.  I'm currently reading through intro(2) to 
see if there is something simple I've missed.

I've attached the code for your enjoyment...I'm sure someone will be able 
to point out my mistake pretty quickly.  Also I've found a couple of web 
pages that kind of explain socket handling but am looking for other 
resources that I can consult.

Thanks in advance, I'm not a normal subscriber to -hackers so a cc to 
[EMAIL PROTECTED] would be appreciated.

Regards

Gene Dinkey
[EMAIL PROTECTED]

--CUT HERE--

#include sys/types.h
#include sys/socket.h
#include netinet/in.h
#include string.h
#include stdio.h
#include unistd.h
#include signal.h
#include sys/wait.h

#define MYPORT  2525
#define MAXRECV 5
#define MAXBUFSIZE  50

int main ( void );
int establish ( unsigned short portnum );
int get_connect ( int sockfd );
int  get_data ( int sockfd );
void sigchld_handler ( int s );


int main ( void )
{
int s;
//  struct sigaction sa;

if ((s=establish(MYPORT))  0)  //establish the socket
exit(-1);

s = get_connect(s); //wait for a 
connection


/* The commented code was supposed to reap the children in but it didn't
   the call to signal() is supposed to do the same thing, but it doesn't */

/*  sa.sa_handler = sigchld_handler;
   sigemptyset(sa.sa_mask);
   sa.sa_flags = SA_RESTART;
   if (sigaction(SIGCHLD, sa, NULL) == -1) {
  perror(main:sigaction);
  exit(-1);
  } */

signal(SIGCHLD, sigchld_handler);

if (s != -1) {
get_data(s);// get the incoming data
close(s);   // finally close the socket
}
exit(0);
}

/*handles zombied processes*/
void sigchld_handler ( int s )
{
while(waitpid(-1, NULL, WNOHANG)  0);
}


/* Establishses a socket and starts to listen */
int establish ( unsigned short portnum )
{
   int sockfd;
   struct sockaddr_in my_addr;

   if ((sockfd = socket(AF_INET, SOCK_STREAM, 0))  0) {//establish 
the socket
perror(establish:socket);
return(-1);
}

/* host information */
   my_addr.sin_family = AF_INET;
   my_addr.sin_port = htons(portnum);
   my_addr.sin_addr.s_addr = htonl(INADDR_ANY);
   memset((my_addr.sin_zero), '\0', 8);

/* bid the socket to a port */
   if (bind(sockfd, (struct sockaddr *) my_addr, sizeof(struct sockaddr))  0) {
perror(establish:bind);
return(-1);
}

/* finally tell the socket to start listening */
if (listen(sockfd, MAXRECV)  0) {
perror(establish:listen);
close(sockfd);
return(-1);
}

   return(sockfd);  // return the socket handler
}


/*accepts connections*/
int get_connect ( int sockfd )
{
int sin_size, new_fd;
struct sockaddr_in their_addr;

sin_size = sizeof(struct sockaddr_in);

for(;;) {   //begin the accept loop

/* accept the new connection and get the new socket id */
if ((new_fd = accept(sockfd, (struct sockaddr *)their_addr, sin_size))  0) {
perror(get_connect:accept);
return(-1);
}
printf(Server got connection\n);

/* fork the new connection */
if(!fork()) {
close(sockfd);  // close the listener, child doesn't need it 
return(new_fd); // return the new active socket
}
}
return(-1);
}

/*gets some data and prints it out*/
int get_data ( int sockfd )
{
char buffer[MAXBUFSIZE];
int datain;

for (;;) {  //begin the retreival loop

/* get the incoming data and check a few things */
if ((datain = recv(sockfd, buffer, MAXBUFSIZE, 0))  0) {
perror(get_data:recv);
return(-1);
}
else if (datain  0) {  //print number of bytes received
printf(Received 

Re: Properly reaping children from a fork()

2003-02-24 Thread Jan Grant
On Mon, 24 Feb 2003, G-der wrote:

 I've attached the code for your enjoyment...I'm sure someone will be able
 to point out my mistake pretty quickly.  Also I've found a couple of web
 pages that kind of explain socket handling but am looking for other
 resources that I can consult.

You never establish the signal handler.

 Thanks in advance, I'm not a normal subscriber to -hackers so a cc to
 [EMAIL PROTECTED] would be appreciated.

If you've got a bit of spare cash, you might want to peruse a couple of
excellent texts: Advanced Programming in the Unix Environment (APUE) and
Unix Network Programming (particularly volume 1); both by the late W.
Richard Stevens. Well worth the money (and you'll probably receive
several recommendations along the same lines).

Cheers,
jan

-- 
jan grant, ILRT, University of Bristol. http://www.ilrt.bris.ac.uk/
Tel +44(0)117 9287088 Fax +44 (0)117 9287112 http://ioctl.org/jan/
You know something's gone badly wrong when your algorithm
takes O(n^2) time but uses O(2^n) space.



To Unsubscribe: send mail to [EMAIL PROTECTED]
with unsubscribe freebsd-hackers in the body of the message


Re: Properly reaping children from a fork()

2003-02-24 Thread G-der
Thank you everyone for your replies.  The bug was a pretty silly one and 
one that I should have caught just stepping through the code.

The call to signal() to install the handler was beging made after the 
fork().  So the children had a handler installed but not the parent.  I 
moved the signal() call above the accept loop and before the fork and that 
seems to have cleared up that issue.

Again, thanks for the prompt responses...

Regards

Gene Dinkey


To Unsubscribe: send mail to [EMAIL PROTECTED]
with unsubscribe freebsd-hackers in the body of the message


Re: Properly reaping children from a fork()

2003-02-24 Thread Terry Lambert
G-der wrote:
 This is a first attempt for me but I seem to have problems when it comes
 to ensuring that all the children exit like they should.  What happens is
 that each child process remains in a zombied state (as seen through ps).
 Also if you check sockstat you can see that each zombied process still has
 a connection open.

Unless you explicitly close the connection, it's open.  In the case
of a zombie, though, there is a zombie status structure, and the files
are in fact closed.  Probably what you are seeing is the TCP connection
is not being properly closed by the client, or the connection rate is
high enough that the 2MSL timer has not expired by the time you are
looking at it, so they appear open.


 I'm not sure if the problem is how I am trying to close the socket or if
 it is with my signal handling.  I'm currently reading through intro(2) to
 see if there is something simple I've missed.

Who is closing the socket, the client or the server?  If the client
is closing the socket, it must call shutdown(2) on the socket, and
then explicitly call close.   This is because certain user space TCP
implementations, such as those in Windows, do not have proper resource
tracking for sockets, which are implemented in user space, instead of
being implemented in kernel space.

There are also a number fo test applications, which are nothing
more than SYN-guns, i.e. they do not implement the full handshake,
and their only purpose is to connection-load the server.  Many of
these send RST, and then drop all knowledge of the connection.  This
will, over time, result in a large number of outstanding open sockets
on the server, since RST packets do not time out and repeat, since
they do not require ACK'ing: because of that, if they get lost, then
they are lost forever.  This can happen even on a local wire, if you
get a collision or some other event (for example).


 /* The commented code was supposed to reap the children in but it didn't
the call to signal() is supposed to do the same thing, but it doesn't */
 
 /*  sa.sa_handler = sigchld_handler;
sigemptyset(sa.sa_mask);
sa.sa_flags = SA_RESTART;
if (sigaction(SIGCHLD, sa, NULL) == -1) {
   perror(main:sigaction);
   exit(-1);
   } */
 
 signal(SIGCHLD, sigchld_handler);

This is bogus.  You don't want SA_RESTART behaviour here, since it's
not like you have to worry about a system call.  Calling the old
signal code overrides pretty much everything you do with the
sigaction() in any case.

Your main() exits, instead of hanging in wait() or sleep(), and
looping forever, in order to reap connections.

Your fork() returns the wrong way to main() (i.e. child exits and
parent stays around forever), so you have some identity confusion.


NB: An alternate way of setting up automatic reaping, since your
handler seems to not care about exit status, is to set SIG_IGN
as the handler for SIGCLD.

Actually, you'd do well to get a copy of UNIX Network Programming
by Stevens, or, minimally, download the example source code from
the publisher's web site.  It would help you out by providing you
working simple server example source code.

-- Terry

To Unsubscribe: send mail to [EMAIL PROTECTED]
with unsubscribe freebsd-hackers in the body of the message