Re: select for UNIX sockets?

Krzysztof Halasa
2003-06-07T23:55:23Z

"David Schwartz" <davids@webmaster.com> writes:

> You are doing something wrong. You are using 'select' along with
> blocking
> I/O operations. You can't make bricks without clay. If you don't want to
> block, you must use non-blocking socket operations. End of story.

There is a little problem here. Do you see any place for select() here?
There isn't any.

If you have a working select(), you can use (blocking or non-blocking)
I/O functions a get a) low latency b) small CPU overhead.
If you want to use non-blocking I/O, either with broken select() or
without it at all, you get either a) high latency, or b) high CPU overhead.

> Just because 'select' indicates a write hit, you are not assured
> that some
> particular write at a later time will not block. Past performance does not
> guarantee future results.

The problem is select() on UNIX datagram sockets returns immediately,
and thus it could be well substituted by a NOP. There isn't any
"performance".

> Suppose, for example, a machine has two network interfaces. One is very
> busy, queue full, and one is totally idle, queue empty. What do you think
> 'select' for write on an unconnected UDP socket should do? If you say it
> should block, then it can block forever even if there's plenty of buffer
> space on the network card you were going to send to. So, it can't block, it
> must indicate writability.

That's a little different problem, and a datagram will be transmitted by
this busy interface at last (while you will never send a datagram if nobody
is reading the socket).

Hoverer, select() doesn't work on connected sockets either (I missed
the fact the example program doesn't connect at first, but it's
unimportant here).

> You have any number of sane choices. My suggestion is that you make the
> socket non-blocking and treat an EWOULDBLOCK return as equivalent to
> success. You can additionally take it as a hint that the packet will be as
> if it was dropped.

You essentially transform a code such as:
while () {
select();
blocking_send();
}

into:

while() {
non_blocking_send();
}

Not very CPU-friendly :-(

Having working select() on at least connected sockets is a must.

intrepid:/tmp$ strace -f ./test 2>&1 |egrep 'socket|bind|connect|send|recv'

[pid 1051] socket(PF_UNIX, SOCK_DGRAM, 0) = 3
[pid 1051] bind(3, {sa_family=AF_UNIX, path="/tmp/test"}, 11) = 0
[pid 1050] socket(PF_UNIX, SOCK_DGRAM, 0) = 3
[pid 1050] connect(3, {sa_family=AF_UNIX, path="/tmp/test"}, 11) = 0
[pid 1050] send(3, "\1", 1, 0) = 1
[pid 1050] send(3, "\1", 1, 0) = 1
[pid 1050] send(3, "\1", 1, 0) = 1
[pid 1050] send(3, "\1", 1, 0) = 1
[pid 1050] send(3, "\1", 1, 0) = 1
[pid 1050] send(3, "\1", 1, 0) = 1
[pid 1050] send(3, "\1", 1, 0) = 1
[pid 1050] send(3, "\1", 1, 0) = 1
[pid 1050] send(3, "\1", 1, 0) = 1
[pid 1050] send(3, "\1", 1, 0) = 1
[pid 1050] send(3, "\1", 1, 0) = 1 <<<<< the last packet queued
[pid 1050] send(3, "\1", 1, 0 <unfinished ...> <<<<<< doesn't fit in queue
[pid 1051] recvfrom(3, "\1", 2000, 0, NULL, NULL) = 1
[pid 1051] recvfrom(3, "\1", 2000, 0, NULL, NULL) = 1
[pid 1051] recvfrom(3, "\1", 2000, 0, NULL, NULL) = 1
[pid 1051] recvfrom(3, "\1", 2000, 0, NULL, NULL) = 1
[pid 1051] recvfrom(3, "\1", 2000, 0, NULL, NULL) = 1 <<<<< makes room
[pid 1050] <... send resumed> ) = 1
[pid 1050] send(3, "\1", 1, 0) = 1
[pid 1050] send(3, "\1", 1, 0) = 1
[pid 1050] send(3, "\1", 1, 0) = 1
[pid 1050] send(3, "\1", 1, 0) = 1
[pid 1050] send(3, "\1", 1, 0 <unfinished ...>
--
Krzysztof Halasa
Network Administrator