I am using the following code to set the value of a file descriptor
fd_set current_sockets, ready_sockets;
FD_SET(sock_fd, ¤t_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.
Related
I'm trying to understand the difference between select() and poll() better. For this I tried to implement a simple program that will open a file as write-only, add its file descriptor to the read set and than execute select in hopes that the function will block until the read permission is granted.
As this didnt work (and as far as I understood, this is intended behaviour) I tried to block access to the file using flock before the select() executen. Still, the program did not block its execution.
My sample code is as follows:
#include <stdio.h>
#include <poll.h>
#include <sys/file.h>
#include <errno.h>
#include <sys/select.h>
int main(int argc, char **argv)
{
printf("[+] Select minimal example\n");
int max_number_fds = FOPEN_MAX;
int select_return;
int cnt_pollfds;
struct pollfd pfds_array[max_number_fds];
struct pollfd *pfds = pfds_array;
fd_set fds;
int fd_file = open("./poll_text.txt", O_WRONLY);
struct timeval tv;
tv.tv_sec = 10;
tv.tv_usec = 0;
printf("\t[+] Textfile fd: %d\n", fd_file);
//create and set fds set
FD_ZERO(&fds);
FD_SET(fd_file, &fds);
printf("[+] Locking file descriptor!\n");
if(flock(fd_file,LOCK_EX) == -1)
{
int error_nr = errno;
printf("\t[+] Errno: %d\n", error_nr);
}
printf("[+] Executing select()\n");
select_return = select(fd_file+1, &fds, NULL, NULL, &tv);
if(select_return == -1){
int error_nr = errno;
printf("[+] Select Errno: %d\n", error_nr);
}
printf("[+] Select return: %d\n", select_return);
}
Can anybody see my error in this code? Also: I first tried to execute this code with two FDs added to the read list. When trying to lock them I had to use flock(fd_file,LOCK_SH) as I cannot exclusively lock two FDs with LOCK_EX. Is there a difference on how to lock two FDs of the same file (compared to only one fd)
I'm also not sure why select will not block when a file, that is added to the Read-set is opened as Write-Only. The program can never (without a permission change) read data from the fd, so in my understanding select should block the execution, right?
As a clarification: My "problem" I want to solve is that I have to check if I'm able to replace existing select() calls with poll() (existing in terms of: i will not re-write the select() call code, but will have access to the arguments of select.). To check this, I wanted to implement a test that will force select to block its execution, so I can later check if poll will act the same way (when given similar instructions, i.e. the same FDs to check).
So my "workflow" would be: write tests for different select behaviors (i.e. block and not block), write similar tests for poll (also block, not block) and check if/how poll can be forced do exactly what select is doing.
Thank you for any hints!
When select tells you that a file descriptor is ready for reading, this doesn't necessarily mean that you can read data. It only means that a read call will not block. A read call will also not block when it returns an EOF or error condition.
In your case I expect that read will immediately return -1 and set errno to EBADF (fd is not a valid file descriptor or is not open for reading) or maybe EINVAL (fd is attached to an object which is unsuitable for reading...)
Edit: Additional information as requested in a comment:
A file can be in a blocking state if a physical operation is needed that will take some time, e.g. if the read buffer is empty and (new) data has to be read from the disk, if the file is connected to a terminal and the user has not yet entered any (more) data or if the file is a socket or a pipe and a read would have to wait for (new) data to arrive...
The same applies for write: If the send buffer is full, a write will block. If the remaining space in the send buffer is smaller than your amount of data, it may write only the part that currently fits into the buffer.
If you set a file to non-blocking mode, a read or write will not block but tell you that it would block.
If you want to have a blocking situation for testing purposes, you need control over the process or hardware that provides or consumes the data. I suggest to use read from a terminal (stdin) when you don't enter any data or from a pipe where the writing process does not write any data. You can also fill the write buffer on a pipe when the reading process does not read from it.
I am trying to understand what this line of code means:
int dfd;
fd2 = fcntl(dfd, F_DUPFD);
It is intended to set fd2 to another file descriptor referencing the same open-file record as dfd.
There should be a third argument, the lowest acceptable file descriptor to return. Since that third argument is missing, the call might do just about anything. A correct call could look like this:
fd2 = fcntl(dfd, F_DUPFD, 0);
From the fcntl man page:
F_DUPFD (int)
Find the lowest numbered available file descriptor greater than or equal to arg and make it be a copy of fd. This is different from dup2(2), which uses exactly the descriptor specified.
On success, the new descriptor is returned.
See dup(2) for further details.
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.
Curl's curl_multi_fdset function has the following declaration:
CURLMcode curl_multi_fdset(CURLM *multi_handle,
fd_set *read_fd_set,
fd_set *write_fd_set,
fd_set *exc_fd_set,
int *max_fd);
Curl documentation does not explain the meaning of exc_fd_set. What is the meaning of exc_fd_set?
I have just updated the curl_multi_fdset() man page to better explain what the arguments are actually used for:
If the read_fd_set argument is not a null pointer, it points to an
object of type fd_set that on returns specifies the file descriptors
to be checked for being ready to read.
If the write_fd_set argument is not a null pointer, it points to an
object of type fd_set that on return specifies the file descriptors to
be checked for being ready to write.
If the exc_fd_set argument is not a null pointer, it points to an
object of type fd_set that on return specifies the file descriptors to
be checked for error conditions pending.
Very likely the same meaning as with select(2), as the function is meant to supply the FD sets for it.
Those listed in readfds will be watched to see if characters become available for reading (more precisely, to see if a read will not block; in particular, a file descriptor is also ready on end-of-file), those in writefds will be watched to see if a write will not block, and those in exceptfds will be watched for exceptions.
Could the socket function call in C return 0 or 1 as the value for the socket descriptor?
int socket(int domain, int type, int protocol);
According to the man page I have:
RETURN VALUE
-1 is returned if an error occurs; otherwise the return value is a
descriptor referencing the socket.
It seems like it could, or at least the man page makes no mention of any reserved values. Is it written somewhere else that valid socket descriptors need to be 2 or greater?
I'm specifically running on a linux 2.4.22 kernel, but I'm curious to know for any unix based implementation of socket.
Both 0 and 1 are valid return vales, and might indeed be returned if the application has closed its standard input or output file descriptors.
When your process starts, 0 is stdin and 1 is stdout, but you can close them, and therefore, you could get these FD back
0 or 1 will precisely come if you have closed the stdin or stdout descriptors. This could be happening, because by mistake you might be passing a variable (most probably the one in which you store your socket descriptor) to the socket closing function after initialization. Since the variable might be initialized to 0, it might cause closure of stdin.
The only values that are not valid file descriptors are those less than 0. -1 will indicate an error and errno will be set. You shouldn't ever see a negative value that is not -1.
According to the man page, yes, it could.