Linux网络编程 --- 高级IO
前言 IO Input&&Output
read && write
1、在应用层read && write的时候,本质把数据从用户层写给OS --- 本质就是拷贝函数
2、IO = 等待 + 拷贝。
等的是:要进行拷贝,必须先判断读写事件成立。读写事件缓冲区空间满了没有,read的时候需要缓冲区有数据。
什么叫做高效的IO呢?
单位事件内我们拷贝的数据越多,越有效率。单位事件内,IO过程中,等的比重越小,IO效率就越高。几乎所有的提高IO效率的策略,本质就是减少等的时间。
一、五种IO模型
1、张三:钓鱼界的新手,一直盯着鱼钩。谁都不理,有鱼上钩,提起鱼竿。阻塞式钓鱼。
2、李四:每隔一小时定盯着鱼漂。不会卡在那里。会看手机什么的。非阻塞式钓鱼。非阻塞轮询
3、王五:鱼竿上放一个铃铛,不会卡在那里,会看手机什么的。鱼钩上有鱼了铃铛响了。信号驱动式IO
以上鱼上钩的概率不如下面的。
4、赵六:一卡车鱼竿。轮询每一个鱼竿。多路复用,多路转接。
以上的IO称为异步IO
5、田七:小王,桶,一个电话,鱼竿。田七就走了,小王开始钓鱼。田七就是钓鱼行为的发起者。田七的钓鱼方式称为异步IO。小王为操作系统。
阻塞IO vs 非阻塞IO IO = 等待 + 拷贝。非阻塞不用一直等。他们等的方式不同!
同步IO vs 异步IO 张三、李四,王五,赵六都在等待,同步就是参不参与IO。参与就是同步,不参与只是发起IO就是异步。
同步IO 和线程同步?不一样。老婆和老婆饼的关系,没有任何关系。
其中多路复用式最有效率的。异步IO的代码逻辑比较混乱,已经有新的技术代替了,比如协程。
非阻塞接口
1、设置称为非阻塞,如果底层fd数据没有就绪,recv/read/write/send,返回值会以出错的形式返回。
2、a、真的出错了, b、底层没有就绪!
3、我们通过errno区分
如果errno == EWOULDBLOCK,当前不是错了, 而是fd中的数据没有就绪。
IO多路转接至select
IO = 等待 + 拷贝
select:只负责等待,而且可以等待多个文件描述符。
返回值:n > 0 有n个fd就绪了
n == 0 超时,没有错误,但是也没有fd就绪。
n < 0 等待出错。
第一个参数 nfds maxfd + 1。最大的等待文件描述符的值 + 1。
struct timeval timeout 给select设置等待方式。就是你设置的是5秒超时,那么就是进程会在select函数阻塞等待5秒,要是5秒中没有事件就绪,他会返回要是5秒中,有事件就绪了,他也会立即返回
timeout = {5,0};每隔5s,timeout一次。在5s之内没有文件描述符就绪,返回。
= {0,0};立马返回,非阻塞的一种。
NULL:阻塞等待。
上面的timeout是输入输出型参数。等待了2秒,如果我们设置的是3秒。select会把这个三秒返回。
fd_set 内核提供的一种数据类型。我们目前关心的fd上面的事件。读事件成立,写事件成立,异常事件。
fd_set* readfds 输入输出型参数,
输入时:用户告诉内核,我给你的一个或者多个fd,你要帮我关心fd上面的读事件,如果读时间就绪了,你要告诉我。
输出时:内核告诉用户,用户你让我关心的多个fd中,有哪些已经就绪了,用户赶紧读取吧。
从右向左比特位的位置,标识文件描述符编号,比特位的内容0,1是否需要内核关心。
其他的位图也是这样的。
返回时:比特位的内容0 or 1,用户关心的哪些fd,上面的读时间已经就绪了。
fd_set 是一张位图,让用户和内核传递fd是否就绪的信息的!
会有很多的位图操作。所以内核提供了很多的文件描述位图的操作。
直接写代码:
#include <iostream>
#include "log.hpp"
#include "Sock.hpp"
#include <sys/select.h>
#include <ctime>
static const int fd_num_max = sizeof(fd_set) * 8;
static const uint16_t defaultport = 8080;
int defaultfd = -1;
class selectserver
{
public:selectserver(uint16_t port = defaultport) : _port(port){// 初始化数组for (int i = 0; i < fd_num_max; i++){fd_array[i] = defaultfd;}}// 初始化服务器,这里应该绑定监听端口,创建套接字void Init(){// 绑定监听套接字。_listensock.Socket();_listensock.Bind(_port);_listensock.Listen();}void Accepter(){// 接收套接字std::string clientip;uint16_t clientport = 0;int sockfd = _listensock.Accept(&clientip, &clientport); // 这里不会被阻塞if (sockfd < 0)return;lg(Info, "accept success, %s: %d, sock fd: %d", clientip.c_str(), clientport, sockfd);// 如果大于0 把新接收的套接字放入到辅助数组中int pos = 1; // fd_array[0] = listensock,所以要从1开始for (; pos < fd_num_max; pos++){// 寻找-1位置if (fd_array[pos] != defaultfd)continue;elsebreak;}if (pos == fd_num_max){lg(Warning, "server is full, close %d now!", sockfd);close(sockfd);}else{fd_array[pos] = sockfd;printFd();}}void Recvr(int fd, int pos){// demochar buffer[1024];ssize_t n = read(fd, buffer, sizeof(buffer) - 1); // bug?if (n > 0){buffer[n] = 0;std::cout << "get a messge: " << buffer << std::endl;}else if (n == 0){lg(Info, "client quit, me too, close fd is : %d", fd);close(fd);fd_array[pos] = defaultfd; // 这里本质是从select中移除}else{lg(Warning, "recv error: fd is : %d", fd);close(fd);fd_array[pos] = defaultfd; // 这里本质是从select中移除}}void Dispatcher(fd_set &rfds){// 在这里我们需要对事件进行处理,从listensock套接字里连接的文件描述符也需要被我们的select等待,我们需要一个辅助// 数组。for (int i = 0; i < fd_num_max; i++){int fd = fd_array[i];//std::cout << fd << std::endl;if (fd == defaultfd)continue;if (FD_ISSET(fd, &rfds)){if (fd == _listensock.Fd()){Accepter();std::cout << "新文件描述符完毕" << std::endl;}else{// 不是listen套接字那就是普通的套接字进行读。std::cout << "开始交互" << std::endl;Recvr(fd, i);}}}}void Start(){// 这里应该为服务器提供服务。这里我们使用select接口,让文件描述符等待连接。// 服务器是一个死循环。// 在这里我们需要把listensock的文件描述符放入到我们的辅助数组里。int listensock = _listensock.Fd();fd_array[0] = listensock;std::cout << fd_array[0] << std::endl;for (;;){// 这里需要对listensock套接字进行等待。等待是否有链接,有连接说明该文件描述符上有事件就绪。// 传入的参数需要有一个位图,这个位图是输入输出型参数,用户设置需要等待的文件描述符,内核把// 事件就绪的文件描述符放入到其中。每次事件就绪的文件描述符,就会覆盖原来的文件描述符的问题,所以我们// 需要在select之前对位图重新设置。fd_set rfds;FD_ZERO(&rfds);// 边循环边设置我们的文件描述符到位图中,然后求出文件描述符中最大的值。int maxfd = fd_array[0];for (int i = 0; i < fd_num_max; i++){if (fd_array[i] == defaultfd)continue;// 找到文件描述符设置到我们的描述符位图中FD_SET(fd_array[i], &rfds);if (maxfd < fd_array[i]){maxfd = fd_array[i];lg(Info, "max fd update, max fd is: %d", maxfd);}}// 把listensock文件描述符设置到位图中// FD_SET(_listensock.Fd(), &rfds);// 设置等待的事件// struct timeval timeout = {5, 0};struct timeval timeout = {0, 0};int n = select(/*文件描述符中值最大的一个 + 1 _listensock.Fd() + 1*/ maxfd + 1, &rfds, /*不关心写事件,只关心读事件*/ nullptr, nullptr, /*&timeout*/ nullptr);// 判断返回值 = 0代表没有事件就绪// -1 等待错误//> 0 有n个文件描述符的写事件就绪switch (n){case 0:std::cout << "time out,timeout: " << timeout.tv_sec << timeout.tv_usec << std::endl;break;case -1:std::cerr << "select err" << std::endl;break;default:// 等待成功需要做什么。std::cout << "进入事件派发器" << std::endl;Dispatcher(rfds);break;}}}void printFd(){std::cout << "online list:";for (int i = 0; i < fd_num_max; i++){if (fd_array[i] == defaultfd){continue;}std::cout << fd_array[i] << " ";}std::cout << std::endl;}~selectserver(){_listensock.Close();}private:Sock _listensock;uint16_t _port;int fd_array[fd_num_max];
};
不能直接accept,检测并获取listensock上面的事件,新连接到来,等价于读事件就绪。
select,如果事件就绪,如果上层不处理,select会一直通知。select告诉你就绪了,接下来的一次读取,我们读取fd的时候,不会阻塞。
位图的大小为1024个比特位,所以一次可以等待1024个文件描述符。
select的优点是:多路转接的方案。一个进程处理多个用户的连接。
select的缺点是:
1、等待的fd是有上限的。
2、输入输出型参数比较多,数据拷贝的频率比较高。
3、输入输出型参数比较多,每次都要对关心的fd进行事件重置。
4、使用第三方数组管理用户fd,用户层需要很多次遍历,内核中检测fd事件就绪,也要遍历。
IO多路转接之poll
#include <iostream>
#include "log.hpp"
#include "Sock.hpp"
#include <poll.h>
#include <ctime>
static const int fd_num_max = 64;
static const uint16_t defaultport = 8080;
int defaultfd = -1;
int non_event = 0;
class pollserver
{
public:pollserver(uint16_t port = defaultport) : _port(port){// 初始化数组for (int i = 0; i < fd_num_max; i++){_fd_events[i].fd = defaultfd;_fd_events[i].events = non_event;_fd_events[i].events = non_event;}}// 初始化服务器,这里应该绑定监听端口,创建套接字void Init(){// 绑定监听套接字。_listensock.Socket();_listensock.Bind(_port);_listensock.Listen();}void Accepter(){// 接收套接字std::string clientip;uint16_t clientport = 0;int sockfd = _listensock.Accept(&clientip, &clientport); // 这里不会被阻塞if (sockfd < 0)return;lg(Info, "accept success, %s: %d, sock fd: %d", clientip.c_str(), clientport, sockfd);// 如果大于0 把新接收的套接字放入到辅助数组中int pos = 1; // fd_array[0] = listensock,所以要从1开始for (; pos < fd_num_max; pos++){// 寻找-1位置if (_fd_events[pos].fd != defaultfd)continue;elsebreak;}if (pos == fd_num_max){lg(Warning, "server is full, close %d now!", sockfd);close(sockfd);}else{_fd_events[pos].fd = sockfd;_fd_events[pos].events = POLLIN;_fd_events[pos].revents = non_event;printFd();}}void Recvr(int fd, int pos){// demochar buffer[1024];ssize_t n = read(fd, buffer, sizeof(buffer) - 1); // bug?if (n > 0){buffer[n] = 0;std::cout << "get a messge: " << buffer << std::endl;}else if (n == 0){lg(Info, "client quit, me too, close fd is : %d", fd);close(fd);_fd_events[pos].fd= defaultfd; // 这里本质是从select中移除}else{lg(Warning, "recv error: fd is : %d", fd);close(fd);_fd_events[pos].fd= defaultfd; // 这里本质是从select中移除}}void Dispatcher(){// 在这里我们需要对事件进行处理,从listensock套接字里连接的文件描述符也需要被我们的select等待,我们需要一个辅助// 数组。for (int i = 0; i < fd_num_max; i++){int fd = _fd_events[i].fd;//std::cout << fd << std::endl;if (fd == defaultfd)continue;//读事件就绪,且是监听sockif (_fd_events[i].revents & POLLIN){if (fd == _listensock.Fd()){Accepter();std::cout << "新文件描述符完毕" << std::endl;}else{// 不是listen套接字那就是普通的套接字进行读。std::cout << "开始交互" << std::endl;Recvr(fd, i);}}}}void Start(){// 这里应该为服务器提供服务。这里我们使用select接口,让文件描述符等待连接。// 服务器是一个死循环。// 在这里我们需要把listensock的文件描述符放入到我们的辅助数组里。int listensock = _listensock.Fd();for (;;){//把listenfd交给poll管理_fd_events[0].fd = listensock;_fd_events[0].events = POLLIN;_fd_events[0].revents = non_event;//设置超时事件3000毫秒int timeout = 3000;ssize_t n = poll(_fd_events,fd_num_max,timeout);switch (n){case 0:std::cout << "timeout" << std::endl;break;case -1:std::cerr << "poll err" << std::endl;break;default:// 等待成功需要做什么。std::cout << "进入事件派发器" << std::endl;Dispatcher();break;}}}void printFd(){std::cout << "online list:";for (int i = 0; i < fd_num_max; i++){if ( _fd_events[i].fd == defaultfd){continue;}std::cout << _fd_events[i].fd << " ";}std::cout << std::endl;}~pollserver(){_listensock.Close();}private:Sock _listensock;uint16_t _port;struct pollfd _fd_events[fd_num_max];
};
poll只负责等待。
timeout 证书毫秒。
struct pollfd
nfds_t struct pollfd的数组大小。
将输入输出事件进行了分离。
poll的缺点:
1、遍历,用户层和内核也得遍历。效率问题。
IO多路转接之epoll
1、快速认识epoll的接口
epoll_create:
参数被忽略,设置成大于0就可以。返回值是一个文件描述符。
对epoll新增描述特定的读写事件
epoll_wait:
参数返回已经就绪的fd和事件。
已经就绪的fd的个数。
epoll_event结构体:
epoll_ctl
第一个参数epoll_create的返回值,第二个参数三个选项,第三个参数需要被控制的fd,第四个参数哪些事件被设置。
它不同于 select() 是在监听事件时告诉内核要监听什么类型的事件 , 而是在这里先注册要监听的事件类型 .第一个参数是 epoll_create() 的返回值 (epoll 的句柄 ).第二个参数表示动作,用三个宏来表示 .第三个参数是需要监听的 fd.第四个参数是告诉内核需要监听什么事 .
2、epoll的原理
用户只需要从就绪队列中获取就绪节点即可。上面的三套机制称为epoll模型。红黑树,就绪队列,回调。epoll模型被统一接入到了文件描述符表里,所以epoll_create的返回值是文件描述符。
epoll_create就是在创建struct_file,也就是创建epoll模型。
epoll_ctl 修改红黑树。
epoll_wait 中的events参数是输出型参数,把就绪队列中的节点,一个一个的放入到我们的epoll_event数组里。
优势:1、检测就绪O(1),获取就绪O(N)
2、fd_event没有上限。
3、这颗红黑树,就是select,poll自己维护的数组。
4、返回值n,表示有几个fd就绪了,就绪事件是连续的!有返回值个。
3、快速写代码 --- echo server
#pragma once
#include <iostream>
#include <sys/epoll.h>
#include "log.hpp"
class Epoller
{static const int size = 128;
public:Epoller(){//创建epoll模型epollfd = epoll_create(/*这里的参数已经可以忽略了*/size);if(epollfd == -1){lg(Error,"epoll create err %d %s",errno,strerror(errno));}else{lg(Info,"epoll create success %d",epollfd);}}//返回epoll中的就绪队列int waitEpoller(struct epoll_event revents[],int num){int n = epoll_wait(epollfd,revents,num,-1);return n;}//通过系统调用把我们的文件描述符放入到内核中的红黑树中int updataEpoller(int sock,int oper,uint32_t event){int n = 0;if(oper == EPOLL_CTL_DEL){n = epoll_ctl(epollfd,oper,sock,nullptr);if(n!=0){lg(Error,"epoll_ctl err");}}else{//EPOLL_CTL_ADD || EPOLL_CTL_MODstruct epoll_event ev;ev.data.fd = sock;ev.events = event;n = epoll_ctl(epollfd,oper,sock,&ev);if(n != 0){lg(Error,"epoll_ctl err %d %s",errno,strerror(errno));}}return n;}~Epoller(){if(epollfd >= 0){close(epollfd);}}
private:int epollfd;int timeout{3000};
};
#include <iostream>
#include "Sock.hpp"
#include "log.hpp"
#include "nocopy.hpp"
#include "Epoller.hpp"
#include <memory>
int EVENT_IN = (EPOLLIN);
int EVENT_OUT = (EPOLLOUT);
static const uint16_t defaultport = 8080;
static const int defaultfd = -1;
class epollserver : public nocopy
{static const int num = 64;public:epollserver(uint16_t port = defaultport) : _port(port), _listensockfd_ptr(new Sock()), _epoller_ptr(new Epoller()){}bool Init(){// 对套接字进行创建绑定监听_listensockfd_ptr->Socket();_listensockfd_ptr->Bind(_port);_listensockfd_ptr->Listen();return true;}void Accepter(){// 接收套接字std::string clientip;uint16_t clientport = 0;int sockfd = _listensockfd_ptr->Accept(&clientip, &clientport); // 这里不会被阻塞if (sockfd < 0)return;lg(Info, "accept success, %s: %d,", clientip.c_str(), clientport);if (sockfd > 0){//std::cout << "Accept 放入内核中" << std::endl;_epoller_ptr->updataEpoller(sockfd, EPOLL_CTL_ADD, EVENT_IN);lg(Info, "get a new link, client info@ %s:%d %d", clientip.c_str(), clientport,sockfd);}}void Recvr(int fd){// demochar buffer[1024];ssize_t n = read(fd, buffer, sizeof(buffer) - 1); // bug?if (n > 0){buffer[n] = 0;std::cout << "get a messge: " << buffer << std::endl;std::string echo_string = "server say#";echo_string += buffer;write(fd, echo_string.c_str(), echo_string.size());}else if (n == 0){lg(Info, "client quit, me too, close fd is : %d", fd);close(fd);_epoller_ptr->updataEpoller(fd, EPOLL_CTL_DEL,0); // 这里本质是从select中移除}else{lg(Warning, "recv error: fd is : %d", fd);close(fd);_epoller_ptr->updataEpoller(fd, EPOLL_CTL_DEL,0); // 这里本质是从select中移除}}void Dispatcher(struct epoll_event revents[], int num){for (int i = 0; i < num; i++){int fd = revents[i].data.fd;//std::cout << fd << std::endl;uint32_t events = revents[i].events;if (events & EVENT_IN){if (fd == _listensockfd_ptr->Fd()){Accepter();}else{Recvr(fd);}}}}void Start(){// 我们只需要把listenfd放入到红黑树中_epoller_ptr->updataEpoller(_listensockfd_ptr->Fd(), EPOLL_CTL_ADD, EVENT_IN);struct epoll_event revents[num];// for(int i = 0;i<num;i++)// {// revents[i].data.fd = defaultfd;// revents[i].events = 0;// }for (;;){// 在开始阶段我们只有listensock,所以我们可以把listensock等待数据的就绪。// 获取内核就绪事件的fd,获取完成后,查看是否是读事件就绪。int n = _epoller_ptr->waitEpoller(revents, num);if (n > 0){lg(Debug, "event happened, fd is : %d", revents[0].data.fd);Dispatcher(revents, n);}else if (n == 0){std::cout << "time out" << std::endl;}else{std::cerr << "epoll err" << std::endl;}}}~epollserver(){}private:std::shared_ptr<Sock> _listensockfd_ptr;std::shared_ptr<Epoller> _epoller_ptr;uint16_t _port;
};
epoll的工作模式:LT和ET。
水平触发模式 LEVEL Triggered
事件到来,但是上层不处理,一直通知
边缘触发模式Edge Triggered
数据或者连接,从无到有,从有到多,变化的时候,才会通知我们一次。ET的通知效率更高。倒逼程序员,每次通知,都必须把本轮数据全部取走。循环读取,读取出错证明缓冲区没有数据了 ->fd默认是阻塞的 -> ET,所有的fd必须是non_block。不仅如此ET的IO效率也是更高的!通知一次必须把缓冲区的数据全部读走,以为着tcp回向对方通告一个更大的窗口,从而从概率上让对方一次给我发送更多的的数据。
ET vs LT ? LT可不可以将所有的fd设置成为non_block,然后循环读取呢,通知第一次的时候,就全部取走,不就和ET一样了吗?
LT和ET的本质区别是添加就绪队列的方式,LT是次次都添加,而ET只添加一次。
相关文章:

Linux网络编程 --- 高级IO
前言 IO Input&&Output read && write 1、在应用层read && write的时候,本质把数据从用户层写给OS --- 本质就是拷贝函数 2、IO 等待 拷贝。 等的是:要进行拷贝,必须先判断读写事件成立。读写事件缓冲区空间满…...

Python中给定一个数组a = [2,3,9,1,0],找出其中最大的一个数,并打印出来 求解?
Python有内置的max函数可以取最大值: max([2,3,9,1,0])也可以使用sorted先排序,再索引取出最大值: sorted([2,3,9,1,0])[-1]如果不用内置函数,自己排序算法来找出最大值,也有很多选择。 比如冒泡排序、循环排序、交…...

系统优化工具 | PC Cleaner v9.7.0.3 绿色版
PC Cleaner是一款功能强大的电脑清理和优化工具,旨在通过清理系统垃圾文件、解除恶意软件和优化系统性能来提高计算机的运行效率。该软件提供了多种功能,可以帮助用户维护和提升计算机的整体表现。 PC Cleaner 支持 Windows 7 及以上操作系统࿰…...

JavaSE、JavaEE 与 JavaWeb 的详解与区别
一、JavaSE(Java Standard Edition)——标准版 1. 什么是JavaSE JavaSE,全称Java Standard Edition,译为Java标准版,是Java平台的基础,也是开发者最常使用的Java版本。JavaSE包含了编程中最基础的核心库,如Java的基本语法、面向对象编程、集合框架、多线程、网络编程、…...

HCIE和CCIE,哪个含金量更高点?
在现在内卷的大环境下,技术岗可谓人人自危,也因此各种认证的重视程度直线升高。 特别是华为认证的HCIE和思科认证的CCIE,它们都代表着网络技术领域的顶尖水平。 但面对这两个高含金量的认证,不得不让人问出这个问题:同…...

2024.9.14 Python与图像处理新国大EE5731课程大作业,马尔可夫随机场和二值图割,校正立体图像的深度
1.马尔科夫随机场和二值图割 马尔可夫随机场(MRF, Markov Random Field): MRF 是一种用来描述图像像素之间空间关系的概率模型。它假设图像中的像素不仅取决于自身的值,还与周围像素有关。这种模型经常用于图像分割、去噪等任务。…...

工业大模型市场图谱:53个工业大模型全面梳理
工业场景要求严谨、容错率低,核心业务场景对模型准确率的要求达到95%以上、对幻觉的容忍率为0,因此通用基础大模型的工业知识往往不足以满足工业场景的应用需求。 根据沙丘智库发布的《2024年中国工业大模型应用跟踪报告》,工业大模型是指在…...

【代码随想录训练营第42期 Day58打卡 - 图论Part8 - 拓扑排序
目录 一、拓扑排序介绍 定义 特点 实现方法(2种) 应用 二、题目与题解 题目:卡码网 117. 软件构建 题目链接 题解:拓扑排序 - Kahn算法(BFS) 三、小结 一、拓扑排序介绍 对于拓扑排序,…...

JVM内部结构解析
Java虚拟机(JVM)是Java程序运行的基础环境,它为Java程序提供了一个与平台无关的执行环境。了解JVM的内部结构对于Java开发者来说至关重要,因为它可以帮助开发者优化程序性能,理解垃圾回收机制,以及诊断和解…...

誉龙视音频综合管理平台 RelMedia/FindById SQL注入漏洞复现
0x01 产品简介 誉龙视音频综合管理平台是深圳誉龙数字技术有限公司基于多年的技术沉淀和项目经验,自主研发的集视音频记录、传输、管理于一体的综合解决方案。该平台支持国产化操作系统和Windows操作系统,能够接入多种类型的记录仪,实现高清实时图传、双向语音对讲、AI应用…...

MATLAB系列01:MATLAB介绍
MATLAB系列01:MATLAB介绍 1. MATLAB介绍1.1 MATLAB的优点1.2 MATLAB的缺点1.3 MATLAB的开发环境1.3.1 获取帮助的方法:1.3.2 一些重要的命令:1.3.3 MATLAB搜索路径 1. MATLAB介绍 MATLAB(矩阵实验室的简称)是一种专业的计算机程序࿰…...

GEE 按范围导出 Sentinel-2 卫星影像
Sentinel-2 卫星提供了高分辨率的地表覆盖图像,广泛应用于农业监测、城市规划、环境变化分析等诸多领域。在 Google Earth Engine (GEE) 中,我们能够按特定地理范围导出这些影像,以支持更深入的研究和分析。 使用方法 💻 GEE 提供…...

队列OJ题——用队列实现栈
文章目录 一、题目链接二、解题思路三、解题代码 一、题目链接 用队列实现栈 二、解题思路 三、解题代码 class MyStack {public Queue<Integer> queue1;public Queue<Integer> queue2;public int usedSize;public MyStack() {queue1 new LinkedList<>()…...

RK3588镜像打包制作,替换文件系统
1.在开发板上安装async apt-get async 2.在另一台linux机器上执行命令拷贝文件系统 注意: 这里使用root权限或者账户 mkdir rootfs rsync -avx root192.168.1.3:/ rootfs 3.制作空镜像文件 先去开发板上验证自己的系统使用了多少空间,然后输入命令制…...

Open-Sora代码详细解读(2):时空3D VAE
Diffusion Models视频生成 前言:目前开源的DiT视频生成模型不是很多,Open-Sora是开发者生态最好的一个,涵盖了DiT、时空DiT、3D VAE、Rectified Flow、因果卷积等Diffusion视频生成的经典知识点。本篇博客从Open-Sora的代码出发,深…...

基于微信平台的旅游出行必备商城小程序+ssm(lw+演示+源码+运行)
摘 要 随着社会的发展,社会的方方面面都在利用信息化时代的优势。互联网的优势和普及使得各种系统的开发成为必需。 本文以实际运用为开发背景,运用软件工程原理和开发方法,它主要是采用java语言技术和mysql数据库来完成对系统的设计。整个…...

AI绘画:科技赋能艺术的崭新时代
💯AI绘画:走进艺术创新的新时代 人工智能在改变世界的过程中,AI绘画工具逐渐成为创新的典范。 本文将为您揭示AI绘画背后的技术秘密、潜在的应用场景,并为您推荐几款出色的AI绘画工具,助您领略这一技术带来的艺术新体…...

性能诊断的方法(四):自下而上的资源诊断方法和发散的异常信息诊断方法
关于性能诊断的方法,我们可以按照“问题现象—直接原因—问题根源”这样一个思路去归纳。我们先从问题的现象去入手,包括时间的分析、资源的分析和异常信息的分析。接下来再去分析产生问题现象的直接原因是什么,这里我们归纳了自上而下的资源…...

GDPU Vue前端框架开发 计数器
计数器算不到你双向绑定的进度。 重要的更新公告 !!!GDPU的小伙伴,感谢大家的支持,希望到此一游的帅哥美女能有所帮助。本学期的前端框架及移动应用,采用专栏订阅量达到50才开始周更了哦( •̀ .̫ •́ )✧…...

最大流笔记
概念 求两点间的路径中可在同一时间内通过的最大量 EK算法 通过bfs找通路,找到后回溯; 每确定一条边时,同时建立一天反方向的边以用来进行反悔操作(毕竟一次性找到正确方案的概率太低了) code #include<bits/st…...

el-tree父子不互相关联时,手动实现全选、反选、子级全选、清空功能
el-tree父子不互相关联时,手动实现全选、反选、子级全选、清空功能 1、功能实现图示 2、实现思路 当属性check-strictly为true时,父子节点不互相关联,如果需要全部选中或选择某一节点下的全部节点就必须手动选择每个节点,十分麻…...

模板与泛型编程笔记(一)入门篇
1. 推荐书籍 《C新经典 模板与泛型编程》难得的很容易看得懂的好书,作者讲技术不跳跃,娓娓道来,只要花点时间就能看懂。 2. 笔记 2.1 模板基础 模板为什么要用尖括号?因为便于编译器解析,可以将模板和普通函数声明…...

浅谈WebApi
一、基本介绍 Web API(Web应用程序编程接口)是一种用于构建应用程序的接口,它允许软件应用程序通过HTTP请求与Web服务器进行交互。Web API通常用于构建客户端-服务器应用程序,其中客户端可以是Web浏览器、移动应用程序、桌面应用程…...

9月14日,每日信息差
第一、宝马集团宣布对设计部门进行重组,并将于 2024 年 10 月 1 日成立一个跨品牌设计团队,由范・霍伊顿克领导。该团队将引入极星汽车设计主管马克西米利安・米索尼,负责宝马中高档和豪华车型以及宝马 Alpina 的设计工作。 第二、小鹏汇天飞…...

无人机控制与三维AI感知处理平台正式上线!
低空经济被誉为推动我国经济高质量发展的全新增长引擎,是一种以民用有人驾驶和无人驾驶航空器的各类低空飞行活动为牵引,辐射带动相关领域融合发展的综合性经济形态,2024年全国两会首次被纳入政府工作报告。 大势智慧积极响应国家低空经济政…...

9.11-kubeadm方式安装k8s
一、安装环境 编号主机名称ip地址1k8s-master192.168.2.662k8s-node01192.168.2.773k8s-node02192.168.2.88 二、前期准备 1.设置免密登录 [rootk8s-master ~]# ssh-keygen [rootk8s-master ~]# ssh-copy-id root192.168.2.77 [rootk8s-master ~]# ssh-copy-id root192.168…...

限流,流量整形算法
写在前面 源码 。 本文看下流量整形相关算法。 目前流量整形算法主要有三种,计数器,漏桶,令牌桶。分别看下咯! 1:计数器 1.1:描述 单位时间内只允许指定数量的请求,如果是时间区间内超过指…...

【C++知识扫盲】------C++ 中的引用入门
在 C 中,引用(reference) 是一个非常重要的概念,它提供了一种别名机制,让我们可以给已经存在的变量起一个新的名字,并且能够通过这个别名直接操作原始变量。本文将详细介绍引用的定义、使用场景及其与指针的…...

【机器学习】6 ——最大熵模型
机器学习6——最大熵模型 目录 机器学习6——最大熵模型最大熵(maximum entropy)模型模型模型学习(估计参数)模型评价应用 最大熵(maximum entropy)模型 选择熵最大的概率模型 熵是衡量不确定性的…...

小程序——生命周期
文章目录 运行机制更新机制生命周期介绍应用级别生命周期页面级别生命周期组件生命周期生命周期两个细节补充说明总结 运行机制 用一张图简要概述一下小程序的运行机制 冷启动与热启动: 小程序启动可以分为两种情况,一种是冷启动,一种是热…...