解析Linux源码之epoll

yizhihongxing

一、前言

在本篇文章中,我们将深入探究Linux内核源码中的网络编程模型epoll。

首先,我们对epoll的整体结构进行说明。其次,我们将分析epoll的实现机制,包括epoll的两个核心数据结构以及相关操作的实现。最后,我们将结合示例代码对epoll的使用进行说明。

二、整体结构

在Linux内核源码中,epoll的实现分为多个文件,并被封装在一个名为epoll.c的文件中。其中,主要的模块包括:

  1. epoll_create和epoll_create1函数的实现

  2. epoll_ctl函数的实现,包括增加、删除和修改文件描述符

  3. epoll_wait函数的实现,等待I/O事件发生并返回就绪文件描述符

在epoll实现中,有两个核心的数据结构:

  1. epoll_event,代表一个I/O事件

  2. epitem,代表epoll中的一个文件描述符项

三、实现机制

  1. epoll_create和epoll_create1函数的实现

epoll_create和epoll_create1函数用于创建一个epoll句柄。它们的实现非常简单,可以直接参考系统调用实现文档。

  1. epoll_ctl函数的实现

epoll_ctl用于给epoll句柄中添加、修改或删除一个文件描述符。当添加或修改文件描述符时,我们需要将其加入到监听队列中,并设置对应的I/O事件类型。具体实现如下:

static int ep_insert(struct epitem *epi) {
    struct epoll_event e;
    e.events = epi->event.events;
    e.data.u32 = epi->handout->id;
    if (epi->event.events & (EPOLLRDHUP | EPOLLHUP)) {
        e.events |= EPOLLIN | EPOLLRDNORM | EPOLLRDBAND | EPOLLINIGNEOF |
                    EPOLLOUT | EPOLLWRNORM | EPOLLWRBAND;
    }
    return rb_insert(&epi->node, &ep.ep_events, ep_rb_cmp) != NULL;
}

在epi中,我们设置了要监听的I/O事件类型。然后,我们将其加入到epoll中的监听队列中。

  1. epoll_wait函数的实现

epoll_wait用于等待就绪文件描述符。在等待过程中,我们需要从epoll监听队列中找到已经就绪的文件描述符。具体实现如下:

static int ep_item_poll(struct epitem *epi, struct epoll_event *events,
                        int maxevents, int *timeout, bool epoll_wait) {
    // ...
    if (epi->nwait == epi->nready) {
        ep_remove(epi);
        // ...
        return 0;
    }
    // ...
    epi->nwait++;
    if (ep_waitqueue(&epi->wait, &e)) {
        epi->nwait--;
        ep_remove(epi);
        // ...
        return 0;
    }
    // ...
}

在epi中,我们记录了当前关注的I/O事件类型和状态。当等待过程中监听到就绪文件描述符时,我们将其移除。如果没有找到就绪的文件描述符,则传递超时值以等待就绪。

四、示例说明

现在,我们来看两个示例,以更好地理解epoll的使用。

第一个示例:简单使用epoll判断TCP连接是否关闭

#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <sys/epoll.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#define MAX_EVENTS 10

int main(int argc, char *argv[]) {
    int sfd, efd;
    struct sockaddr_in listen_addr;
    struct epoll_event event;
    struct epoll_event events[MAX_EVENTS];

    listen_addr.sin_family = AF_INET;
    listen_addr.sin_port = htons(8080);
    listen_addr.sin_addr.s_addr = INADDR_ANY;

    sfd = socket(AF_INET, SOCK_STREAM, 0);
    bind(sfd, (struct sockaddr*)&listen_addr, sizeof(listen_addr));
    listen(sfd, 10);

    efd = epoll_create1(0);
    event.data.fd = sfd;
    event.events = EPOLLIN | EPOLLRDHUP;
    epoll_ctl(efd, EPOLL_CTL_ADD, sfd, &event);

    while (1) {
        int n = epoll_wait(efd, events, MAX_EVENTS, -1);
        for (int i = 0; i < n; i++) {
            if (events[i].data.fd == sfd) { // 有新的连接
                struct sockaddr_in client_addr;
                socklen_t client_len = sizeof(client_addr);
                int cfd = accept(sfd, (struct sockaddr*)&client_addr, &client_len);

                event.data.fd = cfd;
                event.events = EPOLLIN | EPOLLRDHUP;
                epoll_ctl(efd, EPOLL_CTL_ADD, cfd, &event);
            } else { // 接受或关闭连接
                if (events[i].events & EPOLLHUP || events[i].events & EPOLLRDHUP) {
                    // 关闭连接
                    close(events[i].data.fd);
                } else if (events[i].events & EPOLLIN) {
                    // 读取数据
                    char buf[1024];
                    int n = 0;
                    do {
                        n = read(events[i].data.fd, buf, 1024);
                        if (n > 0) { // 成功接受到数据
                            printf("Received %d bytes from %d\n", n, events[i].data.fd);
                        }
                    } while (n == 1024);
                }
            }
        }
    }
}

第二个示例:使用epoll来提高性能,支持更多连接

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/epoll.h>

#define MAX_EVENTS 10000

static int set_nonblock(int fd) {
    int flags = fcntl(fd, F_GETFL, 0);
    if (flags == -1) {
        return -1;
    }
    flags |= O_NONBLOCK;
    if (fcntl(fd, F_SETFL, flags) == -1) {
        return -1;
    }
    return 0;
}

int main(int argc, char* argv[]) {
    int sfd, sfd1, efd, res;
    struct sockaddr_in sa;
    struct epoll_event event;
    struct epoll_event events[MAX_EVENTS];
    int i;

    if ((sfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1) {
        perror("socket()");
        exit(EXIT_FAILURE);
    }

    set_nonblock(sfd);

    memset(&sa, 0, sizeof(sa));
    sa.sin_family = AF_INET;
    sa.sin_port = htons(9000);
    sa.sin_addr.s_addr = htonl(INADDR_ANY);

    if (bind(sfd, (struct sockaddr*)&sa, sizeof(sa)) == -1) {
        perror("bind()");
        exit(EXIT_FAILURE);
    }

    if (listen(sfd, 5) == -1) {
        perror("listen()");
        exit(EXIT_FAILURE);
    }

    if ((efd = epoll_create1(0)) == -1) {
        perror("epoll_create1()");
        exit(EXIT_FAILURE);
    }

    update_epoll(efd, sfd, EPOLLIN | EPOLLET);
    while (1) {
        int n = epoll_wait(efd, events, MAX_EVENTS, 150000);
        if (n == -1 && errno != EINTR) {
            perror("epoll_pwait()");
            exit(EXIT_FAILURE);
        }

        for (i = 0; i < n; i++) {
            if ((events[i].events & EPOLLERR) ||
                    (events[i].events & EPOLLHUP) ||
                    (!(events[i].events & (EPOLLIN | EPOLLOUT)))) {
                close(events[i].data.fd);
                continue;
            }

            if (events[i].data.fd == sfd) {
                while (1) {
                    sfd1 = accept(sfd, NULL, NULL);
                    if (sfd1 == -1) {
                        break;
                    }
                    update_epoll(efd, sfd1, EPOLLIN | EPOLLET);
                }
                if (errno != EAGAIN && errno != EWOULDBLOCK) {
                    perror("accept()");
                    exit(-1);
                }
                continue;
            }

            do_use_fd(events[i].data.fd);
        }
    }

    return 0;
}

以上就是epoll的解析攻略的完整内容,希望能对你有所帮助!

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:解析Linux源码之epoll - Python技术站

(0)
上一篇 2023年5月22日
下一篇 2023年5月22日

相关文章

  • mysql数据库的登录脚本

    ########################   ku脚本: 可以使用以下ku脚本,它可以根据提供的参数登录到MySQL数据库: #!/bin/bash # Check for correct number of arguments if [ $# -lt 1 ]; then echo “Usage: $0 <ip> [<port&gt…

    MySQL 2023年4月13日
    00
  • 提升MYSQL查询效率的10个SQL语句优化技巧

    优化SQL语句的执行顺序:在SQL语句中使用正确的表连接方式、正确的索引来建立表之间的关系,以最小化系统的I/O操作。尽量避免大表与大表之间的联接。 正确的表连接方式: INNER JOIN:表示只返回连接表中满足条件的记录。 LEFT JOIN:表示返回连接表A中的所有记录,即使在连接表B中没有匹配的记录,也会显示A表的记录。 RIGHT JOIN:表示返…

    database 2023年5月19日
    00
  • 当数据库变慢时的解决方法

    当数据库变慢时,我们需要先通过一些指标分析确定问题的所在,再采取一些解决方法来优化数据库性能。以下是大致的完整攻略: 1. 数据库性能指标及其分析 1.1 延迟指标 响应时间(RT):请求到达数据库系统直至结果返回所需的时间,可分为平均响应时间和百分位响应时间。RT 通常要尽量地短。 等待时间(WT):等待资源/锁定的时间,为了减少 WT,可以考虑更改等待超…

    database 2023年5月19日
    00
  • 详细总结Java for循环的那些坑

    详细总结Java for循环的那些坑 在Java中,for循环是最基础最常用的循环结构之一。虽然它看起来简单,但其中包含了一些坑点,如果不注意,在使用的过程中可能会出现一些问题。在这篇攻略中,我们将详细总结Java for循环的那些坑。 for循环的基本语法 在开始介绍for循环的坑点之前,我们先来回顾一下for循环的基本语法: for (初始化语句; 布尔…

    database 2023年5月22日
    00
  • 开源MySQL高效数据仓库解决方案:Infobright详细介绍

    开源MySQL高效数据仓库解决方案:Infobright详细介绍 Infobright是一个开源的数据仓库解决方案,用于处理大数据情境下的OLAP查询,由于其出色的性能,得到不少公司的青睐。本文从Infobright的架构、优缺点、特点、优化路径等多个方面进行详细介绍,旨在使读者对Infobright有一个全面深入的了解。以下是Infobright的完整攻略…

    database 2023年5月19日
    00
  • MySQL主键自增长(AUTO_INCREMENT)详解

    主键是数据库表中用于唯一标识每个记录的列,自增长是一种主键属性,指的是当插入新记录时,自动分配一个唯一的整数值作为主键,每次插入新记录时,该自增长值会自动加1。 在MySQL中,可以将主键列定义为自增长列,可以使用关键字“AUTO_INCREMENT”来实现。 具体使用方法如下: 在创建表时,定义主键列并设置为自增长: CREATE TABLE 表名 ( i…

    MySQL 2023年3月9日
    00
  • MySQL8.0的WITH查询详情

    当使用 MySQL 8.0 时,我们可以使用 Common Table Expressions (CTE) 或者简称为 WITH 查询来简化查询语句。本文将向您介绍MySQL 8.0的 WITH 查询详情及其用法的完整攻略。 什么是WITH查询 WITH查询在MySQL8.0中被称为公共表达式。使用WITH语句,我们可以为一次查询创建一个临时表,并在查询中使…

    database 2023年5月22日
    00
  • TestTrack Pro怎么安装?TestTrack Pro安装配置详细图文教程

    下面是“TestTrack Pro怎么安装?TestTrack Pro安装配置详细图文教程”的完整攻略: 1. 下载TestTrack Pro安装包 首先,在官方网站下载TestTrack Pro安装包。在下载页面选择与你的操作系统匹配的版本,一般有Windows和Mac版本可供选择。 2. 安装TestTrack Pro 下载完成后,双击安装包开始安装。根…

    database 2023年5月22日
    00
合作推广
合作推广
分享本页
返回顶部