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

【Linux】进程间通信(1):进程通信概念与匿名管道

人与人之间是如何通信的?举个简单的例子,假如我是月老,我要为素不相识的但又渴望爱情的男女两方牵红线。我需要收集男方的信息告诉女方,收集女方的信息告诉男方,然后由男女双方来决定是否继续。对于他们而言,通信由一方发起,另一方判断是否进行接收,我是他们之间进行通信的媒介,是否继续进行通信的决定权在男女双方手中。男女两方通过月老进行传话、从月老获取信息的方式实现了男女双方间的间接通信。

在上述场景中,月老独立于男女方之间,但又能为男女方之间建立联系。而进程拥有独立的地址空间,进程之间的交流也需要依赖于类似于“月老”的“公共资源”。不同进程间通过对公共资源的读写操作,就可以间接实现进程间的通信。

一、进程间通信方式概述

        进程间通信就是在不同的进程间传播或交换信息,但由于进程的用户空间是相互独立的,他们之间是不能直接进行互相访问的。在这种情况下,我们想让不同进程间进行通信,就必须为这些进程之间提供一个“公共区域”,而这块公共区域不能单独属于某一个进程的地址空间,必须独立于各个进程间,且允许被进行通信的进程进行访问。显然,广义上系统(内核)空间、磁盘文件、数据库等都可以作为进程间交流的媒介。

        但通常,我们将使用内核空间进行进程间交流的方式称为“进程间通信”。

        在Linux系统中,进程间的通信方式有管道、消息队列、信号、信号量、共享内存、套接字等方式。而管道通信方式又分为“匿名管道”和“命名管道”两种方式。

常见的进程间通信机制

  • 管道(Pipes):允许数据在两个进程之间流动。包括匿名管道和命名管道(FIFO)。
  • 消息队列(Message Queues):允许进程以消息的形式交换信息。
  • 共享内存(Shared Memory):多个进程可以访问同一块内存区域,以实现高效的数据交换。
  • 信号量(Semaphores):用于实现进程间的同步和互斥控制。
  • 信号(Signals):用于通知进程某个事件的发生。
  • 套接字(Sockets):用于不同主机或同一主机上不同进程之间的通信,尤其适用于网络通信。

二、进程间通信的目的

  • 数据传输:在不同进程之间传递数据或状态信息。
  • 协调与同步:协调进程间的操作,避免冲突,确保一致性。
  • 资源共享:允许多个进程共享系统资源,如文件或内存。
  • 任务分配:将任务分配给多个进程,提高处理效率和负载均衡。
  • 消息传递:传递控制信息、指令或状态更新等。

三、管道的特点

        

1、匿名管道只能用于父子进程、兄弟进程等具有“亲缘关系”的进程之间。

2、管道是半双工的,即数据只能往一个方向流动;如果需要进行进程间的双方通信,则需要创建两个管道。

3、管道单独构成一种独立的文件系统。管道对于两端的进程而言,就是一个文件,但他不是普通的文件,并不属于某种文件系统,而是自立门户,单独构成一种文件系统,并且只存在于内存中

4、一个进程向管道中写入的内容被管道的另一端的进程读出。写入的内容每次被添加在管道缓冲区的末尾,并且每次都是从缓冲区的头部读出数据。

5、管道的写入操作是非原子的,这意味着如果尝试一次性写入大量数据,当数据量超过管道的最大容量时(一般是4096字节,可以自行测试),操作系统可能不会将所有数据一次性放入管道。相反,它可能会将数据分成几个部分,并且接收端可能需要多次读取操作才能接收完整的消息。类似的,当管道中还有未被读取的数据时,当管道中的剩余空间不足以一次性将数据全部写入时,写端会先将剩余空间写满,然后写端进程会阻塞在写端。当读端进程对管道数据进行读入时,管道一出现剩余空间,写端进程就会继续写入。

管道中的数据就像流动的水,而管道就像一个水管。我们将写端视为自来水厂,读端视为水龙头。当水龙头打开时,水流会源源不断地从水厂经过管道流到水龙头处,工厂提供多少水,就用多少水。而当水龙头关闭时,水厂的水就会阻塞在管道前,等待下一次供水。而当水厂倒闭后,再次打开水龙头时,流出的水量只是当初残留在管道中的部分(也可能管道中已经没有残留的水了),当使用完管道中的水后,水龙头也没有存在的必要了,因为与水龙头连接的供应端已经无法供应水流,这时我们只能关闭水龙头了。

在上述例子中,写端就是自来水厂,管道就是水管,读端就是水龙头。当管道中有数据时,我们就可以对其进行读取操作。数据在管道中也可以看作是“流动的”,当读端读取数据后,所读取到的数据在管道中也就“不复存在了”,此时管道中会出现“剩余空间”,只要还有剩余空间,写端就可以往管道中进行写入。

假设你有一个管道,缓冲区大小是 4KB,如果你试图写入 10KB 的数据,操作系统会将数据分成多个块进行处理:

  1. 第一次写入 4KB 数据,管道的缓冲区会填满。
  2. 如果缓冲区满了,写操作会阻塞,直到有足够的空间。
  3. 等到读取端消费了一部分数据,管道中会有新的空间,写操作才会继续。

这也是为什么Linux不保证管道写入数据的原子性——数据是“流动的”。

(写入的原子性:只有写完数据和不写入数据两种结果,不存在只写一部份的情况。由此可见,管道的写入不符合原子性。)

四、 匿名管道的创建

在Linux系统中,pipe函数用于创建一个匿名管道,并且当这个函数成功执行后,管道的读端和写端文件描述符是默认被打开的。这些文件描述符可以用于在进程之间传递数据,实现进程间的通信。匿名管道只能作用于有亲缘关系的进程之间的通信,比如父进程和子进程。

                        

函数原型:

int pipe(int pipefd[2]);

参数:

  • pipefd[2] 是一个整型数组,输出型参数,用于返回管道的读端和写端的文件描述符
  • pipefd[0] 是管道的读端文件描述符,pipefd[1]  是管道的写端文件描述符。

返回值:

  • 函数执行成功返回0;
  • 若失败则返回 -1,并将错误原因存于errno中。

当父进程调用pipe函数创建匿名管道文件时,操作系统会在调用该函数的进程空间中为其分配两个新的文件描述符,一个用于管道的读端,另一个用于写端。如果之前没有打开其他文件或管道,文件描述符 0(标准输入)、1(标准输出)和 2(标准错误)通常会被保留,而管道的文件描述符会被分配为 3、4 或更高的数字。

        

五、父子进程使用匿名管道进行通信的简单示例

当使用fork()创建子进程后,子进程会继承父进程的资源,这其中就包括父进程中为已经打开的文件分配的文件描述符。所以子进程的文件描述符表中也会存在父进程所创建的匿名管道文件的读端和写端文件描述符。因此匿名管道只能作用于有亲缘关系的进程之间的通信的实质是具有亲缘关系 的进程中拥有相同的文件描述符表。

当我们确定好数据的流向后,应关闭不再需要的文件描述符,原因如下:1、长时间运行的程序如果不断打开新的文件描述符而不关闭旧的,最终可能会耗尽系统资源。2、管道是单向的,数据从写端流向读端。如果一个进程同时拥有读端和写端的文件描述符,那么它就可以既读取又写入数据,这可能会导致不可预测的行为和数据混乱。通过在一个进程中关闭读端,在另一个进程中关闭写端,可以明确数据流的方向,并确保每个进程只负责管道的一端。

例如,当确定父进程进行写入、子进程进行读取时,我们需要父进程的读端和子进程的写端,确保数据是从父进程单向流入子进程的。

自此,我们通过write()函数对父进程中的写端进行写入、使用read()函数对子进程中的读端进行读取,自此就实现了父子进程间的通信。

下面是一个父进程通过管道向子进程传递“hello world!\n"字符串并打印至屏幕上的例子:

#include <unistd.h>
#include <errno.h>
#include <iostream>
#include <cstring>
#include <sys/types.h>
#include <sys/wait.h>#define BUFFER_SIZE 1024 //定义缓冲区大小int main()
{int pipefd[2];if (pipe(pipefd) == -1){std::cerr << "Pipe create failed : " << strerror(errno) << std::endl;exit(-1);}pid_t id = fork();if (id == 0){close(pipefd[1]);//子进程先关闭写端char buffer[BUFFER_SIZE];int number = 0;if ((number = read(pipefd[0], buffer, sizeof(buffer) - 1)) == -1)//读入size-1个字节的数据,为'\0'字符留一个空间{std::cerr << "Read failed : " << strerror(errno) << std::endl;exit(-1);}buffer[number] = '\0';//字符数组末尾设置为'\0'字符std::cout << "子进程输出:" << buffer << std::endl;close(pipefd[0]);//子进程退出前关闭读端}else if (id > 0){close(pipefd[0]);//父进程关闭读端char str[] = "hello world!";if (write(pipefd[1], str, sizeof(str)) == -1)//向管道中写入字符串{std::cerr << "Write failed : " << strerror(errno) << std::endl;exit(-1);}close(pipefd[1]);//父进程关闭写端int status = 0;if (waitpid(id, &status, 0) == -1)//等待子进程退出{std::cerr << "Wait failed : " << strerror(errno) << std::endl;}else{if (WIFEXITED(status))//如果是正常退出{std::cout << "退出状态:" << WEXITSTATUS(status) << std::endl;} else if (WIFSIGNALED(status)) //如果是收到信号{std::cout << "终止信号:" << WTERMSIG(status) << std::endl;}}}else{std::cerr << "fork failed!" << strerror(errno) << std::endl;exit(-1);}return 0;
}

六、 匿名管道的规则与特征

1、如果管道的写端没有关闭,但进程A未往管道中写入数据,此时管道中没有数据。在进程B的读端未关闭的情况下,读端进程将阻塞在read()处,直到管道中写入数据,read()函数成功读取数据之后继续执行后续代码。

2、如果写端关闭了,如果管道中仍有未被读取的数据,则读端继续读,直到读完管道中的数据时,read()函数返回0,表示读到了文件的末尾。如果写端关闭且管道中没有数据,则read()函数直接返回0,意味着读到了文件的末尾。此时我们应在代码中进行判断,当read()函数返回值为0时主动关闭读端,退出程序。

3、如果管道的读端没有关闭,但读端不读入数据,写端会一直向管道中写入数据,直到管道被写满。此时写进程会阻塞在写端,直到读端进程对管道中的数据进行读入。

4、如果读端关闭了,写端还在写,那么操作系统将会给写端进程发送SIGPIPE信号,默认终止写端进程。

对于如上规则,对“模块五”中的代码稍作修改即可验证,此处不再进行代码验证。由于规则4需要对管道流向进行改变,所以此处仅验证规则4:

由于我们需要判断当读端关闭时写端进程是否会收到SIGPIPE信号,所以我们让父进程作为读端进程,子进程作为写端进程,使用waitpid()函数获取子进程的退出信号。

#include <unistd.h>
#include <errno.h>
#include <iostream>
#include <cstring>
#include <sys/types.h>
#include <sys/wait.h>#define BUFFER_SIZE 1024 // 定义缓冲区大小int main()
{int pipefd[2];if (pipe(pipefd) == -1){std::cerr << "Pipe create failed : " << strerror(errno) << std::endl;exit(-1);}pid_t id = fork();if (id == 0){close(pipefd[0]); // 父进程关闭读端char str[] = "hello world!";while (true){if (write(pipefd[1], str, sizeof(str)) == -1) // 向管道中写入字符串{std::cerr << "Write failed : " << strerror(errno) << std::endl;exit(-1);}}close(pipefd[1]); // 父进程关闭写端}else if (id > 0){close(pipefd[1]); // 子进程先关闭写端char buffer[BUFFER_SIZE];int r_number = 0;while (true){if ((r_number = read(pipefd[0], buffer, sizeof(buffer) - 1)) == -1) // 读入size-1个字节的数据,为'\0'字符留一个空间{std::cerr << "Read failed : " << strerror(errno) << std::endl;break;}if (r_number == 0){std::cout << "写端关闭,读到了文件末尾,读端进程退出" << std::endl;break;}else{buffer[r_number] = '\0'; // 字符数组末尾设置为'\0'字符std::cout << "子进程输出:" << buffer << std::endl;}close(pipefd[0]); // 子进程退出前关闭读端}int status = 0;if (waitpid(id, &status, 0) == -1) // 等待子进程退出{std::cerr << "Wait failed : " << strerror(errno) << std::endl;}else{if (WIFEXITED(status)) // 如果是正常退出{std::cout << "退出状态:" << WEXITSTATUS(status) << std::endl;}else if (WIFSIGNALED(status)) // 如果是收到信号{std::cout << "终止信号:" << WTERMSIG(status) << std::endl;}}}else{std::cerr << "fork failed!" << strerror(errno) << std::endl;exit(-1);}return 0;
}

可以观察到,当读端关闭后,read()函数读取失败,返回-1。此时写进程收到13号信号,由于写进程中未对信号进行捕捉处理,所以写端进程默认进行退出。

七、匿名管道的应用实例 

实例一:用于shell

管道可用于输入输出重定向,他将一个命令的输出直接定向到另一个命令的输入。例如,当在命令行中输入“who | wc -l”后,shell程序将创建who 和 wc两个进程,以及这两个进程间的管道:

实例二: 父进程向子进程派发任务

场景:程序中存在一系列任务,父进程通过对子进程发送任务编号/任务名称等方式,让子进程去实现指定的任务。

#include <unistd.h>
#include <errno.h>
#include <iostream>
#include <cstring>
#include <sys/types.h>
#include <sys/wait.h>
#include <functional>
#include <vector>#define BUFFER_SIZE 1024 // 定义缓冲区大小
#define TASK_NUM 2 // 定义任务数量using func_t = std::function<int(int, int)>;
std::vector<func_t> func(TASK_NUM);void SetTask()
{func[0] = [](int a, int b){return a + b;};func[1] = [](int a, int b){return a - b;};
}int HandleTask(int index)
{if(index < 0 || index > 1){std::cout << "任务列表中暂无此任务" << std::endl;return 0;}std::cout << "请输入两个操作数:";int a = 0, b = 0;std::cin >> a >> b;int ret = func[index](a, b);std::cout << "结果为:" << ret << std::endl;return 1;
}int main(int argc, char *argv[])
{SetTask();//设置任务int pipefd[2];if (pipe(pipefd) == -1){std::cerr << "Pipe create failed : " << strerror(errno) << std::endl;exit(-1);}pid_t id = fork();if (id == 0){close(pipefd[1]); // 子进程先关闭写端while(true){char buffer[2];int number = 0;if ((number = read(pipefd[0], buffer, sizeof(buffer) - 1)) == -1) // 读入size-1个字节的数据,为'\0'字符留一个空间{std::cerr << "Read failed : " << strerror(errno) << std::endl;break;}else if (number == 0){std::cout << "子进程退出" << std::endl;break;}else {buffer[number] = '\0'; // 字符数组末尾设置为'\0'字符int ans = atoi(buffer);if(!HandleTask(ans)){std::cout << "子进程调用任务失败" << std::endl;break;}}  }close(pipefd[0]); // 子进程退出前关闭读端}else if (id > 0){close(pipefd[0]); // 父进程关闭读端for(int i = 1; i < argc; i++){if (write(pipefd[1], argv[i], strlen(argv[i])) == -1) // 向管道中写入字符串{std::cerr << "Write failed : " << strerror(errno) << std::endl;exit(-1);}}close(pipefd[1]); // 父进程关闭写端int status = 0;if (waitpid(id, &status, 0) == -1) // 等待子进程退出{std::cerr << "Wait failed : " << strerror(errno) << std::endl;}else{if (WIFEXITED(status)) // 如果是正常退出{std::cout << "退出状态:" << WEXITSTATUS(status) << std::endl;}else if (WIFSIGNALED(status)) // 如果是收到信号{std::cout << "终止信号:" << WTERMSIG(status) << std::endl;}}}else{std::cerr << "fork failed!" << strerror(errno) << std::endl;exit(-1);}return 0;
}

【下一节:基于匿名管道通信实现的进程池】

相关文章:

【Linux】进程间通信(1):进程通信概念与匿名管道

人与人之间是如何通信的&#xff1f;举个简单的例子&#xff0c;假如我是月老&#xff0c;我要为素不相识的但又渴望爱情的男女两方牵红线。我需要收集男方的信息告诉女方&#xff0c;收集女方的信息告诉男方&#xff0c;然后由男女双方来决定是否继续。对于他们而言&#xff0…...

Spring从入门到精通 01

文章目录 1. 依赖注入 (Dependency Injection, DI)2. 面向切面编程 (Aspect-Oriented Programming, AOP)3. 事务管理4. 简化 JDBC 开发5. 集成各种框架和技术6. 模块化和扩展性&#xff1a;主要的 Spring 模块&#xff1a;Core Container&#xff1a;AOP 模块&#xff1a;Data …...

C语言经典习题25

冒泡排序 对一维数组进行升序排序&#xff0c;然后在数组中输入20个数&#xff0c;将排序后的结果打印输出。 #include<stdio.h> #define N 20 int main() {int a[N];int i;for(i0;i<N;i) //初始化数组的数 {scanf("%d",&a);}for(i0;…...

2-47 基于matlab的时域有限差分法(FDTD法)拉夫等效原理进行时谐场外推

基于matlab的时域有限差分法(FDTD法)拉夫等效原理进行时谐场外推。外推边界距离吸收边界的距离、电磁场循环、傅立叶变换提起幅值和相位、各远区剖分点电场、方向系数计算等操作&#xff0c;得出可视化结果。程序已调通&#xff0c;可直接运行。 2-47 时域有限差分法(FDTD法) 拉…...

JupyterNotebook快捷键 自用

COMMAND MODE —————————————————————————————— Up Down cells的上下选择 A B 在上/下方插入cell C V X 复制/粘贴/剪切cell 双击D 删除所选cell Z 恢复被删除的cell 双击I Interrupt中断内核 Shift Enter 运行cell并选择下方 EDIT MODE ———…...

【我的OpenGL学习进阶之旅】讲一讲GL_TEXTURE_2D和GL_TEXTURE_EXTERNAL_OES的区别

在使用OpenGL ES进行图形图像开发时,我们常使用GL_TEXTURE_2D纹理类型,它提供了对标准2D图像的处理能力。这种纹理类型适用于大多数场景,可以用于展示静态贴图、渲染2D图形和进行图像处理等操作。 另外,有时我们需要从Camera或外部视频源读取数据帧并进行处理。这时,我们…...

Makefile 如何将生成的 .o 文件放到指定文件夹

研究了不少文章&#xff0c;我行通了一个&#xff0c;但是也不全&#xff0c;目前只能适用当前文件夹&#xff0c;如果源文件有子文件夹处理不了&#xff0c;还得继续研究。很多人说编译完把O文件移动走或者直接删掉。我想说的是不符合我的要求&#xff0c;移走或者删除O文件&a…...

聊一聊知识图谱结合RAG

因为最近在做一些关于提高公司内部使用的聊天机器人的回答准确率&#xff0c;并且最近微软官方也是开源了一下graphrag的源码&#xff0c;所以想聊一聊这个知识图谱结合rag。 rag在利用私有数据增强大模型回答的领域是一种比较典型的技术&#xff0c;也就是我们提出问题的时候&…...

Java面试锦集 之 一、Java基础(1)

一、Java基础&#xff08;1&#xff09; 1.final 关键字的作用&#xff1f; 修饰变量&#xff1a; 一旦被赋值&#xff0c;就不能再被修改&#xff0c;保证了变量值的稳定性。 例&#xff1a; final int NUMBER 10; //之后就不能再改变 NUMBER 的值了。修饰方法&#xff1a;…...

【leetcode】排列序列

给出集合 [1,2,3,...,n]&#xff0c;其所有元素共有 n! 种排列。 按大小顺序列出所有排列情况&#xff0c;并一一标记&#xff0c;当 n 3 时, 所有排列如下&#xff1a; "123""132""213""231""312""321" 给定…...

【Cesium开发实战】视频融合功能的实现,可自定义位置和视频路径

Cesium有很多很强大的功能,可以在地球上实现很多炫酷的3D效果。今天给大家分享一个视频融合功能。 1.话不多说,先展示 视频融合 2.设计思路 点击绘制开始在地图上绘制视频融合的点位,形成视频播放的区域,双击弹框输入名称和要播放视频的路径,即可对应区域播放对应视频,…...

【秋招笔试题】小明的美食

解析&#xff1a;思维题。由于需要互不相同&#xff0c;每次操作取重复的值与最大值相加即可&#xff0c;这样即可保证相加后不会新增重复的值。因此统计重复值即可。 #include <iostream> #include <algorithm>using namespace std; const int maxn 1e5 5; int…...

基于OpenLCA、GREET、R语言的生命周期评价方法、模型构建及典型案例应用

生命周期分析 (Life Cycle Analysis, LCA) 是评价一个产品系统生命周期整个阶段——从原材料的提取和加工&#xff0c;到产品生产、包装、市场营销、使用、再使用和产品维护&#xff0c;直至再循环和最终废物处置——的环境影响的工具。这种方法被认为是一种“从摇篮到坟墓”的…...

Linux操作系统 -socket网络通信

同一台主机之间的进程 1.古老的通信方式 无名管道 有名管道 信号 2、IPC对象通信 system v 消息队列 共享内存 信号量集 由于不同主机间进程通信 3.socket网络通信 国际网络体系结构&#xff1a; 七层OSI模型(理论…...

【苍穹】完美解决由于nginx更换端口号导致无法使用Websocket

一、报错信息 进行到websocket开发的过程中&#xff0c;遇到了前端报错&#xff0c;无法连接的提示&#xff1a; 经过F12排查很明显是服务端和客户端并没有连接成功。这里就涉及到之前的坑&#xff0c;现在需要填上了。 二、报错原因和推导 应该还记得刚开苍穹的第一天配置前…...

Qt中在pro中实现一些宏定义

在pro文件中利用 DEFINES 定义一些宏定义供工程整体使用。&#xff08;和在cpp/h文件文件中定义使用有点类似&#xff09;可以利用pro的中的宏定义实现一些全局的判断 pro中实现 #自定义一个变量 DEFINES "PI\"3.1415926\"" #自定义宏 DEFINES "T…...

bash XXX.sh文件和直接运行XXX.sh的区别

区别&#xff1a; bash XXX.sh 明确说明使用bash作为脚本的解释器不需要文件有执行权限 XXX.sh 需要指定相关解释器。如果第一行是#!/bin/bash则使用bash&#xff0c;如果是#!/bin/sh&#xff0c;则使用sh作为解释器需要有执行权限:通过chmod x 文件名指定 注意: #!是特殊标…...

【Python机器学习】k-近邻算法简单实践——改进约会网站的配对效果

需求背景&#xff1a; XX一直使用约会网站寻找适合自己的约会对象&#xff0c;ta会把人分为3种类型&#xff1a; 不喜欢、魅力一般、非常有魅力 对人分类轴&#xff0c;发现了对象样本的以下3种特征&#xff1a; 1、每年获得的飞行里程数 2、玩视频游戏所耗时间百分比 3、…...

vue3前端开发-小兔鲜项目-登录组件的开发表单验证

vue3前端开发-小兔鲜项目-登录组件的开发表单验证&#xff01;现在开始写登录页面的内容。首先这一次完成基础的首页按钮点击跳转&#xff0c;以及初始化一些简单的表单的输入验证。后期还会继续完善内容。 1&#xff1a;首先还是准备好login页面的组件代码内容。 <script …...

Winform上位机TCP客户端/服务端、串口通信

Winform上位机TCP客户端/服务端、串口通信 背景 日常练习&#xff0c;着急换工作&#xff0c;心态都快乱了。 工具 串口调试助手 网络调试助手 代码 客户端 using Microsoft.VisualBasic.Logging; using System.Net.Sockets; using System.Text;namespace TcpClientDem…...

Linux基础复习(二)

前言 本文介绍了一下Linux命令行基本操作及网络配置 一、 命令行提示含义 [当前用户主机名 工作目录]$ 若当前用户是root&#xff0c;则最后一个字符为# 否则&#xff0c;最后一个字符为$ 二、常用Linux命令及其解释 修改主机名 一般在创建一台主机后会使用hostname相关命…...

nginx漏洞修复 ngx_http_mp4_module漏洞(CVE-2022-41742)【低可信】 nginx版本升级

风险描述&#xff1a; Nginx 是一款轻量级的Web服务器、反向代理服务器。 Nginx 的受影响版本中的ngx _http_mp4_module模块存在内存越界写入漏洞&#xff0c;当在配置中使用 mp4 directive时&#xff0c;攻击者可利用此漏洞使用使用ngx_http_mp4_module模块处理特制的音频或视…...

网格布局 HTML CSS grid layout demo

文章目录 页面效果代码 (HTML CSS)参考 页面效果 代码 (HTML CSS) <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"…...

Java算法之递归算法-如何计算阶乘的值

上一篇学了递归之后&#xff0c;练习一下递归算法。 题目&#xff1a;使用递归算法计算阶乘的值&#xff0c;也就是5&#xff01;5*4*3*2*1&#xff0c;直接使用循环是非常简单的&#xff0c;这边练习一下递归算法。 先写一下两个条件 基线条件&#xff1a;等于1的时候返回1…...

python爬虫入门小案例

python爬虫 以下内容仅供学习交流,请勿用作其他用途,若涉及隐私和版权问题,请及时联系我删除 闲来无事,学了学爬虫小知识,适合入门,文笔拙劣,还望见谅 爬虫是什么: 爬取网页上的文字,图片,视频,音频 自动化操作浏览器,比如填写表单,打卡,提高工作效率爬虫的注意事项: 爬虫…...

【昇腾AI创新大赛集训营南京站学习笔记】-Ascend算子开发课程

昇腾AI创新大赛训练营 14:00-14:30 基础知识-理论课 一、CANN 、达芬奇架构和算子 1.AI Core逻辑架构 达芬奇架构包含三部分&#xff1a; 1&#xff09;计算类&#xff1a;矩阵计算单元&#xff08;两个矩阵扔进去相乘&#xff09;、向量计算单元、标量计算单元 2&#xff09;控…...

系统架构设计师教程 第4章 信息安全技术基础知识-4.5 密钥管理技术4.6 访问控制及数字签名技术-解读

系统架构设计师教程 第4章 信息安全技术基础知识-4.5 密钥管理技术&4.6 访问控制及数字签名技术 4.5 密钥管理技术4.5.1 对称密钥的分配与管理4.5.1.1 密钥的使用控制4.5.1.1.1 密钥标签4.5.1.1.2 控制矢量4.5.1.2 密钥的分配4.5.1.2.1物理方式14.5.1.2.2 物理方式24.5.1…...

C语言日常练习Day13

目录 一、设半径r1.5&#xff0c;圆柱高h3&#xff0c;求圆周长、圆面积、圆球表面积、圆球体积、圆柱体积 二、编写程序&#xff0c;用getchar函数读入两个字符给c1,c2&#xff0c;然后分别用putchar函数和printf函数输出这两个字符 三、输入4个整数&#xff0c;要求按由小…...

map、foreach、filter这些方法你还不知道什么时候该用哪个吗?那就看过来

forEach&#xff1a;‌主要用于遍历数组并对每个元素执行某种操作&#xff0c;‌通常用于改变当前数组里的值。‌它不会返回新数组&#xff0c;‌而是直接在原数组上进行操作。‌forEach方法不支持return、‌break、‌continue等语句&#xff0c;‌因为这些语句在forEach中不会…...

6.3 面向对象技术-设计模式

设计模式 创建型模式 结构型模式...

Mac 中安装内网穿透工具ngrok

ngrok 是什么&#xff1f; Ngrok 是一个网络工具&#xff0c;主要用于在网络中创建从公共互联网到私有或本地网络中运行的web服务的安全隧道。它充当了一个反向代理&#xff0c;允许外部用户通过公共可访问的URL访问位于防火墙或私有网络中的web应用程序或服务。Ngrok 特别适用…...

python count返回什么

描述 count() 方法用于统计字符串中某个子字符串出现的次数&#xff0c;可选参数为开始搜索与结束搜索的位置索引。 语法 count() 方法语法&#xff1a; S.count(sub[,start0[,endlen(S)]]) 参数 sub -- 搜索的子字符串。 S -- 父字符串。 start -- 可选参数&#xff0c;…...

mac清理软件哪个好用免费 MacBook电脑清理软件推荐 怎么清理mac

随着使用时间的增长&#xff0c;mac电脑会积累一些不必要的垃圾文件&#xff0c;这些文件会占用宝贵的存储空间&#xff0c;影响电脑的运行速度和稳定性。因此&#xff0c;定期清理mac电脑的垃圾文件是非常有必要的。市场上有许多优秀的Mac清理软件&#xff0c;包括一些出色的国…...

学生党百元蓝牙耳机哪个性价比高?精选四款超强性价比耳机型号

现阶段&#xff0c;蓝牙耳机技术逐渐成熟&#xff0c;蓝牙耳机在我们的学习和娱乐中承担着很重要的角色&#xff0c;那么在面对众多品牌和型号中&#xff0c;学生党们在选择蓝牙耳机上纠结不已&#xff0c;到底学生党百元蓝牙耳机哪个性价比高&#xff1f;作为一个蓝牙耳机重度…...

中文之美,美在辞藻富丽,也美在情感含蓄内敛。

文章目录 引言句句不提幸福,句句都是幸福句句不提释怀,句句都是释怀句句不提爱意,句句都是爱意句句不提安慰,句句都是安慰句句不提遗憾,句句都是遗憾句句不提思念,句句都是思念引言 许多句子没有将主题直抒胸臆,却通过字词间的呼应、碰撞,让人感受到“言未表而意无穷”…...

FPGA与ASIC:深入解析芯片设计的双子星

前言 在半导体世界里&#xff0c;FPGA&#xff08;Field-Programmable Gate Array&#xff0c;现场可编程门阵列&#xff09;与ASIC&#xff08;Application-Specific Integrated Circuit&#xff0c;专用集成电路&#xff09;是两种截然不同的芯片设计策略&#xff0c;各自在…...

深入 Symfony 服务容器:依赖注入的艺术

“深入 Symfony 服务容器&#xff1a;依赖注入的艺术” 是一个涵盖了 Symfony 服务容器核心概念和依赖注入机制的复杂话题。为了全面理解 Symfony 服务容器的运作&#xff0c;我们将详细探讨以下几个方面&#xff1a; 服务容器的概念和作用依赖注入的基本原理Symfony 服务容器…...

基于Java+SpringMvc+Vue技术的慈善捐赠平台设计与实现(源码+LW+部署讲解)

项目含有源码、文档、PPT、配套开发软件、软件安装教程、项目发布教程、包运行成功以及课程答疑&#xff01; 软件开发环境及开发工具&#xff1a; 操作系统&#xff1a;Windows 10、Windows 7、Windows 8 开发语言&#xff1a;java 前端技术&#xff1a;JavaScript、VUE.j…...

dsp c6657 SYS/BIOS学习笔记

1 SYS/BIOS简介 SYS/BIOS是一种用于TI的DSP平台的嵌入式操作系统&#xff08;RTOS&#xff09;。 2 任务 2.1 任务调度 SYS/BIOS任务线程有0-31个优先级&#xff08;默认0-15&#xff0c;优先级0被空闲线程使用&#xff0c;任务最低优先级为1&#xff0c;最高优先级为15&am…...

分布式搜索引擎ES-DSL搜索详解

1.DSL搜索-入门语法 建立索引&#xff1a; xxx(自定义名称) 自定义mapping: POST /shop/_mapping {"properties": {"id": {"type": "long"},"age": {"type": "integer"},"username": {&quo…...

vue zip文件下载请求封装与使用

axios封装&#xff08;重点是响应拦截&#xff09; 这里把响应超时时间注释是文件下载接口返回需要较长时间 import axios from axios import {ElMessageBox} from "element-plus"; import router from "/router";const service axios.create({baseURL: …...

Windows波形音频MMEAPI简介

Windows波形音频MMEAPI简介 使用MMEAPI时需要导入头文件&#xff1a;#include<mmeapi.h> mmeapi.h文件的主要内容 mmeapi.h 文件是 Windows 多媒体 API 的一部分&#xff0c;主要用于处理波形音频&#xff08;Waveform Audio&#xff09;的输入和输出。以下是该文件的…...

sklearn聚类算法用于图片压缩与图片颜色直方图分类

上期文章:机器学习之SKlearn(scikit-learn)的K-means聚类算法 我们分享了sklearn的基本知识与基本的聚类算法,这里主要是机器学习的算法思想,前期文章我们也分享过人工智能的深度学习,二者有如何区别,可以先参考如下几个实例来看看机器学习是如何操作的 不同K值下的聚…...

Llama 3.1要来啦?!测试性能战胜GPT-4o

哎呀&#xff0c;Meta声称将于今晚发布的Llama 3.1&#xff0c;数小时前就在Hugging Face上泄露出来了&#xff1f;泄露的人很有可能是Meta员工&#xff1f; 还是先来看泄露出来的llama3.1吧。新的Llama 3.1模型包括8B、70B、405B三个版本。 而经过网友测试&#xff0c;该base…...

C++使用opencv处理图像阴影部分

1. 直方图均衡化 直方图均衡化是一种增强图像对比度的方法&#xff0c;可以通过均衡化图像的灰度级分布来改善图像中阴影部分的亮度。 #include <opencv2/opencv.hpp>using namespace cv;int main() {// 读取图像Mat image imread("input_image.jpg", IMREA…...

4.Java Web开发模式(javaBean+servlet+MVC)

Java Web开发模式 一、Java Web开发模式 1.javaBean简介 JavaBeans是Java中一种特殊的类&#xff0c;可以将多个对象封装到一个对象&#xff08;bean&#xff09;中。特点是可序列化&#xff0c;提供无参构造器&#xff0c;提供getter方法和setter方法访问对象的属性。名称中…...

centos7 mysql 基本测试(6)主从简单测试

centos7 xtrabackup mysql 基本测试&#xff08;6&#xff09;主从简单测试 mysql -u etc -p 1234aA~1 参考&#xff1a; centos7 时区设置 时间同步 https://blog.csdn.net/wowocpp/article/details/135931129 Mysql数据库&#xff1a;主从复制与读写分离 https://blog.csd…...

信息安全工程师题

防火墙安全策略有两种类型&#xff1a;白名单策略、黑名单策略白名单策略&#xff1a;只允许符合安全规则的包通过防火墙&#xff0c;其他通信包禁止黑名单策略&#xff1a;禁止与安全规则相冲突的包通过防火墙&#xff0c;其他通信包允许实现网络地址转换的方式主要有静态NAT、…...

springcloud rocketmq 新增的消费者组从哪里开始消费

如果新建一个新的消费者组&#xff0c;是否会消费历史消息&#xff0c;导致重复消费&#xff1f; 直接在 console 界面新增消费者组&#xff0c;但是没有办法绑定订阅关系&#xff0c;没有找到入口&#xff0c;在 控制台项目源码 rocketmq-externals 也没有找到可以确定订阅关系…...

Redis-缓存

什么是缓存&#xff1f; 缓存就像自行车和越野车的避震器&#xff0c;降低硬着陆造成的损害 缓存就是系统的避震器&#xff0c;,防止过高的数据访问猛冲系统,导致其操作线程无法及时处理信息而瘫痪 缓存(Cache),就是数据交换的缓冲区,俗称的缓存就是缓冲区内的数据,一般从数…...