解析Linux源码之epoll

一、前言

在本篇文章中,我们将深入探究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视图之创建视图(CREATE VIEW)和使用限制实例详解

    当我们需要从数据库中查询某些数据时,有时候我们需要的数据需要从多个表中关联查询,这时就可以使用mysql视图,mysql视图就是一种虚拟的表,它并不真实存在于数据库中,但是我们可以查询它,它是由一条或者多条SELECT语句组成的,查询它就相当于执行这些SELECT语句。 创建视图 我们可以使用CREATE VIEW语句来创建视图,CREATE VIEW语句的…

    database 2023年5月21日
    00
  • 如何在Python中使用psycopg2库连接PostgreSQL数据库?

    在Python中,我们可以使用psycopg2库连接PostgreSQL数据库。psycopg2是一个Python PostgreSQL适配器,它允许我们在Python中连接、操作和管理PostgreSQL数据库。以下是如何在Python中使用psycopg2库连接PostgreSQL数据库的完整使用攻略,包括连接数据库、创建表、插入数据、查询数据、更新数据…

    python 2023年5月12日
    00
  • MySQL版本低了不支持两个时间戳类型的值解决方法

    MySQL版本低了不支持两个时间戳类型的值解决方法 问题描述:在MySQL版本较低的情况下,如果要存储两个时间戳类型的值,可能会遇到错误提示类似于“ERROR 1292 (22007): Incorrect datetime value: ‘2021-10-30 12:00:00’ for column ‘datetime_column’”,提示无法正确处理…

    database 2023年5月22日
    00
  • Oracle WebLogic Server 12.2.1.2安装部署教程

    Oracle WebLogic Server 12.2.1.2 安装部署教程 本文将介绍 Oracle WebLogic Server 12.2.1.2 的安装和部署过程。 准备工作 在开始安装前,您需要进行以下准备工作: 下载安装程序:从官方网站下载 WebLogic Server 12.2.1.2 的安装程序,或者从 Oracle 官方 Docker H…

    database 2023年5月22日
    00
  • Oracle 11g安装错误提示未找到wfmlrsvcapp.ear的解决方法

    针对在Oracle 11g安装过程中出现”未找到wfmlrsvcapp.ear”错误的问题,我们可以采取下列步骤进行解决。 问题原因分析 在安装Oracle 11g时,会遇到需要找到”wfmlrsvcapp.ear”文件的提示,但是该文件并不在Oracle 11g安装光盘中,因此需要我们手动下载并添加该文件到指定目录下。 解决方案步骤 打开Oracle官网(…

    database 2023年5月22日
    00
  • mysql 常用命令集锦[绝对精华]

    MySQL 常用命令集锦 1. 登录 MySQL 要使用 MySQL 命令行客户端,必须先登录到服务器上的 MySQL 服务。 使用以下命令登录到 MySQL: mysql -h 主机名 -u 用户名 -p 其中: -h:指定主机名,如果是本机 MySQL 服务,可以省略。 -u:指定连接 MySQL 的用户名。 -p:表示 MySQL 用户需要输入密码来进…

    database 2023年5月22日
    00
  • MySQL创建唯一索引时报错Duplicate entry * for key问题

    MySQL创建唯一索引时报错”Duplicate entry *** for key”问题通常是因为在唯一索引列中存在同名的两条记录,导致插入数据时出现了重复键值。有以下几种方法可以解决此问题: 方法一:删除重复数据 第一步:找出重复数据 可以通过以下语句找出重复数据: SELECT col1,col2,COUNT(*) FROM table_name GR…

    database 2023年5月22日
    00
  • postgreSQL数据库基本概念教程

    PostgreSQL数据库基本概念教程 PostgreSQL是一种高度可扩展的开源关系型数据库管理系统。它有着广泛的使用领域,包括web应用、大数据、金融、人力资源、物流等等。本教程将介绍PostgreSQL数据库的基本概念。 数据类型 PostgreSQL支持多种数据类型,包括整数、浮点数、字符串、日期、数组、JSON等等。以下是一些常用数据类型的示例: …

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