当前位置: 首页 > news >正文

【Linux】网络高级IO

在这里插入图片描述

欢迎来到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 10int 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文件描述符并退出程序。
在这里插入图片描述


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

相关文章:

【Linux】网络高级IO

欢迎来到Cefler的博客&#x1f601; &#x1f54c;博客主页&#xff1a;折纸花满衣 &#x1f3e0;个人专栏&#xff1a;Linux 目录 &#x1f449;&#x1f3fb;五种IO模型&#x1f449;&#x1f3fb;消息通信的同步异步与进程线程的同步异步有什么不同&#xff1f;&#x1f449…...

【C++ ——— 继承】

文章目录 继承的概念即定义继承概念继承定义定义格式继承关系和访问限定符继承基类成员访问方式的变化 基类对象和派生类对象的赋值转换继承中的作用域派生类中的默认成员函数继承与友元继承与静态成员菱形继承虚继承解决数据冗余和二义性的原理继承的总结继承常见笔试面试题 继…...

kafka-守护启动

文章目录 1、kafka守护启动1.1、先启动zookeeper1.1.1、查看 zookeeper-server-start.sh 的地址1.1.2、查看 zookeeper.properties 的地址 1.2、查看 jps -l1.3、再启动kafka1.3.1、查看 kafka-server-start.sh 地址1.3.2、查看 server.properties 地址 1.4、再次查看 jps -l 1…...

TypeScript 中的命名空间和模块化

1. 命名空间&#xff08;Namespace&#xff09; 命名空间提供了一种逻辑上的代码分组机制&#xff0c;用于避免命名冲突和将相关代码组织在一起。它使用 namespace 关键字来定义命名空间&#xff0c;并通过点运算符来访问其中的成员。例如&#xff1a; // 定义命名空间 names…...

9 html综合案例-注册界面

9 综合案例-注册界面 一个只有html骨架的注册页面 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>…...

Xilinx RFSOC 47DR 8收8发 信号处理板卡

系统资源如图所示&#xff1a;  FPGA采用XCZU47DR 1156芯片&#xff0c;PS端搭载一组64Bit DDR4,容量为4GB,最高支持速率&#xff1a;2400MT/s;  PS端挂载两片QSPI X4 FLASH&#xff1b;  PS支持一路NVME存储&#xff1b;  PS端挂载SD接口&#xff0c;用于存储程序&…...

ros2 launch 用法以及一些基础功能函数的示例

文章目录 launch启动一个节点的launch示例launch文件中添加节点的namespacelaunch文件中的话题名称映射launch文件中向节点内传入命令行参数launch文件中向节点内传入rosparam使用方法多节点启动命令行参数配置资源重映射ROS参数设置加载参数文件在launch文件中使用条件变量act…...

如何使用Python获取图片中的文字信息

如下有三中方法&#xff1a; 方法1. 使用Tesseract OCR&#xff08;pytesseract&#xff09; 安装依赖 首先&#xff0c;确保你已经安装了Tesseract OCR引擎&#xff08;例如&#xff0c;通过你的操作系统的包管理器&#xff09;。然后&#xff0c;你可以通过pip安装pytesse…...

C++知识点

1. 构造函数&#xff1a;当没有写任何构造函数(含拷贝构造)&#xff0c;系统会生成默认的无参构造&#xff0c;并且访问属性是共有。 默认拷贝构造&#xff1a;当没有写任何的拷贝构造&#xff0c;系统会生成默认的拷贝构造->是一个浅拷贝 写了拷贝构造函数&#xff0c;这…...

反转字符串中的单词-力扣

此题将问题分为三步进行解决&#xff1a; 第一步&#xff0c;删除字符串中多余的空格&#xff0c;removeSpaces函数中删除所有的空格&#xff0c;并手动在每个单词后添加一个空格&#xff0c;最后重构字符串s第二步&#xff0c;将整个字符串反转第三步&#xff0c;对反转后的字…...

Kotlin 重写与重载

文章目录 重写&#xff08;Override&#xff09;重载&#xff08;Overload&#xff09; 重写&#xff08;Override&#xff09; 重写通常是指子类覆盖父类的属性或方法&#xff0c;通常会标记为override&#xff1a; open class Base {open val name "Base"open f…...

关于高版本 Plant Simulation 每次保存是 提示提交comm对话框的处理方法

关于高版本 Plant Simulation 每次保存是 提示提交comm对话框的处理方法 如下图 将model saving history 修改为None即可 关于AutoCAD 2022 丢失模板库的问题 从新从以下地址打开即可&#xff1a; D:\Program Files\Autodesk\AutoCAD 2022\UserDataCache\zh-cn\Template...

C语言之旅:探索单链表

目录 一、前言 二、实现链表的功能&#xff1a; 打印 创建节点 尾插 尾删 头插 头删 查找 在指定位置之前插入数据 指定位置删除 在指定位置之后插入数据 打印 销毁 三、全部源码&#xff1a; 四、结语 一、前言 链表是一个强大且基础的数据结构。对于很多初…...

【安卓基础】-- 消息机制 Handler

目录 消息机制 Handler面试问题 消息机制 Handler 对handler机制的基本作用、用法、时序流程进行介绍&#xff0c;针对handler机制中的内存泄漏问题讲解&#xff1a;一篇读懂Android Handler机制 Android-Handler机制详解 全面解析 | Android之Handler机制 需要掌握的&#x…...

Optional 类

概述 到目前为止&#xff0c;臭名昭著的空指针异常是导致 Java 应用程序失败的最常见原因。以前&#xff0c;为了解决空指针异常&#xff0c;Google 公司著名的 Guava 项目引入了 Optional 类&#xff0c; Guava 通过使用检查空值的方式来防止代码污染&#xff0c;它鼓励程序员…...

自动微分技术在 AI for science 中的应用

本文简记我在学习自动微分相关技术时遇到的知识点。 反向传播和自动微分 以 NN 为代表的深度学习技术展现出了强大的参数拟合能力&#xff0c;人们通过堆叠固定的 layer 就能轻松设计出满足要求的参数拟合器。 例如&#xff0c;大部分图神经网络均基于消息传递的架构。在推理…...

ASM OMF single-file creation form 重命名

OMF下不能自动命名&#xff0c;需要重新命名的话&#xff1a;1 1. spfile 可以 create pfile from spfile 后再create spfile from pfile 2 redo&#xff1f; 3 datafile&#xff1f; Here are some details of the copy problem: a) You are not allowed to set the numbe…...

VGGNet

VGGNet CNN卷积网络的发展史 1. LetNet5(1998) 2. AlexNet(2012) 3. ZFNet(2013) 4. VGGNet(2014) 5. GoogLeNet(2014) 6. ResNet(2015) 7. DenseNet(2017) 8. EfficientNet(2019) 9. Vision Transformers(2020) 10. 自适应卷积网络(2021) 上面列出了发展到现在CNN的一些经典…...

SpringMVC:转发和重定向

1. 请求转发和重定向简介 参考该链接第9点 2. forward 返回下一个资源路径&#xff0c;请求转发固定格式&#xff1a;return "forward:资源路径"如 return "forward:/b" 此时为一次请求返回逻辑视图名称 返回逻辑视图不指定方式时都会默认使用请求转发in…...

961操作系统知识总结

部分图片可能无法显示&#xff0c;参考这里&#xff1a;https://zhuanlan.zhihu.com/p/701247894 961操作系统知识总结 一 操作系统概述 1. 操作系统的基本概念 重要操作系统类型&#xff1a;批处理操作系统(批量处理作业&#xff0c;单道批处理/多道批处理系统&#xff0c;用…...

电脑死机问题排查

情况描述&#xff1a;2024年6月2日下午16&#xff1a;04分电脑突然花屏死机&#xff0c;此情况之前遇到过三次&#xff0c;认为是腾讯会议录屏和系统自带录屏软件冲突导致。 报错信息&#xff1a;应用程序-特定 权限设置并未向在应用程序容器 不可用 SID (不可用)中运行的地址…...

百度地图1

地图的基本操作 百度地图3.0文档 百度地图3.0实例中心 设置地图 centerAndZoom(center: Point, zoom: Number)设初始化地图,center类型为Point时&#xff0c;zoom必须赋值&#xff0c;范围3-19级&#xff0c; // 百度地图API功能var map new BMap.Map("map"); //…...

Ubuntu 24.04 LTS 安装Docker

1 更新软件包索引&#xff1a; sudo apt-get update 2 安装必要的软件包&#xff0c;以允许apt通过HTTPS使用仓库&#xff1a; sudo apt-get install apt-transport-https ca-certificates curl software-properties-common 3 添加Docker的官方GPG密钥&#xff1a; curl -fs…...

【架构设计】Java如何利用AOP实现幂等操作,防止客户端重复操作

1实现方案详解 在Java中,使用AOP(面向切面编程)来实现幂等操作是一个常见的做法,特别是当你想在不修改业务代码的情况下添加一些横切关注点(如日志、事务管理、安全性等)时。幂等操作指的是无论执行多少次,结果都是相同的操作。 为了利用AOP实现幂等操作以防止客户端重…...

笔记:美团的测试

0.先启动appium 1.编写代码 如下&#xff1a; from appium import webdriver from appium.webdriver.extensions.android.nativekey import AndroidKeydesired_caps {platformName: Android,platformVersion: 10,deviceName: :VOG_AL10,appPackage: com.sankuai.meituan,ap…...

【30天精通Prometheus:一站式监控实战指南】第15天:ipmi_exporter从入门到实战:安装、配置详解与生产环境搭建指南,超详细

亲爱的读者们&#x1f44b;   欢迎加入【30天精通Prometheus】专栏&#xff01;&#x1f4da; 在这里&#xff0c;我们将探索Prometheus的强大功能&#xff0c;并将其应用于实际监控中。这个专栏都将为你提供宝贵的实战经验。&#x1f680;   Prometheus是云原生和DevOps的…...

STM32F103借助ESP8266连接网络

ESP8266配置 STM32F103本身是不具备联网功能的&#xff0c;所以我们必须借助其他单片机来进行联网&#xff0c;然后让STM32与联网单片机通信&#xff0c;就可以实现STM32联网了。 本文借助的是ESP8266模块&#xff0c;其通过UART协议与STM32通信&#xff08;http://t.csdnimg.c…...

Feature Manipulation for DDPM based Change Detection

基于去噪扩散模型的特征操作变化检测 文章提出了一种基于去噪扩散概率模型&#xff08;DDPM&#xff09;的特征操作变化检测方法。变化检测是计算机视觉中的经典任务&#xff0c;涉及分析不同时间捕获的图像对&#xff0c;以识别场景中的重要变化。现有基于扩散模型的方法主要…...

第十三届蓝桥杯国赛大学B组填空题(c++)

A.2022 动态规划 AC; #include<iostream> #define int long long using namespace std; int dp[2050][15]; //dp[i][j]:把数字i分解为j个不同的数的方法数 signed main(){dp[0][0]1;for(int i1;i<2022;i){for(int j1;j<10;j){//一种是已经分成j个数,这时只需每一个…...

conda源不能用了的问题

conda旧没用了&#xff0c;不知道什么原因&#xff0c;安装源出问题&#xff0c;报如下错&#xff1a; Loading channels: failedUnavailableInvalidChannel: HTTP 404 NOT FOUND for channel anaconda/pkgs/main <https://mirrors.aliyun.com/anaconda/pkgs/main>The c…...