从 select 到 epoll:拆解 I/O 多路复用的演进与实战

news2025/5/20 10:12:49

目录

一、引言:为什么需要 I/O 多路复用?

二、select

1.函数介绍

2.原理

3.样例代码

4.优缺点总结

三、poll

1.函数介绍

2.样例代码

3.优缺点总结

四、epoll

1.函数介绍

2.原理

3.LT和ET两种工作模式

4.优缺点总结

五、核心机制对比:select vs poll vs epoll

六、高频面试题解析

七、结语


一、引言:为什么需要 I/O 多路复用?

I/O 多路复用(I/O Multiplexing)是为了解决 高并发场景下传统阻塞式或非阻塞式 I/O 模型的效率缺陷 而诞生的核心技术。阻塞式IO,无论是单线程阻塞式IO还是多线程阻塞式IO,在数据未就绪时线程会被挂起,直到数据就绪后才恢复执行。而且,多线程还涉及到线程切换,以及内存开销。线程切换那是需要时间的,频繁的切换会导致效率降低。至于非阻塞式的IO,轮询高频发生,浪费CPU资源。试想这样的场景:当你有一万个网络连接需要同时处理时,为每个连接开一个线程是否可行?答案显然是否定的,抛开线程上下文切换,单从内存上看,Linux默认一个线程大小是8MB,所以内存消耗80000MB / 1024 ≈ 78G。

二、select

多路复用(也叫多路转接)三剑客之一,单进程就可以监视多个文件描述符的可读、可写和异常状态。

1.函数介绍

select函数原型:

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

1)nfds:监视的最大文件描述符+1。

2)readfds、writefds、 exceptfds分别表示可读、可写、异常文件描述符的集合。

3)timeout,用来设置等待时间,可避免一直阻塞。

4)返回值

● 返回值大于0,表示监视的文件描述符中已就绪的个数。

● 返回值等于0,表示并没有文件描述符就绪,超过timeout时间。

● 返回值小于0,表示发生错误。

5)timeval结构体,第一个成员变量单位是秒,第二个单位是微秒。

2.原理

select底层是用三张位图来存放对应的对应的文件描述符集合的,并且提供了一套对位图的操作方法。

       void FD_CLR(int fd, fd_set *set);    / /将指定文件描述符移出集合,对应位设为0
       int  FD_ISSET(int fd, fd_set *set);   / /检查fd是否在集合中(对应位是否为1),返回非0表示存在
       void FD_SET(int fd, fd_set *set);    / /将指定文件描述符加入集合,对应位设为1
       void FD_ZERO(fd_set *set);           / /清空集合,将位图中所有位设为0

select底层是会把集合给修改的,例如设置进可读文件描述符集合的是0011 0000,当6号文件描述符可读然后函数返回后,可读文件描述符集合被修改成了0010 0000,没有事件发生的fd = 5被清空。如果我们下次调用还要关心fd = 5的话,需要再次设置进可读文件描述符集合中,需要频繁的重复设置,所以实践中干脆用一个辅助数组来存放这些下次还要关心的fd(避免丢失),每次在调用select之前都遍历一遍辅助数组来设置这些fd,在返回时亦是如此,需要遍历整个位图才知道哪些fd就绪了,这是导致效率比较低的一个原因。

3.样例代码

#include <iostream>
#include <sys/select.h>
#include <unistd.h>

int main()
{
    fd_set read_fds;
    FD_ZERO(&read_fds);
    FD_SET(0, &read_fds);//检测标准输入

    while(true)
    {
        struct timeval timeout;
        timeout.tv_sec = 2;//设置超时时间为2秒
        int ret = select(1, &read_fds, nullptr, nullptr, &timeout);
        if(ret < 0)
        {
            perror("select");
            continue;
        }
        if(ret == 0)
        {
            std::cout << "timeout" << std::endl;
            continue;
        }
        if(FD_ISSET(0, &read_fds))
        {
            char buffer[1024] = { 0 };
            int n = read(0, buffer, sizeof(buffer) - 1);
            if(n > 0)
            {
                std::cout << buffer << std::endl;
            }
        }
        //内核会修改位图,需要重新设置进去
        FD_ZERO(&read_fds);
        FD_SET(0, &read_fds);
    }
    return 0;
}

4.优缺点总结

先说优点,select的跨平台兼容性比较好,几乎主流的操作系统都是兼容的,这相对于epoll来说是个优点(epoll只能在Linux上用)。

缺点有:

文件描述符数量的上限低,通常是1024个,这对于动辄上万个连接的高并发场景来说是远远不够的。

②其次,select需要做不少的遍历工作,用户需要遍历返回后的位图,查找哪些是已经就绪的,然而有时候,遍历一千个文件描述符才有屈指可数的几个就绪也是有的。这就导致了很多无效的遍历。在内核层面,每次调用select函数时亦是如此,需要遍历所有的监听描述符判断是否就绪。这样,随着监听描述符的增多,效率就会不断下降。

③同时,在每一次调用select函数时,需要将fd集合从用户态拷贝到内核态,返回时内核在将结果拷贝回用户态

④除了拷贝上的时间开销,还有内核在将处理结果拷贝到用户态是,会覆盖原有的数据,如果下次还想监听的描述符因此被覆盖的话,下次还要再重新设置。

三、poll

1.函数介绍

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

1)fds是poll函数监听的结构列表,每一个元素中包含三个成员变量:文件描述符、监听事件的集合、返回的事件集合。pollfd结构体如下:

// pollfd 结构

struct pollfd {

int fd;              /* file descriptor */

short events;  /* requested events */

short revents; /* returned events */

};

2)nfds表示fds的长度。

3)timeout表示超时时间,单位是毫秒。 

4)返回值含义和select一样。

2.样例代码

#include <iostream>
#include <poll.h>
#include <unistd.h>

int main()
{
    struct pollfd poll_fd;
    poll_fd.fd = 0;         //监听标准输入
    poll_fd.events = POLLIN;//可读事件

    while(true)
    {
        int ret = poll(&poll_fd, 1, 3000);
        if(ret < 0)
        {
            perror("poll");
            continue;
        }
        if(ret == 0)
        {
            std::cout << "timeout" << std::endl;
            continue;
        }
        if(poll_fd.revents == POLLIN)//判断返回的事件
        {
            char buffer[1024] = { 0 };
            int n = read(0, buffer, sizeof(buffer) - 1);
            if(n > 0)
            {
                std::cout << buffer << std::endl;
            }
        }
    }
    return 0;
}

3.优缺点总结

先说优点,首先值得一提的是,poll突破了文件描述符数量的限制。使用结构体数组(struct polled[ ])而非位图(fd_set),可监听任意数量的文件描述符,仅受系统资源的限制。第二点,相对于select来说,poll做到了事件和返回结果的解耦(events和revents),不会再像select那样,从内核拷贝到用户时会覆盖原有数据,进而需要再次设置。

缺点是,调用poll时还是需要将数据拷贝到内核,返回时从内核拷贝到用户。而且也需要遍历整个struct polled[ ]数组,检查哪些fd已就绪。

四、epoll

epoll是 Linux 特有的高效 I/O 多路复用机制,专为解决select和poll的性能瓶颈而设计,尤其适合处理高并发问题。按照man手册的说法,epoll是为了处理大量句柄而做了改进的poll。所谓句柄,就是epoll_create函数返回的文件描述符。

1.函数介绍

int epoll_create(int size);

这个函数的作用就是创建一个epoll的句柄。size参数已被忽略,可任意传值。这个句柄(也就是文件描述符)用完以后,需要调用close函数关闭。

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

● epoll的事件注册函数。

● 第一个参数是epoll的句柄,即epoll_create函数的返回值。

● 第二个参数表示动作,用三个宏来表示。

        EPOLL_CTL_ADD:注册新的fd

        EPOLL_CTL_MOD:修改已经注册fd的监听事件

        EPOLL_CTL_DEL:删除fd

● 第三个参数是要监听的fd。

● 第四个参数是告诉内核要监听什么事件。

至此,可能比较迷的就是struct epoll_event是什么鬼?下面是它的结构:

struct epoll_event {
    uint32_t     events;  // 监控的事件类型(如 EPOLLIN、EPOLLOUT)
    epoll_data_t data;    // 用户数据(常保存 fd 或回调函数指针)
};

typedef union epoll_data {
    void    *ptr;
    int      fd;        // 常用:与事件关联的 fd
    uint32_t u32;
    uint64_t u64;
} epoll_data_t;

下面看看epoll_ctl函数是怎么用的。

struct epoll_event ev;
ev.data.fd = fd;
ev.events = EPOLLIN;
int ret = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd, &ev);//epoll_fd为句柄

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

● 等待事件触发。

● 第一个参数是epoll句柄。

● 输出参数,存放就绪事件的结构体数组

maxevents,events数组大小(单次返回的最多事件数)

● 超时时间(ms),-1 表示阻塞直到事件触发

● 返回值:>0:触发的事件数量  0:超时   -1:错误

函数使用样例:

epoll_event events[1000];
int nfds = epoll_wait(epoll_fd, events, sizeof(events) / sizeof(events[0], -1);

2.原理

epoll原理围绕红黑树、就绪列表、回调函数机制这三者展开。当调用epoll_create方法时,Linux内核会创建一个eventpoll结构体,这两个结构体中有两个关键的成员——红黑树和就绪列表。

struct eventpoll{
....
/*红黑树的根节点,这颗树中存储着所有添加到 epoll 中的需要监控的事件*/
struct rb_root rbr;
/*双链表中则存放着将要通过 epoll_wait 返回给用户的满足条件的事件。就绪列表*/
struct list_head rdlist;
....
};

通过epoll_ctl方法添加的事件,最终就会被挂到红黑树中。而所有添加到epoll中的事件都会与设备驱动程序建立回调关系,然后在监控的事件发生时调用这个回调方法。这个回调方法在内核中叫做ep_poll_callback,它会把发生的事件添加到就绪列表rdlist中。然后,当调用epoll_wait方法时,只需要检查eventpoll中的就绪列表rdlist是否有元素即可。如果就绪列表不为空,那么就将发生的事件拷贝给用户,同时将事件的数量返回给用户。 与select和poll相比,epoll就不用去遍历所有的事件,导致一些无效的遍历和判断。下面继续深入到一些细节问题。

 红黑树中的每个结点,都是一个epitem结构体。下面看看这个结构体长啥样。

struct epitem{
struct rb_node rbn;       //红黑树节点
struct list_head rdllink; //双向链表节点
struct epoll_filefd ffd;  //事件句柄信息
struct eventpoll *ep;     //指向其所属的 eventpoll 对象
struct epoll_event event; //期待发生的事件类型
}

总结一下:

红黑树的作用就是存储所有需要监控的文件描述符(fd)及其关联的事件(epoll_event) 。

就绪列表的作用就是临时存储已触发的fd(就绪事件)。当事件触发时,由回调函数ep_poll_callback将对应的结点添加到就绪列表中。当调用epoll_wait方法时,将就绪事件拷贝给用户然后清空就绪列表。

 重谈epoll_create、epoll_ctl、epoll_wait三个函数。

1)当调用epoll_create时,创建eventpoll结构体,然后初始化红黑树的根节点和就绪列表表头。

2)当调用epoll_ctl(epfd, EPOLL_CTL_ADD, fd, event)时,内核首先会检查红黑树中是否已经存在对应的fd结点。如果不存在,那就新        建epitem结点并入树。然后设置该fd对应的回调函数ep_poll_callback,绑定到其底层设备驱动。

3)当调用epoll_wait时,就去检查就绪列表,如果不为空,则拷贝到用户态,然后清空就绪列表。

3.LT和ET两种工作模式

LT(Level-Triggered,水平触发),只要文件描述符fd满足可读/可写的条件,epoll_wait会持续通知应用层,直到数据被完全处理。

当fd的接收缓冲区有数据时,会触发EPOLLIN事件,只要数据未读完,每次调用epoll_wait方法都会重复报告此fd的可读事件。同理,如果发送缓冲区未满(可写入),则持续触发EPOLLOUT。

优点:容错性强,未处理的事件不会被遗漏,LT模式下会持续通知上层应用。而且编程比较简单。

缺点:若应用层未及时处理完数据,频繁触发事件会增加epoll_wait的调用次数。一个高负载的fd可能会导致epoll_wait长期处于忙碌状态。

ET(Edge-Triggered,边缘触发),仅在fd状态变化时触发通知(比如从不可读变为可读),后续如果数据未读完也不在通知,直到下一次状态变化(比如数据由少变多)。

当接收缓冲区从空变为非空时,触发一次EPOLLIN,之后即使仍有数据未读完也不在重复通知。因此应用层必须在一次通知中处理完所有数据,否则可能永远无法收到下一个状态变化的通知,进而丢失数据。

优点:仅在状态变化时触发一次,减少了epoll_wait的调用频率。同时一次性把接收缓冲区中的数据全部读完,这样发送方可以收到更大的窗口大小通知,进而可以发送更多的数据,提高网络的吞吐量。

缺点:编程复杂,如果处理数据不当,可能丢失数据。

特性

LT(水平触发)

ET(边缘触发)

触发条件

数据未处理完则持续触发

仅当状态变化时触发一次

系统调用次数

可能较多(重复触发)

较少(严格依赖状态变化)

编程复杂度

高(需非阻塞 IO + 循环处理)

容错性

高(未处理完会再次触发)

低(漏处理可能导致事件丢失)

适用场景

简单场景、对性能不敏感的延迟容忍型

高并发、延迟敏感型(如高频交易系统)

4.优缺点总结

优点:

1)epoll有着高效的事件通知机制(基于回调函数),epoll_wait直接返回给用户就绪事件列表,无需遍历所有的文件描述符,性能与活跃连接数无关,对比select和poll的O(n)遍历,epoll在高并发场景下性能卓越。

2)支持大并发连接,文件描述符数量仅受系统资源限制。相比与select,这是一个优点。

3)事件触发模式灵活,根据不同的场景,有LT(默认)和ET两种模式可选。

4)无重复数据拷贝。select和poll在每次调用时,都需要将文件描述符集从用户空间拷贝到内核空间,而epoll在添加对应文件描述符和它对应的事件后,再调用epoll_wait时,无需再传入监听的文件描述符集,进而没有重复拷贝。

缺点:仅限Linux平台,代码可移植性差。

五、核心机制对比:select vs poll vs epoll

指标

select

poll

epoll

最大并发连接数

1024

10k+(受内存限制)

100k+(内存依赖)

时间复杂度

O(n)

O(n)

O(1)

内存拷贝开销

每次调用传递全量 fd

同 select

注册后无需重复传递

触发模式

仅 LT

仅 LT

LT 和 ET

适用平台

跨平台

类 Unix

Linux

六、高频面试题解析

epoll 为什么用红黑树不用哈希表?

1)红黑树性能稳定可靠。哈希表查找的平均时间复杂度为O(1),但是在有大量哈希冲突,可能退化到O(N)。内核需要保证稳定的响应时间,无法接受性能波动,这对于高并发场景非常关键。红黑树作为自平衡的二叉搜素树,插入、查找、删除操作均严格保证O(logN)的时间复杂度,性能可预测且稳定。

2)红黑树具有更高效的动态操作。哈希表可能需要扩容,扩容就需要重新计算哈希值并迁移数据,导致短暂的性能抖动。红黑树天然支持动态的增删结点,无需全局调整结构,可以平滑的支持扩展。

3)红黑树的内存利用率比较高而且不存在冲突。在稀疏连接的场景下,哈希表中可能存在不少空桶,导致浪费。红黑树是按需分配结点内存的,不存在额外的浪费。而且红黑树是以fd来作为键值的,天然就避免了冲突。 

ET 模式必须配非阻塞 socket 吗?为什么? 

在ET(边缘触发)模式下使用非阻塞socket是强烈建议且几乎必须的。

1)ET模式下,要求上层一次读完数据,为了确保一次读完数据,需要通过循环读取,比如循环的调用read。但是,在接收缓冲区中的数据读完以后,再次调用read会导致阻塞,进而线程被挂起,直到下一波数据的到达。线程一旦被挂起,就无法去处理其他事件,严重影响并发性能。

2)非阻塞可以维持事件循环的高效性。非阻塞socket在每次IO操作后立即返回,使得应用可以快速处理完当前事件后,继续通过epoll_wait监听其他就绪事件,保持事件循环的高吞吐。如果是阻塞式的socket,单次处理可能长时间占用线程,导致其他就绪时间延迟处理,降低系统整体的响应速度。

如果 fd 频繁增减,epoll 是否仍高效? 

 依然高效,理由如下:

1)epoll使用红黑树作为管理fd的底层结构,其增删查操作均严格保证O(logN)的时间复杂度(n是当前监听的fd数量)。即使fd频繁增删,单次操作的性能仍被控制在可控范围内。对比select和poll这种基于线性扫描的模型,性能随fd数量的增长线性下降。而epoll的 O(log n) 操作显著优于这种线性开销。

2)epoll事件通知机制的天然优势——无轮询开销。epoll通过事件驱动的方式通知就绪的fd(基于回调机制),而非遍历整个集合。fd的频繁增删不会直接关联事件通知的开销。epoll_wait只返回就绪的fd。

七、结语

多路转接在面试中还是比较常见的,还请诸位慎重。

路漫漫其修远兮,吾将上下而求索!如有错误,请不吝指出,感谢支持!


完结~

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

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

相关文章

【力扣hot100题】(017)矩阵置零

还是挺简单的&#xff0c;使用哈希表记录需要置换的行列即可&#xff0c;这样就可以避免重复节省时间。 class Solution { public:void setZeroes(vector<vector<int>>& matrix) {unordered_set<int> row;unordered_set<int> line;for(int i0;i&l…

One Commander 3,文件管理新体验

One Commander 3 是一款集多功能于一体 Windows 10/11的文件管理工具&#xff0c;其设计目的在于为用户带来多元化的操作体验。这款工具通过支持多栏界面布局&#xff0c;让用户能够迅速且高效地组织和管理文件。此外&#xff0c;它还提供了多主题选项和多种图标集&#xff0c;…

非手性分子发光有妙招:借液晶之力,实现高不对称圆偏振发光

*本文只做阅读笔记分享* 一、圆偏振发光研究背景与挑战 圆偏振发光&#xff08;CPL&#xff09;材料在3D显示、光电器件等领域大有用处&#xff0c;衡量它的一个重要指标是不对称发光因子&#xff08;glum&#xff09;。早期CPL材料的glum值低&#xff0c;限制了实际应用。为…

YOLOv8+ Deepsort+Pyqt5车速检测系统

该系统通过YOLOv8进行高效的目标检测与分割&#xff0c;结合DeepSORT算法完成目标的实时跟踪&#xff0c;并利用GPU加速技术提升处理速度。系统支持模块化设计&#xff0c;可导入其他权重文件以适应不同场景需求&#xff0c;同时提供自定义配置选项&#xff0c;如显示标签和保存…

【干货】前端实现文件保存总结

⚠️⚠️文前推荐一下&#x1f449; 前端必备工具推荐网站(图床、API和ChatAI、智能AI简历、AI思维导图神器等实用工具): 站点入口&#xff1a;http://luckycola.com.cn/ 前端实现文件保存实现总结 在Web开发中&#xff0c;文件下载是常见的交互需求。本文将系统总结前端实现文…

并发编程之FutureTask.get()阻塞陷阱:深度解析线程池CPU飚高问题排查与解决方案

FutureTask.get方法阻塞陷阱&#xff1a;深度解析线程池CPU飚高问题排查与解决方法 FutureTask.get()方法阻塞陷阱&#xff1a;深度解析线程池CPU飚高问题排查与解决方法1、情景复现1.1 线程池工作原理1.2 业务场景模拟1.3 运行结果1.4 发现问题&#xff1a;线程池没有被关闭1.…

在Ubuntu中固定USB设备的串口号

获取设备信息 lsusb # 记录设备的Vendor ID和Product ID&#xff08;例如&#xff1a;ID 0403:6001&#xff09;# 获取详细属性&#xff08;替换X和Y为实际设备号&#xff09; udevadm info -a /dev/ttyUSBX 结果一般如下 创建udev规则文件 sudo gedit /etc/udev/rules.d/us…

javaSE————文件IO(2)、

文件内容的读写——数据流 我们对于文件操作使用流对象Stream来操作&#xff0c;什么是流对象呢&#xff0c;水流是什么样的&#xff0c;想象一下&#xff0c;水流的流量是多种的&#xff0c;可以流100ml&#xff0c;也可以流1ml&#xff0c;流对象就和水流很像&#xff0c;我…

前端常问的宏观“大”问题详解(二)

JS与TS选型 一、为什么选择 TypeScript 而不是 JavaScript&#xff1f; 1. 静态类型系统&#xff1a;核心优势 TypeScript 的静态类型检查能在 编译阶段 捕获类型错误&#xff08;如变量类型不匹配、未定义属性等&#xff09;&#xff0c;显著减少运行时错误风险。例如&…

智慧电力:点亮未来能源世界的钥匙

在科技日新月异的今天&#xff0c;电力行业正经历着前所未有的变革。智慧电力&#xff0c;作为这一变革的核心驱动力&#xff0c;正逐步改变着我们对电力的认知和使用方式。它不仅是电力行业的一次技术革新&#xff0c;更是推动社会可持续发展、实现能源高效利用的重要途径。 智…

架构师面试(二十三):负载均衡

问题 今天我们聊微服务相关的话题。 大中型微服务系统中&#xff0c;【负载均衡】是一个非常核心的组件&#xff1b;在微服务系统的不同位置对【负载均衡】进行了实现&#xff0c;下面说法正确的有哪几项&#xff1f; A. LVS 的负载均衡一般通过前置 F5 或是通过 VIP keepa…

NSSCTF(MISC)—[justCTF 2020]pdf

相应的做题地址&#xff1a;https://www.nssctf.cn/problem/920 binwalk分离 解压文件2AE59A.zip mutool 得到一张图片 B5F31内容 B5FFD内容 转换成图片 justCTF{BytesAreNotRealWakeUpSheeple}

坚持“大客户战略”,昂瑞微深耕全球射频市场

北京昂瑞微电子技术股份有限公司&#xff08;简称“昂瑞微”&#xff09;是一家聚焦射频与模拟芯片设计的高新技术企业。随着5G时代的全面到来&#xff0c;智能手机、智能汽车等终端设备对射频前端器件在通信频率、多频段支持、信道带宽及载波聚合等方面提出了更高需求&#xf…

LiteDB 数据库优缺点分析与C#代码示例

LiteDB 是一个轻量级的 .NET NoSQL 嵌入式数据库,完全用 C# 开发,支持跨平台(Windows、Linux、MacOS),并提供类似于 MongoDB 的简单 API。它以单文件形式存储数据,类似于 SQLite,支持事务和 ACID 特性,确保数据的一致性和可靠性。 优缺点分析 优点: 轻量级与嵌入式:…

Linux系统中快速安装docker

1 查看是否安装docker 要检查Ubuntu是否安装了Docker&#xff0c;可以使用以下几种方法&#xff1a; 方法1&#xff1a;使用 docker --version 命令 docker --version如果Docker已安装&#xff0c;输出会显示Docker的版本信息&#xff0c;例如&#xff1a; Docker version …

CP15 协处理器

ARMv7-A 一共支持 16 个协处理器&#xff0c;编号从 CP0~CP15。这里仅对CP15进行描述。 1、ARMv7-A 协处理器 ARMv7-A 处理器除了标准的 R0~R15&#xff0c;CPSR&#xff0c;SPSR 以外&#xff0c;由于引入了 MMU、TLB、Cache 等内容&#xff0c;ARMv7-A 使用协处理器来对这些…

网络运维学习笔记(DeepSeek优化版)026 OSPF vlink(Virtual Link,虚链路)配置详解

文章目录 OSPF vlink&#xff08;Virtual Link&#xff0c;虚链路)配置详解1. 虚链路核心特性2. 基础配置命令3. 状态验证命令3.1 查看虚链路状态3.2 验证LSDB更新 4. 关键技术要点4.1 路径选择机制4.2 虚链路的链路优化 5. 环路风险案例 OSPF vlink&#xff08;Virtual Link&a…

【区块链安全 | 第十六篇】类型之值类型(三)

文章目录 函数类型声明语法转换成员合约更新时的值稳定性示例 函数类型 函数类型是函数的类型。函数类型的变量可以通过函数进行赋值&#xff0c;函数类型的参数可以用来传递函数并返回函数。 函数类型有两种类型&#xff1a;内部函数和外部函数。 内部函数只能在当前合约内调…

Kubernetes对象基础操作

基础操作 文章目录 基础操作一、创建Kubernetes对象1.使用指令式命令创建Deployment2.使用指令式对象配置创建Deployment3.使用声明式对象配置创建Deployment 二、操作对象的标签1.为对象添加标签2.修改对象的标签3.删除对象标签4.操作具有指定标签的对象 三、操作名称空间四、…

Java与代码审计-Java基础语法

Java基础语法 package com.woniuxy.basic;public class HelloWorld {//入口函数public static void main(String[] args){System.out.println("Hello World");for(int i0;i< args.length;i){System.out.println(args[i]);}} }运行结果如下: 但是下面那个没有参数…