【Linux操作系统】进程间通信之匿名管道与命名管道
目录
- 一、进程间通信的目的:
- 二、进程间通信的种类
- 三、什么是管道
- 四、匿名管道(共同祖先的进程之间)
- 1.匿名管道的使用
- 2.匿名管道举例
- 3.匿名管道的原理
- 4.管道特点
- 5.管道的读写规则
- 1. 当管道内没有数据可读时
- 2.当管道满的时候
- 3.管道端被关闭
- 4.数据写入的原子性
- 五、命名管道(让两个不相干的进程通信)
- 1.命名管道的概念与原理
- 2.命名管道的创建与删除
- 3.匿名管道与命名管道的区别
- 4.命名管道的打开规则
- 5.命名管道举例(附完整代码)
- 1.makefile文件
- 2.wFifo.cpp
- 3.rFifo.cpp
- 六、关于匿名管道与命名管道的细节问题
- 匿名管道
- 命名管道
一、进程间通信的目的:
在学习通信间进程前我们要明白进程间通信的目的意义是是什么,从而方便我们在学习的过程中对我们所学的知识有一个清晰的认知
1.传输数据
将一个进程的数据发送给另一个进程
2.资源共享
让多个进程共享所需要的资源
3.通知事件
一个进程向另一个进程发送消息,通知它发生了某种事件(如进程终止时要通知父进程)
4.进程控制
有些进程希望完全控制另一个进程的执行(如Debug进程),此时控制进程希望能够拦截另一个进程的所有陷入和异常,并能够及时知道它的状态改变
二、进程间通信的种类
1.管道
- 匿名管道
- 命名管道
2.System V
- System V 消息队列
- System V 共享内存
- System V 信号量
3.POSIX
- 消息队列
- 共享内存
- 信号量
- 互斥锁
- 条件变量
- 读写锁
在本章中主要讲解管道通信
三、什么是管道
- 管道是操作系统中最古老的进程间通信的方式
- 我们把一个进程连接到另一个进程的一个数据流称为一个“管道”

四、匿名管道(共同祖先的进程之间)
1.匿名管道的使用
#include <unistd.h>
int pipe(int fd[2]);
匿名管道的功能是创建一个无名管道
- fd:文件描述符数组,其中fd[0]表示读端,fd[1]表示写端
- 返回值:成功返回0,失败返回错误代码

如果上述看的不太明白的话可以去看看这篇文章,里面有对文件描述符及数组的详细讲解
https://blog.csdn.net/liuty0125/article/details/140828056?spm=1001.2014.3001.5502
2.匿名管道举例
1.先创建管道, 进而创建子进程, 父子进程使用管道进行通信
父进程向管道当中写“i am father”,
子进程从管道当中读出内容, 并且打印到标准输出
#include<unistd.h>
#include<sys/types.h>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<string>
using namespace std;
int main()
{int fd[2];int f=pipe(fd);char buf[100];const char* fa="I am father";int len=strlen(fa);if(f==-1){perror("make pipe");exit(1);}int pid = fork();if(pid==-1){perror("fork failed");return 1;}if(pid>0)//父进程{write(fd[1],fa,len);}if(pid==0){read(fd[0],buf,len);printf("%s",buf);}return 0;
}
2.从键盘读取数据,写入管道,读取管道,写到屏幕
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include<sys/types.h>
#include<string>
int main(void)
{int fds[2];char buf[100];int len;if (pipe(fds) == -1)perror("make pipe"), exit(1);// read from stdinwhile (fgets(buf, 100, stdin)) {len = strlen(buf);// write into pipeif (write(fds[1], buf, len) != len) {perror("write to pipe");break;}memset(buf, 0x00, sizeof(buf));// read from pipeif ((len = read(fds[0], buf, 100)) == -1) {perror("read from pipe");break;}// write to stdoutif (write(1, buf, len) != len) {perror("write to stdout");break;}}
}
3.匿名管道的原理
我们知道,进程将一个文件以读方式打开一次会产生一个FILE*,再以写方式打开一次,也会产生一个FILE*,但是这两个文件流指针指向的是同一个缓存区
内容只有一份基于文件的,让不同进程看到同一份资源的通信方式,叫做管道,管道只能被设计成为单向通信
如:ajx | ps | … 这中间的管道是兄弟关系
我们可以让父子进程都打开这个文件,再让父进程关闭读端,子进程关闭写端,这样就形成了一条由父进程通向子进程的管道,反之则形成由子进程通向父进程的管道

在这个地方其实有个bug:
通过父进程创建多个子进程,用管道文件连接父进程与各个子进程,产生的管道文件有多个读端,因为每个子进程的文件描述符表都会和创建他的父进程的文件描述符表一样,父进程的读端没有关闭,所以与接连创建的各个子进程的管道会带有上个父进程的读端
解决方案1:最后一个管道一定只有一个读写端,可以从后往前删
解决方案2:每次创建管道文件时,记录父进程的fd,然后每次创建时通过记录的上个父进程的文件描述符关闭不需要的fd
4.管道特点
- 匿名管道只能用于共同祖先的进程之间进行通信,一般都是一个管道由一个进程创建,然后该进程调用fork,此后父子进程之间就可以应用改管道
- 一般而言,匿名管道的生命周期随进程,进程退出,管道释放
- 管道是半双工的,数据只能向一个方向流动,需要双方通信时,则需要建立起两个管道

5.管道的读写规则
管道读写操作的行为会根据是否设置了非阻塞标志(O_NONBLOCK)以及管道的状态(是否有数据可读、是否已满等)有所不同
1. 当管道内没有数据可读时
- O_NONBLOCK 未设置(阻塞模式):
read调用阻塞,即进程暂停执行,一直等到有数据来到为止
- O_NONBLOCK 已设置(非阻塞模式):
read调用返回-1,errno值为EAGAIN
2.当管道满的时候
- O_NONBLOCK 未设置(阻塞模式):
write调用阻塞,直到有进程读走数据
- O_NONBLOCK 已设置(非阻塞模式):
调用返回-1,errno值为EAGAIN
3.管道端被关闭
- 所有管道的写端对应的文件描述符被关闭:
read 调用会返回 0,表示已经到达文件末尾(EOF),没有更多的数据可以读取
- 所有管道的读端对应的文件描述符被关闭:
write 调用会产生 SIGPIPE 信号,默认情况下,这会导致进程终止。可以通过捕获 SIGPIPE 信号来避免进程终止
4.数据写入的原子性
- 当要写入的数据量不大于 PIPE_BUF 时:
Linux 保证写入的原子性,即要么全部写入成功,要么完全不写入。这有助于避免数据在管道中被分割成多个部分,从而保证了数据的完整性。
- 当要写入的数据量大于 PIPE_BUF 时:
Linux 不再保证写入的原子性,数据可能会被分割成多个部分写入管道。这可能导致读端在读取数据时,需要多次调用 read 才能获取完整的数据。
五、命名管道(让两个不相干的进程通信)
1.命名管道的概念与原理
学习了上面的匿名管道,我们知道匿名管道其实是有缺陷的,它只能在具有共同祖先(具有亲缘关系)的进程间通信,那如果我们想在不相关的进程之间交换数据呢?
想要在两个不相干进程之间进行通信,我们的目的是让不同进程看到同一份资源
比如进程A通过文件描述符表打开一个文件,会为文件创建一个struct FILE,并通过文件缓冲区与磁盘进行交互
进程B也通过文件描述符打开同一个文件,也会为文件再创建一个struct FILE,但会使用同一个缓冲区,指向同一个内存空间,因此我们就可以通过这段内存空间进行通信
不过我们只是需要两个进程之间进行通信,并不需要把打开的文件内容写到缓冲区中,因此这里的文件是一个特殊的文件,也就是我们下面说的命名管道
如果我们想在不相关的进程之间交换数据,可以使用FIFO文件来做这项工作,它经常被称为命名管道
- 怎么保证两个进程打开的是同一个文件?
只需要保证文件的路径与文件名一样即可,因为路径具有唯一性且同一路径下不能有同名文件
2.命名管道的创建与删除
1.在命令行上创建
- 使用
mkfifofilename

p开头为管道文件
2.在程序中创建
int mkfifo(const char *filename,mode_t mode);
int main(int argc, char *argv[])
{mkfifo("p2", 0644);return 0;
}
3.删除文件
使用unlink()删除文件,可以用于析构函数
unlink(filename);
也就是说,想让两个不相干的进程通信,只需通过mkfifo创建一个管道文件,一个进程写,一个进程读即可
在程序中,管道文件只能创建一个,一个进程创建后,另一个进程只需直接操作就行,不需要再创建管道文件
管道中的通信都是基于文件的,可以说管道通信是基于文件的二次创新
如果写端没打开,而去先打开读端,读端open时会阻塞,直到把写端打开,读端才会返回
3.匿名管道与命名管道的区别
- 匿名管道由pipe函数创建并打开
- 命名管道由mkfifo函数创建,打开用open
- FIFO(命名管道)与pipe(匿名管道)之间唯一的区别在它们创建与打开的方式不同,一但这些工作完成之后,它们具有相同的语义
4.命名管道的打开规则
- 为读而打开FIFO
O_NONBLOCK 未设置(阻塞模式):
如果当前没有进程为写而打开该FIFO,则打开操作会阻塞,直到有进程以写模式打开它。这意味着,如果FIFO是空的,并且没有写端打开,那么尝试以读模式打开FIFO的进程将被挂起,直到另一个进程以写模式打开它
O_NONBLOCK 已设置(非阻塞模式):
如果当前没有进程为写而打开该FIFO,则打开操作会立即成功,但是后续的read操作可能会失败,并返回-1,同时设置errno为EAGAIN(表示资源暂时不可用,可以在以后重试)。
- 为写而打开FIFO
O_NONBLOCK 未设置(阻塞模式):
如果当前没有进程为读而打开该FIFO,则打开操作会阻塞,直到有进程以读模式打开它。这意味着,如果FIFO没有读端,那么尝试以写模式打开FIFO的进程将被挂起,直到另一个进程以读模式打开它
O_NONBLOCK 已设置(非阻塞模式):
如果当前没有进程为读而打开该FIFO,则打开操作会立即失败,并返回-1,同时设置errno为ENXIO(表示设备不存在或没有这样的设备文件)。
在非阻塞模式下,如果没有读端可用,写端不能立即打开FIFO。
5.命名管道举例(附完整代码)
创建出来的命名管道可以供两个进程通信
进程A 向管道当中写 “i am process A”
进程B 从管道当中读 并且打印到标准输出
1.makefile文件
.PHONY:all
all:w rw:wFifo.cppg++ -o $@ $^ -std=c++11r:rFifo.cppg++ -o $@ $^ -std=c++11
PHONY:clean
clean:rm -f r w
2.wFifo.cpp
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <string>
#include <string.h>
#include <errno.h>
#include <strings.h>
#include <stdio.h>
#include<unistd.h>void errS(const char* s)
{perror(s);exit(EXIT_FAILURE);
}int main()
{const char* s="/home/xiaoliu/code/Fifo/__fifo";char a[1024]="i am process A";mkfifo(s,0666);int wfd=open(s,O_WRONLY);if(wfd < 0){errS("open");}int w=write(wfd,a,sizeof(a-1));if(w<0){errS("write");}return 0;
}
3.rFifo.cpp
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <string>
#include <string.h>
#include <errno.h>
#include <strings.h>
#include <stdio.h>
#include<unistd.h>
#include <iostream>void errS(const char* s)
{perror(s);exit(EXIT_FAILURE);
}int main()
{const char* s="/home/xiaoliu/code/Fifo/__fifo";char buf[1024];int rfd=open(s,O_RDONLY);if(rfd<0){errS("open");}int n = read(rfd,buf,sizeof(buf)-1);if(n>0){buf[n]='\0';std::cout<<"A said: "<<buf;}else if(n==0){std::cout<<"A didn't speak";}else{std::cerr << "read failed, errno: " << errno << ", errstring: " << strerror(errno) << std::endl;}return 0;
}
六、关于匿名管道与命名管道的细节问题
匿名管道
- 进程之间可以通过地址访问进行相互通行吗?
错误,进程之间具有独立性,拥有自己的虚拟地址空间,因此无法通过各自的虚拟地址进行通信(A的地址经过B的页表映射不一定映射在什么位置)
- 所有的进程间通信都是通过内核中的缓冲区实现的吗?
错误,除了内核中的缓冲区之外还有文件以及网络通信的方式可以实现
- 管道的容量仅受磁盘容量大小限制吗?
错误,管道的本质是内核中的缓冲区,通过内核缓冲区实现通信,命名管道的文件虽然可见于文件系统,但是只是标识符,并非通信介质
- 一个管道只能有一个读进程或写进程对其操作吗?
错误,多个进程只要能够访问同一管道就可以实现通信,不限于读写个数
- 程对管道进行读操作和写操作都可能被阻塞吗?
正确,管道自带同步(没有数据读阻塞,缓冲区写满写阻塞)与互斥
- 管道的本质是内核中的一块缓冲区?
正确,管道本质是内核中的一块缓冲区,多个进程通过访问同一块缓冲区实现通信
命名管道
- .命名管道可以用于同一主机上的任意进程间通信?
正确,命名管道可用于同一主机上的任意进程间通信
- 向命名管道中写入的数据越多,则管道文件越大?
错误,管道的通信本质是通过内核中一块缓冲区(内存)时间数据传输,而命名管道的管道文件只是一个标识符,用于让多个进程能够访问同一块缓冲区,不是磁盘文件
- 多个进程在通过管道通信时,删除管道文件则无法继续通信?
错误,管道的生命周期随进程,本质是内核中的缓冲区,命名管道文件只是标识,用于让多个进程找到同一块缓冲区,删除后,之前已经打开管道的进程依然可以通信
- 命名管道的本质和匿名管道的本质相同都是内核中的一块缓冲区?
正确
- .命名管道在磁盘空间足够的情况下可以持续写入数据?
错误,管道在缓冲区写满后会写阻塞,跟磁盘空间并无关系
- 若以只读或只写的方式打开命名管道时,则会阻塞?
正确,当一个进程尝试以只读方式打开命名管道时,如果该管道尚未被任何进程以写方式打开,则打开操作会阻塞,阻塞将持续到另一个进程以写方式打开该管道为止。此时,读进程可以继续进行,并尝试从管道中读取数据。反之亦然
相关文章:
【Linux操作系统】进程间通信之匿名管道与命名管道
目录 一、进程间通信的目的:二、进程间通信的种类三、什么是管道四、匿名管道(共同祖先的进程之间)1.匿名管道的使用2.匿名管道举例3.匿名管道的原理4.管道特点5.管道的读写规则1. 当管道内没有数据可读时2.当管道满的时候3.管道端被关闭4.数…...
慢sql优化和Explain解析
要想程序跑的快,sql优化不可懈怠!今日来总结一下常用的慢sql的分析和优化的方法。 1、慢sql的执行分析: 大家都知道分析一个sql语句执行效率的方法是用explain关键词: 举例:sql:select * from test where bussiness_…...
ALIGN_ Tuning Multi-mode Token-level Prompt Alignment across Modalities
文章汇总 当前的问题 目前的工作集中于单模提示发现,即一种模态只有一个提示,这可能不足以代表一个类[17]。这个问题在多模态提示学习中更为严重,因为视觉和文本概念及其对齐都需要推断。此外,仅用全局特征来表示图像和标记是不…...
【Java SE】代码注释
代码注释 注释(comment)是用于说明解释程序的文字,注释的作用在于提高代码的阅读性(可读性)。Java中的注释类型包括3种,分别是: 单行注释多行注释文档注释 ❤️ 单行注释 基本格式ÿ…...
如何在算家云搭建Llama3-Factory(智能对话)
一、Llama3-Factory 简介 当地时间 4 月 18 日,Meta 在官网上宣布公布了旗下最新大模型 Llama 3。目前,Llama 3 已经开放了 80 亿(8B)和 700 亿(70B)两个小参数版本,上下文窗口为 8k。Llama3 是…...
操作数据表
创建表 创建表语法: CREATE TABLE table_name ( field1 datatype [COMMENT 注释内容], field2 datatype [COMMENT 注释内容], field3 datatype ); 注意: 1. 蓝色字体为关键字 2. CREATE TABLE 是创建数据表的固定关键字,表…...
C# 实现进程间通信的几种方式(完善)
目录 引言 一、基本概念 二、常见的IPC方法 1. 管道(Pipes) 2. 共享内存(Shared Memory) 3. 消息队列(Message Queues) 4. 套接字(Sockets) 5. 信号量(Semaphore…...
MySQL Workbench Data Import Wizard:list index out of range
MySQL Workbench的Data Import Wizard功能是用python实现的,MySQL Workbench自带了一个python,数据导入的时候出现错误提示 22:55:51 [ERR][ pymforms]: Unhandled exception in Python code: Traceback (most recent call last): File "D…...
微信支付宝小程序SEO优化的四大策略
在竞争激烈的小程序市场中,高搜索排名意味着更多的曝光机会和潜在用户。SEO即搜索引擎优化,对于小程序而言,主要指的是在微信小程序商店中提高搜索排名,从而增加曝光度和用户访问量。有助于小程序脱颖而出,提升品牌知名…...
AutoDIR: Automatic All-in-One Image Restoration with Latent Diffusion论文阅读笔记
AutoDIR: Automatic All-in-One Image Restoration with Latent Diffusion 论文阅读笔记 这是ECCV2024的论文,作者单位是是港中文和上海AI Lab 文章提出了一个叫AutoDIR的方法,包括两个关键阶段,一个是BIQA,基于vision-language…...
SQLite 数据库设计最佳实践
SQLite特点 SQLite是一款功能强大的 轻量级嵌入式数据库 ,具有以下显著特点: 体积小 :最低配置仅需几百KB内存,适用于资源受限环境。 高性能 :访问速度快,运行效率高于许多开源数据库。 高度可移植 :兼容多种硬件和软件平台。 零配置 :无需复杂设置,开箱即用。 自给自…...
【论文精读】ID-like Prompt Learning for Few-Shot Out-of-Distribution Detection
🌈 个人主页:十二月的猫-CSDN博客 🔥 系列专栏: 🏀论文精读_十二月的猫的博客-CSDN博客 💪🏻 十二月的寒冬阻挡不了春天的脚步,十二点的黑夜遮蔽不住黎明的曙光 注:下文…...
Android 10.0 根据包名禁用某个app的home事件
1.前言 在10.0的系统rom定制化开发中,在某些app中,需要禁用home事件,在普通的app中又无法 禁用home事件,所以就需要从系统中来根据包名禁用home事件了,接下来分析下 系统中处理home事件的相关流程 2.根据包名禁用某个app的home事件的核心类 frameworks/base/services/c…...
Rust 文档生成与发布
目录 第三节 文档生成与发布 1. 使用 RustDoc 生成项目文档 1.1 RustDoc 的基本使用 1.2 文档注释的格式与实践 1.3 生成文档的其他选项 1.4 在 CI/CD 中生成文档 2. 发布到 crates.io 的步骤与注意事项 2.1 创建 crates.io 账户 2.2 配置 Cargo.toml 2.3 生成发布版…...
【C++动态规划】有效括号的嵌套深度
本文涉及知识点 C动态规划 LeetCode1111. 有效括号的嵌套深度 有效括号字符串 定义:对于每个左括号,都能找到与之对应的右括号,反之亦然。详情参见题末「有效括号字符串」部分。 嵌套深度 depth 定义:即有效括号字符串嵌套的层…...
2024年优秀的天气预测API
准确、可操作的天气预报对于许多组织的成功至关重要。 事实上,在整个行业中,天气条件会直接影响日常运营,包括航运、按需、能源和供应链(仅举几例)。 以公用事业为例。根据麦肯锡的数据,在 1.4 年的时间里…...
Android和iOS有什么区别?
Android 和 iOS 有以下区别: 开发者与所属公司: Android:由谷歌公司开发以及开放手机联盟维护。它是基于 Linux 内核和其他开源软件的修改版本,代码开源程度较高,许多厂商都可以基于 Android 源代码进行深度定制和开发…...
NVR小程序接入平台/设备EasyNVR多个NVR同时管理多平台级联与上下级对接的高效应用
政务数据共享平台的建设正致力于消除“信息孤岛”现象,打破“数据烟囱”,实现国家、省、市及区县数据的全面对接与共享。省市平台的“级联对接”工作由多级平台共同构成,旨在满足跨部门、跨层级及跨省数据共享的需求,推动数据流通…...
Spring Cloud Sleuth(Micrometer Tracing +Zipkin)
分布式链路追踪 分布式链路追踪技术要解决的问题,分布式链路追踪(Distributed Tracing),就是将一次分布式请求还原成调用链路,进行日志记录,性能监控并将一次分布式请求的调用情况集中展示。比如各个服务节…...
人工智能:机遇与挑战
人工智能(AI)作为当今世界科技发展的前沿领域,正在以前所未有的速度和规模影响着我们的生活和工作方式。AI技术的应用前景广阔,从医疗健康到金融服务,从教育到交通,再到娱乐和家庭生活,AI正在逐…...
Chapter03-Authentication vulnerabilities
文章目录 1. 身份验证简介1.1 What is authentication1.2 difference between authentication and authorization1.3 身份验证机制失效的原因1.4 身份验证机制失效的影响 2. 基于登录功能的漏洞2.1 密码爆破2.2 用户名枚举2.3 有缺陷的暴力破解防护2.3.1 如果用户登录尝试失败次…...
【网络】每天掌握一个Linux命令 - iftop
在Linux系统中,iftop是网络管理的得力助手,能实时监控网络流量、连接情况等,帮助排查网络异常。接下来从多方面详细介绍它。 目录 【网络】每天掌握一个Linux命令 - iftop工具概述安装方式核心功能基础用法进阶操作实战案例面试题场景生产场景…...
7.4.分块查找
一.分块查找的算法思想: 1.实例: 以上述图片的顺序表为例, 该顺序表的数据元素从整体来看是乱序的,但如果把这些数据元素分成一块一块的小区间, 第一个区间[0,1]索引上的数据元素都是小于等于10的, 第二…...
【Oracle APEX开发小技巧12】
有如下需求: 有一个问题反馈页面,要实现在apex页面展示能直观看到反馈时间超过7天未处理的数据,方便管理员及时处理反馈。 我的方法:直接将逻辑写在SQL中,这样可以直接在页面展示 完整代码: SELECTSF.FE…...
【快手拥抱开源】通过快手团队开源的 KwaiCoder-AutoThink-preview 解锁大语言模型的潜力
引言: 在人工智能快速发展的浪潮中,快手Kwaipilot团队推出的 KwaiCoder-AutoThink-preview 具有里程碑意义——这是首个公开的AutoThink大语言模型(LLM)。该模型代表着该领域的重大突破,通过独特方式融合思考与非思考…...
如何在看板中有效管理突发紧急任务
在看板中有效管理突发紧急任务需要:设立专门的紧急任务通道、重新调整任务优先级、保持适度的WIP(Work-in-Progress)弹性、优化任务处理流程、提高团队应对突发情况的敏捷性。其中,设立专门的紧急任务通道尤为重要,这能…...
【android bluetooth 框架分析 04】【bt-framework 层详解 1】【BluetoothProperties介绍】
1. BluetoothProperties介绍 libsysprop/srcs/android/sysprop/BluetoothProperties.sysprop BluetoothProperties.sysprop 是 Android AOSP 中的一种 系统属性定义文件(System Property Definition File),用于声明和管理 Bluetooth 模块相…...
多模态大语言模型arxiv论文略读(108)
CROME: Cross-Modal Adapters for Efficient Multimodal LLM ➡️ 论文标题:CROME: Cross-Modal Adapters for Efficient Multimodal LLM ➡️ 论文作者:Sayna Ebrahimi, Sercan O. Arik, Tejas Nama, Tomas Pfister ➡️ 研究机构: Google Cloud AI Re…...
【开发技术】.Net使用FFmpeg视频特定帧上绘制内容
目录 一、目的 二、解决方案 2.1 什么是FFmpeg 2.2 FFmpeg主要功能 2.3 使用Xabe.FFmpeg调用FFmpeg功能 2.4 使用 FFmpeg 的 drawbox 滤镜来绘制 ROI 三、总结 一、目的 当前市场上有很多目标检测智能识别的相关算法,当前调用一个医疗行业的AI识别算法后返回…...
Maven 概述、安装、配置、仓库、私服详解
目录 1、Maven 概述 1.1 Maven 的定义 1.2 Maven 解决的问题 1.3 Maven 的核心特性与优势 2、Maven 安装 2.1 下载 Maven 2.2 安装配置 Maven 2.3 测试安装 2.4 修改 Maven 本地仓库的默认路径 3、Maven 配置 3.1 配置本地仓库 3.2 配置 JDK 3.3 IDEA 配置本地 Ma…...
