【Linux】网络高级IO

news2024/11/16 9:23:06

在这里插入图片描述

欢迎来到Cefler的博客😁
🕌博客主页:折纸花满衣
🏠个人专栏:Linux

在这里插入图片描述


目录

  • 👉🏻五种IO模型
  • 👉🏻消息通信的同步异步与进程线程的同步异步有什么不同?
  • 👉🏻非阻塞IO
    • fcntl函数
  • 👉🏻I/O多路转接之select
    • select函数
    • select使用示例: 检测标准输入输出
  • 👉🏻I/O多路转接之poll
    • poll函数
    • poll示例: 使用poll监控标准输入
  • 👉🏻I/O多路转接之epoll
    • 简单介绍
    • epoll_create()、epoll_ctl()和epoll_wait()
    • epoll_event结构体
    • epoll示例: 使用epoll监控标准输入

👉🏻五种IO模型

在这里插入图片描述

网络高级IO的五大模型主要包括:

  1. 阻塞IO(Blocking IO)

    • 描述:在内核将数据准备好之前,系统调用会一直等待。所有的套接字,默认都是阻塞方式。
    • 示例:服务端在处理客户端的连接和数据时,会阻塞在acceptread操作上,等待建立连接和读取数据。
    • 特点:两个阶段(等待读就绪和读数据)都是阻塞的。
      在这里插入图片描述
  2. 非阻塞IO(Non-blocking IO)

    • 描述:如果内核还未将数据准备好,系统调用仍然会直接返回,并返回EWOULDBLOCK错误码。
    • 示例:虽然可以通过多线程实现伪非阻塞,但真正的非阻塞IO需要操作系统提供非阻塞的支持。
    • 特点:用户进程发起请求后,如果数据未准备好,则立即返回,不会阻塞用户进程。
      在这里插入图片描述
  3. IO多路复用(IO Multiplexing)

    • 描述:通过一种机制(如select、poll、epoll等)同时监控多个文件描述符的就绪状态,从而避免阻塞在单个文件描述符上。
    • 示例:select是其中一种实现方式,它使用事件集合方式来监控多个文件描述符。
    • 特点:能够同时等待多个文件描述符的就绪状态,提高IO效率。
      在这里插入图片描述
  4. 信号驱动IO(Signal-driven IO)

    • 描述:内核将数据准备好的时候,使用SIGIO信号通知应用程序进行IO操作。
    • 示例:在处理僵尸进程时,可以使用信号来通知父进程回收子进程的退出信息。
    • 特点:通过信号机制来通知应用程序进行IO操作,避免了轮询的开销。
      在这里插入图片描述
  5. 异步IO(Asynchronous IO)

    • 描述:由内核在数据拷贝完成时,通知应用程序。
    • 示例:类似于钓鱼的比喻,异步IO是“帮你钓鱼”,即内核完成数据拷贝后通知应用程序。
    • 特点:应用程序不需要等待数据拷贝完成,内核在数据拷贝完成后会主动通知应用程序。
      在这里插入图片描述

这五大模型各有特点,适用于不同的场景和需求。在实际应用中,需要根据具体情况选择合适的IO模型来提高程序的性能和效率。

参考文章:浅谈5种IO模型

👉🏻消息通信的同步异步与进程线程的同步异步有什么不同?

消息通信的同步异步与进程线程的同步异步在概念和应用上存在一些差异,以下是具体的分析和归纳:

🌹 消息通信的同步与异步

  1. 定义

    • 同步:在消息通信中,同步意味着发送方发送消息后,会等待接收方的响应,即一次调用,一次返回。发送方在收到接收方的响应之前,不会继续执行其他操作。
    • 异步:异步通信中,发送方发送消息后不会立即等待接收方的响应,而是继续执行其他操作。接收方在接收到消息后,会通过某种方式(如回调函数、事件通知等)通知发送方。
  2. 特点

    • 同步
      • 严格按照顺序执行,发送方和接收方之间保持紧密的同步关系。
      • 适用于需要确保消息被正确处理并获取结果的情况。
      • 可能会降低系统效率,因为发送方需要等待接收方的响应。
    • 异步
      • 发送方和接收方之间相对独立,发送方发送消息后可以继续执行其他任务。
      • 适用于需要并行处理多个任务或不需要立即获取结果的情况。
      • 可以提高系统效率,因为发送方不需要等待接收方的响应。

🌹 进程与线程的同步与异步

  1. 定义

    • 同步:在进程或线程中,同步指的是多个任务按照特定的顺序依次执行,即一个任务执行完毕后再开始执行下一个任务。
    • 异步:异步则是指多个任务可以同时执行,不需要等待前一个任务完成。
  2. 特点

    • 同步
      • 确保任务的顺序执行,有助于管理资源和避免竞态条件。
      • 可能会降低系统效率,因为需要等待前一个任务完成才能开始下一个任务。
    • 异步
      • 提高系统效率,允许多个任务同时执行。
      • 需要额外的机制来管理任务之间的依赖关系和协调资源的访问。

🌹总结

消息通信的同步异步与进程线程的同步异步在概念上有所相似,但应用场景和关注点有所不同。消息通信主要关注消息发送和接收之间的同步或异步关系,而进程线程的同步异步则关注任务之间的执行顺序和并发性。在实际应用中,需要根据具体的需求和场景来选择合适的同步异步方式。

👉🏻非阻塞IO

fcntl函数

fcntl函数是计算机中用于文件描述符控制的一种函数,它允许对已打开的文件性质进行修改。以下是fcntl函数的用法介绍:

🥕 一、函数声明

#include <fcntl.h>
#include <unistd.h>

int fcntl(int fd, int cmd, ...);

fcntl`函数接受三个参数:

  1. fd:文件描述符,代表要操作的已打开文件。
  2. cmd:操作命令,指定要对文件描述符进行的操作类型。
  3. :可选参数,根据cmd的值,可能需要一个int argstruct flock *lock作为第三个参数。

🥕 二、功能介绍

fcntl函数根据cmd参数的值执行不同的操作,主要有以下几种:

  1. F_DUPFD

    • 复制一个现有的文件描述符。
    • 查找大于或等于参数arg的最小且仍未使用的文件描述符,并复制参数fd的文件描述符。
    • 成功时返回新复制的文件描述符。
  2. F_GETFD/F_SETFD

    • F_GETFD:取得与文件描述符fd联合的close-on-exec标志。
    • F_SETFD:设置close-on-exec标志。如果文件描述符设置了此标志,则在执行exec()相关函数时,文件将被关闭。
  3. F_GETFL/F_SETFL

    • F_GETFL:取得文件描述符状态标志。
    • F_SETFL:设置文件描述符状态标志。可以更改的标志包括O_APPEND(追加写)、O_NONBLOCK(非阻塞IO)和O_ASYNC(异步IO通知)。
  4. F_GETLK/F_SETLK/F_SETLKW

    • 用于获取、设置和等待文件锁。
    • F_SETLKW与F_SETLK功能相同,但无法建立锁定时会阻塞等待。
  5. F_GETOWN/F_SETOWN

    • 用于获取/设置异步IO的所有权,即哪个进程或线程将接收SIGIO和SIGURG信号。

🥕 三、返回值

  • 如果成功,根据cmd的值,fcntl可能返回不同的值。
  • 如果出错,所有命令都返回-1,并设置全局变量errno以指示错误。

🥕四、使用实例

在网络编程中,fcntl常被用于将文件描述符设置为非阻塞模式,以便在数据未就绪时不会阻塞进程。以下是一个简单的示例,展示了如何使用fcntl将标准输入设置为非阻塞模式:

#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <stdio.h>

int main(void) {
    int flags, n;
    //char buf[10];

    // 获取stdin的当前标志
    flags = fcntl(STDIN_FILENO, F_GETFL);
    if (flags == -1) {
        perror("fcntl error");
        return 1;
    }

    // 添加O_NONBLOCK标志
    flags |= O_NONBLOCK;

    // 设置stdin为非阻塞模式
    if (fcntl(STDIN_FILENO, F_SETFL, flags) == -1) {
        perror("fcntl error");
        return 1;
    }

    // 接下来的read调用将不会阻塞,如果数据未就绪,将返回-1并设置errno为EAGAIN或EWOULDBLOCK
    // ...
	while (1) {
	 char buf[1024] = {0};
 	 ssize_t read_size = read(0, buf, sizeof(buf) - 1);
 		
 		if (read_size < 0) {
 		perror("read:");
 		sleep(1);
 		continue;
	 }
	 printf("input:%s\n", buf);
 }
    return 0;
}

🥕五、总结

fcntl是一个功能强大的函数,它允许程序对文件描述符进行精细的控制。通过fcntl,程序可以改变文件的性质、设置锁、更改异步IO行为等。在编写涉及文件操作或网络编程的程序时,fcntl是一个值得了解和掌握的函数。

👉🏻I/O多路转接之select

select函数

select函数是一个用于I/O多路复用的系统调用,它允许程序监视多个文件描述符的状态变化(例如可读、可写或发生异常)。这对于实现高效的I/O操作,尤其是非阻塞I/O和服务器程序中的并发处理非常有用。

🥕 一、函数声明

在POSIX兼容的系统中,select函数的声明通常如下:

#include <sys/select.h>
#include <sys/time.h>

int select(int nfds, fd_set *readfds, fd_set *writefds,
           fd_set *exceptfds, struct timeval *timeout);

🥕 二、参数说明

  • nfds:指定被监听的文件描述符集合中最大文件描述符加1。通常设置为监听的文件描述符集合中的最大值加1。
  • readfds:指向一个文件描述符集合的指针,该集合中的文件描述符用于监视读状态变化。
  • writefds:指向一个文件描述符集合的指针,该集合中的文件描述符用于监视写状态变化。
  • exceptfds:指向一个文件描述符集合的指针,该集合中的文件描述符用于监视异常状态变化。
  • timeout:指向一个timeval结构的指针,该结构指定了select函数的超时时间。如果设置为NULL,则select会无限期地等待。

🥕 三、返回值

  • 成功时,select返回就绪的文件描述符的总数。
  • 如果超时,返回0。
  • 如果出错,返回-1并设置errno以指示错误。

🥕 四、文件描述符集合

fd_set是一个文件描述符集合,它通常通过一系列宏来操作,例如FD_ZEROFD_SETFD_CLRFD_ISSET。这些宏定义在<sys/select.h>头文件中。

🥕 五、使用实例

以下是一个简单的select使用示例,用于监视标准输入(stdin)的可读状态:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/select.h>

int main(void) {
    fd_set readfds;
    struct timeval tv;
    int ret;

    // 初始化文件描述符集合
    FD_ZERO(&readfds);
    FD_SET(STDIN_FILENO, &readfds);

    // 设置超时时间为5秒
    tv.tv_sec = 5;
    tv.tv_usec = 0;

    // 调用select函数
    ret = select(STDIN_FILENO + 1, &readfds, NULL, NULL, &tv);
    if (ret == -1) {
        perror("select error");
        exit(EXIT_FAILURE);
    } else if (ret == 0) {
        printf("No data within 5 seconds.\n");
    } else {
        // 检查标准输入是否可读
        if (FD_ISSET(STDIN_FILENO, &readfds)) {
            // 读取数据...
            // ...
            printf("Data is available on stdin.\n");
        }
    }

    return 0;
}

🥕六、select缺点

  • 每次调用select, 都需要手动设置fd集合, 从接口使用角度来说也非常不便.
  • 每次调用select,都需要把fd集合从用户态拷贝到内核态,这个开销在fd很多时会很大
  • 同时每次调用select都需要在内核遍历传递进来的所有fd,这个开销在fd很多时也很大
  • select支持的文件描述符数量太小

🥕七、总结

select函数是一个强大的工具,允许程序同时监视多个文件描述符的状态变化。然而,在处理大量文件描述符时,select可能会遇到性能瓶颈,因为它需要遍历所有被监视的文件描述符。在这种情况下,更现代的替代品(如pollepoll)可能更适合。不过,对于许多常见的应用场景,select仍然是一个简单而有效的解决方案。

select使用示例: 检测标准输入输出

#include <stdio.h>
#include <unistd.h>
#include <sys/select.h>
int main()
{
    fd_set read_fds;
    FD_ZERO(&read_fds);
    FD_SET(0, &read_fds);
    for (;;)
    {
        printf("> ");
        fflush(stdout);
        int ret = select(1, &read_fds, NULL, NULL, NULL);
        if (ret < 0)
        {
            perror("select");
            continue;
        }
        if (FD_ISSET(0, &read_fds))
        {
            char buf[1024] = {0};
            read(0, buf, sizeof(buf) - 1);
            printf("input: %s", buf);
        }
        else
        {
            printf("error! invaild fd\n");
            continue;
        }
        FD_ZERO(&read_fds);
        FD_SET(0, &read_fds);
    }
    return 0;
}

在这里插入图片描述

👉🏻I/O多路转接之poll

poll函数

poll函数是Linux中用于I/O多路复用的系统调用之一,类似于select函数,但它在处理大量文件描述符时更加灵活和高效。以下是poll函数的详细用法介绍:

🥕 一、函数声明

#include <poll.h>

int poll(struct pollfd *fds, nfds_t nfds, int timeout);

🥕二、参数说明

  1. fds:指向pollfd结构数组的指针,每个pollfd结构表示一个需要监视的文件描述符。

    • fd:待监视的文件描述符。
    • events:指定要监视的事件类型,如POLLIN(数据可读)、POLLOUT(数据可写)等。
    • revents:函数返回时,表示实际发生的事件类型。
  2. nfdsfds数组中的元素数量,即要监视的文件描述符的数量。

  3. timeout:指定poll函数的超时时间(以毫秒为单位)。

    • -1:表示poll调用将阻塞等待,直到至少有一个文件描述符上发生事件。
    • 0:表示poll调用将立即返回,无论是否有文件描述符上发生事件。
    • 正整数:表示poll调用将在指定的毫秒数内等待,如果在此期间没有文件描述符上发生事件,则超时返回。

🥕 三、返回值

  • 正整数:表示在指定时间内,有多少个文件描述符上的事件已经就绪。
  • 0:表示在指定的超时时间内,没有任何文件描述符上的事件发生。
  • -1:表示函数调用失败,此时会设置全局变量errno以指示错误。

🥕 四、使用步骤

  1. 创建一个pollfd结构数组,并设置每个元素的fdevents字段。
  2. 调用poll函数,传入pollfd结构数组、数组长度和超时时间。
  3. 检查poll函数的返回值,以及每个pollfd结构的revents字段,以确定哪些文件描述符上的事件已经就绪。
  4. 根据需要处理就绪的文件描述符上的事件。

🥕 五、示例代码

以下是一个简单的示例代码,展示了如何使用poll函数来监视标准输入(stdin)的可读状态:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <poll.h>

int main(void) {
    struct pollfd fds[1];
    char buffer[1024];
    int n;

    // 初始化pollfd结构数组
    fds[0].fd = STDIN_FILENO;
    fds[0].events = POLLIN; // 监视可读事件

    // 调用poll函数,设置超时时间为5秒
    int timeout = 5000; // 5秒转换为毫秒
    n = poll(fds, 1, timeout);

    if (n == -1) {
        perror("poll error");
        exit(EXIT_FAILURE);
    } else if (n == 0) {
        printf("No data within 5 seconds.\n");
    } else {
        // 检查标准输入是否可读
        if (fds[0].revents == POLLIN) {
            // 读取数据
            ssize_t bytes_read = read(STDIN_FILENO, buffer, sizeof(buffer) - 1);
            if (bytes_read > 0) {
                buffer[bytes_read] = '\0'; // 添加字符串终止符
                printf("Read %zd bytes: %s", bytes_read, buffer);
            } else {
                perror("read error");
            }
        }
    }

    return 0;
}

🥕 六、总结

poll函数提供了一种高效的方式来监视多个文件描述符的状态变化。与select函数相比,poll在处理大量文件描述符时更加灵活和高效,因为它没有select函数中的文件描述符数量限制。然而,对于非常大的文件描述符集合,更现代的替代品(如epoll)可能更加适合。

poll示例: 使用poll监控标准输入

#include <poll.h>
#include <unistd.h>
#include <stdio.h>
int main()
{
    struct pollfd poll_fd;
    poll_fd.fd = 0;
    poll_fd.events = POLLIN;

    for (;;)
    {
        int ret = poll(&poll_fd, 1, 5000);
        if (ret < 0)
        {
            perror("poll");
            continue;
        }
        if (ret == 0)
        {
            printf("poll timeout\n");
            continue;
        }
        if (poll_fd.revents == POLLIN)
        {
            char buf[1024] = {0};
            read(0, buf, sizeof(buf) - 1);
            printf("stdin:%s", buf);
        }
    }
}

在这里插入图片描述

👉🏻I/O多路转接之epoll

简单介绍

I/O多路转接之epoll

epoll是Linux内核为处理大批量文件描述符而作的改进的poll,是Linux下多路复用IO接口select/poll的增强版本。它能显著提高程序在大量并发连接中只有少量活跃的情况下的系统CPU利用率。以下是关于epoll的详细解释:

一、epoll的特点和优势

  1. 高效性

    • 相较于select和poll,epoll使用了基于事件驱动的方式,仅对活跃的文件描述符进行操作,避免了线性扫描整个文件描述符集合,因此效率更高。
    • epoll通过内核与用户空间共享一个事件表,当文件描述符的状态发生变化时,内核会通知用户空间,从而减少了不必要的系统调用。
    • epoll支持边缘触发(Edge Triggered)模式,使得用户空间程序有可能缓存IO状态,减少epoll_wait/epoll_pwait的调用,进一步提高应用程序效率。
  2. 无限制的文件描述符数量

    • select和poll有文件描述符数量的限制,而epoll没有这样的限制,仅受系统中进程能打开的最大文件数目限制。
  3. 内存使用优化

    • epoll使用mmap()文件映射内存加速内核与用户空间的消息传递,避免了内核与用户空间之间的数据拷贝,提高了效率。

二、epoll的接口和工作原理

epoll提供了三个主要的系统调用:epoll_create()、epoll_ctl()和epoll_wait()。

  • epoll_create():创建一个epoll实例,并返回一个文件描述符。
  • epoll_ctl():用于向epoll实例中添加、修改或删除文件描述符的监视事件。
  • epoll_wait():用于等待注册在epoll实例上的文件描述符的事件发生。当事件发生时,epoll_wait()会返回,并告知哪些文件描述符上的事件已经就绪。

epoll使用红黑树来管理待检测的文件描述符集合,这使得在添加、删除和查找文件描述符时具有对数时间复杂度,从而提高了效率。

三、epoll的使用场景

当需要同时监视多个文件描述符(如sockets、文件、管道等)上的事件,并在有事件发生时通知应用程序进行相应的处理时,epoll是一个非常好的选择。特别是在处理大量并发连接但只有少量活跃连接的情况下,epoll的性能优势尤为明显。

四、总结

epoll作为Linux内核提供的一种高效的多路复用IO接口,其特点在于高效性、无限制的文件描述符数量和内存使用优化。通过epoll,可以方便地同时监视多个文件描述符上的事件,并在事件发生时进行高效的处理。因此,在高并发、低延迟的应用场景中,epoll是一个值得考虑的解决方案。

epoll_create()、epoll_ctl()和epoll_wait()

当使用epoll进行I/O多路复用时,主要涉及到三个系统调用:epoll_create(), epoll_ctl(), 和 epoll_wait()。以下是这些函数的原型和参数解释:

😉 1. epoll_create()

函数原型

int epoll_create(int size);
int epoll_create1(int flags); // 这是 epoll_create 的一个扩展版本

参数解释

  • size(对于epoll_create):这个参数是告诉内核这个监听的数目最大值。注意这个值只是内核初始分配内部数据结构的大小,并不是限制。在Linux 2.6.8及以后的版本中,这个参数被忽略,但是为了代码的可移植性,通常还是传递一个合适的大小值,比如 1。
  • flags(对于epoll_create1):这是一个位掩码,用于修改epoll实例的行为。常用的标志有EPOLL_CLOEXEC(当执行exec()函数时,关闭文件描述符)。

返回值:

  • 成功时返回一个非负整数,即新的epoll文件描述符。
  • 失败时返回-1,并设置全局变量errno以指示错误。

作用

创建一个新的epoll实例,并返回一个文件描述符,用于后续通过epoll_ctl()添加、修改或删除要监视的文件描述符,以及通过epoll_wait()等待文件描述符上的事件。

😉 2. epoll_ctl()

函数原型

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

参数解释

  • epfd:由epoll_create()epoll_create1()返回的文件描述符。
  • op:操作码,可以是以下三种之一:
    • EPOLL_CTL_ADD:注册新的文件描述符到epfd
    • EPOLL_CTL_MOD:修改已经注册的文件描述符的监听事件。
    • EPOLL_CTL_DEL:从epfd中注销一个文件描述符。
  • fd:需要添加、修改或删除的文件描述符。
  • event:指向epoll_event结构的指针,描述了要监听的事件和与之关联的数据。

返回值:

  • 成功时返回0。
  • 失败时返回-1,并设置全局变量errno以指示错误。

作用

用于向epoll实例中添加、修改或删除要监视的文件描述符及其相关的事件。

😉 3. epoll_wait()

函数原型

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

参数解释

  • epfd:由epoll_create()epoll_create1()返回的文件描述符。
  • events:指向epoll_event结构数组的指针,用于存储从内核返回的事件。
  • maxevents:告诉内核这个events数组有多大,这个值不能大于创建epoll_create时的size
  • timeout:等待超时时间(毫秒),-1 表示永远等待。

返回值:

  • 成功时返回发生事件的文件描述符的数量,这些事件被存储在events数组中。
  • 如果在timeout毫秒内没有事件发生,返回0。
  • 失败时返回-1,并设置全局变量errno以指示错误。

作用

等待在epfd上注册的文件描述符上的事件。当这些事件中的任何一个发生时,epoll_wait()将返回,并将所有触发的事件存储在events数组中。

注意:在使用epoll时,还需要了解epoll_event结构体,它描述了注册到epoll实例中的事件和与之关联的数据。这个结构体通常包含两个成员:events(表示要监听的事件类型)和data(用户定义的数据,通常用于在事件触发时识别是哪个文件描述符触发了事件)。

epoll_event结构体

epoll_event 结构体是 epoll 机制中用于注册、修改和接收文件描述符上事件的重要数据结构。它定义在 <sys/epoll.h> 头文件中,并用于 epoll_ctl() 函数中注册感兴趣的事件和 epoll_wait() 函数中接收已触发的事件。

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

🍚 结构体成员解释

  1. events

    • 这是一个位掩码,表示你感兴趣的事件类型。常见的事件类型有:
      • EPOLLIN:当相应的文件描述符可读时触发。
      • EPOLLOUT:当相应的文件描述符可写时触发。
      • EPOLLPRI:当相应的文件描述符有优先读取数据可读时触发(不常用)。
      • EPOLLERR:当相应的文件描述符发生错误时触发。
      • EPOLLHUP:当相应的文件描述符被挂起时触发(如 TCP 连接被对方关闭)。
      • EPOLLET:设置文件描述符为边缘触发(Edge Triggered)模式。默认是水平触发(Level Triggered)模式。
      • 以及其他一些不太常用的事件。
  2. data

    • 这是一个联合体,允许用户关联任意类型的数据到事件上。这样,当事件触发时,你可以通过这个联合体来识别是哪个文件描述符触发了事件。
      • ptr:一个指向任意类型数据的指针。
      • fd:一个文件描述符。这通常用于存储与事件关联的文件描述符,但请注意,这与 epoll_event 结构体中的 events 成员中引用的文件描述符不同。
      • u32u64:无符号的 32 位和 64 位整数。你可以使用这些字段来存储自定义的整数数据。

🍚 使用方法

epoll_ctl() 调用中,你会创建一个 epoll_event 结构体实例,并设置其 events 成员为你感兴趣的事件类型,以及 data 成员为你想要关联的数据。然后,你将这个结构体的指针传递给 epoll_ctl()

epoll_wait() 调用中,你会传递一个 epoll_event 结构体数组以及它的大小给该函数。当 epoll_wait() 返回时,它会更新这个数组中的 epoll_event 结构体实例,以反映实际触发的事件。然后,你可以遍历这个数组,检查每个 epoll_event 结构体的 events 成员来确定哪些事件被触发了,并使用 data 成员来获取与事件关联的数据。

epoll示例: 使用epoll监控标准输入

为了使用epoll来监控标准输入(通常是文件描述符0,即stdin),我们可以编写一个简单的程序来演示epoll_create(), epoll_ctl(), 和 epoll_wait() 的使用。以下是一个简单的C程序示例:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <sys/epoll.h>

#define MAX_EVENTS 10

int main(void) {
    int epfd, nfds;
    struct epoll_event ev, events[MAX_EVENTS];

    // 创建一个 epoll 实例
    epfd = epoll_create1(0);
    if (epfd == -1) {
        perror("epoll_create1");
        exit(EXIT_FAILURE);
    }

    // 配置要监控的事件
    ev.events = EPOLLIN; // 监听可读事件
    ev.data.fd = STDIN_FILENO; // 标准输入的文件描述符

    // 向 epoll 实例添加监控的文件描述符
    if (epoll_ctl(epfd, EPOLL_CTL_ADD, STDIN_FILENO, &ev) == -1) {
        perror("epoll_ctl: add");
        exit(EXIT_FAILURE);
    }

    // 等待事件发生
    for (;;) {
        nfds = epoll_wait(epfd, events, MAX_EVENTS, -1); // 阻塞等待
        if (nfds == -1) {
            perror("epoll_wait");
            break;
        }

        // 遍历所有触发的事件
        for (int n = 0; n < nfds; ++n) {
            if (events[n].data.fd == STDIN_FILENO) {
                // 读取标准输入
                char buffer[1024];
                ssize_t bytes_read = read(STDIN_FILENO, buffer, sizeof(buffer) - 1);
                if (bytes_read > 0) {
                    // 去掉换行符并打印
                    buffer[bytes_read] = '\0';
                    char *newline = strchr(buffer, '\n');
                    if (newline) *newline = '\0';
                    printf("Read from stdin: %s\n", buffer);
                } else if (bytes_read == 0) {
                    printf("EOF reached on stdin\n");
                    break;
                } else {
                    perror("read");
                    break;
                }
            }
        }
    }

    // 关闭 epoll 文件描述符
    close(epfd);

    return 0;
}

在这个程序中,我们首先使用epoll_create1()创建一个epoll实例。然后,我们使用epoll_ctl()添加一个事件来监听标准输入(stdin)的可读事件。在无限循环中,我们使用epoll_wait()等待事件发生。当标准输入上有数据可读时,我们读取这些数据并打印出来。如果读取到文件结束符(EOF),或者读取操作失败,我们退出循环。最后,我们关闭epoll文件描述符并退出程序。
在这里插入图片描述


如上便是本期的所有内容了,如果喜欢并觉得有帮助的话,希望可以博个点赞+收藏+关注🌹🌹🌹❤️ 🧡 💛,学海无涯苦作舟,愿与君一起共勉成长
在这里插入图片描述
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1713997.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【Java】HOT100+代码随想录:动态规划(下)

目录 三、打家劫舍 LeetCode198&#xff1a;打家劫舍 LeetCode213&#xff1a;打家劫舍ii LeetCode337&#xff1a;打家劫舍iii&#xff08;树形&#xff09; 四、股票问题 时间不多了&#xff0c;其他的先不写了 LeetCode121&#xff1a;买卖股票的最佳时机 五、子序列…

前端知识1-4:性能优化进阶

性能优化进阶 Navigation Timing API navigationStart / end 表示从上一个文档卸载结束时 > 如果没有上一个文档&#xff0c;这个值和fetchStart相等 unloadEventStart / end 标识前一个网页unload的时间点 redirectStart / end 第一个http重定向发生和结束的时间 fetch…

PADS做CAM文件时,提示填充宽度对于精确的焊盘填充过大

1、开发环境&#xff1a; PADS VX1.2 2、问题复现&#xff1a; 同一个PCB文件&#xff0c;设计验证没有错误。但是输出CAM光辉文件时&#xff0c;总是弹出“填充宽度对于精确的焊盘填充过大&#xff0c;填充宽度……”&#xff0c;如下图&#xff1a; 3、错误的方法&#xff1…

【Vue】小案例-小黑的书架

需求&#xff1a; 根据左侧数据渲染出右侧列表&#xff08;v-for&#xff09; 点击删除按钮时&#xff0c;应该把当前行从列表中删除&#xff08;获取当前行的id&#xff0c;利用filter进行过滤&#xff09; PS&#xff1a;id和下标同时存在&#xff0c;则优先根据id来删除&a…

分布式数据库OceanBase的安装

OceanBase是阿里自研的分布式数据库&#xff0c;单集群规模超过 1500 节点&#xff0c;具有云原生、强一致性、高度兼容 Oracle/MySQL 等特性。今天讲一讲OceanBase的安装&#xff0c;官网上面有详细的介绍&#xff0c;这里主要是针对容器部署详细介绍安装步骤一些过程中遇到的…

服务器的远程桌面无法连接,服务器远程桌面无法连接问题处理教程

服务器的远程桌面无法连接&#xff0c;服务器远程桌面无法连接问题处理教程。 一、问题概述 服务器远程桌面无法连接是日常运维中常见的问题之一。它可能由多种原因造成&#xff0c;如网络问题、服务器配置错误、远程桌面服务未启动等。本教程将指导您逐步排查并解决这些问题。…

小红书广告如何推广?投放费用是多少?

小红书以其独特的社区文化和用户粘性&#xff0c;成为了众多品牌争相推广的热门平台&#xff0c;如何在小红书上有效推广广告&#xff0c;让品牌信息精准触达目标用户&#xff0c;成为了众多营销人员关注的焦点&#xff0c;云衔科技为企业提供专业的开户和代运营服务。 一、小…

云端力量:利用移动云服务器高效部署Spring Boot Web应用

文章目录 一、移动云介绍二、移动云产品选择三、体验云主机ECS四、使用移动云服务器部署SpringBoot Web应用4.1移动云ECS安装JDK4.2移动云ECS安装MySQL4.3移动云ECS数据库插入数据4.4移动云ECS部署Spring Boot Web应用 总结 一、移动云介绍 移动云是中国移动基于自研的先进技术…

C++数据结构之:队Queue

摘要&#xff1a; it人员无论是使用哪种高级语言开发东东&#xff0c;想要更高效有层次的开发程序的话都躲不开三件套&#xff1a;数据结构&#xff0c;算法和设计模式。数据结构是相互之间存在一种或多种特定关系的数据元素的集合&#xff0c;即带“结构”的数据元素的集合&am…

STL库--priority_queue

目录 priority_queue定义 prority_queue容器内元素的访问 priority_queue()常用函数实例解析 priority_queue内元素优先级的设置 priority_queue的常见用途 priority_queue又称为优先队列&#xff0c;其底层是用堆来进行实现的。在优先队列中&#xff0c;队首元素一定是当…

【软件测试】LoadRunner参数化属性设置_单个参数

目录 为什么使用参数化属性详解Select next rowSequential&#xff08;顺序&#xff09;Random&#xff08;随机&#xff09;Unique&#xff08;唯一&#xff09;Same line as XXX&#xff08;和XXX属性的取值方式一样&#xff09; Update value onEach iteration&#xff08;每…

用于脑肿瘤分割的跨模态深度特征学习| 文献速递-深度学习肿瘤自动分割

Title 题目 Cross-modality deep feature learning for brain tumor segmentation 用于脑肿瘤分割的跨模态深度特征学习 01 文献速递介绍 作为最致命的流行病&#xff0c;脑肿瘤的研究越来越受到关注。本文研究了一种基于深度学习的自动分割胶质瘤的方法&#xff0c;称为脑…

百度ERNIE系列预训练语言模型浅析(4)-总结篇

总结&#xff1a;ERNIE 3.0与ERNIE 2.0比较 &#xff08;1&#xff09;相同点&#xff1a; 采用连续学习 采用了多个语义层级的预训练任务 &#xff08;2&#xff09;不同点&#xff1a; ERNIE 3.0 Transformer-XL Encoder(自回归自编码), ERNIE 2.0 Transformer Encode…

vue3学习(二)

前言 上一篇分享了vue的基础指令&#xff0c;这篇记录下vue3的核心内容&#xff0c;也是自己的学习笔记&#xff0c;可能有些核心还不全&#xff0c;大佬请略过。 一、核心内容 分享这个之前&#xff0c;先声明下&#xff0c;我这里是用的脚手架的写法&#xff0c;分享的讲解截…

什么是数字化采购?一文解析!

在快速发展的数字经济时代&#xff0c;越来越多的企业开始想要了解什么是数字化采购&#xff1f;因为数字化采购已经成为提升效率、降低成本的关键举措。简单来说&#xff0c;采购数字化就是利用先进的数字化技术和工具&#xff0c;对传统的采购流程进行改造和优化&#xff0c;…

如何降本增效获得目标客户?AI企业使用联盟营销这个方法就对了!

AI工具市场正在迅速发展&#xff0c;现仍有不少企业陆续涌出&#xff0c;那么如何让你的工具受到目标群体的关注呢&#xff1f;这相比是AI工具营销人员一直在思考的问题。 为什么AI企业难以获客呢&#xff1f; 即使这个市场正蓬勃发展&#xff0c;也无法保证营销就能轻易成功…

MQ第②讲~保证消息可靠性

前言 上一讲我们讲了MQ实际工作中常见的应用场景&#xff0c;这一节讲一下消息的可靠性&#xff0c;如果对MQ掌握程度比较高的铁子&#xff0c;可以不用看&#xff0c;节省您宝贵的时间。 消息的大致链路 消息从投递到消费需要考虑如下几个问题 生产者的消息是否成功投递到消…

mac安装的VMware虚拟机进行桥接模式配置

1、先进行网络适配器选择&#xff0c;选择桥接模式 2、点击网络适配器 设置... 3、选择WiFi&#xff08;我使用的是WiFi&#xff0c;所以选择这个&#xff09;&#xff0c;注意看右边的信息&#xff1a;IP和子网掩码&#xff0c;后续配置虚拟机的ifcfg-ens文件会用到 4、编辑if…

HarmonyOS-9(stage模式)

配置文件 {"module": {"requestPermissions": [ //权限{"name": "ohos.permission.EXECUTE_INSIGHT_INTENT"}],"name": "entry", //模块的名称"type": "entry", //模块类型 :ability类型和…

小迪和小捷的太空之旅——海底观光篇

书接上回&#xff0c;小迪和小捷来到了美丽的海底世界~