I/O复用(epoll)

epoll是Linux特有的I/O复用函数。它在实现和使用上与select、poll有很大差异。首先,epoll使用一组函数来完成任务,而不是单个函数。其次,epoll把用户关心的文件描述符上的事件放在内核里的一个事件表中,从而无需像select和poll那样每次调用都要重复传入文件描述符集或事件集。但epoll需要使用一个额外的文件描述符,来唯一标识内核中的这个事件表。这个文件描述符由epoll_creat函数来创建。(《Linux高性能服务器编程》)

int    epoll_ctl  (int epfd  ,   int op   ,   int fd   ,  struct epoll_event *event)

fd参数是要操作的文件描述符,op指定操作类型。操作类型有如下3种:

1、EPOLL_CTL_ADD     往事件表中注册fd上的事件

2、EPOLL_CTL_MOD     修改fd上的注册事件

3、EPOLL_CTL_DEL       删除fd上注册的事件

event参数指定事件,它是epoll_event结构指针类型。

struct   epoll_event

{

        _uint32_t   events;         events成员描述事件类型

         epoll_data_t    data;      data成员用于存储用户数据

};epoll_data_t是个联合体

epoll_ctl 成功时返回0,失败则返回-1 并设置errno

epoll_wait函数

epoll系列系统调用的主要接口是epoll_wait函数。它在一段超时时间内等待一组文件描述符上的事件

int epoll_wait (int epfd  ,   struct epoll_event*events  ,  int maxevents  ,  int timeout);

该函数成功时返回就绪文件描述符的个数,失败时返回-1并设置erron。

timeout:超时时间

maxevents参数指定最多监听多少个事件,它必须大于0

epoll_wait函数如果检测到事件,就将所有就绪的事件从内核事件表(由epfd参数指定)中复制到它的第二个参数events指向的数组中。这个数组只用于输出epoll_wait函数检测到的就绪事件

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/time.h>
#include<string.h>
#include<assert.h>
#include<sys/socket.h>
#include<sys/types.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<sys/epoll.h>
#define SIZE 100

int main()
{
    int sockfd=socket(AF_INET,SOCK_STREAM,0);
    assert(sockfd!=-1);


    struct sockaddr_in ser ,cli;
    memset(&ser,0,sizeof(ser));
    ser.sin_family=AF_INET;
    ser.sin_port=htons(6000);
    ser.sin_addr.s_addr=inet_addr("127.0.0.1");

    int res = bind(sockfd,(struct sockaddr*)&ser,sizeof(ser));
    assert(res!=-1);


    listen(sockfd,5);
    int epollfd=epoll_create(5);//创建内核时间表
    assert(epollfd!=-1);
    struct epoll_event event;
    event.events=EPOLLIN;//事件类型
    event.data.fd=sockfd;

    epoll_ctl(epollfd,EPOLL_CTL_ADD,sockfd,&event);//添加描述符,注册到内核事件表中
    while(1)
    {
	struct epoll_event events[SIZE];
	int n = epoll_wait(epollfd,events,SIZE,-1);//就绪文件描述符的个数events就是就绪的文件描述符
	if(n<=0)
	{
	    printf("epoll wait error\n");
	    continue;
	}
	int i=0;
	for(;i<n;++i)
	{
	    int fd=events[i].data.fd;
	    if(fd==sockfd)
	    {
		int len=sizeof(cli);
		int c=accept(fd,(struct sockaddr*)&cli,&len);
	
	    if(c<0)
	    {
		continue;
	    }
	    event.events=EPOLLIN|EPOLLRDHUP;
	    event.data.fd=c;//事件所从属的目标文件描述符
	    epoll_ctl(epollfd,EPOLL_CTL_ADD,c,&event);//往事件表epollfd中注册c事件
	    }
	    else if(events[i].events & EPOLLRDHUP)//判断哪个事件文件描述符关闭
	    {
		epoll_ctl(epollfd,EPOLL_CTL_DEL,fd,NULL);
		close(fd);
		printf("%d was over\n",fd);

	    }
	    else if(events[i].events & EPOLLIN)
	    {
		char buff[128]={0};
		recv(fd,buff,127,0);
		printf("%d:%s\n",fd,buff);
		send(fd,"OK",2,0);
	    }
	}
    }
    }

1、select poll:每次循环都需要从用户空间向内核空间传递数据

                epoll:直接在内核中创建事件表,每个描述符仅需要传递一次

2、select poll :在内核中以轮询的方式检测有就绪事件的描述符(O(n))

                epoll:在每个描述符上注册回调函数,事件就绪后,执行回调函数将描述符添加到就绪队列(O(1))

3、select  poll:返回后需要遍历所有文件描述符,才能找到就绪的(O(n))

                 epoll:返回后直接得到就绪文件描述符,不需要遍历所有文件描述符(O(1))

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值