Linux I/O复用--epoll

本文深入解析Linux下epoll机制,一种高效的I/O复用技术,对比select/poll,介绍epoll的核心思想、API使用及代码实现,提升并发处理能力。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Linux I/O复用--epoll

编程TWO 编程小兔崽 今天

epoll是Linux内核为处理大批量文件描述符而作了改进的poll,是Linux下多路复用IO接口select/poll的增强版本,它能显著提高程序在大量并发连接中只有少量活跃的情况下的系统CPU利用率。另一点原因就是获取事件的时候,它无须遍历整个被侦听的描述符集,只要遍历那些被内核IO事件异步唤醒而加入Ready队列的描述符集合就行了。epoll除了提供select/poll那种IO事件的水平触发外,还提供了边缘触发,这就使得用户空间程序有可能缓存IO状态,减少epoll_wait/epoll_pwait的调用,提高应用程序效率。

 

 

1、epoll()

  epoll()是Linux特有的I/O复用函数,它的实现与使用上和select()、poll()、有很大差异。

  epoll()用一组函数来完成任务,而不是单个函数;其次,epoll()把文件描述放到内核事件表中,只需一个额外的文件描述符,来标识内核中唯一的这个事件表。

  需要使用的API:

  int epoll_create(int size);

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

需要使用的结构体信息:

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 */
  };

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

2、epoll_wait()

  关键:对epoll_wait()函数的核心理解

  (1)、返回值:事件表中就绪客户端的个数;

  (2)、参数events:将事件表中的就绪客户端的信息放到了events数组中。

3、epoll()的核心思想

   就是创建一个内核事件表,存放所监听客户端的套接字和当前的事件,在利用epoll_wait()函数查找就绪的套接字,最后经过增加、删除、修改利用epoll_ctl()函数进行;当然了,这其中还有一批搭配使用的宏;

3、代码实现

(1)、utili.h

#include<unistd.h>
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<sys/socket.h>
#include<arpa/inet.h>
#include<netinet/in.h>
#include<assert.h>
#include<sys/select.h>
#define SERVER_IP "127.0.0.1"
#define SERVER_PORT  8787
#define LISTEN_QUEUE 5
#define SIZE 10
#define BUFFER_SIZE 256#include<poll.h>
#define OPEN_MAX 1000
#include<sys/epoll.h>
#define FDSIZE      1000
#define EPOLLEVENTS 100

(2)、ser.c

#include"../utili.h"
static int socket_bind(const char *ip, int port)
{
    int listenfd = socket(AF_INET, SOCK_STREAM, 0);
    struct sockaddr_in addrSer;
    addrSer.sin_family = AF_INET;
    //addrSer.sin_addr.s_addr = inet_addr(ip);
    inet_pton(AF_INET, ip, &addrSer.sin_addr);
    addrSer.sin_port = htons(port);
    bind(listenfd, (struct sockaddr*)&addrSer, sizeof(struct sockaddr));
    return listenfd;
 }
 
 static void add_event(int epollfd, int fd, int state){
    struct epoll_event ev;
    ev.events = state;
    ev.data.fd = fd;
    epoll_ctl(epollfd, EPOLL_CTL_ADD, fd, &ev);
    }
    static void delete_event(int epollfd, int fd, int state){
    struct epoll_event ev;
    ev.events = state;
    ev.data.fd = fd;
    epoll_ctl(epollfd, EPOLL_CTL_DEL, fd, &ev);
    
 }
 static void modify_event(int epollfd, int fd, int state){
    struct epoll_event ev;
    ev.events = state;
    ev.data.fd = fd;
    epoll_ctl(epollfd, EPOLL_CTL_MOD, fd, &ev);
 }
 
 static void handle_accept(int epollfd, int listenfd)
 {
    int clifd;
    struct sockaddr_in addrCli;
    socklen_t len = sizeof(struct sockaddr);
    clifd = accept(listenfd, (struct sockaddr*)&addrCli, &len);
    if(clifd != -1)
    {
        add_event(epollfd, clifd, EPOLLIN);
    }
  }
    static void do_read(int epollfd,  int fd, char *buf){
    int nread = read(fd, buf, BUFFER_SIZE);
    if(nread == -1)
    {
        close(fd);
        delete_event(epollfd, fd, EPOLLIN);
    }
    else
    {
        printf("read msg:>%s\n",buf);
        modify_event(epollfd, fd, EPOLLOUT);
    }
    }
    static void do_write(int epollfd, int fd, char *buf){
    int nwrite = write(fd, buf, strlen(buf)+1);
    if(nwrite == -1)
    {
        close(fd);
        delete_event(epollfd, fd, EPOLLOUT);
    } 
    else
    {
        modify_event(epollfd, fd , EPOLLIN);
    }
    memset(buf, 0, BUFFER_SIZE);
    }
    static void handle_events(int epollfd, struct epoll_event *events, int num,
                            int listenfd, char *buf)
   {
        int i;
        int fd;
        for(i=0; i<num; ++i)
        {
            fd = events[i].data.fd;
            if((fd==listenfd) && (events[i].events&EPOLLIN)) //根据其结果分别进入三种状态
                handle_accept(epollfd, listenfd);  //申请与服务器连接
            else if(events[i].events & EPOLLIN)
                do_read(epollfd, fd, buf);  //只读
            else if(events[i].events & EPOLLOUT)        
            do_write(epollfd, fd, buf);  //只写
    }
  }
 static void do_epoll(int listenfd)
 {
    int ret;
    char buffer[BUFFER_SIZE];
    struct epoll_event events[EPOLLEVENTS];
    int epollfd = epoll_create(FDSIZE);
    add_event(epollfd, listenfd, EPOLLIN);
    for(;;)
   {
        //select poll
        ret = epoll_wait(epollfd, events, EPOLLEVENTS, -1);
        handle_events(epollfd, events, ret, listenfd, buffer);
    }
    close(epollfd);}int main(void)
   {
        int listenfd;
        listenfd = socket_bind(SERVER_IP, SERVER_PORT);
        listen(listenfd, LISTEN_QUEUE);
        do_epoll(listenfd);
        return 0;
      }

(3)、cli.c

#include"../utili.h"
static void add_event(int epollfd, int fd, int state)
{
    struct epoll_event ev;
    ev.events = state;
    ev.data.fd = fd;
    epoll_ctl(epollfd, EPOLL_CTL_ADD, fd, &ev);
 }
 static void delete_event(int epollfd, int fd, int state){
    struct epoll_event ev;
    ev.events = state;
    ev.data.fd = fd; 
    epoll_ctl(epollfd, EPOLL_CTL_DEL, fd, &ev);}static void modify_event(int epollfd, int fd, int state){
    struct epoll_event ev; 
    ev.events = state;
    ev.data.fd = fd; 
    epoll_ctl(epollfd, EPOLL_CTL_MOD, fd, &ev);}static void do_read(int epollfd,  int fd, int sockfd, char *buf){
    int nread = read(fd, buf, BUFFER_SIZE);
    
    if(nread == -1) 
    {
        close(fd);
        delete_event(epollfd, fd, EPOLLIN);
    }
    else
    {
        if(fd == STDIN_FILENO)
            add_event(epollfd, fd, EPOLLIN);
        else
        {
            delete_event(epollfd, fd, EPOLLIN);
            add_event(epollfd, STDOUT_FILENO, EPOLLOUT);
        }
    }
    printf("Ser :>%s", buf);}static void do_write(int epollfd, int fd, int sockfd, char *buf){
    int nwrite = write(fd, buf, strlen(buf)+1);
    if(nwrite == -1)
    {
        perror("write");
        close(fd);
    }
    else
    {
        if(fd == STDOUT_FILENO)
        {
            delete_event(epollfd, fd, EPOLLOUT);
        }
        else
        {
            modify_event(epollfd, fd, EPOLLIN);
        }
    }    
    memset(buf, 0, BUFFER_SIZE);}static void handle_events(int epollfd, struct epoll_event *events, int num,
                            int sockfd, char *buf)
  {
    int i;
    int fd;
    for(i=0; i<num; ++i)
    {
        fd = events[i].data.fd;
        if(events[i].events & EPOLLIN)
            do_read(epollfd, fd, sockfd, buf);
        else if(events[i].events, fd, sockfd, buf)
            do_write(epollfd, fd, sockfd, buf);
    }
    
    }
    static void handle_connection(int sockfd)
    {
        struct epoll_event events[EPOLLEVENTS];
        int epollfd = epoll_create(FDSIZE);
        add_event(epollfd, STDIN_FILENO, EPOLLIN);

        int ret;
        char buffer[BUFFER_SIZE];
        for(;;)
        {
            ret = epoll_wait(epollfd, events, EPOLLEVENTS, -1);
            handle_events(epollfd, events, ret, sockfd, buffer);
    }    
    close(epollfd);}int main(void)
   {
        int sockfd = socket(AF_INET, SOCK_STREAM, 0);
        struct sockaddr_in addrSer;
        addrSer.sin_family = AF_INET;
        addrSer.sin_port = htons(SERVER_PORT);
        addrSer.sin_addr.s_addr = inet_addr(SERVER_IP);
        connect(sockfd, (struct sockaddr*)&addrSer, sizeof(struct sockaddr));

    handle_connection(sockfd);
    close(sockfd);
    return 0;
   }

运行结果

服务器端就是等待客户端的使用;

客户端1

客户端2

利用epoll()函数,不用轮询每个套接字,效率更高效一些;

Linux编程重点的知识也跟大家讲了一部分,接下来会跟大家分享进程之间的通信、线程之间的通信这些也是平时面试和开发经常用到的知识,还有一个跟大家说一下,最近工作比较忙,项目比较紧,可能更新文章没有那么频繁,不过基本上每周都会更新至少3篇,谢谢大家的支持,加油每天进步一点点!

推荐阅读:

Linux I/O复用——poll()

Linux I/O复用—select()

线程池网络服务

多线程网络服务

Socket网络编程

线程高级操作

Linux多线程编程

线程

欢本文的朋友们,欢迎长按下图关注订阅号编程小兔崽,收看更多精彩内容

每天进步一点点,如果有用给小编点个赞

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值