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

多路复用IO

一。进程处理多路IO请求

在没有多路复用IO之前,对于多路IO请求,一般只有阻塞与非阻塞IO两种方式

1.1 阻塞IO

需要结合多进程/多线程,每个进程/线程处理一路IO
在这里插入图片描述
缺点:客户端越多,需要创建的进程/线程越多,相对占用内存资源较多

1.2 非阻塞IO

单进程可以处理,但是需要不断检测客户端是否发出IO请求,需要不断占用cpu,消耗 cpu 资源
在这里插入图片描述

二.多路复用IO简介

  • 本质上就是通过复用一个进程来处理多个IO请求
  • 基本思想:由内核来监控多个文件描述符是否可以进行I/O操作,如果有就绪的文件描述符,将结果告知给用户进程,则用户进程在进行相应的I/O操作
    在这里插入图片描述
    类似于下图的老师检查学生作业
    在这里插入图片描述

三.多路复用I/O方案

目前在Linux系统有三种多路复用I/O的方案

  1. select方案
  2. poll方案
  3. epoll方案

四.select 方案

4.1 设计思想

  • 通过单进程创建一个文件描述符集合,将需要监控的文件描述符添加到这个集合中
  • 由内核负责监控文件描述符是否可以进行读写,一旦可以读写,则通知相应的进程进行相应的I/O操作
    在这里插入图片描述

4.2 实现方式

select多路复用I/O在实现时主要是以调用 select 函数来实现

select 函数
函数头文件
#include <sys/select.h>函数原型
int select(int nfds, fd_set *readfds,fd_set*writefds,fd_set *exceptfds,struct timeval *timeout);函数功能
监控一组文件描述符,阻塞当前进程,由内核检测相应的文件描述符是否就绪,一旦有文件描述符就绪,将就绪的文件描述符拷贝给进程,唤醒进程处理函数参数
nfds:最大文件描述符加1
readfds:读文件描述符集合的指针
writefds:写文件描述符集合的指针
exceptfds:其他文件描述符集合的指针
timeout:超时时间结构体变量的指针函数返回值
成功:返回已经就绪的文件描述符的个数。如果设置timeout,超时就会返回0
失败:-1,并设置errno

操作文件描述符集合函数

void FD_CLR(int fd,fd_set *set)
将fd从文件描述符集合中删除int FD_ISSET(int fd,fd_set *set)
判断fd是否在文件描述符集合中void FD_SET(int fd,fd_set *set)
将文件描述符添加到文件描述符集合中void FD_ZERO(fd_set *set)
将文件描述符集合清空参数描述:
fd:文件描述符
set:文件描述符集合的指针

在这里插入图片描述
示例代码:使用select函数监控有名管道,如果有输入,则打印相应的信息

write.cpp

using namespace std;
#include<iostream>
#include<string>
#include <cstring>
#include<vector>
#include<deque>
#include<cstdio>
#include<ctime>
#include<deque>
#include<cstdlib>
#include<pthread.h>
#include <unistd.h>
#include <sys/select.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#define PATH "/home/linuxfu/pipo"
int main(){int ret = access(PATH,F_OK);if(ret == -1){mkfifo(PATH,0644);}int fd = open(PATH,O_WRONLY);if(fd == -1){cout << "create failed" <<endl;exit(EXIT_FAILURE);} char buf[128] = { 0 };while(1){memset(buf,0,sizeof(buf));cout << "请输入你想要输入的字符串:";fgets(buf,sizeof(buf) - 1,stdin);buf[strlen(buf) - 1] = '\0';ssize_t wtypes = write(fd,buf,size(buf));if(wtypes == -1){cout << "write failed" << endl;close(fd);exit(EXIT_FAILURE); }}close(fd);return 0;      	
}

select.cpp

using namespace std;
#include<iostream>
#include<string>
#include<vector>
#include<deque>
#include<ctime>
#include<deque>
#include<cstdlib>
#include<pthread.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/select.h>
#define PATH "/home/linuxfu/pipo"
int main(){int ret = access(PATH,F_OK);if(ret == -1){mkfifo(PATH,0644);}int fd = open(PATH,O_RDONLY);if(fd == -1){cout << "create failed" <<endl;exit(EXIT_FAILURE);}fd_set rfds;FD_ZERO(&rfds);FD_SET(0,&rfds);FD_SET(fd,&rfds);struct timeval tv;tv.tv_sec = 5;tv.tv_usec = 0;fd_set temp_rfds;//备份struct timeval temp_tv;//备份while(1){temp_rfds = rfds;temp_tv = tv;int ret = select(fd + 1,&temp_rfds,NULL,NULL,&temp_tv);if(ret == -1){perror("ret");}else if(ret == 0){cout << "time out" << endl;}else{for(int i = 0;i < ret;i++){if(FD_ISSET(0,&temp_rfds)){string temp;cin >> temp;cout << temp << endl;}if(FD_ISSET(fd,&temp_rfds)){char buf[128] = { 0 };ssize_t rtype = read(fd,buf,size(buf));if(rtype > 0){printf("content:%s",buf);}}}}}}

答案图片:
请添加图片描述
注意点:

  1. struct timeval结构体
struct timeval {
__kernel_old_time_t tv_sec; /* seconds *///秒数
__kernel_suseconds_t tv_usec; /* microseconds *///毫秒数
};

2.select 函数理解
在这里插入图片描述
3.超时时间的说明

  • 如果timeout之后,文件描述符集合中没有任何就绪的文件描述符,select函数就会返回0
  • 超时之后,timeout会被select函数修改,表示超时时间已经使用完。如果想继续使用超时时间,需要备份之前的struct timeval
  • 超时之后,表示没有就绪的文件描述符,此时文件描述符集合被赋值为空
    在这里插入图片描述
    因此,需要将之前的文件描述符集合进行备份。

4.3多路复用IO-select底层原理分析

文件描述符集合

typedef struct
{
/* XPG4.2 requires this member name. Otherwise avoid the name
from the global namespace. */
#ifdef __USE_XOPEN
__fd_mask fds_bits[__FD_SETSIZE / __NFDBITS];
# define __FDS_BITS(set) ((set)->fds_bits)
#else
__fd_mask __fds_bits[__FD_SETSIZE / __NFDBITS];
# define __FDS_BITS(set) ((set)->__fds_bits)
#endif
} fd_set;

数组的类型为 long int 类型,在 64 位系统中 long int 的大小为 8 个字节

typedef long int __fd_mask;

数组的大小为16 __FD_SETSIZE / __NFDBITS

#define __FD_SETSIZE 1024
#define __NFDBITS (8 * (int) sizeof (__fd_mask))

文件描述符集合的数组最终在存储时,使用了位图的方式来记录相应的文件描述符,具体原理如下:

  • 数组中没有直接存储文件描述符,而是使用某一位来表示该文件描述符是否需要监控
  • 需要监控的文件描述符需要转成数组的某一个元素的某一位,然后将对应的位设置为1,例如当 fd = 60 的成员需要监控,则需要将数组的第0个成员的第 [60] bit 设置为1,当 fd = 64时,则需要将数组的第1个成员的第[0] bit 设置为1

在这里插入图片描述

从上面的文件描述符集合内存管理可以分析出,select 最终只能存储1024个文件描述符

select 底层原理分析

在这里插入图片描述

.select() 函数中一共需要使用三个文件描述符集合,分别是1.in : 读文件描述符集合,主要包含 需要进行读的文件描述符的集合,反映在底层实际可以从设备中读取数据2.out : 写文件描述符集合,主要包含 需要进行写的文件描述符的集合,反映在底层实际可以将数据写入到设备中3.exp : 其他文件描述符集合,主要包含其他类型的操作的文件描述符集合二.调用了select() 函数,内核做了如下事情:1.从用户空间将集合的文件描述符拷贝到内核空间2.循环遍历 fd_set 中所有的文件描述符,来检测是否有文件描述符可进行I/O操作(1)如果有文件描述符可进行I/O操作,则设置返回的文件描述符集对应位1(res_in,res_out,res_exp),跳出循环,直接返回。最终会赋值给 in,out,exp 文件描述符集合(2)如果没有文件描述符可进行I/O操作,则继续循环检测。如果设置 timeout ,则在超时后返回,此时select() 函数返回0.select() 函数减少了多进程/多线程的开销,但仍然有很多缺点:1.每次调用select()函数都需要将fd集合拷贝到内核空间,这个开销在fd很多时就越大2.每次都需要遍历所有的文件描述符集合,这个开销在fd很多时就越大3.支持的文件描述符只有1024

五.poll 方案

5.1 基本原理

多路复用poll的方式与select多路复用原理类似,但有很多地方不同,下面是具体的对比

  • 在应用层是以结构体struct pollfd数组的形式来进行管理文件描述符,在内核中基于链表对数组进行扩展;select方式以集合的形式管理文件描述符且最大支持1024个文件描述

  • 在这里插入图片描述

  • poll将请求与就绪事件通过结构体进行分开

  • select将请求与就绪文件描述符存储在同一个集合中,导致每次都需要进行重新赋值才能进行下一次的监控

  • 在内核中仍然使用的是轮询的方式,与 select 相同,当文件描述符越来越多时,则会影响效率

5.2 poll 方案

poll多路复用实现主要调用 poll 函数

函数头文件
#include <poll.h>函数原型
int poll(struct pollfd *fds, nfds_t nfds, int timeout);函数功能
监控多个文件描述符的变化函数参数
fds:sturct pollfd结构体指针
nfds:fds结构体的数量
timeout:超时时间,单位为ms函数返回值
成功:大于0,返回就绪的文件描述符数量;=0,超时返回,没有文件描述符就绪
失败:-1,并设置errno

参数相关说明

  1. struct pollfd 结构体说明
struct pollfd {
int fd; /* file descriptor */
short events; /* requested events */
short revents; /* returned events */
};
  1. nfds_t 类型定义
typedef unsigned long int nfds_t;

poll 事件说明

在这里插入图片描述
示例代码:使用poll函数监控有名管道,如果有输入,则获取标准输入的内容并打印

write.cpp

using namespace std;
#include<iostream>
#include<string>
#include <cstring>
#include<vector>
#include<deque>
#include<cstdio>
#include<ctime>
#include<deque>
#include<cstdlib>
#include<pthread.h>
#include <unistd.h>
#include <sys/select.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#define PATH "/home/linuxfu/pipo"
int main(){int ret = access(PATH,F_OK);if(ret == -1){mkfifo(PATH,0644);}int fd = open(PATH,O_WRONLY);if(fd == -1){cout << "create failed" <<endl;exit(EXIT_FAILURE);} char buf[128] = { 0 };while(1){memset(buf,0,sizeof(buf));cout << "请输入你想要输入的字符串:";fgets(buf,sizeof(buf) - 1,stdin);buf[strlen(buf) - 1] = '\0';ssize_t wtypes = write(fd,buf,size(buf));if(wtypes == -1){cout << "write failed" << endl;close(fd);exit(EXIT_FAILURE); }}close(fd);return 0;      	
}

poll.cpp

using namespace std;
#include<iostream>
#include<string>
#include <cstring>
#include<vector>
#include<deque>
#include<cstdio>
#include<ctime>
#include<deque>
#include<cstdlib>
#include<pthread.h>
#include <unistd.h>
#include <poll.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#define PATH "/home/linuxfu/pipo"
int main(){int ret = access(PATH,F_OK);if(ret == -1){mkfifo(PATH,0644);}int rfd = open(PATH,O_RDONLY);if(rfd == -1){cout << "create failed" <<endl;exit(EXIT_FAILURE);} struct pollfd fds[2] = { 0 };fds[0].fd = 0;fds[0].events = POLLIN;fds[1].fd = rfd;fds[1].events = POLLIN;while(1){int ret = poll(fds,2,5000);if(ret == -1){perror("ret");}else if(ret == 0){cout << "time out" << endl;}else{for(int i = 0;i < 2;i++){if(fds[0].revents == POLLIN){char buf[128] = { 0 };fgets(buf,sizeof(buf) - 1,stdin);buf[strlen(buf) - 1] = '\0';cout << "buf:"<< buf << endl;} if(fds[1].revents == POLLIN){char buf[128] = { 0 };ssize_t wtypes = read(rfd,buf,sizeof(buf));if(wtypes > 0){cout << "字符串" << buf << endl;}}}}}close(rfd);return 0;      	
}

请添加图片描述

6.epoll 方案

6.1epoll 基本原理

epoll相对于select与poll有较大的不同,主要是针对前面两种多路复用 IO 接口的不足
select/poll的不足:

  • select 方案使用数组存储文件描述符,最大支持1024个
  • select 每次调用都需要将文件描述符集合拷贝到内核中,非常消耗资源
  • poll 方案解决文件描述符存储数量限制问题,但其他问题没有得到解决
  • select / poll 底层使用轮询的方式检测文件描述符是否就绪,文件描述符越多,则效率越低

**epoll优点:

(1) epoll底层使用红黑树,没有文件描述符数量的限制,并且可以动态增加与删除节点,不用重复拷贝
(2)epoll底层使用callback机制,没有采用遍历所有描述符的方式,效率较高**

在这里插入图片描述

6.2 select/poll 方案图解

在这里插入图片描述

6.3 epoll 方案图解

在这里插入图片描述

6.4epoll 创建

1.epoll创建需要调用epoll_create函数,用于创建epoll实例

函数头文件
#include <sys/epoll.h>函数原型
int epoll_create(int size);函数描述
epoll_create() creates a new epoll(7) instance. Since Linux 2.6.8, the size argument is ignored, but must be greater than zero;函数功能
创建一个epoll实例,分配相关的数据结构空间函数参数
size:需要填一个大于0的数,从Linux 2.6.8开始,size参数被忽略函数返回值
成功:返回epoll文件描述符
失败:返回-1,并设置errno

示例代码:创建一个epoll 实例

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/epoll.h>
int main()
{
int epfd = epoll_create(1);
if(epfd == -1)
{
perror("epoll_create");
exit(EXIT_FAILURE);
}
printf("epfd=%d\n",epfd);
return 0;
}
  1. epoll 函数控制
    epoll控制函数主要用于文件描述符集合的管理,包括增加、修改、删除等操作。
    epoll_ctl函数详细信息如下
函数头文件
#include <sys/epoll.h>函数原型
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);函数参数
epfd:epoll 实例
op:epoll 操作命令字EPOLL_CTL_ADD:在epoll实例中添加新的文件描述符(相当于向红黑树中添加节点),并将事件与fd关联EPOLL_CTL_MOD:更改与目标文件描述符fd相关联的事件EPOLL_CTL_DEL:从epoll实例中删除目标文件描述符fd ,事件参数被忽略在系统中定义如下:
#define EPOLL_CTL_ADD 1 /* Add a file descriptor to the interface. */
#define EPOLL_CTL_DEL 2 /* Remove a file descriptor from the interface.
*/
#define EPOLL_CTL_MOD 3 /* Change file descriptor epoll_event structure.
*/
fd:操作的文件描述符
event:struct epoll_event结构体对象指针

struct epoll_event 结构体定义如下:

ypedef 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 */
};

events : epoll事件,事件具体定义如下:

enum EPOLL_EVENTS
{
EPOLLIN = 0x001, // 读事件有效(非常常用)
#define EPOLLIN EPOLLIN
EPOLLPRI = 0x002,
#define EPOLLPRI EPOLLPRI
EPOLLOUT = 0x004, // 写事件有效(非常常用)
#define EPOLLOUT EPOLLOUT
EPOLLRDNORM = 0x040,
#define EPOLLRDNORM EPOLLRDNORM
EPOLLRDBAND = 0x080,
#define EPOLLRDBAND EPOLLRDBAND
EPOLLWRNORM = 0x100,
#define EPOLLWRNORM EPOLLWRNORM
EPOLLWRBAND = 0x200,
#define EPOLLWRBAND EPOLLWRBAND
EPOLLMSG = 0x400,
#define EPOLLMSG EPOLLMSG
EPOLLERR = 0x008,
#define EPOLLERR EPOLLERR
EPOLLHUP = 0x010,
#define EPOLLHUP EPOLLHUP
EPOLLRDHUP = 0x2000,
#define EPOLLRDHUP EPOLLRDHUP
EPOLLEXCLUSIVE = 1u << 28,
#define EPOLLEXCLUSIVE EPOLLEXCLUSIVE
EPOLLWAKEUP = 1u << 29,
#define EPOLLWAKEUP EPOLLWAKEUP
EPOLLONESHOT = 1u << 30,
#define EPOLLONESHOT EPOLLONESHOT
EPOLLET = 1u << 31 // 将EPOLL设为边缘触发(Edge Triggered)模式
#define EPOLLET EPOLLET
};

epoll_data是一个共用体,主要使用 fd 成员用于存储文件描述符

epoll 等待事件发生(关联的文件描述符就绪),这里调epoll_wait 函数
epoll_wait 函数详细信息如下:

函数头文件
#include <sys/epoll.h>函数原型
int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);函数功能
等待文件描述符关联的事件发生函数参数
epfd:epoll实例对象
events:存储就绪集合的数组的地址
maxevents:就绪集合的最大值
timeout:超时时间函数返回值
成功:返回就绪的文件描述符数量,超时返回0
失败:返回-1,并设置errno

6.5示例代码:

将有名管道描述符添加到epoll实例中,等待用户输入数据,如果没有则打印 timeout,否则获取用户输入并输出。

write.cpp

using namespace std;
#include<iostream>
#include<string>
#include <cstring>
#include<vector>
#include<deque>
#include<cstdio>
#include<ctime>
#include<deque>
#include<cstdlib>
#include<pthread.h>
#include <unistd.h>
#include <sys/select.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#define PATH "/home/linuxfu/pipo"
int main(){int ret = access(PATH,F_OK);if(ret == -1){mkfifo(PATH,0644);}int fd = open(PATH,O_WRONLY);if(fd == -1){cout << "create failed" <<endl;exit(EXIT_FAILURE);} char buf[128] = { 0 };while(1){memset(buf,0,sizeof(buf));cout << "请输入你想要输入的字符串:";fgets(buf,sizeof(buf) - 1,stdin);buf[strlen(buf) - 1] = '\0';ssize_t wtypes = write(fd,buf,size(buf));if(wtypes == -1){cout << "write failed" << endl;close(fd);exit(EXIT_FAILURE); }}close(fd);return 0;      	
}

epoll.cpp

using namespace std;
#include<iostream>
#include<string>
#include <cstring>
#include<vector>
#include<deque>
#include<cstdio>
#include<ctime>
#include<deque>
#include<cstdlib>
#include<pthread.h>
#include <unistd.h>
#include <sys/epoll.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#define PATH "/home/linuxfu/pipo"
int main(){int ret = access(PATH,F_OK);if(ret == -1){mkfifo(PATH,0644);}int rfd = open(PATH,O_RDONLY);if(rfd == -1){cout << "create failed" <<endl;exit(EXIT_FAILURE);} int epfd = epoll_create(1);if( epfd == -1){perror("epfd");exit(EXIT_FAILURE);}//将标准输入文件描述符添加到epoll中struct epoll_event ev;ev.events = EPOLLIN;ev.data.fd = rfd;int ret1 = epoll_ctl(epfd,EPOLL_CTL_ADD,rfd,&ev);if(ret1 == -1){perror("ret1");exit(EXIT_FAILURE);}struct epoll_event ev1;ev1.events = EPOLLIN;ev1.data.fd = 0;int ret2 = epoll_ctl(epfd,EPOLL_CTL_ADD,0,&ev1);if(ret2 == -1){perror("ret2");exit(EXIT_FAILURE);}struct epoll_event events[2];while(1){int res = epoll_wait(epfd,events,2,4000);if(res == -1){perror("res");}else if(res == 0){printf("timeout\n");}else if(res > 0){for(int i = 0;i < res;i++){if(events[i].data.fd == 0){char buf[128] = { 0 };fgets(buf,sizeof(buf)-1,stdin);buf[strlen(buf) - 1] = '\0';cout << "buf:" << buf << endl;}if(events[i].data.fd == rfd){char buf[128] = { 0 };ssize_t wtypes = read(rfd,buf,sizeof(buf));if(wtypes > 0){cout << "str:" << buf << endl;}}}}}close(rfd);return 0;      	
}

请添加图片描述

6.6 epoll 底层结构

在这里插入图片描述

相关文章:

多路复用IO

一。进程处理多路IO请求 在没有多路复用IO之前&#xff0c;对于多路IO请求&#xff0c;一般只有阻塞与非阻塞IO两种方式 1.1 阻塞IO 需要结合多进程/多线程&#xff0c;每个进程/线程处理一路IO 缺点&#xff1a;客户端越多&#xff0c;需要创建的进程/线程越多&#xff0c…...

C++ prime plus-7-編程練習

1&#xff0c; #include <iostream>// 函数声明 double harmonicMean(double x, double y);int main() {double x, y, result;while (true) {std::cout << "请输入两个数&#xff08;其中一个为0时结束&#xff09;: ";std::cin >> x >> y;…...

计算1 / 1 - 1 / 2 + 1 / 3 - 1 / 4 + 1 / 5 …… + 1 / 99 - 1 / 100 的值,打印出结果

我们写这道题的时候需要俩变量接受&#xff0c;一个总数一个分母&#xff0c;我们发现分母变化是有规律的从1~100循环。 #include<stdio.h> int main() {int i 0;int tag 1;double sum 0.0;for (i 1; i < 101; i){if (i % 2 0){sum sum - 1.0 / i;}else{sum s…...

Linux本地服务器搭建开源监控服务Uptime Kuma与远程监控实战教程

文章目录 前言**主要功能**一、前期准备本教程环境为&#xff1a;Centos7&#xff0c;可以跑Docker的系统都可以使用本教程安装。本教程使用Docker部署服务&#xff0c;如何安装Docker详见&#xff1a; 二、Docker部署Uptime Kuma三、实现公网查看网站监控四、使用固定公网地址…...

JS 历史简介

目录 1. JS 历史简介 2. JS 技术特征 1. JS 历史简介 举例&#xff1a;在提交用户的注册信息的时候&#xff0c;为避免注册出现错误后重新填写信息&#xff0c;可以在写完一栏信息后进行校验&#xff0c;并提示是否出现错误&#xff0c;这样会大大提高用户提交的成功率&…...

爬虫逆向学习(七):补环境动态生成某数四代后缀MmEwMD

声明&#xff1a;本篇文章内容是整理并分享在学习网上各位大佬的优秀知识后的实战与踩坑记录 前言 这篇文章主要是研究如何动态生成后缀参数MmEwMD的&#xff0c;它是在文章爬虫逆向学习(六)&#xff1a;补环境过某数四代的基础上进行研究的&#xff0c;代码也是在它基础上增…...

光伏电站并网验收需要注意什么细节

一、设备质量及安装验收 光伏组件&#xff1a;检查光伏组件的外观是否完好无损&#xff0c;无明显的缺陷和破损&#xff0c;表面是否清洁无污染。同时&#xff0c;需要验证光伏组件的型号、参数是否与设备台账资料一致。 逆变器&#xff1a;确认逆变器具备防雷、防尘、防潮等…...

页面禁用鼠标右键属于反爬虫措施吗 ?

是的&#xff0c;禁用鼠标右键通常被视为一种反爬虫&#xff08;anti-scraping&#xff09;措施。网站开发者常常采用这种技术来防止用户通过右键菜单复制文本、图像或其他内容&#xff0c;特别是在内容保护和数据安全方面。以下是禁用鼠标右键的一些背景和目的&#xff1a; 1…...

视频理解大模型最新进展

文章目录 Video-LLaMAVision-Language BranchAudio-Language Branch Video-ChatGPTMiniGPT4-videoCogVLM2-Video&#xff08;1&#xff09;Pre-training&#xff08;2&#xff09;Post-training Qwen2-VLMA-LMMChat-UniVi大模型对比 Video-LLaMA 2023&#xff1a;阿里达摩院的…...

cocos creator 使用 protobuf 的步骤与注意事项

移除可能曾安装过的protobuf // 移除全局 npm remove -g protobufjs npm remove -g protobufjs-cli npm remove -g pbjs // 移除项目中的 npm remove --save protobufjs npm remove --save protobufjs-cli npm remove --save pbjs全局安装 npm i -g protobufjs //或者 cnpm …...

mac访达查找文件目录

mac访达查找文件目录 在Mac上使用访达&#xff08;Finder&#xff09;查找文件或目录的方法如下&#xff1a; 打开访达。 在访达窗口的侧边栏中&#xff0c;选择“ Go to Folder”&#xff08;转到文件夹&#xff09;选项&#xff0c;或者使用快捷键ShiftCommandG打开一个对…...

【数据结构】点分治 点分树

求树上长度小于等于k的路径 #include <iostream> #include <cstring> #include <algorithm>using namespace std;const int N 10010, M N * 2;int n, m; int h[N], e[M], w[M], ne[M], idx; //邻接表 bool st[N]; //记录每个点是否被删掉 int p[N]; //存储…...

K8s Calico替换为Cilium,以及安装Cilium过程(鲁莽版)

迁移CNI插件的3种办法&#xff1a; 1、创建一个新的集群&#xff0c;通过Gitops的方式迁移负载&#xff0c;然而&#xff0c;这可能涉及大量的准备工作和潜在的中断。 2、另一种方法是重新配置/etc/cni/net.d/指向Cilium。但是&#xff0c;现有的pod仍将由旧的…...

背景图鼠标放上去切换图片过渡效果

文章目录 css鼠标放上去之前效果鼠标放上去时效果 css <li class"message"></li>.message {width: 22px;height: 22px;background-image: url(/assets/message-01.png);background-size: cover;background-position: center;transition: background-ima…...

【Linux】当前进展

驱动层日志添加了下文件目录&#xff0c;函数&#xff0c;代码行的打印&#xff08;这里要小心&#xff0c;驱动目录源代码打印日志里边添进程号可能有问题&#xff0c;因为在驱动初始化的时候&#xff0c;内核还没有创建进程&#xff0c;不过猜测可以先不打印进程相关信息&…...

阿里云云效多个ssh密钥对配置

实现功能 windows本地多个ssh密钥对,分别对应不同的阿里云账号的云效 实现办法 1.生成ssh密钥对 ssh-keygen -t rsa -f C:\xxx\id_rsa_customname(我这里C:\Users\admin\.ssh\id_rsa_customname) 2.配置.ssh目录的config文件 # ruiyi Host customnameHostName codeup.al…...

前后端跨域问题及其在ThinkPHP中的解决方案

在现代Web开发中&#xff0c;前后端分离的架构越来越普遍&#xff0c;但这也带来了跨域问题。跨域指的是在一个域下的网页试图请求另一个域的资源&#xff0c;浏览器出于安全考虑会限制这种行为。本文将探讨如何在ThinkPHP中解决跨域问题。 #### 1. 什么是跨域&#xff1f; 跨…...

基于CentOS7上安装MicroK8s(最小生产的 Kubernetes)

简介 MicroK8s是一个轻量级的Kubernetes发行版,其内存和存储要求远低于全尺寸Kubernetes集群。它可以在几分钟内通过一条命令快速创建功能齐全的Kubernetes集群,极大地简化了部署过程。 兼容性:MicroK8s与Kubernetes全面兼容,确保用户可以无缝迁移和扩展他们的应用程序。 …...

从《GTA5》的反外挂斗争看网络安全的重要性

摘要&#xff1a; 在网络游戏的世界里&#xff0c;外挂&#xff08;作弊软件&#xff09;一直是破坏游戏公平性和玩家体验的一大难题。作为一款深受全球玩家喜爱的游戏&#xff0c;《GTA5》&#xff08;Grand Theft Auto V&#xff09;在线模式也不例外地遭遇了外挂问题。本文将…...

python如何将字符转换为数字

python中的字符数字之间的转换函数 int(x [,base ]) 将x转换为一个整数 long(x [,base ]) 将x转换为一个长整数 float(x ) 将x转换到一个浮点数 complex(real [,imag ]) 创建一个复数 str(x ) 将对象 x 转换为字…...

TikTok流量不佳:是网络环境选择不当还是其他原因?

TikTok&#xff0c;作为全球短视频社交平台的佼佼者&#xff0c;每天都有海量的内容被上传和分享。然而&#xff0c;很多用户和内容创作者发现&#xff0c;他们的TikTok视频流量并不理想。这引发了一个问题&#xff1a;TikTok流量不佳&#xff0c;是因为网络环境选择不当&#…...

QT菜单栏设计(二级菜单栏)

目的&#xff1a;创建一级菜单栏&#xff0c;添加对应选项&#xff0c;并向一级菜单栏中添加二级菜单栏选项。 #include "mainwindow.h" #include "ui_mainwindow.h"MainWindow::MainWindow(QWidget *parent): QMainWindow(parent), ui(new Ui::MainWindo…...

网站建设中,常用的后台技术有哪些,他们分别擅长做什么网站平台

PHP、Python、JavaScript、Ruby、Java和.NET各自适用于不同类型的网站平台。以下是对这些编程语言适用场景的具体介绍&#xff1a; PHP Web开发&#xff1a;PHP是一种广泛使用的开源服务器端脚本语言&#xff0c;特别适合Web开发。全球有超过80%的网站使用PHP作为服务器端编程语…...

【线程】POSIX信号量---基于环形队列的生产消费者模型

信号量概念 这篇文章是以前写的&#xff0c;里面讲了 System V的信号量的概念&#xff0c;POSIX信号量和SystemV信号量作用相同&#xff0c;都是用于同步操作&#xff0c;达到无冲突的访问共享资源目的。 但POSIX可以用于线程间同步。 信号量的概念 POSIX信号量的接口 初始化…...

Excel 设置自动换行

背景 版本&#xff1a;office 专业版 11.0 表格内输入长信息&#xff0c;发现默认状态时未自动换行的&#xff0c;找了很久设置按钮&#xff0c;遂总结成经验帖。 操作 1&#xff09;选中需设置的单元格/区域/行/列。 2&#xff09;点击【开始】下【对齐方式】中的【自动换…...

UNI-SOP使用说明

UNI-SOP提供了两个集成客户端&#xff1a;SpringBoot2.x/JAVA1.8和SpringBoot3.x/JAVA17&#xff0c;满足不同项目的集成需求。 平台接入 使用UNI-SOP之前&#xff0c;业务平台需要进行接入&#xff0c;完成校验后才能正常使用&#xff0c;先引入客户端开发SKD包。 <depen…...

记录-java web 生成并下载zip文件

java生成zip文件&#xff0c;zip文件分两种&#xff1a;一种是包含文件夹、一种是不包含文件夹 生成zip文件的方式 ZipOutputStream zipOutputStream new ZipOutputStream(response.getOutputStream());// 文件夹名称String folder "download/";ZipEntry ze new Z…...

大数据集群部署文档

大数据集群部署文档 注意&#xff1a;需配合大数据集群启动&检查文档进行部署&#xff0c;以便可以检验每一个组件是否部署成功。 文章目录 大数据集群部署文档一、部署前准备1. 确保所有机器可以访问外网2. 配置root用户ssh连接3. 解决Vmware ESXi 6.5 Ubuntu虚拟机ssh连…...

HTML中的表单(超详细)

一、表单 1.语法 <!-- action&#xff1a;提交的地方 method&#xff1a;提交的方式&#xff08;get会显示&#xff0c;post不会&#xff09; --> <form action"#" method"get"><p>名字&#xff1a;<input name"name" ty…...

初识 C 语言(一)

目录 一、 第一个 C 程序1. printf() 函数和 stdio.h 头文件2. main() 函数和 return 语句 二、类型和变量1. C 语言中的基本类型2. 变量的创建和命名规则3. 类型和变量的大小 三、printf() 函数和 scanf() 函数1. printf() 函数的使用2. 各种类型的输出格式3. scanf() 函数的使…...