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.
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.
There are many files generated on network shared file system (NFS).
There is a similar question without proper solution: inotify with NFS.
I use select() to test if the file have new data could be read.
(In fact, some are come from socket descriptor, just simplified here).
But, I found even the file till end of file, it still return ready to read state.
Could you suggest better method to write this code?
fd_set rfds;
struct timeval tv;
int retval;
int i,n,f1,f2,maxfd;
char buf[512];
f1 = fileno(fopen("f1", "rb"));
f2 = fileno(fopen("f2", "rb"));
maxfd = (f1 > f2) ? f1 : f2;
for (i=0; i<3; i++) {
FD_ZERO(&rfds);
FD_SET(f1, &rfds);
FD_SET(f2, &rfds);
tv.tv_sec = 5;
tv.tv_usec = 0;
retval = select(maxfd+1, &rfds, NULL, NULL, &tv);
if (retval == -1)
perror("select()");
else if (retval) {
printf("Data is available now.\n");
if (FD_ISSET(f1, &rfds)) {
n = read(f1, buf, sizeof(buf));
printf("f1 is ready:%d read %d bytes\n", i, n);
}
if (FD_ISSET(f2, &rfds)) {
n = read(f2, buf, sizeof(buf));
printf("f2 is ready:%d read %d bytes\n", i, n);
}
} else
printf("No data within five seconds.\n");
}
The output will like following if my f1 and f2 contains 3 bytes.
Data is available now.
f1 is ready:0 read 3 bytes
f2 is ready:0 read 3 bytes
Data is available now.
f1 is ready:1 read 0 bytes <- I wish won't enter here
f2 is ready:1 read 0 bytes <- I wish won't enter here
Data is available now.
f1 is ready:2 read 0 bytes <- I wish won't enter here
f2 is ready:2 read 0 bytes <- I wish won't enter here
NFS doesn't have a way to notify clients when files change, so you're unfortunately out-of-luck. You'll need to poll.
In Unix, regular files are always considered "fast devices", so they cannot be polled. That is, as you have found out, they always return "ready" if you try to select() or poll() on them. IIRC the Linux-specific epoll returns an error outright if you try to poll on a regular fd.
If you want to integrate something like this into your event loop you'll have to apply some duct tape. E.g. have a separate thread which at suitable intervals tries to read()/fstat()/stat() the file/fd, then if it detects that new data is available, send a message to a pipe. In the main event loop you can then poll the pipe.
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.
I open a mesage queue in a .c file, and upon success it says the message queue id is 3. While that program is still running, in another terminal I start another program (of another .c file), that creates a new message queue with a different mqd_t. But its id also appears as 3. Is this a problem?
server file goes like this:
void server(char* req_mq) {
struct mq_attr attr;
mqd_t mqdes;
struct request* msgptr;
int n;
char *bufptr;
int buflen;
pid_t apid;
//attr.mq_maxmsg = 300;
//attr.mq_msgsize = 1024;
mqdes = mq_open(req_mq, O_RDWR | O_CREAT, 0666, NULL);
if (mqdes == -1) {
perror("can not create msg queue\n");
exit(1);
}
printf("server mq created, mq id = %d\n", (int) mqdes);
and the client goes like:
void client(char* req_mq, int min, int max, char* dir_path_name, char* outfile) {
pid_t pid;
/* get the process id */
if ((pid = getpid()) < 0) {
perror("unable to get client pid");
}
mqd_t mqd, dq;
char pfx[50] = DQ_PRFX;
char suffix[50]; //
sprintf(suffix, "%d", pid);
strcat(pfx, suffix);
dq = mq_open(pfx, O_RDWR | O_CREAT, 0666, NULL);
if (dq == -1) {
perror("can not open data queue\n");
exit(1);
}
printf("data queue created, mq id = %d\n", (int) dq);
mqd = mq_open(req_mq, O_RDWR);
if (mqd == -1) {
perror("can not open msg queue\n");
exit(1);
}
mqdes and dq seem to share the same id 3.
Think of a handle as an array index. An index into an array held by the operating system, one for every process in the system.
When you open a handle (be it for a file, message queue, socket, etc) the operating system records the settings for that object in an array that is unique for your process. The operating system then returns an index into that array to your program.
Every time your program uses that "handle" the operating system is really just looking up a structure in that private array it keeps to find out how to deal with the object related to that "handle".
Linux typically reserves handles 0, 1 and 2 for STDIN, STDOUT, and STDERR respectively. But from then on any handles you open will be numbered 3, 4, and so forth. And your handle 3 might relate to file "/tmp/foo.txt" while another process's handle 3 might relate to file "/tmp/bar.txt". So if two processes are using similar file "handles" it is irrelevant.
By the way, you shouldn't need to know, or care, about what a handle actually contains. In theory it could be anything - a magic number, a pointer, an integer, it doesn't matter. The handle is really a secret token that you just hand to the operating system any time you want access to your system object.
maybe you didn't close the first message queue. because in that situation os gives the same id (index) to the new one.
Message queues are distinguished by the name you give them when you open a message queue using mq_open(3). Message queue descriptors returned by mq_open(3) are only meaningful within a process scope. Put in other words, what another process have as value for message queue descriptor is totally irrelevant to another process. This is completely analogous to file paths and file descriptors. Indeed, Linux is special in the sense that the message queue descriptors are actually file descriptors - see Linux manual page mq_overview(7). This is the explanation for the common value 3 that you see two processes get when opening message queue (in the most typical case 0, 1, 2 having been already opened for the purpose of providing STDIN, STDOUT, and STDERR like PP. already mentions in his answer).
I've got a C program I'm writing. Here's what it does:
Create n fifos using mkfifo
Open them for read (with the O_NONBLOCK flag set)
Open them for write
Spawn a thread
In the thread, run in a loop:
Create an fd_set of the file descriptors for all n fifos
Call select(n, &my_set, NULL, NULL, NULL)
For each fd ready for I/O (FD_ISSET(fd, &my_set)):
Read a string from the fd (read(fd, buf, buf_len))
Print the string
If string == "kill", mark the fd as dead and remove it from the list (n--)
If n == 0, terminate the thread
In the main program:
For i = 0 to n
Write to fds[i] with a string (write(fds[i], buf, buf_len))
For i = 0 to n
Write to fds[i] with the string "kill"
Join on the thread I created
Exit
The behavior I'm seeing is that select() will return once with a length of 1, being the first fd in the list. The second time through the loop, select will just sit there forever.
Here's my output:
thread created
Waiting on 4 file descriptors
> Wrote 'Hello to target 0 from writer 0' to 0
> Wrote 'Hello to target 0 from writer 1' to 1
> Wrote 'Hello to target 1 from writer 0' to 2
> Wrote 'Hello to target 1 from writer 1' to 3
> Sending kill to 0:0 (#0)
> Sending kill to 0:1 (#1)
> Sending kill to 1:0 (#2)
> Sending kill to 1:1 (#3)
< Got string: 'Hello to target 0 from writer 0'
Waiting on 4 file descriptors
^C
The OS is Linux, in case it matters.
Link to the code: https://dl.getdropbox.com/u/188590/fifotest.c
(Sorry it's a bit heinous)
Thanks,
Nathan
As Lance Richardson said, the first problem is that you need to pass the number of the maximum file descriptor plus one, not the number of file descriptors.
You then have to clean up the housekeeping in the listener thread - I got most of the data, but ended up listening to 6 file descriptors, not 4. (The reported number was now the largest fd, not the number of file descriptors.)
You also have a problem that you write a string plus a null byte to each pipe, then a second string plus a null byte. Since the scheduling is non-deterministic, the main program actually gets to write both its strings to each fifo, so when the listener thread gets to read it, it reads both strings. When I printed out lengths, I got a total length of 41 read (read_len), but the length of the string per strlen() was 31. In other words, the first read included the 'kill' part, but you didn't notice in the printout because of the trailing null at the end of the first message. Hence you were waiting for that which will never happen.
The first parameter in the call to select() should be the highest-numbered file descriptor plus 1, not the number of file descriptors in the fd_set.
Here's what I changed to fix this issue:
--- fifotest-1.c 2009-05-22 23:44:03.000000000 -0400
+++ fifotest.c 2009-05-22 23:34:00.000000000 -0400
## -34,19 +34,22 ##
sim_arg_t* ifs = arg;
uint32_t num_ifs;
uint32_t select_len;
+ int maxfd;
num_ifs = ifs->num_ifs;
while (num_ifs > 0) {
FD_ZERO (&set);
select_len = 0;
- for (i = 0; i < ifs->num_ifs; ++i) {
+ for (maxfd=0, i = 0; i < ifs->num_ifs; ++i) {
if (ifs->if_list[i].valid) {
FD_SET(ifs->if_list[i].fh, &set);
- ++select_len;
+ if (ifs->if_list[i].fh > maxfd)
+ maxfd = ifs->if_list[i].fh;
+ select_len++;
}
}
printf("Waiting on %d file descriptors\n", select_len);
- ret = select(select_len, &set, NULL, NULL, NULL);
+ ret = select(maxfd+1, &set, NULL, NULL, NULL);
if (ret < 0) {
fprintf(stderr, "Select returned error!\n");
continue;