2
votes

According to this article poll vs select vs event-based:

select() only uses (at maximum) three bits of data per file descriptor, while poll() typically uses 64 bits per file descriptor. In each syscall invoke poll() thus needs to copy a lot more over to kernel space. A small win for select().

Here is the implementation of fd_set (found on Advisories : multiple applications fd_set structure bitmap array index overflow

#ifndef FD_SETSIZE
#define FD_SETSIZE  1024
#endif
#define NBBY    8       /* number of bits in a byte */
typedef long    fd_mask;
#define NFDBITS (sizeof (fd_mask) * NBBY)   /* bits per mask */
#define howmany(x,y)    (((x)+((y)-1))/(y))
typedef struct _types_fd_set {
    fd_mask fds_bits[howmany(FD_SETSIZE, NFDBITS)];
} _types_fd_set;

#define fd_set _types_fd_set

So, in the end, fd_set is just an array of long. It is also written:

A call to FD_SET sets a bit to 1 using socket number as an index:

which means, if I got a socket fd number 5, the element indexed 5 will be seleted, and its first bit will be flipped from 0 to 1. Since select() uses 3 bits, I guess the other two bits are for sending and receive. Is this correct? Why does select() use a long when it only needs 3 bits?

Also, stated above, poll() uses 64 bits for checking. Why does poll need to check every bit in pollfd struct? Here is the pollfd struct:

struct pollfd {
    int fd;         // the socket descriptor
    short events;   // bitmap of events we're interested in
    short revents;  // when poll() returns, bitmap of events that occurred
};

The total bits in the struct is 64 bits, for a 32-bit int and two 16-bit short. I know the usual way of checking a bit flag is using AND (&) operator to filter out other irrelevant bits. Does that apply to this case?

3

3 Answers

5
votes

poll() typically uses 64 bits per file descriptor. In each syscall invoke poll() thus needs to copy a lot more over to kernel space. A small win for select().

Fallacy -- if you only want to monitor one fd, poll(2) will get you away with those 64 bits, while for select(), you have to copy up to like 12288 bits (3 sets, each 4096 bits usually).

Also, poll() supports fd with a value larger than FD_SETSIZE.

4
votes

The reason select() uses longs is to encode many bits into a single variable value. A long can hold 32 (or sometimes 64, these days) bits typically, which means you can represent a 32-file set using a single value of type long.

The "three bits per file" comes from the three different fd_set values you typically use in order to select for both read, write and exceptions. Three long values is (assuming a 32-bit long), 32 * 3 = 96 bits, but can select for all three conditions for 32 different files, thus spending 3 bits per file. This assumes you select() for each of those 32 files in all three sets, which I think is at least somewhat rare.

Since poll represents file descriptors directly using an int-sized field, rather than implicitly by index into a bit set, it uses more space.

Basically, select()'s design takes advantage of the assumption that file descriptors are * small* integers, and that they're allocated from 0 and up.

3
votes

It doesn't use 1 long per se. It uses a bit field of FD_SETSIZE bits, in this case 32 32 bits longs or 16 64 bits longs. By using longs, it allows to use less memory operations on 64 bit machines (which are widespread on Unix already for 20 years). They could have used normal int but then they wouldn't have had an advantage on 64 bit machines. It's only forward thinking.

If you used select on several file descriptors you need the room for all the file descriptors. Imagine you want to read from 3 sockets, from a SCSI device and a file and the file descriptor values are respectively 3, 7, 10, 12 and 67, how would you represent that? Use a bitset where the bit with that number is set.

 00010001.00101000:00000000.00000000:00000000.00000000:00000000.00000000   00010000.00000000
    ^   ^   ^ ^                                                               ^
    0   0   1 1                                                               6 
    3   7   0 2                                                               7