linux: the first parameter of select - c

int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *errorfds, struct timeval *timeout);
For the first parameter nfds, here is what I get from wiki:
This is an integer one more than the maximum of any file descriptor in
any of the sets. In other words, while adding file descriptors to each
of the sets, you must calculate the maximum integer value of all of
them, then increment this value by one, and then pass this as nfds.
I have a simple question:
If I have more than one socket to process, how should I set the first parameter of select?
Should I set it with the largest socket number + 1?
If so, does it mean that the select is listening all of file descriptors which is less than the largest socket number + 1?
For example, I have three sockets: 111, 222 and 333. If I set the first parameter as 334, does it mean that I'm listening all of file descriptors from 0 to 333?

Should I set it with the largest socket number + 1?
Yes!
If so, does it mean that the select is listening all of file
descriptors which is less than the largest socket number + 1?
No, it only performs its operations on the fd_sets that are listed in
readfds, writefds, and exceptfds
For example, I have three sockets: 111, 222 and 333. If I set the
first parameter as 334, does it mean that I'm listening all of file
descriptors from 0 to 333?
No, you are only doing $select$ on 111, 222 and 333.
Internally sys_select sets up 3 bitmaps which have a bit set to 1 for each of the three bitsets and then if any of these bits are set (which in turn corrospond the file descriptor operation) then the wait_key_set operation is performed on it.
The reason for this interface is that in the kernel it devolves into a very predictable for-loop; making it quite safe to work with; rather than trying to do the calculation internally in the kernel.

Related

FD_SET not putting the file descriptor in the set

I am using the following code to set the value of a file descriptor
fd_set current_sockets, ready_sockets;
FD_SET(sock_fd, &current_sockets);
In the above sock_fd is 3. And after the execution of this line I don't see this value 3 in
current_sockets. In fact I see some weird set of values. What could be the reason for this ?
When you declare fd_set current_sockets, ready_sockets;, both of those variables are uninitialized. man 2 select says this:
FD_ZERO()
This macro clears (removes all file descriptors from) set.
It should be employed as the first step in initializing a
file descriptor set.
But you skipped this non-optional step, so your FD sets are full of random garbage.
Also, the contents of the fd_set structure are unspecified, so you shouldn't expect to be able to make sense of them by any means other than using FD_ISSET.

Counting bytes received by posix read()

I get confused with one line of code:
temp_uart_count = read(VCOM, temp_uart_data, 4096);
I found more about read function at http://linux.die.net/man/3/read, but if everything is okay it returns 0, so how we can get num of bytes received from that?
temp_uart_count is used to count how much bytes we received from virtual COM port and stored it to temp_uart_data which is 4096 bytes wide.
Am I really getting how much bytes i received with this line of code?
... but if everything is okay it returns 0, so how we can get num of bytes received from that?
A return code of zero simply means that read() was unable to provide any data.
Am I really getting how much bytes i received with this line of code?
Yes, a positive return code (i.e. >= 0) from read() is an accurate count of bytes that were returned in the buffer. Zero is a valid count.
If you're expecting more data, then simply repeat the read() syscall. (However you may have setup the termios arguments poorly, e.g. VMIN=0 and VTIME=0).
And - zero indicates end of file
If you get 0, it means that the end of file (or an equivalent condition) has been reached and there is nothing else to read.
The above (one from a comment, and the other in an answer) are incorrect.
Reading from a tty device (e.g. a serial port) is not like reading from a file on a block device, but is temporal. Data for reading is only available as it is received over the comm link.
A non-blocking read() will return with -1 and errno set to EAGAIN when there is no data available.
A blocking non-canonical read() will return zero when there is no data available. Correlate your termios configuration with this to confirm that a return of zero is valid (and does not indicate "end of file").
In either case, the read() can be repeated to get more data when/if it arrives.
Also when using non-canonical (aka raw) mode (or non-blocking reads), do not expect or rely on the the read() to perform message or packet management for you. You will need to add a layer to your program to read bytes, concatenate those bytes into a complete message datagram/packet, and validate it before that message can be processed.
ssize_t read(int fd, void *buf, size_t count); returns you the size of bytes he read and stores it into the value you passed in parameters. And when errors happen it returns -1 (with errno set to EINTR) or to return the number of bytes already read..
From the linux man :
On files that support seeking, the read operation commences at the current file offset, and the file offset is incremented by the number of bytes read. If the current file offset is at or past the end of file, no bytes are read, and read() returns zero.
Yes, temp_uart_count will contain the actual number of bytes read, and obviously that number will be smaller or equal to the number of elements of temp_uart_data. If you get 0, it means that the end of file (or an equivalent condition) has been reached and there is nothing else to read.
If it returns -1 this indicate that an error has occurred and you'll need to check the errno variable to understand what happened.

Is it reasonable to expect that in Linux, fd < maximum number of open file descriptors?

I'm writing a server that needs to handle many open sockets, so I use setrlimit() to set the maximum number of open file descriptors (as root, before dropping privileges) like so:
#include <sys/resource.h>
#define MAX_FD_C 9001
if (setrlimit(
RLIMIT_NOFILE, &(struct rlimit){.rlim_cur = MAX_FD_C, .rlim_max = MAX_FD_C}
) == -1) {
perror("Failed to set the maximum number of open file descriptors");
return EXIT_FAILURE;
}
Now, I realize there probably won't be any guarantees and that I'm at the mercy of whatever method the Linux kernel uses to implements file descriptor tables; but in practice, is it reasonable to assume that any fd this program receives from the Linux kernel will have a value less than the MAX_FD_C I set above?
I'd like to keep per socket data as compact as possible which could mean simply using an array like static struct client clients[MAX_FD_C] = {{0}}; and using the fd as the index to the client struct (which would basically be my own version of the FDT).
There are functions in the POSIX standard which assume this already. Have a look at FD_SETSIZE, select(), FD_SET.

Select from multiply sockets - right NFDS value?

I think that NFDS in select() determines how many sockets the function will check in READFDS and the other fd_sets. So if we set 3 sockets in our fd_set, but I want to check only first one, I have to call select(1 + 1,...). Is this right?
Or does "nfds is the highest-numbered file descriptor in any of the three sets, plus 1" in linux select man means something different? Also why do we need to add + 1?
Example code - fixed
int CLIENTS[max_clients];//Clients sockets
int to_read;
FD_ZERO(&to_read);
int i;
int max_socket_fd = 0;
for (i = 0 ; i < max_clients ; i++)
{
if(CLIENTS[i] < 0)
continue;
int client_socket = CLIENTS[i];
if(client_socket > max_socket_fd)
max_socket_fd = client_socket;
FD_SET(client_socket , &to_read);
}
struct timeval wait;
wait.tv_sec = 0;
wait.tv_usec = 1000;
int select_ret = select(max_socket_fd + 1, &read_flags, NULL, NULL, &wait);
...
int select_ret = select(current_clients + 1, &read_flags, NULL, NULL, &wait);
Your code is wrong. You don't need to pass the number of file descriptors monitored. You need to pick the biggest descriptor you're interested in and add 1.
The standard says:
The nfds argument specifies the range of descriptors to be tested. The
first nfds descriptors shall be checked in each set; that is, the
descriptors from zero through nfds-1 in the descriptor sets shall be
examined
So it's just the expected semantics of select: nfds is not the number of file descriptors (as its name would imply) but rather the upper limit of the watched range.
The bold part in the quote also explains why you need to add 1 to your nfds.
"nfds is the highest-numbered file descriptor in any of the three sets, plus 1"
Every file descriptor is represented by an integral value. So they are not asking for the x-th descriptor that you want to check, they are asking for the highest integral value of the descriptors in your READFDS +1.
Btw, you should check out poll(2) and ppoll(2).
Basically, the "fd" you put into the FD_SET() and similar calls are integer numbers. The "nfds" required by select is the max() of all these values, plus 1.

Query on Select System Call

select() is defined as :
int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *errorfds, struct timeval *timeout);
nfds represents the highest file descriptor in all given sets plus one. I would like to know why is this data required for select() when the fd_set information is available.
If the FDs in the set are say, 4, 8, 9 ,the value of nfds would be 10. Would select() moniter fds 9,8,7,6,5,4 ?
The catch is that fd_set is not really a "set" in the way you're thinking. The behind-the-scenes detail is that the implementation of an fd_set is just an integer that is used as a bitfield. In other words, executing
fd_set foo;
FD_CLEAR(&foo);
FD_SET(&foo, 3);
Sets foo to decimal value 8 - it sets the fourth-least-singificant bit to 1 (remember that 0 is a valid descriptor).
FD_SET(&foo, 3);
is equivalent to
foo |= (1 << 3);
So in order for select to work right, it needs to know which bits of the fd_set are bits that you care about. Otherwise there would be no way for it to tell a zero bit that is "in" the set but set to false from a zero bit that is "not in" the set.
In your example, a fd_set with 4, 8, and 9 set and n = 10 is interpreted as "A set with 10 entries (fds 0-9). Entries 4, 8, and 9 are true (monitor them). Entries 1,2,3,5,6,7 are false (don't monitor them). Any fd value greater than 9 is simply not in the set period."
Select monitors those FDs which you have enabled using the FD_SET macro. If you do not enable any FD for monitoring, select() does not monitor any.
"nfds" is definitely redundant, but it is part of the select() interface, so you need to use it :)
Anyway, if you have {4, 8, 9} in the set, you set nfds to 10 (as you mentioned), and select() will only monitor the three FDs 4, 8 and 9.
It's probably an optimization so that select doesn't have to walk through the whole fd_set to find out which descriptors are actually used. Without that parameter, select would always need to look at the whole set to find which descriptors are actually used in the call, with the parameter, some of that work can be omitted.

Resources