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

【Linux网络】poll{初识poll / poll接口 / poll vs select / poll开发多客户端echo服务器}

文章目录

  • 1.初识poll
    • poll与select的主要联系与区别
    • poll的原理
    • poll的优点
    • poll的缺点
    • poll vs select
  • 2.poll开发多客户端echo服务器
    • 封装套接字接口
    • Makefile
    • 主函数
    • 日志服务
    • 聊天服务器

1.初识poll

在这里插入图片描述
在这里插入图片描述

poll是Linux系统中的一个系统调用,它用于监控多个文件描述符(file descriptors,如套接字、管道、文件等)的状态变化。通过poll,程序可以同时等待多个文件描述符上发生的特定事件(如可读、可写、错误等),而无需为每个文件描述符创建单独的线程或进程。这使得poll成为处理多个并发连接和I/O操作的有效手段。

#include <poll.h>
int poll(struct pollfd *fds, nfds_t nfds, int timeout);

fds:是一个指向struct pollfd结构体数组的指针,每个元素指定了一个要检查的文件描述符及其感兴趣的事件。
nfds:指定了fds数组中元素的数量。
timeout:指定了poll调用阻塞的时间,以毫秒为单位。如果设置为-1,poll将无限期地等待;如果设置为0,poll将立即返回;如果设置为正数,poll将在指定的毫秒数后超时。

poll与select的主要联系与区别

  1. 联系
    多路复用:poll和select都是I/O多路复用机制,允许程序同时监控多个文件描述符,提高了程序处理并发I/O的效率。
    事件通知:两者都提供了一种机制,通过事件通知程序哪些文件描述符已经准备好进行读取、写入或有异常情况。
    非阻塞I/O:它们都可以与非阻塞I/O结合使用,使得程序可以在没有数据可读或写入时继续执行其他任务。
  2. 区别
  • 文件描述符数量限制:
    select:通常受到系统文件描述符数量限制的限制,一般在1024个左右。
    poll:没有文件描述符数量的硬限制,理论上可以监控更多的文件描述符(但实际上受限于系统资源和内存限制)。
  • 数据结构:
    select:使用三个独立的位图(bitmap)来跟踪文件描述符的状态,这些位图在内核空间维护。
    poll:使用一个struct pollfd结构体数组来存储要监控的文件描述符及其事件,这个数组在用户空间维护。
  • 事件信息丰富度:
    select:需要程序遍历位图来查找事件,对于每个文件描述符的状态变化,select提供的信息相对较少。
    poll:struct pollfd结构体中的revents字段在事件发生时会被内核设置,提供了更丰富的信息关于每个文件描述符的状态变化。
  • 性能:
    select:在文件描述符数量较多时,性能会下降,因为需要遍历整个位图来查找就绪的文件描述符。
    poll:虽然也需要在用户空间和内核空间之间复制文件描述符集合,但由于其数据结构的设计,在处理大量文件描述符时可能具有更好的性能。然而,如果监控的文件描述符数量非常大,仍然可能遇到性能瓶颈。
  • 超时精度:
    select:超时参数是一个整数值,表示调用应该等待的秒数,精度较低。
    poll:超时参数是一个毫秒值,提供了更高的时间精度。
  • 移植性:
    两者都具有良好的可移植性,可以在不同的Unix-like系统中使用。但需要注意的是,在某些特定系统或环境下,poll的支持可能不如select广泛。

综上所述,poll和select在Linux系统中都扮演着重要的角色,但在具体使用时需要根据应用场景、文件描述符数量、性能要求等因素进行选择。

poll的原理

poll函数是Linux系统中的一个重要系统调用,用于监控多个文件描述符(file descriptors)的状态变化。下面从参数和底层原理两个方面对poll函数进行简要叙述。

#include <poll.h>  
int poll(struct pollfd fds[], nfds_t nfds, int timeout);

fds:这是一个指向struct pollfd结构体数组的指针。每个pollfd结构体代表了一个要监控的文件描述符及其感兴趣的事件。pollfd结构体的定义通常如下:

c
struct pollfd {  int fd;          // 文件描述符  short events;    // 等待的事件  short revents;   // 实际发生了的事件  
};

fd:要监控的文件描述符。
events:请求监控的事件,可以是读、写、异常等多种事件的组合。
revents:函数返回时,由内核设置,表示实际发生的事件。
nfds:指定了fds数组中元素的数量,即要监控的文件描述符的总数。
timeout:指定了poll调用阻塞的时间,以毫秒为单位。
如果timeout为正数,poll将等待指定的毫秒数。
如果timeout为0,poll将立即返回,不阻塞。
如果timeout为-1,poll将无限期地等待,直到有事件发生。

poll函数的底层原理

poll函数的底层实现原理主要基于等待队列。当调用poll函数时,它会遍历传入的pollfd结构体数组,为每个感兴趣的文件描述符注册一个等待事件。这些等待事件会被挂接到内核中相应的等待队列上。

等待队列:内核中的每个文件描述符都可能关联有一个或多个等待队列,用于存放等待该文件描述符上特定事件发生的进程或线程。
轮询与阻塞:poll函数会轮询检查每个文件描述符的等待队列,查看是否有事件发生。
如果有文件描述符上发生了感兴趣的事件,poll函数会立即返回,并将这些事件记录在对应pollfd结构体的revents字段中。
如果没有任何文件描述符上发生事件,并且timeout参数指定了非零值,poll函数会进入阻塞状态,直到超时时间到达或至少有一个文件描述符上发生了事件。

返回与错误处理:

当有文件描述符上发生事件或超时时间到达时,poll函数会返回。返回值表示发生了事件的文件描述符数量(如果大于0),或者在超时时返回0,或者在发生错误时返回-1并设置errno以指示错误原因。
效率与限制:虽然poll函数没有像select那样有文件描述符数量的硬限制,但在处理大量文件描述符时,仍然需要将整个pollfd数组在用户空间和内核空间之间复制,这可能会导致性能下降。此外,poll函数在内部仍然需要遍历所有要监控的文件描述符,因此在大规模并发场景下可能不是最高效的解决方案。
综上所述,poll函数通过轮询和等待队列机制实现了对多个文件描述符状态的监控,是Linux系统中处理I/O多路复用的重要手段之一。然而,在处理大量文件描述符时,可能需要考虑其性能限制并探索更高效的解决方案(如epoll)。

源码

#include <poll.h>
int poll(struct pollfd *fds, nfds_t nfds, int timeout);
// pollfd结构
struct pollfd {int fd; /* file descriptor */short events; /* requested events */short revents; /* returned events */
};

多路转接包括:用户告诉内核你需要关心什么 && 内核告诉用户你让我关心的fd有哪些就绪了。select 用位图,poll 用结构体数组。poll 在用户传给内核的时候,告诉内核需要关心 struct pollfd 结构体中的 fd 中的 events 事件;返回时,内核设置struct pollfd 结构体中的 revents 事件,表示该fd的该事件就绪。poll 最大的特点:将输入和输出事件进行分离!

内核怎么知道是关心读事件还是写事件还是其他事件呢?当内核返回用户也一样。 events 和 revents 都是 short 类型,都是 16 个比特位,在 Linux 中,使用比特位传参!把事件设置成位图的形式。
在这里插入图片描述
poll 的本质是将读写事件分离,传入用户定的数组元素的大小,通过 events 和 revents 以位图的方式来传递就绪和关心标记位的解决方案!

poll的优点

poll 也是多路转接方案的一种,它主要解决的就是 select 中的等待 fd 有上限的问题,以及每次都要对关心的 fd 进行事件重置的问题。

不同与select使用三个位图来表示三个fdset的方式,poll使用一个pollfd的指针实现.
pollfd结构包含了要监视的event和发生的event,不再使用select“参数-值”传递的方式. 接口使用比select更方便。
poll并没有最大数量限制 (但是数量过大后性能也是会下降).

poll的缺点

poll中监听的文件描述符数目增多时和select函数一样,poll返回后,需要轮询pollfd来获取就绪的描述符.
每次调用poll都需要把大量的pollfd结构从用户态拷贝到内核中.
同时连接的大量客户端在一时刻可能只有很少的处于就绪状态, 因此随着监视的描述符数量的增长, 其效率也会线性下降.

poll vs select

poll 本质上是通过一个结构体数组来等待 fd 的,它解决了 select 等待 fd 有上限的问题,如何解决?_event_fds 这个数组的大小是自己定的,可以定的非常大,大到内存扛不住,此时就是操作系统/内存 软件或硬件的问题了,不是 poll 接口本身的问题。select 等待 fd 有上限的问题,本质上是接口本身的问题,poll 本质上是解决了 select 等待 fd 有上限的问题。
poll 与 select 都需要遍历检测有哪些文件描述符就绪,poll 在内核中需要遍历检测有哪些文件描述符就绪;在用户层需要遍历检测有哪些事件已经就绪。
poll 和 select 都避免不开遍历的问题,当fd过多,效率提升不明显。

2.poll开发多客户端echo服务器

封装套接字接口

#pragma once#include <iostream>
#include <string>
#include <unistd.h>
#include <cstring>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include "Log.hpp"enum
{SocketErr = 2,BindErr,ListenErr,
};const int g_backlog = 10;class Sock
{
private:int _sockfd;public:Sock(){}~Sock(){}public:void Socket(){_sockfd = socket(AF_INET, SOCK_STREAM, 0);if (_sockfd < 0){lg(Fatal, "socker error, %s: %d", strerror(errno), errno);exit(SocketErr);}}void Bind(uint16_t port){struct sockaddr_in local;memset(&local, 0, sizeof(local));local.sin_family = AF_INET;local.sin_port = htons(port);local.sin_addr.s_addr = INADDR_ANY;if (bind(_sockfd, (struct sockaddr *)&local, sizeof(local)) < 0){lg(Fatal, "bind error, %s: %d", strerror(errno), errno);exit(BindErr);}}void Listen(){if (listen(_sockfd, g_backlog) < 0){lg(Fatal, "listen error, %s: %d", strerror(errno), errno);exit(ListenErr);}}int Accept(std::string *clientip, uint16_t *clientport){struct sockaddr_in peer;socklen_t len = sizeof(peer);int newfd = accept(_sockfd, (struct sockaddr *)&peer, &len);if (newfd < 0){lg(Warning, "accept error, %s: %d", strerror(errno), errno);return -1;}char ipstr[64];inet_ntop(AF_INET, &peer.sin_addr, ipstr, sizeof(ipstr));*clientip = ipstr;*clientport = ntohs(peer.sin_port);return newfd;}bool Connect(const std::string &ip, const uint16_t &port){struct sockaddr_in peer;memset(&peer, 0, sizeof(peer));peer.sin_family = AF_INET;peer.sin_port = htons(port);inet_pton(AF_INET, ip.c_str(), &(peer.sin_addr));int n = connect(_sockfd, (struct sockaddr *)&peer, sizeof(peer));if (n == -1){std::cerr << "connect to " << ip << ":" << port << " error" << std::endl;return false;}return true;}void CloseFd(){close(_sockfd);}int getSocketFd(){return _sockfd;}
};

Makefile

poll_server:Main.ccg++ -o $@ $^ -std=c++11
.PHONY:clean
clean:rm -f poll_server

主函数

#include "PollServer.hpp"
#include <memory>int main()
{std::unique_ptr<PollServer> svr(new PollServer());svr->Init();svr->Start();return 0;
}

日志服务

#pragma once#include <iostream>
#include <time.h>
#include <stdarg.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>#define SIZE 1024#define Info 0
#define Debug 1
#define Warning 2
#define Error 3
#define Fatal 4#define Screen 1
#define Onefile 2
#define Classfile 3#define LogFile "log.txt"class Log
{
private:int printMethod;std::string path;public:Log(){printMethod = Screen;path = "./";}void Enable(int method){printMethod = method;}std::string levelToString(int level){switch (level){case Info:return "Info";case Debug:return "Debug";case Warning:return "Warning";case Error:return "Error";case Fatal:return "Fatal";default:return "None";}}/*void logmessage(int level, const char *format, ...){time_t t = time(nullptr);struct tm *ctime = localtime(&t);char leftbuffer[SIZE];snprintf(leftbuffer, sizeof(leftbuffer), "[%s][%d-%d-%d %d:%d:%d]", levelToString(level).c_str(),ctime->tm_year + 1900, ctime->tm_mon + 1, ctime->tm_mday,ctime->tm_hour, ctime->tm_min, ctime->tm_sec);va_list s;va_start(s, format);char rightbuffer[SIZE];vsnprintf(rightbuffer, sizeof(rightbuffer), format, s);va_end(s);// 格式:默认部分+自定义部分char logtxt[SIZE * 2];snprintf(logtxt, sizeof(logtxt), "%s %s\n", leftbuffer, rightbuffer);// printf("%s", logtxt);printLog(level, logtxt);}*/// lg(Warning, "accept error, %s: %d", strerror(errno), errno);void operator()(int level, const char *msg_format, ...){time_t timestamp = time(nullptr);struct tm *ctime = localtime(&timestamp);//level 年月日char leftbuffer[SIZE];snprintf(leftbuffer, sizeof(leftbuffer), "[%s][%d-%d-%d %d:%d:%d]", levelToString(level).c_str(),ctime->tm_year + 1900, ctime->tm_mon + 1, ctime->tm_mday,ctime->tm_hour, ctime->tm_min, ctime->tm_sec);//自定义msgva_list arg_list;//存储可变参数列表信息va_start(arg_list, msg_format);//初始化 使其指向函数参数列表中format参数之后的第一个可变参数char rightbuffer[SIZE];vsnprintf(rightbuffer, sizeof(rightbuffer), msg_format, arg_list);va_end(arg_list);//清理va_list变量// 格式:默认部分+自定义部分char log_content[SIZE * 2];snprintf(log_content, sizeof(log_content), "%s %s", leftbuffer, rightbuffer);// printf("%s", logtxt); // 暂时打印printLog(level, log_content);}void printLog(int level, const std::string &log_content){switch (printMethod){case Screen:std::cout << log_content << std::endl;break;case Onefile:printOneFile(LogFile, log_content);break;case Classfile:printClassFile(level, log_content);break;default:break;}}void printOneFile(const std::string &log_filename, const std::string &log_content){//path = "./"; #define LogFile "log.txt"std::string _logFilename = path + log_filename;int fd = open(_logFilename.c_str(), O_WRONLY | O_CREAT | O_APPEND, 0666); // "log.txt"if (fd < 0)return;write(fd, log_content.c_str(), log_content.size());close(fd);}void printClassFile(int level, const std::string &log_content){//#define LogFile "log.txt"std::string filename = LogFile;filename += ".";filename += levelToString(level); // "log.txt.Debug"printOneFile(filename, log_content);}~Log(){}
};Log lg;/*
int sum(int n, ...)
{va_list s; // char*va_start(s, n);int sum = 0;while(n){sum += va_arg(s, int); // printf("hello %d, hello %s, hello %c, hello %d,", 1, "hello", 'c', 123);n--;}va_end(s); //s = NULLreturn sum;
}
*/

聊天服务器

#pragma once#include <iostream>
#include <poll.h>
#include <sys/time.h>
#include "Socket.hpp"using namespace std;static const uint16_t defaultport = 8888;
static const int fd_num_max = 64;
int defaultFd = -1;
int non_event = 0;class PollServer
{
private:Sock _listensock;uint16_t _port;struct pollfd _event_fds[fd_num_max]; // 结构体数组// struct pollfd *_event_fds; 动态数组 可扩容// int fd_array[fd_num_max];// int wfd_array[fd_num_max];
public:PollServer(uint16_t port = defaultport): _port(port){for (int i = 0; i < fd_num_max; i++){_event_fds[i].fd = defaultFd;_event_fds[i].events = non_event;_event_fds[i].revents = non_event;}}bool Init(){_listensock.Socket();_listensock.Bind(_port);_listensock.Listen();return true;}void Accepter(){// 连接事件就绪std::string clientip;uint16_t clientport = 0;int sock = _listensock.Accept(&clientip, &clientport); // 会不会阻塞在这里?不会if (sock < 0)return;lg(Info, "accept success, %s: %d, sock fd: %d", clientip.c_str(), clientport, sock);// sock -> _event_fds[]int pos = 1;for (; pos < fd_num_max; pos++) // 第二个循环{if (_event_fds[pos].fd != defaultFd)continue;elsebreak;}if (pos == fd_num_max){lg(Warning, "server is full, close %d now!", sock);close(sock);// 也可以不关闭fd 扩容存fd}else{_event_fds[pos].fd = sock;_event_fds[pos].events = POLLIN;_event_fds[pos].revents = non_event;PrintFd();}}void Recver(int fd, int pos){char buffer[1024];ssize_t n = read(fd, buffer, sizeof(buffer) - 1); // 需要考虑是否是完整数据包问题(此处忽略)if (n > 0){buffer[n] = 0;cout << "get a messge: " << buffer << endl;}else if (n == 0){lg(Info, "client quit, me too, close fd is : %d", fd);close(fd);_event_fds[pos].fd = defaultFd; }else{lg(Warning, "recv error: fd is : %d", fd);close(fd);_event_fds[pos].fd = defaultFd; }}void Dispatcher(){for (int i = 0; i < fd_num_max; i++) // 这是第三个循环{int fd = _event_fds[i].fd;if (fd == defaultFd)continue;if (_event_fds[i].revents & POLLIN){if (fd == _listensock.getSocketFd())Accepter(); // 连接管理器elseRecver(fd, i); // non listenfd}}}void Start(){_event_fds[0].fd = _listensock.getSocketFd();_event_fds[0].events = POLLIN;int timeout = 3000; // 3sfor (;;){int n = poll(_event_fds, fd_num_max, timeout);switch (n){case 0:cout << "time out... " << endl;break;case -1:cerr << "poll error" << endl;break;default:// 有事件就绪了cout << "get a new link!!!!!" << endl;Dispatcher();break;}}}void PrintFd(){cout << "online fd list: ";for (int i = 0; i < fd_num_max; i++){if (_event_fds[i].fd == defaultFd)continue;cout << _event_fds[i].fd << " ";}cout << endl;}~PollServer(){_listensock.CloseFd();}
};

相关文章:

【Linux网络】poll{初识poll / poll接口 / poll vs select / poll开发多客户端echo服务器}

文章目录 1.初识pollpoll与select的主要联系与区别poll的原理poll的优点poll的缺点poll vs select 2.poll开发多客户端echo服务器封装套接字接口Makefile主函数日志服务聊天服务器 1.初识poll poll是Linux系统中的一个系统调用&#xff0c;它用于监控多个文件描述符&#xff08…...

数据库db文件损坏修复方法(sqlite3:database disk image is malformed)

参考博客&#xff1a; https://www.cnblogs.com/liuyangQAQ/p/18037546 sqlite3数据库提示database disk image is malformed 解决办法-CSDN博客 【SQL】sqlite数据库损坏报错&#xff1a;database disk image is malformed&#xff08;已解决&#xff09;-CSDN博客 一、第…...

Prometheus 云原生 - 微服务监控报警系统 (Promethus、Grafana、Node_Exporter)部署、简单使用

目录 开始 Prometheus 介绍 基本原理 组件介绍 下文部署组件的工作方式 Prometheus 生态安装&#xff08;Mac&#xff09; 安装 prometheus 安装 grafana 安装 node_exporter Prometheus 生态安装&#xff08;Docker&#xff09; 安装 prometheus 安装 Grafana 安装…...

Spring源码注解篇三:深入理解@Component注解

Component及其派生注解的源码 Spring框架作为Java开发中不可或缺的一部分&#xff0c;其依赖注入机制的核心是通过注解来实现的。本文将深入探讨Spring中Component及其派生注解的源码实现&#xff0c;分析Spring如何通过类路径扫描&#xff08;Classpath Scanning&#xff09;和…...

SpringBoot中常用的注解及其用法

1. 常用类注解 RestController和Controller是Spring中用于定义控制器的两个类注解. 1.1 RestController RestController是一个组合类注解,是Controller和ResponseBody两个注解的组合,在使 用 RestController 注解标记的类中&#xff0c;每个方法的返回值都会以 JSON 或 XML…...

【大语言模型】私有化搭建-企业知识库-知识问答系统

下面是我关于大语言模型学习的一点记录 目录 人工智能学习路线 MaxKB 系统(基于大语言模型的知识问答系统) 部署开源大语言模型LLM 1.CPU模式(没有好的GPU&#xff0c;算力和效果较差) 2.GPU模式&#xff08;需要有NVIDIA显卡支持&#xff09; Ollama网络配置 Ollama前…...

CSS常用的样式

字体和文本样式 font-family: 定义文本字体。 font-size: 设置字体大小。 color: 设置文本颜色。 text-align: 水平对齐文本&#xff08;左对齐、右对齐、居中、两端对齐&#xff09;。 line-height: 设置行间距。 text-decoration: 控制文本装饰线&#xff08;如下划线、…...

结合实体类型信息(2)——基于本体的知识图谱补全深度学习方法

1 引言 1.1 问题 目前KGC和KGE提案的两个主要缺点是:(1)它们没有利用本体信息;(二)对训练时未见的事实和新鲜事物不能预测的。 1.2 解决方案 一种新的知识图嵌入初始化方法。 1.3 结合的信息 知识库中的实体向量表示&#xff0b;编码后的本体信息——>增强 KGC 2基…...

如何在电脑上演示手机上APP,远程排查移动端app问题

0序&#xff1a; 对接客户&#xff0c;给领导演示移动端产品&#xff0c;或者远程帮用户排查移动端产品的问题。都需要让别人能够看到自己在操作手机。 会议室可以使用投屏&#xff0c;但需要切换电脑和手机。 排查问题经常都是截图、或者手机上录制视频&#xff0c;十分繁琐…...

SQL Server 创建用户并授权

创建用户前需要有一个数据库&#xff0c;创建数据库命令如下&#xff1a; CREATE DATABASE [数据库名称]; CREATE DATABASE database1; 一、创建登录用户 方式1&#xff1a;SQL命令 命令格式&#xff1a;CREATE LOGIN [用户名] WITH PASSWORD 密码; 例如&#xff0c;创建…...

网关设备BL122实现Modbus RTU/TCP转Profinet协议

Modbus与Profinet是两种广泛应用于工业自动化领域的通信协议&#xff1a;Modbus因其简单性和兼容性&#xff0c;在许多工业设备中得到广泛应用&#xff1b;而Profinet提供了高速、高精度的通信能力&#xff0c;适合于复杂控制系统和实时应用&#xff0c;但两者之间的差异导致了…...

采购管理软件:改善初创企业的采购流程

说到初创企业&#xff0c;人们往往会联想到一个宽松的工作环境&#xff0c;缺乏严格的流程规范&#xff0c;以及公司收入的迅猛增长。这种快速增长可能会被认为会导致工作流程的无序和缺乏结构&#xff0c;使得员工在决策上具有较大的自由度&#xff0c;例如在采购方面。 在这…...

Python 是一种用途广泛的编程语言,应用于各个领域

1. 网络和互联网开发: Python 拥有丰富的框架和库&#xff0c;使其成为 Web 开发的理想选择。 框架: Django 和 Pyramid 用于构建复杂的 Web 应用。Flask 和 Bottle 则适合轻量级应用和 API。 库: Python 标准库支持处理 HTML、XML、JSON 和电子邮件。此外&#xff0c;还有强大…...

【VUE】9、VUE项目中使用VUEX完成状态管理

Vuex 是一个专为 Vue.js 应用程序设计的状态管理模式&#xff0c;它帮助开发者更有效地管理组件间共享的状态。在 Vue 项目中使用 Vuex&#xff0c;可以解决复杂应用中状态管理的困扰&#xff0c;确保状态变更的可追踪性和一致性。 1、Vuex 核心概念 State&#xff08;状态&a…...

【eNSP模拟实验】单臂路由实现VLAN间通信

实验需求 如下图所示&#xff0c;辅导员办公室需要访问处在不同vlan的学生管理服务器的文件&#xff0c;那么如何实现两台终端相互通信呢&#xff1f;我们可以使用单臂路由的方式来实现。 单臂路由&#xff08;router-on-a-stick&#xff09;是指在路由器的一个接口上通过配置…...

哪些点权衡素材优秀与否

投放信息流素材的过程当中&#xff0c;我们究竟看哪几点来权衡这个素材是优秀的还是不优秀的&#xff1f;我们主要是以三个维度来看。 第一个就是 3 秒和 5 秒的完播率。很多优秀的素材它可能在前三秒和前五秒很平淡的一个过程&#xff0c;但是因为现在是一个非常快节奏的过程&…...

服务器数据恢复—2块硬盘离线且热备盘未完全激活的raid5数据恢复案例

服务器存储数据恢复环境&#xff1a; 北京某企业一台EMC FCAX-4存储上搭建一组由12块成员盘的raid5磁盘阵列&#xff0c;其中包括2块热备盘。 服务器存储故障&#xff1a; raid5阵列中两块硬盘离线&#xff0c;热备盘只有一块成功激活&#xff0c;raid瘫痪&#xff0c;上层LUN…...

Excel 学习手册 - 精进版(包括各类复杂函数及其嵌套使用)

作为程序员从未想过要去精进一下 Excel 办公软件的使用方法&#xff0c;以前用到某功能都是直接百度&#xff0c;最近这两天跟着哔哩哔哩上的戴戴戴师兄把 Excel 由里到外学了一遍&#xff0c;收获良多。程序员要想掌握这些内容可以说是手拿把掐&#xff0c;对后续 Excel 的运用…...

【CUDA】thrust进行前缀和的操作

接上篇文章&#xff0c;可以发现使用CUDA提供的API进行前缀和扫描时&#xff0c;第一次运行的时间不如共享内存访问&#xff0c;猜测是使用到了全局内存。 首先看调用逻辑&#xff1a; thrust::inclusive_scan(thrust::device, d_x, d_x N, d_x);第一个参数指定了设备&#x…...

Qt-QPainter的使用总结

一、QPainter介绍 QPainter是Qt框架中的一个类,主要用于在各种设备上进行2D图形绘制,如窗口、位图、打印设备等。它提供了一系列绘图函数,可以实现绘制点、线、曲线、形状、文本、图像以及复杂的绘画操作。 QPainter 与 QPaintDevice 和 QPaintEngine 类一起构成了Qt绘制系…...

轻松搞定GIS场景编辑,这款免费工具你一定要试试

如果你正苦恼于如何搞定GIS场景编辑&#xff0c;不妨来试试这款免费可视化工具——山海鲸可视化。经过本人测试&#xff0c;这款软件在GIS场景编辑上完全可以做到“零代码”操作&#xff0c;即使没有任何编程技能也可以在三维GIS世界中如鱼得水。现在&#xff0c;让我们一起来看…...

【笔记】一起齿轮箱的故障和相应的数学模拟实验

1.齿轮箱故障一例 出处&#xff1a;设备的故障识别 GearBox的频谱图&#xff0c;原作者不知道是从哪里拷贝来的&#xff0c;待会儿确认一下。 齿轮啮合频率GMF等于齿数乘以齿轮转速频率&#xff1a; ★齿轮啮合频率两边有边频&#xff0c;间距为1X&#xff08;这是由冲击响应…...

官宣:百数低代码平台已顺利通过国家信息安全等级保护三级认证

在数字化转型浪潮中&#xff0c;企业对于数据安全与隐私保护的需求日益增强&#xff0c;尤其是在选择低代码开发平台时&#xff0c;信息安全等级保护&#xff08;简称“等保”&#xff09;成为众多客户关注的焦点。 近日&#xff0c;我们非常荣幸地向社会各界及广大用户宣布&a…...

Spring源码注解篇二:手写@Component注解

Component注解的功能 在Spring框架中&#xff0c;Component 注解是一个核心特性&#xff0c;用于自动检测类并将其注册为Spring应用上下文中的Bean。这大大简化了Bean的配置过程&#xff0c;使得开发者能够通过注解的方式快速地将类标记为组件&#xff0c;并由Spring容器进行管…...

云备份服务端

文件使用工具和json序列化反序列化工具 //文件和json工具类的设计实现 #ifndef __UTIL__ #define __UTIL__ #include<iostream> #include<fstream> #include<string> #include <vector> #include<sys/stat.h> #include"bundle.h" #inc…...

Jupyter Notebook 使用教程

Jupyter Notebook 使用教程 目录 概述启动Jupyter Notebook创建新的NotebookNotebook界面介绍使用代码单元格使用Markdown单元格Notebook的基本操作保存和导出Notebook扩展功能和技巧 1. 概述 Jupyter Notebook是一个开源的Web应用程序&#xff0c;允许您创建和共享包含代码…...

Leetcode 100361100367.切割蛋糕的最小总开销

Medium&#xff1a;动态规划搜索&#xff08;实际就是优化后的dfs&#xff09; class Solution { public: int f[25][25][25][25] {0};int dp(int row1, int col1, int row2, int col2, vector<int>& horizontalCut, vector<int>& verticalCut){if(row1 …...

单网口设备的IP地址识别-还原-自组网

1.如果知道该设备所在网段&#xff1a; 此时可以使用nmap工具&#xff0c;进行网段扫描&#xff1a; nmap -sn 192.168.0.0/24 256个地址的子网10秒就能扫描一轮。关掉设备&#xff0c;打开设备&#xff0c;diff&#xff0c;基本就可以定位所要找到目标设备的IP 2.如果不知道…...

太速科技-FMC207-基于FMC 两路QSFP+光纤收发子卡

FMC207-基于FMC 两路QSFP光纤收发子卡 一、板卡概述 本卡是一个FPGA夹层卡&#xff08;FMC&#xff09;模块&#xff0c;可提供高达2个QSFP / QSFP 模块接口&#xff0c;直接插入千兆位级收发器&#xff08;MGT&#xff09;的赛灵思FPGA。支持利用Spartan-6、Virtex-6、Kin…...

昇思25天学习打卡营第13天|munger85

文本解码原理–以MindNLP为例 重要的就是怎么样把数字最后转化成真正的文字。而且自回归模型它会一个字给一个字的预测&#xff0c;下一个字应该是什么&#xff1f; 如果这个模型下载很慢&#xff0c;你就可以通过这种方式从摩大社区进行下载。 这种方式&#xff0c; 每一次候…...