當(dāng)前位置:首頁 > 嵌入式培訓(xùn) > 嵌入式學(xué)習(xí) > 講師博文 > epoll的使用
以下內(nèi)容是參考《linux/unix系統(tǒng)編程手冊(cè)》對(duì)epoll的一個(gè)個(gè)人總結(jié)。
一、epoll的優(yōu)點(diǎn)
同I/O多路復(fù)用和信號(hào)驅(qū)動(dòng)I/O一樣,linux的epoll API可以檢查多個(gè)文件描述符上的I/O就緒狀態(tài)。epoll API的主要優(yōu)點(diǎn)
1.當(dāng)有大量的文件描述符需要檢查時(shí),epoll的性能延展性比select()和epoll(高很多)
2.epoll API既支持水平觸發(fā)也支持邊緣觸發(fā),與之相反,select和poll只支持水平觸發(fā),而信號(hào)驅(qū)動(dòng)I/O只支持邊緣觸發(fā)
3.可以避免復(fù)雜的信號(hào)處理流程(比如信號(hào)隊(duì)列溢出時(shí)的處理)
4.靈活性高,可以指定我們希望檢查的時(shí)間類型
二、epoll系統(tǒng)調(diào)用組成
1.epoll_create()創(chuàng)建一個(gè)epoll實(shí)例,返回代表該實(shí)例的文件描述符
2.epoll_ctl()操作同epoll實(shí)例相關(guān)聯(lián)的興趣列表
3.epoll_wait()返回與epoll實(shí)例相關(guān)聯(lián)的就緒列表中的成員
#include <sys/epoll.h>
int epoll_create(int size);
功能:創(chuàng)建一個(gè)新的epoll實(shí)例,其對(duì)應(yīng)的興趣列表初始化為空
參數(shù):size 想要檢查文件描述符的個(gè)數(shù)(linux2.6.8之后該參數(shù)不再使用)
返回值:成功文件描述符,失敗-1
#include <sys/epoll.h>
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
功能:修改epoll的興趣列表
參數(shù):epfd epoll_create的返回值
fd 要修改的文件描述符(可以是無名管道、有名管道、套接字、消息隊(duì)列、終端、設(shè)備等,但是不能是普通文件或目錄的文件描述符)
op:
EPOLL_CTL_ADD 添加fd到興趣列表
EPOLL_CTL_MOD 修改已經(jīng)注冊(cè)的fd的監(jiān)聽事件;
EPOLL_CTL_DEL 從epfd中刪除一個(gè)fd
typedef union epoll_data {
void *ptr;
int fd;
__uint32_t u32;
__uint64_t u64;
} epoll_data_t;
struct epoll_event {
__uint32_t events; /* Epoll events */
epoll_data_t data; /* User data variable */
};
常用的事件類型:
EPOLLIN :表示對(duì)應(yīng)的文件描述符可以讀;
EPOLLOUT:表示對(duì)應(yīng)的文件描述符可以寫;
EPOLLPRI:表示對(duì)應(yīng)的文件描述符有緊急的數(shù)據(jù)可讀
EPOLLERR:表示對(duì)應(yīng)的文件描述符發(fā)生錯(cuò)誤;
EPOLLHUP:表示對(duì)應(yīng)的文件描述符被掛斷;
EPOLLET: 表示對(duì)應(yīng)的文件描述符有事件發(fā)生;
返回值:成功0 失敗-1
#include <sys/epoll.h>
int epoll_wait(int epfd, struct epoll_event *events,
int maxevents, int timeout);
功能:返回epoll實(shí)例中處于就緒態(tài)的文件描述符信息,單個(gè)epoll_wait()調(diào)用能返回多個(gè)就緒態(tài)文件描述符的信息。
參數(shù):epfd epoll實(shí)例
events 存放文件描述符的信息
maxevents 文件描述符的大個(gè)數(shù)
timeout -1 阻塞等待
0 非阻塞
>0 阻塞的大時(shí)間
返回值:成功0 失敗-1
三、網(wǎng)絡(luò)中的應(yīng)用實(shí)例
1.net.h
#ifndef _NET_H_
#define _NET_H_
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdlib.h>
#include <fcntl.h>
#include <strings.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <signal.h>
#include <sys/time.h>
#include <sys/epoll.h>
#include <pthread.h>
#define MAX_EVENTS 500
#endif
2.server.c
#include "head.h"
int sockfd, acceptfd,listenfd;
struct sockaddr_in serveraddr, clientaddr;
socklen_t len = sizeof(serveraddr);
struct epoll_event ev, events[MAX_EVENTS];
int rv,i;
int epollfd,fds;
int tcp_create_socket(void)
{
//1、設(shè)定基于TCP通信的標(biāo)準(zhǔn)
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if(sockfd < 0)
{
perror("fail to socket");
return -1;
}
bzero(&serveraddr, len);
serveraddr.sin_family = AF_INET;
serveraddr.sin_port = htons(atoi("8000"));//端口號(hào)
serveraddr.sin_addr.s_addr = inet_addr("192.168.8.199"); //IP地址
//2、綁定IP地址和端口號(hào)
if(bind(sockfd, (struct sockaddr*)&serveraddr, len) < 0)
{
perror("fail to bind");
close(sockfd);
return -1;
}
//3、設(shè)置監(jiān)聽描述符的個(gè)數(shù)
if(listen(sockfd, 5) < 0)
{
perror("fail to listen");
close(sockfd);
return -1;
}
return sockfd;
}
int mz_process_data(int acceptfd)//數(shù)據(jù)處理
{
int bytes = 0;
char buf[100];
bytes = recv(acceptfd, buf, 100, 0);
printf("----------------\n");
if(bytes < 0)
{
perror("recv error");
return -1;
}
if(bytes == 0) //說明客戶端放棄鏈接
{
return -2;
}
printf("client:%s\n", buf);
return 0;
}
int main(int argc, const char *argv[])
{
epollfd = epoll_create(MAX_EVENTS);//創(chuàng)建對(duì)象
if(epollfd < 0)
{
perror("fail to epoll!");
return -1;
}
listenfd = tcp_create_socket();
fcntl(listenfd,F_SETFL, O_NONBLOCK);//設(shè)置非阻塞
ev.data.fd = listenfd;
ev.events = EPOLLIN;
rv = epoll_ctl(epollfd, EPOLL_CTL_ADD, listenfd, &ev);//加入監(jiān)聽事件
if (rv < 0)
{
perror("epoll_ctl err:");
return -1;
}
while(1)
{
fds = epoll_wait(epollfd, events, MAX_EVENTS, -1);
if(fds < 0)
{
perror("epoll_wait error");
return -1;
}
//輪尋被激活的套接子
for(i = 0; i < fds; i++)
{
if(events[i].data.fd == listenfd) //判斷是否是監(jiān)聽的對(duì)象(描述符)
{
acceptfd = accept(listenfd, (struct sockaddr*)&clientaddr, &len);
if(acceptfd < 0)
{
perror("accept error");
continue;
}
ev.data.fd = acceptfd;
ev.events = EPOLLIN | EPOLLET;
//將鏈接的客戶端添加到關(guān)注的事件中去
epoll_ctl(epollfd, EPOLL_CTL_ADD, acceptfd, &ev);
continue;
}
else
{
rv = mz_process_data(events[i].data.fd);
if(rv == -2) //客戶端放棄鏈接,將描述符從事件中清除
{
epoll_ctl(epollfd, EPOLL_CTL_DEL, events[i].data.fd, &ev);
close(events[i].data.fd);
continue;
}
}
}
}
return 0;
}
3.client.c
#include "head.h"
#define N 32
int main(int argc, const char *argv[])
{
int sockfd;
struct sockaddr_in serveraddr;
socklen_t len = sizeof(serveraddr);
char buf[N] = {0};
//1、設(shè)定基于TCP通信的標(biāo)準(zhǔn)
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if(sockfd < 0)
{
perror("fail to socket");
return -1;
}
bzero(&serveraddr, len);
serveraddr.sin_family = AF_INET;
serveraddr.sin_port = htons(atoi("8000"));//端口號(hào)
serveraddr.sin_addr.s_addr = inet_addr("192.168.8.199"); //IP地址
//2、綁定IP地址和端口號(hào)
if(connect(sockfd, (struct sockaddr*)&serveraddr, len) < 0)
{
perror("fail to connect");
close(sockfd);
return -1;
}
//客戶端用來發(fā)送數(shù)據(jù)
while(1)
{
fgets(buf, N, stdin);
buf[strlen(buf) - 1] = '\0';
send(sockfd, buf, N, 0);
if(strncmp(buf, "quit", 4) == 0)
{
break;
}
}
close(sockfd);
return 0;
}