How to discover an empty set of eras

I am studying using epoll and I wrote the following example

#include <assert.h> #include <errno.h> #include <fcntl.h> #include <stdio.h> #include <stdlib.h> #include <sys/epoll.h> #include <unistd.h> int main() { int epfd; struct epoll_event ev; struct epoll_event ret; char buf[200]; int n,k,t; epfd = epoll_create(100); assert(0 == fcntl(0, F_SETFL, fcntl(0, F_GETFL) | O_NONBLOCK) ); ev.data.fd = 0; ev.events = EPOLLIN | EPOLLET; if(epoll_ctl(epfd, EPOLL_CTL_ADD, 0, &ev) != 0) perror("epoll_ctl"); while((n = epoll_wait(epfd, &ret, 1, -1)) > 0) { printf("tick!\n"); if(ret.data.fd == 0) { k=0; while((t=read(0, buf, 100)) > 0) { k+=t; } if(k == 0) { close(0); printf("stdin done\n"); } } } perror("epoll"); return 0; } 

If you try to run it in the terminal, it will not work properly, since fds 0, 1 and 2 all point to the same open file, so close (0) will not remove stdin from the epoll set. You can get around this by running "cat | ./a.out". A dirty trick, I know, but creating a small example with named pipes or sockets will be more complicated.

Now everything works, and the file is deleted from the epoll set, but then the next call to epoll_wait is blocked forever, since it is on an empty set! Therefore, I will need to determine if the file descriptor epoll (epfd) is an empty set of eras.

How can I get around this? (in general, not just calling exit when running stdin) Thanks!

+4
source share
2 answers

Basically, if you use epoll β€œcorrectly”, then you should never have a situation with an unexpectedly empty set of eras. You need to know when there is something to do or not. Well, or that theory at least. Let me see it:

Here you are using EPOLLET (that is, imho is generally thought of as a whole). This means that file descriptor 0 is removed from epoll when it returns to &ret . At this point, you should process it by reading a certain amount of data from 0, just like you, but then β€œre-arm” it by adding the file descriptor 0 to epoll again (if it was not closed, of course). For an example of how this should work, remove the inner loop and simply execute:

 k = read(0, buf, 100); 

reading no more than 100 bytes. The idea is that if you skip a file more than this, it should go through several times throughout the loop. To do this job, if k> 0, after processing the k bytes you need to call epoll_ctl(..EPOLL_CTL_ADD..) again.

Pay attention to the annoying detail: sometimes it is possible that read() returns 0 bytes without meaning that the file or socket is at the end. Check if errno == EAGAIN || errno == EWOULDBLOCK errno == EAGAIN || errno == EWOULDBLOCK . To detect this case, then epoll_ctl(..EPOLL_CTL_ADD..) again.

0
source

The epoll set will be empty when you delete everything that has been added. As far as I know, you cannot understand the epoll set to find out if there are any file descriptors. Thus, it is up to you to decide when the set of eras becomes empty, as indicated in Armin's answer.

Since you have not explained what you expect from your program, I will assume that you expect it to exit when stdin is closed, because doing close(0) will potentially remove the file descriptor 0 from epoll to set. However, the code in the list is erroneous. If you continue to wait for an epoll set that does not contain file descriptors (deleted automatically or with EPOLL_CTL_DEL ), epoll_wait will always wait.

The following code shows this well.

 #include <errno.h> #include <stdio.h> #include <sys/epoll.h> int main() { int epfd; int n; struct epoll_event ret; epfd = epoll_create(100); while((n = epoll_wait(epfd, &ret, 1, -1)) > 0) { /* Never gets here. */ printf("tick!\n"); } return 0; } 

The epoll set does not contain file descriptors, so epoll_wait waits forever. If you had a file connected to stdin in your program, and no other file descriptor in your program was connected to stdin, close(0) would remove fd 0 from the set, the epoll set will become empty, and the next epoll_wait will wait forever.

In general, you manage the file descriptors in the epoll set yourself, do not rely on close calls to automatically remove the file descriptor from the set. It is up to you to decide whether to continue to wait for the epoll set after you close(0) .

I also suggest changing the structure of your program to epoll_wait after read . This ensures that you get data that could be received on stdin before the first call to epoll_wait .

Also, be careful with this code:

 k=0; while((t=read(0, buf, 100)) > 0) { k+=t; } if(k == 0) { close(0); printf("stdin done\n"); } 

If you assume that read in a loop returns 100 and then 0, indicating some data plus the end of the file, close(0) will not be called. The program will loop and hang on epoll_wait . It is best to check the result of each read specifically for the end of the file and errors.

0
source

All Articles