Skip site navigation (1)Skip section navigation (2)
Date:      Thu, 10 Aug 2000 18:08:38 -0700
From:      Alfred Perlstein <bright@wintelcom.net>
To:        Michael Owens <owensmk@earthlink.net>
Cc:        hackers@FreeBSD.ORG
Subject:   Re: R. Stevens select() Collisions Scenario
Message-ID:  <20000810180838.V4854@fw.wintelcom.net>
In-Reply-To: <39934E64.A5BEE8EE@earthlink.net>; from owensmk@earthlink.net on Thu, Aug 10, 2000 at 07:52:52PM -0500
References:  <39934E64.A5BEE8EE@earthlink.net>

next in thread | previous in thread | raw e-mail | index | archive | help
* Michael Owens <owensmk@earthlink.net> [000810 17:51] wrote:
> Purpose:
> 
> I am trying to write a non-blocking, preforked server, specifically to
> run on FreeBSD, and have a general question as to whether or not my
> strategy is sound.
> 
> 
> Problem:
> 
> In Unix Network Programming Vol. 1 (section 27.6, p.741), Stevens
> mentions a scenario under a preforked server design where multiple
> children are calling select() on the same descriptor, causing collisions
> for the kernel to resolve. The children would look something like...
> .
> .
> .
> for( ; ; ){
>     FD_SET(listfd,&rset);
>     Select(listfd+1,&rset,NULL,NULL,NULL);
>     if(FD_ISSET(listfd,&rset)==0)
>         err_quit("listenfd readable");
> 
>     clilen = addrlen;
>     connfd = Accept(listenfd, cliaddr, &clilen);
> 
>     process(connfd);
>     Close(connfd);
> }
> .
> .
> .
> 
> Hypothesis:
> 	
> If you were implement the server such that the children used
> non-blocking I/O, so that select() returned immediately, would this
> alleviate the problem of collisions in the kernel and its associated
> overhead?

Select ignores non-blocking IO, if you have a non-blocking socket and
perform a select on it, you _will_ block (unless you use a zero valued
timeout).

> If so, would you then have to worry about mutual exclusion when select
> did return a ready descriptor for accept(). For example, a child gets
> switched just after calling select() but just before it makes it to
> accept(). Thus, the next child would also receive the same descriptor
> ready for accept()? Now only one of the two children will get to
> accept() first, leaving the other blocking on accept(). Stevens mentions
> a similar case at the end of the Nonblocking I/O chapter (section 15.6,
> p. 422) and to avoid this he recommends 1) setting the listening
> descriptor to non-blocking and 2) ignoring EWOULDBLOCK, ECONNABORTED,
> etc. on accept().

Yes, that is correct and the correct thing to do.

> So, if you make the listening descriptor non-blocking, and treat
> select() and accept() appropriately, you should be alright in both
> avoiding select collisions and there overhead, as well as avoid children
> blocking due to losing possible race conditions for the listen
> descriptor in the event (albiet small) of a context switch between
> select() and accept().

Another way of doing this would be to have a 'master' accept process
that uses fd passing (it's in your book) to hand off connections to
children, this would avoid select collisions at the cost of an additional
context switch.

> 
> Apology:
> 
> The reason I bother you with all this is that while I (think I)
> understand the logic, I (know I) am at the limits of my understanding,
> and might be missing some important considerations as to other goings on
> in the kernel, and that there might be a better way to go about all
> this.

Well for one thing I wouldn't be using select, I would use FreeBSD's
kqueue mechanism, it's vastly superior to select and poll, although
given the choice of select vs poll I would use poll().

> 
> Summary:
> 
> Ultimately, all I am seeking to do is have an efficient and scalable
> server design, and to my knowledge, this would consist of a number of
> preforked children who are non-blocking/multiplexing themselves.
> Furthermore, each child will have a pool of worker threads which will
> handle jobs sent by different clients. As long as the threads' work
> doesn't entail an operation that blocks for appreciable amount of time
> (so that a single thread puts the whole child to sleep), it would seem
> like this might be a decent proposal: multiple clients and multiple jobs
> being handled simultaneously by each child, and the number of children
> can be controlled by the parent according to the load and limits of the
> hardware.
> 
> Does this seem like a good way to go about it?

Yes. :)  When FreeBSD gets scheduler activations you'll be able to change
to a single threaded process that will have excellent performance, the
scheduler activations are just around the corner.

> 
> Thanks:

you're welcome.

> 
> Thanks.

too much coffee today? :)

-Alfred


To Unsubscribe: send mail to majordomo@FreeBSD.org
with "unsubscribe freebsd-hackers" in the body of the message




Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?20000810180838.V4854>