标签:epoll和select用法 epoll和select对比
epoll是Linux内核为处理高并发而作改进的poll,是Linux下多路复用IO接口select/poll的增强版本。这里主要讲epoll和另外两个的区别,另外再把epoll的一个简单运用实例说说。
(一)epoll 有select,poll的主要区别:
一、相比于select与poll, epoll最大的好处在于它不会随着监听fd数目的增长而降低效率;
二、内核中的select与poll的实现是采用轮询来处理的,轮询的fd数据越多,自然耗时也越多;
三、epoll的实现是基于回调的,如果fd有期望的事件发生就通过回调函数将其加入epoll的就绪对了当中,也就是说它只关心活跃的fd
四、内核/用户空间拷贝问题,当内核把fd消息通知给用户空间时,selec和poll采用了内存拷贝的方法,而epoll采用了共享内存的方式。
(二)epoll 相关系统调用
epoll的接口非常简单,一共就三个函数:
一、(1)int epoll_create(int size);
(2)int epoll_create(int flags);
在比较新的Linux的版本中,出现了 int epoll_create(int flags); 这里的flags 一般选EPOLL_CLOEXEC,表示当进程被替换的时候文件描述符会被关闭。
二、 int epoll_ctl(int epfd, int op, int fd, struct
struct epoll_event { __uint32_t events; epoll_data_t data; }; typedef union epoll_data { void *ptr; //这里是个指针,主要用来存放复杂的文件描述符 int fd; __uint32_t u32; __uint64_t u64; } epoll_data_t;
(三)epoll 实例
这里以一个简单的客户端回射为例,程序的运行环境是debian(linux的一个发行版本),从上到下分别是头文件"net.h",epoll_server.cpp,select_client.cpp
服务器端:epoll实现的,干两件事分别为:1.等待客户端的链接,2.接收来自客户端的数据并且回射;
客户端:select实现,干两件事为:1.等待键盘输入,2.发送数据到服务器端并且接收服务器端回射的数据;
/***************** @author:shaosli @data: 2015/7/28 @funtions:network file ***************************/ #include <stdio.h> #ifndef _NET_L #define _NET_L #include <iostream> #include <vector> #include <algorithm> #include <stdio.h> #include <sys/types.h> #include <sys/epoll.h> //epoll ways file #include <sys/socket.h> #include <fcntl.h> //block and noblock #include <stdlib.h> #include <error.h> #include <unistd.h> #include <arpa/inet.h> #include <netinet/in.h> #include <string.h> #include <signal.h> using namespace std; #define hand_error(msg) do{perror(msg); exit(EXIT_FAILURE);}while(0) #endif
服务器端代码:
/**************** @data: 2015/7/28 @authour:shaosli @function: this cpp is used to test epoll **************************/ #include "../public/net.h" #define MAX_EVENTS 10000 int setblock(int sock) { int ret = fcntl(sock, F_SETFL, 0); if (ret < 0 ) hand_error("setblock"); return 0; } int setnoblock(int sock) //设置非阻塞模式 { int ret = fcntl(sock, F_SETFL, O_NONBLOCK ); if(ret < 0) hand_error("setnoblock"); return 0; } int main() { int listenfd; listenfd = socket( AF_INET, SOCK_STREAM,0 ); //create a socket stream if( listenfd < 0 ) hand_error( "socket_create"); setnoblock(listenfd); int on = 1; if( setsockopt( listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on))< 0) hand_error("setsockopt"); struct sockaddr_in my_addr; memset(&my_addr, 0, sizeof(my_addr)); my_addr.sin_family = AF_INET; my_addr.sin_port = htons(5188); //here is host sequeue my_addr.sin_addr.s_addr = inet_addr("127.0.0.1"); if( bind( listenfd, (struct sockaddr *)&my_addr, sizeof(my_addr)) < 0) hand_error("bind"); int lisId = listen(listenfd, SOMAXCONN); if( lisId < 0) //LISTEN hand_error("listen"); struct sockaddr_in peer_addr; //用来 save client addr socklen_t peerlen; //下面是一些初始化,都是关于epoll的。 vector<int> clients; int count = 0; int cli_sock = 0; int epfd = 0; //epoll 的文件描述符 int ret_events; //epoll_wait()的返回值 struct epoll_event ev_remov, ev, events[MAX_EVENTS]; //events 用来存放从内核读取的的事件 ev.events = EPOLLET | EPOLLIN; //边缘方式触发 ev.data.fd = listenfd; epfd = epoll_create(MAX_EVENTS); //create epoll,返回值为epoll的文件描述符 //epfd = epoll_create1(EPOLL_CLOEXEC); //新版写法 if(epfd < 0) hand_error("epoll_create"); int ret = epoll_ctl(epfd, EPOLL_CTL_ADD, listenfd, &ev); //添加时间 if(ret < 0) hand_error("epoll_ctl"); while(1) { ret_events = epoll_wait(epfd, events, MAX_EVENTS, -1); //类似于select函数,这里是等待事件的到来。 if(ret_events == -1) { cout<<"ret_events = "<<ret_events<<endl; hand_error("epoll_wait"); } if( ret_events == 0) { cout<<"ret_events = "<<ret_events<<endl; continue; } cout<<"ret_events = "<<ret_events<<endl; for( int num = 0; num < ret_events; num ++) { cout<<"num = "<<num<<endl; if(events[num].events == listenfd) //client connect; { cout<<"listen sucess and listenfd = "<<listenfd<<endl; cli_sock = accept(listenfd, (struct sockaddr*)&peer_addr, &peerlen); if(cli_sock < 0) hand_error("accept"); cout<<"count = "<<count++; printf("ip=%s,port = %d\n", inet_ntoa(peer_addr.sin_addr),peer_addr.sin_port); clients.push_back(cli_sock); setnoblock(cli_sock); //设置为非阻塞模式 ev.data.fd = cli_sock; ev.events = EPOLLIN | EPOLLET ; if(epoll_ctl(epfd, EPOLL_CTL_ADD, cli_sock, &ev)< 0) hand_error("epoll_ctl"); } else if( events[num].events & EPOLLIN) { cli_sock = events[num].data.fd; if(cli_sock < 0) hand_error("cli_sock"); char recvbuf[1024]; memset(recvbuf, 0 , sizeof(recvbuf)); int num = read( cli_sock, recvbuf, sizeof(recvbuf)); if(num == -1) hand_error("read have some problem:"); if( num == 0 ) //stand of client have exit { cout<<"client have exit"<<endl; close(cli_sock); ev_remov = events[num]; epoll_ctl(epfd, EPOLL_CTL_DEL, cli_sock, &ev_remov); clients.erase(remove(clients.begin(), clients.end(), cli_sock),clients.end()); } fputs(recvbuf,stdout); write(cli_sock, recvbuf, strlen(recvbuf)); } } } return 0; }客户端代码:
/**************** @data: 2015/7/18 @authour:shaosli @function: this is a client **************************/ #include "../public/net.h" int main() { int sock; sock = socket( AF_INET, SOCK_STREAM,0 ); //create a socket stream if( sock< 0 ) hand_error( "socket_create"); struct sockaddr_in my_addr; //memset my_addr; memset(&my_addr, 0, sizeof(my_addr)); my_addr.sin_family = AF_INET; my_addr.sin_port = htons(5188); //here is host sequeue //my_addr.sin_addr.s_addr = htonl( INADDR_ANY ); my_addr.sin_addr.s_addr = inet_addr("127.0.0.1"); int conn = connect(sock, (struct sockaddr *)&my_addr, sizeof(my_addr)) ; if(conn != 0) hand_error("connect"); char recvbuf[1024] = {0}; char sendbuf[1024] = {0}; fd_set rset; FD_ZERO(&rset); int nready = 0; int maxfd; int stdinof = fileno(stdin); if( stdinof > sock) maxfd = stdinof; else maxfd = sock; while(1) { //select返回后把原来待检测的但是仍没就绪的描述字清0了。所以每次调用select前都要重新设置一下待检测的描述字 FD_SET(sock, &rset); FD_SET(stdinof, &rset); nready = select(maxfd+1, &rset, NULL, NULL, NULL); cout<<"nready = "<<nready<<endl; if(nready == -1 ) break; else if( nready == 0) continue; else { if( FD_ISSET(sock, &rset) ) //检测sock是否已经在集合rset里面。 { int ret = read( sock, recvbuf, sizeof(recvbuf)); //读数据 if( ret == -1) hand_error("read"); else if( ret == 0) { cout<<"sever have close"<<endl; close(sock); break; } else { fputs(recvbuf,stdout); //输出数据 memset(recvbuf, 0, strlen(recvbuf)); } } if( FD_ISSET(stdinof, &rset)) //检测stdin的文件描述符是否在集合里面 { if(fgets(sendbuf, sizeof(sendbuf), stdin) != NULL) { int num = write(sock, sendbuf, strlen(sendbuf)); //写数据 cout<<"sent num = "<<num<<endl; memset(sendbuf, 0, sizeof(sendbuf)); } } } } return 0; }代码在我自己的系统上测试过。
版权声明:本文为博主原创文章,未经博主允许不得转载。
epoll 和select 的区别,epoll和select的实例
标签:epoll和select用法 epoll和select对比
原文地址:http://blog.csdn.net/stefan1240/article/details/47106201