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

【Linux】多路转接 -- epoll

文章目录

  • 1. 认识epoll
  • 2. epoll相关系统调用接口
  • 3. epoll工作原理
  • 4. epoll服务器
  • 5. epoll的优点
  • 6. epoll的工作方式
  • 7. epoll的使用场景

1. 认识epoll

epoll系统调用和select以及poll是一样的,都是可以让我们的程序同时监视多个文件描述符上的事件是否就绪。

epoll在命名上比poll多了一个poll,这个e可以理解为extend,epoll就是为了同时处理大量文件描述符而改进的poll。

epoll在2.5.44内核中被引进,它几乎具备了select和poll的所有优点,被公认为Linux2.6下性能最好的多路IO就绪通知方法。

2. epoll相关系统调用接口

epoll有三个相关系统调用接口,分别是epoll_create,epoll_ctl 和 epoll_wait。

epoll_create

epoll_create函数的作用就是创建一个epoll的文件描述符。

在这里插入图片描述
参数说明:

  • size:自从Linux2.6.8之后,size参数是被忽略的,但size的值必须设置为大于0的值。

返回值说明:

  • epoll模型创建成功返回其对应的文件描述符,否则返回-1,同时错误码会被设置。

注意:当不再使用时,必须调用close函数关闭epoll模型对应的文件描述符,当所有引用epoll实例的文件描述符都已经关闭时,内核将销毁该实例并释放相关资源。

epoll_ctl

epoll_ctl 函数用于向指定的epoll模型中注册事件,它不同于seletct()的一点就是,select在监听事件时告诉内核要监听什么类型的事件,而它是先注册要监听的事件类型。

在这里插入图片描述

参数说明:

  • epfd:epoll_create的返回值
  • op:表示具体的动作,用三个宏来表示
  • fd:需要监视的文件描述符
  • event:告诉内核需要监听什么事件

第二个参数op的取值有以下三种:

  • EPOLL_CTL_ADD:注册新的fd到epfd中。
  • EPOLL_CTL_MOD:修改已经注册的fd的监听事件。
  • EPOLL_CTL_DEL:从epfd中删除指定的文件描述符。

返回值说明:

  • 函数调用成功返回0,调用失败返回-1,同时错误码会被设置。

第四个参数struct epoll_event 结构如下:
在这里插入图片描述
struct epoll_event结构当中有两个成员,第一个成员events表示的是需要监听的事件,第二个成员data是一个联合体结构,一般选择使用该结构当中的fd,表示需要监听的文件描述符。

events常用取值如下:

  • EPOLLIN:表示对应的文件描述符可以读(包括对端SOCKET正常关闭)
  • EPOLLOUT:表示对应的文件描述符可以写
  • EPOLLPRI:表示对应的文件描述符有紧急的数据可读(这里应该表示有带外数据到来)
  • EPOLLERR:表示对应的文件描述符发送错误
  • EPOLLHUP:表示对应的文件描述符被挂断,即对端文件描述符关闭
  • EPOLLET:将epoll的工作方式设置为边缘触发模式。
  • EPOLLONESHOT:只监听一次事件,当监听完这次事件之后,如果还需要继续监听该文件描述符的话,需要重新将该文件描述符添加到EPOLL队列中。

这些取值也是以宏的方式进行定义的,它们的二进制序列当中有且只有一个比特位是1,且为1的比特位是各不相同的。

epoll_wait

epoll_wait 函数用于收集监视的事件中已经就绪的事件
在这里插入图片描述
参数说明:

  • epfd:指定的epoll模型,epoll_create的返回值
  • events:epoll会把发送的事件赋值到events数组中(events不可以是空指针,内核只负责把数组复制到这个events数组中,不会帮助我们在用户态中分配内存)。
  • maxevents:events数组中的元素个数,该值不能大于创建epoll模型使传入的size值。
  • timeout:表示epoll_wait函数的超时时间,单位是毫秒(ms)。

参数timeout的取值:

  • -1:epoll_wait调用后进行阻塞等待,直到被监视的某个文件描述符的某个事件就绪。
  • 0:epoll_wait调用后进行非阻塞等待,无论被监视的文件描述符上的事件是否就绪,epoll_wait检测后都会立刻返回。
  • 特定的时间值:epoll_wait 调用后在直到的时间内进行阻塞等待,如果监视的文件描述符上一直没有事件就绪,则在该时间后epoll_wait进行超时返回。

返回值说明:

  • 如果函数调用成功,则返回有事件就绪的文件描述符个数。
  • 如果timeout时间耗尽,则返回0。
  • 如果函数调用失败,则返回-1,同时错误码会被设置。

epoll_wait 调用失败时,错误码可能被设置为:

  • EBADF:传入的epoll模型对应的文件描述符无效。
  • EFAULT:events指向的数组空间无法通过写入权限访问。
  • EINTR:此调用被信号所中断。
  • EINVAL:epfd不是一个epoll模型对应的文件描述符,或传入的maxevents小于等于0。

3. epoll工作原理

红黑树和就绪队列

在这里插入图片描述
当某一进程调用epoll_create函数时,Linux内核会创建一个eventpoll结构体,也就是我们所说的epoll模型,eventpoll结构体当中的成员rbr和成员rdlist与epoll的使用方式密切相关。
在这里插入图片描述

  • epoll模型当中的红黑树本质就是告诉内核,需要监视哪些文件描述符上的哪些事件,调用epoll_ctl 函数实际就是在对这颗红黑树进行对应的增删查改操作。
  • epoll模型当中的就绪队列本质就是告诉内核,哪些文件描述符上的哪些事件已经就绪了,调用epoll_wait 函数实际就是在从就绪队列当中获取已经就绪的事件。

注意:

  • 每一个epoll对象都有一个独立的eventpoll结构体,用于存放通过epoll_ctl方法向epoll对象中添加进来的事件。
  • 而这些事件都会挂载在红黑树中,如此,重复添加的事件就可以通过红黑树而高效地识别出来。
  • 而所有添加到epoll中的事件都会与设备驱动程序建立回调关系,也就是说,当响应的事件发生时会调用这个回调方法。
  • 这个回调方法在内核中叫ep_poll_callback,它会将发生的事件添加到rdlist双链表中。
  • 在epoll中,对于每一个事件,都会建立一个epitem结构体。

在这里插入图片描述

  • 对于epitem结构当中的rbn成员来说,ffd与event的含义是,需要监视的ffd上的event事件是否就绪。
  • 对于epitem结构当中的rdllink成员来说,ffd与event的含义是,ffd上的event事件已经就绪了。
  • 当调用epoll_wait检查是否有事件发生时,只需要检查eventpoll对象中的rdlist双链表中是否含有epitem元素即可。
  • 如果rdlist不为空,则把发送的事件复制到用户态,同时将事件数量返回给用户,这个操作的时间复杂度为O(1)。

说明一下:

  • 红黑树是一种二叉搜索树,因此必须有键值key,而这里的文件描述符就可以天然地作为红黑树的key值。
  • 调用epoll_ctl向红黑树当中新增节点时,如果设置了EPOLLONESHEOT选项,当监听完这次事件之后,如果还要继续监听该文件描述符则需要重新将其添加到epoll模型中,本质就是当设置了EPOLLONESHOT的事件就绪时,操作系统会自动将其从红黑树当中删除。
  • 而如果调用epoll_ctl向红黑树当中新增节点时没有设置EPOLLONSHOT,那么该节点插入红黑树之后就会一直存在,除非用户调用epoll_ctl将该节点从红黑树当中删除。

回调机制

所有添加到红黑树当中的事件,都会与设备(网卡)驱动程序建立回调方法,这个回调方法在内核中叫做ep_poll_callback。

  • 对于select和poll来说,操作系统在监视多个文件描述符上的事件是否就绪时,需要让操作系统主动对这多个文件描述符进行轮询检测,这一定会增加操作系统的负担。
  • 而对于epoll来说,操作系统不需要主动进行事件的检测,当红黑树中监视的事件就绪时,会自动调用对应的回调方法,将就绪的事件添加到就绪队列当中。
  • 当用户调用epoll_wait函数获取就绪事件时,只需要关注底层就绪队列是否为空,如果不为空则将就绪队列当中的就绪事件拷贝给用户即可。

采用回调机制最大的好处,就是不再需要操作系统主动对就绪事件进行检测了,当时间就绪时会自动调用对应的回调函数进行处理。

说明一下:

  • 只有添加到红黑树当中的事件才会与底层建立回调方法,因此只有当红黑树当中对应的事件就绪时,才会执行对应的回调方法将其添加到就绪队列当中。
  • 当不断有监视的事件就绪时,会不断有回调方法向就绪队列当中插入节点,而上层也会不断调用epoll_wait函数从就绪队列中获取节点,这也是典型的生产者消费者模型。
  • 由于就绪队列可能会被多个执行流同时访问,因此必须要使用互斥锁对其进行保护,eventpoll结构当中的lock和mtx就是保护临界资源的,因此epoll本身是线程安全的。
  • eventpoll结构当中的wa(wait queue)就是等待队列,当多个执行流想要同时访问同一个epoll模型时,就需要在该等待队列下进行等待。

epoll三部曲

  1. 调用epoll_create,创建一个epoll模型
  2. 调用epoll_ctl,将要监视的文件描述符进行注册
  3. 调用epoll_wait,等待文件描述符就绪

4. epoll服务器

为了简单演示一下epoll的使用方式,这里我们实现一个简单的epoll服务器,该服务器是获取客户端发来的数据并进行打印。

EpollServer类

EpollServer类中除了包含监听套接字和端口号两个成员变量之外,最好将epoll模型对应的文件描述符也作为一个成员变量。

  • 在构造EpollServer对象时,需要指明epoll服务器的端口号,当然也可以在初始化epoll服务器的时候指明。
  • 在初始化epoll服务器的时候调用Socket类中的函数(该Socket类中封装了进行TCP传输的方法),一次进行套接字的创建、绑定和监听、此外epoll模型的创建可以在服务器初始化的时候进行。
  • 在析构函数中调用close函数,将监听套接字和epoll模型对应的文件描述符进行关闭。
#include "Socket.hpp"
#include <sys/epoll.h>#define BACK_LOG 5
#define SIZE 256class EpollServer
{
public:EpollServer(int port): _port(port){}void InitEpollServer(){_listen_sock = Socket::SocketCreate();Socket::SocketBind(_listen_sock, _port);Socket::SocketListen(_listen_sock, BACK_LOG);// 创建epoll模型_epfd = epoll_create(SIZE);if (_epfd < 0){std::cerr << "epoll_create error" << std::endl;exit(5);}}~EpollServer(){if (_listen_sock > 0) close(_listen_sock);if (_epfd) close(_epfd);}private:int _listen_sock; // 监听套接字int _port; // 服务器端口号int _epfd; // epollfd
};

运行服务器

服务器初始化完毕之后就可以开始运行了,而epoll服务器要做的就是不断调用epoll_wait函数,从就绪队列中获取就绪事件进行处理即可。

  • 首先,在epoll服务器开始死循环调用epoll_wait之前,需要先调用epoll_ctl将监听套接字添加到epoll模型中,表示服务器开始运行时只需要监视监听套接字的读事件。
  • 此后,epoll服务器就不断调用epoll_wait函数监视读事件是否就绪。如果epoll_wait函数的返回值大于0,则说明已经有文件描述符的读事件就绪,并且此事的返回值代表的就是有事件就绪的文件描述符的个数,接下来就应该对就绪事件进行处理。
  • 如果epoll_wait的函数返回值等于0,则说明timeout时间耗尽,此事直接准备下一次epoll_wait调用即可。
  • 如果epoll_wait函数返回值为-1,此时也让服务器进行下一次epoll_wait调用,但是实际应该进一步判断错误码,根据错误码来判断是否应该继续调用epoll_wait函数。
    void HandlerEvent(struct epoll_event revs[], int num){for (int i = 0; i < num; ++i){int fd = revs[i].data.fd; // 就绪的文件描述符if (fd == _listen_sock && revs[i].events & EPOLLIN){// 连接事件就绪struct sockaddr_in peer;memset(&peer, 0, sizeof(peer));socklen_t len = sizeof(peer);int sock = accept(_listen_sock, (struct sockaddr*)&peer, &len);if (sock < 0){std::cerr << "accept error" << std::endl;continue;}std::string peer_ip = inet_ntoa(peer.sin_addr);int peer_port = ntohs(peer.sin_port);std::cout << "get a new link[" << peer_ip << ":" << peer_port << "]" << std::endl;// 将获取到的文件描述符添加到sock中,并关心其读事件AddEvent(sock, EPOLLIN);  }else if (revs[i].events & EPOLLIN){char buffer[1024];ssize_t size = recv(fd, buffer, sizeof(buffer) - 1, 0);if (size > 0){buffer[size] = 0;std::cout << "echo# " << buffer << std::endl;}else if (size == 0){std::cout << "client quit" << std::endl;close(fd);DelEvent(fd); // 将fd从epoll中删除}else{std::cerr << "recv error" << std::endl;close(fd);DelEvent(fd);}}}}private:void DelEvent(int sock){epoll_ctl(_epfd, EPOLL_CTL_DEL, sock, nullptr);}

epoll服务器测试

#include "EpollServer.hpp"
#include <string>int main(int argc, char* argv[])
{if (argc != 2){std::cout << "Usage: " << "./EpollServer port" << std::endl;exit(1);}int port = atoi(argv[1]);EpollServer* svr = new EpollServer(port);svr->InitEpollServer();svr->Run();return 0;
}

因为我们在调用epoll_wait函数时,将timeout的值设为了-1,因此服务器运行之后如果没有读事件就绪,那么就会阻塞等待。
在这里插入图片描述

5. epoll的优点

  • 接口使用方便:虽然拆分成了三个函数,但是反而使用起来更方便高效,不需要每次循环都设置要关注的文件描述符,也做到了输入输出参数分离开。
  • 数据拷贝轻量:只在合适的时候使用EPOLL_CTL_ADD将文件描述符结构拷贝到内核中,这个操作并不频繁(而select/poll每次循环都需要进行拷贝)。
  • 事件回调机制:避免使用遍历,而是使用回调函数的方式,将就绪的文件描述符加入到就绪队列当中,epoll_wait返回之后,直接访问就绪队列就知道哪些文件描述符就绪,这样即使文件描述符很多,效率也不会受影响。
  • 没有数量限制:文件描述符数目无上限。

注意:

  • 有人说epoll中使用了内存映射机制,内核直接将底层就绪队列通过mmap的方式映射到用户态,此时用户就可以直接读取到内核中就绪队列当中的数据,避免了内存拷贝的额外性能开销。
  • 这种说法是错误的,实际操作系统并没有做任何映射机制,因为操作系统是不相信任何人的,操作系统不会让用户进程直接访问到内核的数据的,用户只能通过系统调用来获取内核的数据。
  • 因此用户要获取内核当中的数据,势必还是需要将内核的数据拷贝到用户空间。

6. epoll的工作方式

epoll有两种工作方式,分别是水平触发模式和边缘触发工作模式。

水平触发(LT, Level Triggered)

  • 只要底层有事件就绪,epoll就会一直通知用户。

epoll默认状态下就是LT工作模式:

  • 由于在LT工作模式下,只要底层有事件就绪就会一直通知用户,因此当epoll检测到底层读事件就绪时,可以不立即进行处理,或者只处理一部分,因为只要底层数据没有处理完,下一次epoll还会通知用户事件就绪。
  • select和poll的模式其实就是LT模型
  • 支持阻塞读写和非阻塞读写。

边缘触发(ET, Edge Triggered)

  • 只有底层就绪事件数量 由无到有 或者 由有到多 的时候,epoll才会通知用户。

如果要将epoll改为ET工作模式,则需要在添加时间时设置EPOLLET选项。

  • 由于在ET工作模式下,只有底层就绪事件 由无到有 或者 由有到多 的时候才会通知用户,所以当epoll检测到底层读事件就绪的时候,必须立即进行处理,而且必须全部处理完毕,因为有可能此后底层再也没有事件就绪,那么epoll就再也不好通知用户进行事件处理。
  • ET工作模式下epoll通知用户的次数比LT少,因此ET的性能一般比LT性能跟高,Nginx就是默认采用ET模式使用epoll的。
  • 只支持非阻塞的读写。

ET工作模式下应该如何进行读写?

因为在ET工作模式下,只有底层就绪事件由无到有或者由有到多时才会通知用户,这就倒逼用户当读事件就绪时必须一次性将数据全部读取完毕,当写事件就绪时就必须一次性将发送缓冲区写满,否则可能再也没有机会进行读写了。

因此读数据时必须循环调用recv函数进行读取,写数据时必须循环调用send函数进行写入。

  • 当底层读事件就绪时,循环调用recv函数进行读取,直到某次调用recv函数时,实际读取到的字节数小于期望读取的字节数,则说明本次底层数据已经读取完毕了。
  • 但有可能最后一次调用recv读取时,刚好实际读取的字节数和期望读取的字节数相等,但此时底层数据恰好读取完毕,如果我们再调用recv函数进行读取,那么recv就会因为没有数据而被阻塞住。
  • 而这里的阻塞是非常严重的,就比如我们这里写的服务器都是单进程的服务器,如果recv被阻塞住,并且此后该数据再也不就绪,那么就相当于我们的服务器挂掉了,因此在ET模式下循环调用recv函数进行读取时,必须将文件描述符设置为非阻塞状态。
  • 调用send函数写数据时也是同样的道理,需要循环调用send函数进行数据的写入,并且必须将对应的文件描述符设置为非阻塞状态。

注意:ET工作模式下,recv和send操作的文件描述符必须设置为非阻塞状态,这是必须的!

LT模式与ET模式对比

  • 在ET模式下,一个文件描述符就绪之后,用户不会反复收到通知,看起来比LT更高效,但是如果在LT模式下能够做到每次都将就绪的文件描述符进行处理,不让操作系统反复通知用户的话,其实LT和ET性能也是一样的。
  • 此外,ET模式的编程难度更高。

7. epoll的使用场景

epoll的高性能,是有特定的场景的,如果场景选择不合适,epoll的性能可能适得其反。

对于多连接,且多连接中只有一部分连接活跃时,比较适合使用epoll。

如果只是系统内部,服务器和服务器之间进行通信,只有少数的几个连接,这种情况下使用epoll就并不合适,具体要根据需求和场景特定来决定使用哪种IO模型。

相关文章:

【Linux】多路转接 -- epoll

文章目录 1. 认识epoll2. epoll相关系统调用接口3. epoll工作原理4. epoll服务器5. epoll的优点6. epoll的工作方式7. epoll的使用场景 1. 认识epoll epoll系统调用和select以及poll是一样的&#xff0c;都是可以让我们的程序同时监视多个文件描述符上的事件是否就绪。 epoll…...

学会RabbitMQ的延迟队列,提高消息处理效率

系列文章目录 手把手教你&#xff0c;本地RabbitMQ服务搭建&#xff08;windows&#xff09; 消息队列选型——为什么选择RabbitMQ RabbitMQ灵活运用&#xff0c;怎么理解五种消息模型 RabbitMQ 能保证消息可靠性吗 推或拉&#xff1f; RabbitMQ 消费模式该如何选择 死信是什么…...

ChatGPT会取代搜索引擎吗?BingChat、GoogleBard与ChatGPT区别

目前暂时不会&#xff0c;ChatGPT为代表的聊天机器人很可能会直接集成到搜索中&#xff0c;而不是取代它。微软已经通过Bing Chat和Bing做到了这一点&#xff0c;它将“聊天”选项卡直接放入Bing搜索的菜单中。Google、百度也分别开始尝试通过其AI生成技术将Google Bard、文心一…...

多个QLabel中文字左右对其问题研究

众所周知&#xff0c;关于QLabel 中的文字对其方式&#xff0c;官方提供多种&#xff0c;具体可参考 AlignmentFlag&#xff0c;这里就不详细列举了。 实际开发中有这样一个需求&#xff1a;多个lab中&#xff0c;文字显示不同&#xff0c;长度不一&#xff0c;但想要实现视觉…...

链式二叉树统计结点个数的方法和bug

方法一&#xff1a; 分治&#xff1a;分而治之 int BTreeSize1(BTNode* root) {if (root NULL) return 0;else return BTreeSize(root->left)BTreeSize(root->right)1; } 方法二&#xff1a; 遍历计数&#xff1a;设置一个计数器&#xff0c;对二叉树正常访问&#…...

C语言-报错集锦-03-malloc(): memory corruption: 0x0000000001496d90 ***

一、报错信息 [2023-8]--[ Debug ]--Push Data To StAccessPath OK. [2023-8]--[ Debug ]--Judge Vertex(0) Is Not Accessed. [2023-8]--[ Debug ]--Judge Vertex(2) Is Accessed. [2023-8]--[ Debug ]--Judge Vertex(3) Is Not Accessed. [2023-8]--[ Debug ]--Judge Vertex…...

现代C++中的从头开始深度学习:【5/8】卷积

一、说明 在上一个故事中&#xff0c;我们介绍了机器学习的一些最相关的编码方面&#xff0c;例如 functional 规划、矢量化和线性代数规划。 现在&#xff0c;让我们通过使用 2D 卷积实现实际编码深度学习模型来开始我们的道路。让我们开始吧。 二、关于本系列 我们将学习如何…...

以太网帧格式与吞吐量计算

以太网帧结构 帧大小的定义 以太网单个最大帧 6&#xff08;目的MAC地址&#xff09; 6&#xff08;源MAC地址&#xff09; 2&#xff08;帧类型&#xff09; 1500{IP数据包[IP头&#xff08;20&#xff09;DATA&#xff08;1480&#xff09;]} 4&#xff08;CRC校验&#xff…...

vue中install方法

1&#xff1a;语法 vue提供install可供我们开发新的插件及全局注册组件等 install方法第一个参数是vue的构造器&#xff0c;第二个参数是可选的选项对象 export default {install(Vue,option){组件指令混入挂载vue原型} }2&#xff1a;注册组件 一&#xff1a;注册单个组件 1…...

Flutter:文件读取—— video_player、chewie、image_picker、file_picker

前言 简单学习一下几个比较好用的文件读取库 video_player 简介 用于视频播放 官方文档 https://pub-web.flutter-io.cn/packages/video_player 安装 flutter pub add video_player加载网络视频 class _MyHomePageState extends State<MyHomePage> {// 控制器late…...

vim的使用

vim文本编辑器 vim介绍命令模式光标移动选中内容复制内容粘贴内容删除撤销/恢复字符转换 编辑模式末行模式保存/退出查找行号显示文件切换 扩展 vim介绍 vim是Linux自带的文本编辑器&#xff0c;具有命令模式、编辑模式、末行模式三种模式。 模式间的切换&#xff1a; 命令模…...

马氏杆法检查斜视

使用 检查水平向斜视时&#xff0c;使用水平向马氏杆检查;重直向斜视时&#xff0c;使用重直问马氏杆;检查旋转斜视时&#xff0c;使用双马氏杆. 检查水平向斜视 双眼屈光不正全矫 双眼同时打开&#xff0c;右眼前加水平向马氏杆&#xff0c;左眼前不加 双眼同时观察点光源&…...

Mac电脑怎么使用“磁盘工具”修复磁盘

我们可以使用“磁盘工具”的“急救”功能来查找和修复磁盘错误。 “磁盘工具”可以查找和修复与 Mac 磁盘的格式及目录结构有关的错误。使用 Mac 时&#xff0c;错误可能会导致意外行为&#xff0c;而重大错误甚至可能会导致 Mac 彻底无法启动。 继续之前&#xff0c;请确保您…...

c++画出分割图像,水平线和垂直线

1、pca 找到图像某个区域的垂直线&#xff0c;并画出来 // 1、 斑块的框 血管二值化图&#xff0c;pca 找到垂直血管壁的直线, 还是根据斑块找主轴方向吧// Step 1: 提取斑块左右范围内的血管像素点坐标&#xff0c;std::vector<cv::Point> points;for (int y 0; y <…...

Python 程序设计入门(015)—— enumerate() 函数的用法

Python 程序设计入门&#xff08;015&#xff09;—— enumerate() 函数的用法 目录 Python 程序设计入门&#xff08;015&#xff09;—— enumerate() 函数的用法一、enumerate() 函数的语法二、为可迭代对象创建索引三、将字符串、列表等转换为字典1、将字符串转换为字典2、…...

__dict__属性

__dict__ 是 Python 中的一个特殊属性&#xff0c;通常存在于大多数 Python 对象中&#xff0c;用于存储该对象的可变属性。 以下是关于 __dict__ 的一些关键点和详细信息&#xff1a; 存储属性&#xff1a;对于大多数自定义的 Python 对象&#xff0c;__dict__ 属性包含了这个…...

k8s之Pod控制器

目录 一、Pod控制器及其功用二、pod控制器的多种类型2.1 pod容器中的有状态和无状态的区别 三、Deployment 控制器四、SatefulSet 控制器4.1 StatefulSet由以下几个部分组成4.2 为什么要有headless&#xff1f;4.3 为什么要有volumeClaimTemplate&#xff1f;4.4 滚动更新4.5 扩…...

逆元(求乘法逆元的几种方法)

目录 逆元 加法逆元 乘法逆元 如何求 快速幂 扩展欧几里得 O(n)求1到n的乘法逆元 逆元 数学中&#xff0c;逆元素&#xff08;英语&#xff1a;Inverse element&#xff09;推广了加法中的加法逆元和乘法中的倒数。直观地说&#xff0c;它是一个可以取消另一给定元素运…...

没点本事,还真做不好数字化转型

数字化转型逐渐成为企业业务增长的利器 然而&#xff0c;在此过程中 企业最应该注重哪些&#xff1f; 效率&#xff1f;质量&#xff1f; 但还有一个至关重要的点不容忽视 那就是安全 有一家硬核企业通过技术与狠活 硬生生提升了应用安全性 保障了产业与数字化的安全融合…...

windows 10 远程桌面配置

1. 修改远程桌面端口&#xff08;3389&#xff09; 打开注册表&#xff08;winr&#xff09;, 输入regedit 找到配置项【计算机\HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Control\Terminal Server\Wds\rdpwd\Tds\tcp】 &#xff0c; 可以通过搜索“Wds”快速定位。 修改端口配…...

华为云AI开发平台ModelArts

华为云ModelArts&#xff1a;重塑AI开发流程的“智能引擎”与“创新加速器”&#xff01; 在人工智能浪潮席卷全球的2025年&#xff0c;企业拥抱AI的意愿空前高涨&#xff0c;但技术门槛高、流程复杂、资源投入巨大的现实&#xff0c;却让许多创新构想止步于实验室。数据科学家…...

Rust 异步编程

Rust 异步编程 引言 Rust 是一种系统编程语言,以其高性能、安全性以及零成本抽象而著称。在多核处理器成为主流的今天,异步编程成为了一种提高应用性能、优化资源利用的有效手段。本文将深入探讨 Rust 异步编程的核心概念、常用库以及最佳实践。 异步编程基础 什么是异步…...

MySQL中【正则表达式】用法

MySQL 中正则表达式通过 REGEXP 或 RLIKE 操作符实现&#xff08;两者等价&#xff09;&#xff0c;用于在 WHERE 子句中进行复杂的字符串模式匹配。以下是核心用法和示例&#xff1a; 一、基础语法 SELECT column_name FROM table_name WHERE column_name REGEXP pattern; …...

用docker来安装部署freeswitch记录

今天刚才测试一个callcenter的项目&#xff0c;所以尝试安装freeswitch 1、使用轩辕镜像 - 中国开发者首选的专业 Docker 镜像加速服务平台 编辑下面/etc/docker/daemon.json文件为 {"registry-mirrors": ["https://docker.xuanyuan.me"] }同时可以进入轩…...

根据万维钢·精英日课6的内容,使用AI(2025)可以参考以下方法:

根据万维钢精英日课6的内容&#xff0c;使用AI&#xff08;2025&#xff09;可以参考以下方法&#xff1a; 四个洞见 模型已经比人聪明&#xff1a;以ChatGPT o3为代表的AI非常强大&#xff0c;能运用高级理论解释道理、引用最新学术论文&#xff0c;生成对顶尖科学家都有用的…...

html css js网页制作成品——HTML+CSS榴莲商城网页设计(4页)附源码

目录 一、&#x1f468;‍&#x1f393;网站题目 二、✍️网站描述 三、&#x1f4da;网站介绍 四、&#x1f310;网站效果 五、&#x1fa93; 代码实现 &#x1f9f1;HTML 六、&#x1f947; 如何让学习不再盲目 七、&#x1f381;更多干货 一、&#x1f468;‍&#x1f…...

#Uniapp篇:chrome调试unapp适配

chrome调试设备----使用Android模拟机开发调试移动端页面 Chrome://inspect/#devices MuMu模拟器Edge浏览器&#xff1a;Android原生APP嵌入的H5页面元素定位 chrome://inspect/#devices uniapp单位适配 根路径下 postcss.config.js 需要装这些插件 “postcss”: “^8.5.…...

SQL慢可能是触发了ring buffer

简介 最近在进行 postgresql 性能排查的时候,发现 PG 在某一个时间并行执行的 SQL 变得特别慢。最后通过监控监观察到并行发起得时间 buffers_alloc 就急速上升,且低水位伴随在整个慢 SQL,一直是 buferIO 的等待事件,此时也没有其他会话的争抢。SQL 虽然不是高效 SQL ,但…...

【C++进阶篇】智能指针

C内存管理终极指南&#xff1a;智能指针从入门到源码剖析 一. 智能指针1.1 auto_ptr1.2 unique_ptr1.3 shared_ptr1.4 make_shared 二. 原理三. shared_ptr循环引用问题三. 线程安全问题四. 内存泄漏4.1 什么是内存泄漏4.2 危害4.3 避免内存泄漏 五. 最后 一. 智能指针 智能指…...

MyBatis中关于缓存的理解

MyBatis缓存 MyBatis系统当中默认定义两级缓存&#xff1a;一级缓存、二级缓存 默认情况下&#xff0c;只有一级缓存开启&#xff08;sqlSession级别的缓存&#xff09;二级缓存需要手动开启配置&#xff0c;需要局域namespace级别的缓存 一级缓存&#xff08;本地缓存&#…...