On Thu, Dec 13, 2007 at 12:35:03PM +0100, Otto Moerbeek wrote:
> On Thu, Dec 13, 2007 at 11:10:51AM +0000, Stuart Henderson wrote:
> 
> > On 2007/12/13 09:09, Otto Moerbeek wrote:
> > > It's depening on the application if sockets staying in CLOSE_WAIT are
> > > a problem or not: it might be intentional (in the hulp duplex case),
> 
> Strange typo by me... that's a Dutch word, but not very relevant in
> this context. It should be half-duplex of course. Stupid autonomous
> fingers ;-)
> 
> > > or it might be a program "forgetting" to do a close.
> > 
> > Does select() notify the application of FIN from the other side?
> > 
> > If not, that would explain things, it wouldn't be reasonable for
> > httpd to manually try and receive from all sockets in keepalive
> > to see whether it needs to close the socket, since it will only
> > wait KeepAliveTimeout (default 15s) before it closes them anyway.
> 
> Nice suggestion, but if you've marked the fd for read I would expect
> select to notify if the other side does a shutdown(SHUT_WR).
> 
> Other scenarios are also thinkable: like the server socket being
> blocked because of outgoing data that cannot be written out. That
> might prevent the server from doing a close too. But in the end the
> close will happen, otherwise you would run out of fd's very soon.
> 

I have been doing a little ktrace:ing on the httpd worker process(es)
that gets stuck with a socket in CLOSE_WAIT.

It appears when it finally (after e.g 8.5 minutes) continues, it:
 28749 httpd    1197541469.470707 EMUL  "native"
 28749 httpd    53.276754 RET   write -1 errno 32 Broken pipe
 28749 httpd    0.000031 CALL  close(0x4)
 28749 httpd    0.000005 RET   close 0
 28749 httpd    0.000012 CALL  gettimeofday(0xcfbd12c8,0)
 28749 httpd    0.000002 RET   gettimeofday 0
 28749 httpd    0.000002 CALL  gettimeofday(0xcfbd12c8,0)
 28749 httpd    0.000001 RET   gettimeofday 0
 28749 httpd    0.000002 CALL  stat(0x2d5a3945,0xcfbd1370)
 28749 httpd    0.000002 NAMI  "/etc/resolv.conf"
 28749 httpd    0.000009 RET   stat 0
 28749 httpd    0.000012 CALL  open(0x2d5a1ffb,0,0x1b6)
 28749 httpd    0.000004 NAMI  "/etc/hosts"
 28749 httpd    0.000005 RET   open 4
 28749 httpd    0.000003 CALL  fstat(0x4,0xcfbd16d0)
 28749 httpd    0.000002 RET   fstat 0
 28749 httpd    0.000003 CALL  mprotect(0x8111e000,0x1000,0x3)
 28749 httpd    0.000005 RET   mprotect 0
 28749 httpd    0.000006 CALL  mprotect(0x8111e000,0x1000,0x1)
 28749 httpd    0.000003 RET   mprotect 0
 28749 httpd    0.000002 CALL  read(0x4,0x8103a000,0x4000)
 28749 httpd    0.000004 GIO   fd 4 read 725 bytes
that must be the start of finding my own hostname, it proceeds with
looking up the remote host over DNS and then writes a log entry:
 28749 httpd    0.000019 CALL  write(0x16,0x7cc0b4ac,0x7c)
 28749 httpd    0.000008 GIO   fd 22 wrote 124 bytes
:
 28749 httpd    0.000002 RET   write 124/0x7c
 28749 httpd    0.000011 CALL  close(0x3)
 28749 httpd    0.000016 RET   close 0
 28749 httpd    0.000003 CALL  sigaction(0x1e,0xcfbd1e00,0xcfbd1df0)
 28749 httpd    0.000003 RET   sigaction 0
 28749 httpd    0.000004 CALL  accept(0x12,0xcfbd1e50,0xcfbd1e3c)
 28749 httpd    2.751318 RET   accept 3
there comes the next connection. Note the close(0x3) call.
The lsof output said the HTTP connection socket had fd 3.

To compare with the end of a later successful request/reply
in the same log:
 28749 httpd    0.000004 CALL  write(0x3,0x85b7200c,0xb57)
 28749 httpd    0.000042 GIO   fd 3 wrote 2903 bytes
:
 28749 httpd    0.000010 RET   write 2903/0xb57
 28749 httpd    0.000006 CALL  gettimeofday(0xcfbd12c8,0)
 28749 httpd    0.000003 RET   gettimeofday 0
 28749 httpd    0.000002 CALL  gettimeofday(0xcfbd12c8,0)
 28749 httpd    0.000002 RET   gettimeofday 0
 28749 httpd    0.000001 CALL  stat(0x2d5a3945,0xcfbd1370)
 28749 httpd    0.000002 NAMI  "/etc/resolv.conf"
 28749 httpd    0.000009 RET   stat 0
and then lookup of my name and the other name, and write log:
 28749 httpd    0.000021 CALL  write(0x16,0x7cc0b2ac,0x97)
 28749 httpd    0.000008 GIO   fd 22 wrote 151 bytes
:
 28749 httpd    0.000002 RET   write 151/0x97
 28749 httpd    0.000011 CALL  shutdown(0x3,0x1)
 28749 httpd    0.000017 RET   shutdown 0
 28749 httpd    0.000003 CALL  select(0x4,0xcfbd1b70,0,0,0xcfbd1b68)
 28749 httpd    0.115966 RET   select 1
 28749 httpd    0.000009 CALL  read(0x3,0xcfbd1bf0,0x200)
 28749 httpd    0.000004 RET   read 0
 28749 httpd    0.000004 CALL  close(0x3)
 28749 httpd    0.000004 RET   close 0
 28749 httpd    0.000005 CALL  sigaction(0x1e,0xcfbd1e00,0xcfbd1df0)
 28749 httpd    0.000002 RET   sigaction 0
 28749 httpd    0.000002 CALL  munmap(0x8b60d000,0xa2b)
 28749 httpd    0.000018 RET   munmap 0
 28749 httpd    0.000016 CALL  accept(0x12,0xcfbd1e50,0xcfbd1e3c)

Note that close(0x3) follows after select() RET 1, read() RET 0.

In the fail case log, close(0x3) is called without select() 
and read() probably because it is already known fd 3 is
dead due to the failing write().

So, the conclusion closest at hand is that it is a write of HTTP data
on fd 3 that times out with "errno 32 Broken pipe" after 8.5 minutes,
even though lsof reports the socket as being in CLOSE_WAIT.

Then the question is if this is a TCP stack bug, or a feature.
One might expect write() to return immediately when the 
socket enters CLOSE_WAIT due to remote end socket close...



>       -Otto

-- 

/ Raimo Niskanen, Erlang/OTP, Ericsson AB

Reply via email to