GirişŞu satırı dahil
ederiz.
#include <sys/epoll.h>
epoll neden Önemli
2002 — LINUX releases epoll API
The 2.5.44 release of LINUX included a new API: epoll. The epoll API delivered effectively constant socket look-up time. If networking software used epoll and multiplexed connections across a handful of threads at most (as opposed to 1 thread per request), one could expect significantly better resource utilization on a server and handle 10K simultaneous connections well. This solution reduced the latency of packet routing within Linux, which enabled better scalability of open connections.
Kullanım1. epoll_create() ile bir epollfd yarat
2. epoll_event yapısını socket fd ile doldur ve epoll_ctl() ile izlemeye başla
3. Döngü içinde epoll_wait metoduu sürekli çağır
4. Event tipine bakarak (EPOLLERR,EPOLLHUP,EPOLLIN,EPOLLOUT) socketler üzerinde işlem yap
5. Ya da socket fd'ye bakarak fd ile işlem yap. Bu yöntem sadece bir kerelik cevap veren sunucu yazılımlarda kullanılabilir.
Tasarım Hatasıepoll tasarım hatası ile ilgili bir açıklama
şöyle. Bu yazıyı ilk kez
bu sorudaki linkte gördüm. Kısaca dup ile çoklanan bir file descriptor close() metodu ile kapatılsa bile epoll açısından kapanmadığı için halen event'leri bildiriyor diyor.
epoll is broken because it mistakes the "file descriptor" with the underlying kernel object (the "file description"). The issue shows up when relying on the close() semantics to clean up the epoll subscriptions.
epoll_ctl(EPOLL_CTL_ADD) doesn't actually register a file descriptor. Instead it registers a tuple1 of a file descriptor and a pointer to underlying kernel object. Most confusingly the lifetime of an epoll subscription is not tied to the lifetime of a file descriptor. It's tied to the life of the kernel object.
Due to this implementation quirk calling close() on a file descriptor might or might not trigger epoll unsubscription. If the close call removes the last pointer to kernel object and causes the object to be freed, then it will cause epoll subscription cleanup. But if there are more pointers to kernel object, more file descriptors, in any process on the system, then close will not cause the epoll subscription cleanup. It is totally possible to receive events on previously closed file descriptors.
1. epoll_create metodu - sizeÖrnek ver
2. epoll_create1 metodu - flagsÖrnekŞöyle
yaparız.
int epollfd = epoll_create1(0);
if (epollfd == -1)
{
perror ("epoll_create");
...
}
ÖrnekŞöyle yaparız.
int efd = epoll_create1 (EPOLL_CLOEXEC);
3. epoll_ctl metodu
4. epoll_event yapısı
Şöyle
tanımlarız.
struct epoll_event events[MAXEVENTS];
events AlanıŞu değerlerden birisi olabilir
EPOLLIN
EPOLLIN = 1 ise oku
EPOLLRDHUP
EPOLLRDHUP = 1 ise kapat. EPOLLRDHUP açıklamasını buradan
aldım.
EPOLLRDHUP (since Linux 2.6.17)
Stream socket peer closed connection, or shut down writing half of connection. (This flag is especially useful for writing simple code to detect peer shutdown when using Edge Triggered monitoring.)
Örnek
Şöyle
yaparız.
int servFd = socket (...);
struct epoll_event epollEvt;
epollEvt.events = EPOLLIN | EPOLLRDHUP;
epollEvt.data.u32 = servFd;
data Alanıepoll_data tipindendir
epoll_data yapısıepoll_event yapısının bir alanıdır. İçi
şöyledir.
typedef union epoll_data {
void *ptr;
int fd;
uint32_t u32;
uint64_t u64;
} epoll_data_t;
Örnekfd alanını kullanmak için şöyle yap
arız.
epoll_event ev;
ev.data.fd = timerfd;
Örnekptr alanını kullanmak için bir ata sınıf
tanımlarız.
class EventHandler {
public:
virtual ~EventHandler() = default;
virtual int fd() const = 0;
virtual void fire() = 0;
};
Bu ata sınıfı ptr alanına vererek epoll() çağrısına dahil
ederiz.
EventHandler* handler = new TimerHandler();
ev.data.ptr = handler;
epoll_ctl(epollfd, EPOLL_CTL_ADD, handler->fd(), &ev);
Sonuçları dolaşırken şöyle
yaparızint n = epoll_wait (epollfd, events , num_events, -1 );
for (int i = 0; i < n; ++i) {
static_cast<EventHandler*>(events[i].data.ptr)->fire();
}
5. epoll_wait metoduŞöyle
yaparız.
int n = epoll_wait (epollfd, events, MAXEVENTS, -1);
Örnek
Sonucu dolaşmak için şöyle
yaparız.
int n = epoll_wait (epollfd, events , num_events, -1 );
for (int i = 0; i < n; ++i) {
if (events[i].data.fd == timerfd) {
handle_timer_callback();
}
else {
// something else
}
}
ÖrnekSonucu dolaşmak için şöyle
yaparız.
for (i = 0; i < n; i++)
{
int fd = events[i].data.fd;
if ((events[i].events & EPOLLERR) ||
(events[i].events & EPOLLHUP))
{
/* An error has occured on this fd, or the socket is not
ready for reading (why were we notified then?) */
close(fd);
}
else if (fd == listendf && (events[i].events & EPOLLIN))
{
/* We have a notification on the listening socket, which
means one or more incoming connections. */
HandleAccept(epollfd, listenfd);
}
else if(events[i].events & EPOLLIN)
{
/* We have data on the fd waiting to be read. Read and
display it. We must read whatever data is available
completely, as we are running in edge-triggered mode
and won't get a notification again for the same
data. */
HandleRead(epollfd, fd);
}
else if (events[i].events & EPOLLOUT)
{
...
}
}
ÖrnekŞöyle
yaparız.
for (;;) {
struct epoll_event pollEvent[512];
int eventCount = epoll_wait (efd, pollEvent, 512, -1);
for (int i = 0; i < eventCount; ++i) {
struct epoll_event* curEvent = &pollEvent[i];
if (curEvent->data.u32 == servFd) {
int clientFd = accept4 (servFd, NULL, NULL, SOCK_NONBLOCK | SOCK_CLOEXEC);
struct epoll_event epollEvt;
epollEvt.events = EPOLLIN | EPOLLRDHUP | EPOLLET;
epollEvt.data.u32 = clientFd;
epoll_ctl (efd, EPOLL_CTL_ADD, clientFd, &epollEvt);
continue;
}
int clientFd = curEvent->data.u32;
char recvBuffer[2048];
recvfrom (clientFd, recvBuffer, 2048, 0, NULL, NULL);
char sndMsg[] = "...";
size_t sndMsgLength = sizeof (sndMsg) - 1;
struct iovec sndBuffer;
sndBuffer.iov_base = sndMsg;
sndBuffer.iov_len = sndMsgLength;
writev (clientFd, &sndBuffer, 1);
close (clientFd);
}
}