Select from multiply sockets - right NFDS value? - c

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.

Related

fgetc still blocking after polling a pipe

Currently, I'm reading lines of input from child programs that are execlp'd. So basically, if a child program fails to execute properly, it shouldn't be piping information when read and an error will be thrown.
I attempted polling the file descriptor and it returns one no matter if the program was executed correctly. So basically I get past poll then fgetc is hanging/blocking as there is nothing to read, but fgetc is also not returning -1.
Reading and Polling:
char* read_line(int fd) {
// fd is a pipe's read end. I know it reads properly.
FILE *file = fdopen(fd, "r");
int ret;
struct pollfd fdinfo[1];
fdinfo[0].fd = fd;
fdinfo[0].events = POLLIN;
ret = poll(fdinfo,1, 1000);
if (ret < 0) {
return "NOPE";
}
char* result = malloc(sizeof(char) * 80);
memset(result, 0, sizeof(int));
int position = 0;
int next = 0;
while (1) {
next = fgetc(file); //STALLING HERE
if (next == '!') {
free(result);
return "!";
}
if (next == EOF || next == '\n') {
result[position] = '\0';
return result;
} else {
result[position++] = (char)next;
}
}
}
You are not adequately checking the information provided to you by poll(). You do detect the case that poll() reports an error by returning -1, but it doing otherwise does not guarantee that there are any data available to read. A negative return value indicates that poll() failed to do its job; it does not tell you about the state of any of the polled file descriptors.
In the first place, poll() returns 0 if it times out. In that case, your code just rolls on ahead and attempts to read from the file descriptor, which will block unless data happen to arrive between the return from poll() and the call to fgetc().
In the second place, poll() signals problems with a designated file descriptor by setting one or more of the corresponding revent bits, and reporting an event on that FD (in part by returning a positive number). Specifically,
poll() sets the POLLHUP, POLLERR and POLLNVAL flag in revents if the condition is true, even if the application did not set the corresponding bit in events
and
A positive [return] value indicates the total number of pollfd structures [...] for which the revents member is non-zero
(POSIX 1003.1-2008; emphasis added)
You might very well see a POLLHUP event if the child process writing to the other end of the pipe crashes. You could see a POLLNVAL event if your program screws up its file descriptor handling in certain ways, and you always have to allow for the possibility of an I/O error, which poll() signals as a POLLERR event.
Thus, to avoid blocking, you should attempt to read from the pipe only when poll() returns 1 and signals POLLIN among the events on the file. Conceivably, you might see that together with a POLLERR or a POLLHUP. I would recommend aborting if ever there is a POLLERR, but a POLLHUP is to be expected when the write end of the pipe is no longer open in any process, and that does not invalidate any data that may still be available to read (which would be signaled by an accompanying POLLIN).

linux: the first parameter of select

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.

select is not saving file descriptors

Yesterday I discovered select, which is a very useful tool, but I'm not able to make it work. This is part of my total code:
/* Code here */
FD_ZERO(&fifo_set);
printf("%d\n", num_proc);
for(i = 0; i < num_proc; ++i)
FD_SET(proc[i].fifowfd, &fifo_set);
/* More code here */
while(1)
{
if(select(FD_SETSIZE, &fifo_set, NULL, NULL, NULL) < 0)
{
log_event(5, "Could not block.");
exit(1);
}
printf("FD_SETSIZE: %d\n", FD_SETSIZE);
for(i = 0; i < FD_SETSIZE; ++i)
printf("ISSET %d: %d\n", i, FD_ISSET(i,&fifo_set));
log_event(1, "Actions to be done.");
/* More code */
The array proc, is an array of processes, given its PID, and a read and write FIFO. The file descriptors to the FIFOs are checked, and are valid.
The problem is: there are 3 processes (num_proc), with the fifowfd values 5, 7 and 9. But when I print all FD_ISSET, only the 5 seems to be registered and have data, but all three have data. FD_SETSIZE has the value 1024.
As #mux pointed, this FIFOs are named like "FIFO for writing". The thing is, that I have a bunch of "name.r.fifo" and "name.w.fifo", which represent "FIFO for reading/writing" from the side of the process. The code I'm showing, is the code from the controller, which reads in the ".w.fifo", and writes to the ".r.fifo".
Am I missing something?
The first argument to select() is the highest-numbered file descriptor plus 1:
select(highest_fd+1, &fifo_set, NULL, NULL, NULL);
Note: the fd set will contain the descriptors that are "ready" after select returns, you should set the fds again if you want to do another select()
If all descriptors have no data when select is called, it will block until at least one descriptor is ready for reading. Probably, descriptor 5 is the first one to be checked and select exits before other pipes have any data to read.
You should also check the actual result of select since it contains number of bits it set.

How to see if a pipe is empty

Assuming a pipe,
int pipe_fd[2];
pipe(pipe_fd);
We fork, and expect that one process will write into the pipe at an arbitrary time. In one of the processes, we want to be able to check the contents of the pipe without blocking.
i.e. While a typical read will block if nothing is present and the write end remains open. I want to go do other stuff and potentially even read a bit at a time, do some stuff, and then check back to see if there's more, a la:
close(pipe_fd[1]);
while(1){
if(/**Check pipe contents**/){
int present_chars = 0;
while( read(pipe_fd[0],&buffer[present_chars],1) != 0)
++present_chars;
//do something
}
else
//do something else
}
Your logic is wrong in that read will not return 0 when it runs out of characters; instead, it will block until it receives more, unless you put the file in non-blocking mode, but then it will return -1 and set errno to EWOULDBLOCK or EAGAIN rather than returning 0. The only time read can ever return 0 is when the size argument was 0 or end-of-file has been reached. And, for pipes, end-of-file means the writing end of the pipe has been closed; end-of-file status does not occur just because there's not any input available yet.
With that said, the simplest way to check is:
if (poll(&(struct pollfd){ .fd = fd, .events = POLLIN }, 1, 0)==1) {
/* data available */
}
but unless you're using nonblocking mode, you'll need to make this check before every single read operation. Passing a larger buffer to read rather than doing it a byte-at-a-time would eliminate most of the cost of checking.
You can check if there is data to be read with the read() function. From read(3):
When attempting to read from an empty pipe or FIFO:
* If some process has the pipe open for writing and
O_NONBLOCK is set, read() shall return -1 and set
errno to [EAGAIN].
* If some process has the pipe open for writing and
O_NONBLOCK is clear, read() shall block the calling
thread until some data is written or the pipe is
closed by all processes that had the pipe open for
writing.
The read() function shall fail if:
EAGAIN or EWOULDBLOCK
The file descriptor is for a socket, is marked
O_NONBLOCK, and no data is waiting to be received.
So if you set O_NONBLOCK, you will be able to tell if something is to be read on the pipe, by simply calling read().
As a reminder, from open(3):
SYNOPSIS
int open(const char *path, int oflag, ... );
DESCRIPTION
Values for oflag are constructed by a
bitwise-inclusive OR of flags from the following
list, defined in <fcntl.h>. Applications shall
specify exactly one of the first three values
(file access modes) below in the value of oflag:
O_NONBLOCK [...]
I hope it helps.
R..'s answer is good however poll returns the number of file descriptor structs that have flags set in "revents". This will be 1 if you can read from fd but will also be 1 if any of the error flags are set. This means R..'s answer will say the pipe is readable if it ever enters an error state. A more robust check could be something like this:
bool canReadFromPipe(){
//file descriptor struct to check if POLLIN bit will be set
//fd is the file descriptor of the pipe
struct pollfd fds{ .fd = fd, .events = POLLIN };
//poll with no wait time
int res = poll(&fds, 1, 0);
//if res < 0 then an error occurred with poll
//POLLERR is set for some other errors
//POLLNVAL is set if the pipe is closed
if(res < 0||fds.revents&(POLLERR|POLLNVAL))
{
//an error occurred, check errno
}
return fds.revents&POLLIN;
}

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