建设网站微商城/上海b2b网络推广外包
IPC 是 Linux 编程中一个重要的概念,IPC 有多种方式,本文主要介绍匿名管道(又称管道、半双工管道),尽管很多人在编程中使用过管道,但一些特殊的用法还是鲜有文章涉及,本文给出了多个具体的实例,每个实例均附有完整的源代码;本文所有实例在 Ubuntu 20.04 上编译测试通过,gcc版本号为:9.4.0;本文适合 Linux 编程的初学者阅读
1 概述
- IPC(Inter-Process Communication) - 进程间通信,提供了各种进程间通信的方法;
- 在 Linux C 编程中,IPC 通常有如下几种方式
- 半双工管道(Unix Pipe),简称管道,又称为匿名管道
- FIFOs - 又称为命名管道
- 消息队列(Message Queues)
- 信号量集(Semaphore Sets)
- 共享内存(Shared Memory Segments)
- 网络 socket(AF_INET Address Family)
- 全双工管道(AF_UNIX Address family)
- 本文主要介绍匿名管道(Unix Pipe)的应用场景及使用方法,并给出多个附有完整源代码的实例;
- 管道又被称为 匿名管道,是相对于命名管道而言的;
- 匿名管道的通信模式是半双工的,所谓半双工指的是在管道中数据流是单方向的,当 A 进程和 B 进程之间使用管道进行通信时,数据要么从 A 发向 B,要么从 B 发向 A,在一个管道上,不能既有 A 向 B 的数据流,又有 B 向 A 的数据流;
- 管道还有一个特性就是只能在有亲缘关系的进程间传递消息,换句话说,只有当两个进程有相同的祖先时,才有可能使用管道进行通信。
2. 管道的基本概念
-
简单地说,管道是一种将一个进程的输出连接到另一个进程的输入的方法;
-
管道是最古老的 IPC 工具,从最早的 UNIX 操作系统开始就存在了;它们提供了进程间单向通信的方法(因此称为半双工);
-
实际上,管道的这个特性广泛应用在 Linux 的命令行上,比如下面的命令:
ls | sort | lp
-
这条命令实际上就建立了一个管道,将
ls
的输出作为sort
的输入,将sort
的输出作为lp
的输入;数据在匿名管道中运行,看上去,数据在管道中从左向右单方向流动; -
管道是在
Linux
内核中实现的,很多程序员在shell
脚本编程中都会频繁使用管道,但很少有人会去想管道在Linux
内核中是如何实现的;当一个进程创建管道时,内核会创建两个文件描述符(
fd[0]
和fd[1]
)供管道使用;一个描述符(fd[1]
)用于将数据写入管道,另一个描述符(fd[0]
)用于从管道中读取数据;此时,管道的实际用处不大,因为创建管道的进程只能使用管道与自身进行通信,毫无意义; -
下图展示了一个进程创建管道后,进程与内核的关系:
-
从上图中,可以看出以下几点:
- 文件描述符是如何连接在一起的;进程通过文件描述符(
fd[1]
)向管道写入数据,也能够从文件描述符(fd[0]
)从管道中读取该数据; - 通过管道传输数据时,数据是通过内核流动的;在
Linux
下,管道在内核内部使用inode
表示,innode
驻留在内核中,并不属于一个物理文件系统。
- 文件描述符是如何连接在一起的;进程通过文件描述符(
-
这样建立的管道毫无用处,一个进程要自言自语,没有必要建立一个管道;但是,如果创建管道的进程再
fork
出一个子进程,由于子进程会从父进程继承管道的描述符,这样父子进程之间有可以通过这个管道进行通信了; -
下图描述了父进程、子进程和内核的关系
-
从上图中,我们可以看到,父进程和子进程都可以访问管道的两个文件描述符,但是很显然,如果父进程和子进程同时向
fd[1]
写入数据,一定会造成混乱,而且如果父、子进程均向fd[1]
写入数据,当从fd[0]
读出数据时,并无法区分读到的数据是那个进程写入的;所以必须要做出抉择,这个建立的管道的数据是向那个方向流动,从父进程流向子进程?还是从子进程流向父进程?两个进程必须达成一致,否则会出现混乱; -
为了讨论方便,我们假定子进程要做一些事务,然后把结果通过管道发送给父进程,如下面图示:
-
至此,管道已经建立完毕,下面就是如何使用管道;前面提到过,管道的文件描述符使用
inode
,所以可以使用低级文件 I/O 的系统调用来直接访问管道; -
向管道中写入数据,使用
write()
系统调用;从管道中读出数据,使用read()
系统调用; -
特别提醒:系统调用
lseek()
不能在管道中使用。
3 如何用C语言创建管道
- 使用
pipe()
系统调用可以创建一个管道,这个调用需要一个由两个整数组成的数组作为参数,调用成功后,该数组将包含管道的两个文件描述符; - 系统调用:pipe()
原型:#include <unistd.h>int pipe(int fd[2]); 返回:调用成功返回 0调用失败返回 -1error = EMFILE (no free descriptors)EMFILE (system file table is full)EFAULT (fd array is not valid)备注: fd[0] 用于从管道中读取数据, fd[1] 用于向管道中写入数据
- 调用成功后,不仅两个管道描述符被建立,而且处于打开状态,可以直接进行读、写操作;
- 再次重申,所有通过管道传输的数据都要通过内核;下面是使用
pipe()
建立管道的代码:#include <stdio.h> #include <unistd.h> #include <sys/types.h>main() {int fd[2];pipe(fd);.. }
- 前面说过,这样建立的管道毫无用处,进程自言自语并不需要使用管道;要使管道有意义,在建立管道后要
fork()
一个子进程;#include <stdio.h> #include <unistd.h> #include <sys/types.h>main() {int fd[2];pid_t childpid;pipe(fd);if ((childpid = fork()) == -1) {perror("fork");exit(1);}.. }
- 如果父进程要从子进程接收数据,父进程应关闭向管道写入的描述符
fd[1]
,而子进程应该关闭从管道读出的描述符fd[0]
;如果父进程要向子进程发送数据,则父进程应关闭从管道读出的描述符fd[0]
,而子进程应该关闭向管道写入的描述符fd[1]
; - 由于管道描述符在父进程和子进程之间是共享的,所以我们要确保关闭掉我们不需要的管道末端,从技术上讲,如果不需要的管道末端没有关闭,则永远不会返回
EOF
; - 下面代码假定父进程要从子进程接收数据:
#include <stdio.h> #include <unistd.h> #include <sys/types.h>main() {int fd[2];pid_t childpid;pipe(fd);if ((childpid = fork()) == -1) {perror("fork");exit(1);}if (childpid == 0) {/* Child process closes up input side of pipe */close(fd[0]);} else {/* Parent process closes up output side of pipe */close(fd[1]);}.. }
- 如前所述,建立了管道以后,就可以像对待普通文件描述符一样对待管道描述符;
- 源程序:pipe.c(点击文件名下载源程序)演示了子进程向父进程发送信息:Hello, world!
4 在管道上使用 dup()
-
大多数已有的
Linux
命令或者自己编写的程序,其默认的输入设备往往是STDIN
,而输出设备是STDOUT
,当我们希望在程序中用某个Linux
命令处理数据时,往往不太好获得命令的输出,或者不好把数据传送给这个程序,这时候管道可以发挥作用; -
比如
Linux
命令sort
,在没有其它参数时,其默认的输入设备就是STDIN
,当我们在程序中希望使用sort
处理一组数据时,我们可以设法把STDIN
连接到管道的输出端,这样,我们向管道中的一端写入数据时,管道的另一端已经启动的sort
就可以从STDIN
读到数据并进行处理; -
系统调用
dup()
和dup2()
可以帮助我们实现这个想法;先看一下这两个系统调用的说明; -
系统调用: dup();
原型:#include <unistd.h>int dup(int oldfd); 说明:dup() 系统调用创建文件描述符 oldfd 的副本,使用编号最小的未使用的文件描述符作为新描述符。 返回:调用成功则返回新描述符调用失败则返回 -1errno = EBADF (oldfd is not a valid descriptor) EBADF (newfd is out of range)EMFILE (too many descriptors for the process) 备注:oldfd 不会被关闭,新描述符和 oldfd 都可以使用。
-
系统调用:dup2();
原型:#include <unistd.h>int dup2(int oldfd, int newfd); 说明:dup2() 系统调用与 dup() 相似,创建文件描述符 oldfd 的副本,但它不使用编号最小的未使用文件描述符,而是使用 newfd 中指定的文件描述符;如果文件描述符 newfd 先前已打开,该调用会首先将其关闭然后再使用。 返回:调用成功则返回新描述符调用失败则返回 -1errno = EBADF (oldfd is not a valid descriptor)EBADF (newfd is out of range)EMFILE (too many descriptors for the process)备注:使用 dup2(),oldfd 会被关闭
-
在子进程中,使用
dup2()
将管道的输出(fd[0]
)复制到STDIN
上,并关闭STDIN
,然后用exec()
启动sort
时,当sort
从STDIN
读入数据时,实际上是从管道中读出数据,当我们从父进程向管道中写入数据时,这个数据将被sort
读取并处理; -
为了搞清楚这种用法,请自行学习 Linux 命令 sort,可以用在线手册
man sort
了解该命令的详细信息; -
下图或许可以更直观地描述这种使用方法:
-
源程序:pipe-dup-stdin.c(点击文件名下载源程序)演示了在管道中使用
dup2()
将 fd[0] 复制到STDIN
的方法:子进程中把 fd[0] 复制到
STDIN
,然后启动sort
,父进程向管道中写入若干个单词,每个单词以\n
结尾,sort
从STDIN
读入数据,实际上是从管道中读入数据,所以sort
程序会对这些单词进行排序,并把结果写入文件sort.log
中,程序运行完毕后,使用cat sort.log
可以看到经过排序的单词; -
同样道理,也可以在子进程中把管道的输入端(
fd[1]
)复制到STDOUT
上,这样,当子进程中启动的程序向STDOUT
输出时,实际上是在向管道上写入数据; -
源程序:pipe-dup-stdout.c(点击文件名下载源程序)演示了在管道中使用
dup2()
将 fd[1] 复制到STDOUT
的方法:子进程中把
fd[1]
复制到STDOUT
,然后启动uname
,uname -r
会输出一个字符串到STDOUT
,实际上是写入到了管道中,父进程从管道中收到了这个字符串并显示出来;子进程中把管道的输入端复制到
STDOUT
后,在子进程中启动任何程序,在主进程中通过读取管道都可以轻易地获得这个程序的输出,比如我们要知道当前系统的是不是 64 位系统,那我们在子进程中启动命令uname -m
,如果主进程在管道上读出的内容是x86_64
,则系统无疑是64位的。
5 使用管道的简单方法
-
上面介绍的在程序中使用管道获取一个外部程序的输出(或者向一个外部程序输入数据)的方法看上去不仅繁琐,而且绕的弯也比较多,其实使用管道还有更为简单的方法;
-
使用标准库函数
popen()
可以很容易地使用管道;库函数:popen();原型:#include <stdio.h>FILE *popen (char *command, char *type); 说明:popen() 函数通过创建一个管道,调用 fork 产生一个子进程,执行 shell 运行命令来开启一个进程。 返回:调用成功则返回一个标准 I/O 流调用 fork() 或 pipe() 失败则返回 NULL
-
该标准库函数通过内部调用
pipe()
创建匿名管道,然后fork()
一个子进程,执行shell
,并在shell
中执行 “command” 参数;数据流的方向由第二个参数type
确定,type
可以是 “r” 或 “w”,表示读或写,不可能两者兼而有之!在Linux
下,管道将以type
参数的第一个字符指定的模式打开,如果您将type
设置为 “rw”,该函数会以 “r” (读)模式打开管道。 -
与直接使用
pipe()
系统调用相比,这个库函数为我们做了很多繁琐的工作,但却让我们失去了对整个过程的精细控制; -
该函数直接使用了 Bourne shell(bash), 所以在
command
参数中可以使用shell
元字符以及元字符扩展(包括通配符); -
使用
popen()
创建的管道必须使用pclose()
关闭;popen()/pclose()
与标准文件流I/O函数fopen()/fclose()
非常相似。库函数:pclose();原型:#include <stdio.h>int pclose(FILE *stream); 说明:pclose()函数等待相关进程终止,并返回由 wait4() 返回的命令退出状态。 返回:返回 wait4() 调用的退出状态码如果 stream 不合法,或者 wait() 执行失败,则返回 -1备注:等待管道进程退出,然后关闭文件 I/O 流
-
pclose()
函数对由popen()
派生的进程执行wait4()
,当wait4()
返回时,它会销毁管道和文件流; -
源程序:pipe-popen.c(点击文件名下载源程序)完成与前面的例子 pipe-dup-stdin.c 一样的功能,但看上去要简单的多;
-
由于
popen()
使用shell
来执行命令,因此shell
扩展字符和元字符都可以使用,此外,使用 popen() 打开管道时,可以使用一些高级技术来执行命令,例如重定向,甚至输出管道;以下调用示例分别使用了扩展字符、重定向和输出管道:popen("ls ~scottb", "r"); popen("sort > /tmp/foo", "w"); popen("sort | uniq | more", "w");
-
源程序:pipe-popen2.c(点击文件名下载源程序)打开了两个管道,一个用于
ls
命令,另一个用于sort
命令; -
下面这个例子试图编写一个通用的管道程序,源程序文件:pipe-popen3.c(点击文件名下载源程序)
- 使用方法为:
./pipe-popen3 [command] [filename]
- 该程序会首先打开文件
filename
- 然后使用
popen()
以写方式打开command
管道 - 从
filename
中读出内容并写入管道 - 可以尝试用以下方式测试这个例子
./pipe-popen3 sort pipe-popen3.c ./pipe-popen3 cat pipe-popen3.c ./pipe-popen3 more pipe-popen3.c ./pipe-popen3 cat pipe-popen3.c | grep main
- 使用方法为:
6 管道的原子操作
- 所谓“原子操作”,是指一个或一系列不可中断的操作,就是说一个原子操作一旦开始执行就不能被中断,直至执行完毕;
- POSIX 标准规定了管道上原子操作的最大缓冲区大小是 512 字节,定义在头文件:
bits/posix1_lim.h
中:#define _POSIX_PIPE_BUF 512
- 根据这一定义,如果一次写入/读出管道的操作大于 512 字节,操作将是非“原子操作”,也就是写入/读出的数据可能会被分割;
- 在 Linux 下,定义的管道上的原子操作的最大缓冲区大小为:4096 字节,定义在头文件:
linux/limits.h
中:#define PIPE_BUF 4096 /* # bytes in atomic write to a pipe */
- 显然,在我们目前的环境下,在管道上进行不大于 4096 字节的读/写操作是原子操作;
- 在多进程环境下,原子操作对管道的读/写操作非常重要,当一个进程写入管道的数据大于阈值时,其写入过程中间会中断,操作系统会产生进程调度,如果这时其它进程也向这个管道写入数据,那么写入管道的数据会产生混乱。
7 匿名管道的其它说明
- 尽管管道是半双工的,但是,打开两个管道,并在子进程中合理地重新分配描述符,可以构建出一个类似全双工的管道;
pipe()
调用必须在fork()
调用之前进行,否则描述符将不会被子进程继承;- 使用匿名管道进行通信的进程都必须有一个共同的祖先,而且这个祖先必须是管道的创建者,由于管道位于内核中,不在管道创建者祖先中的进程都无法对其进行寻址,这与命名管道(FIFO)是不同;
- 由于使用管道的一些限制,在进程间进行通讯时,管道实际上并不是一个常用的方法,但是,如果需要使用已有的 Linux 命令处理数据,或者从 Linux 命令获得结果数据,管道不失为一个好的选择;
- 匿名管道的生命周期与创建它的进程的生命周期一致,当进程结束时,其创建的匿名管道也将被销毁。
相关文章:

IPC之一:使用匿名管道进行父子进程间通信的例子
IPC 是 Linux 编程中一个重要的概念,IPC 有多种方式,本文主要介绍匿名管道(又称管道、半双工管道),尽管很多人在编程中使用过管道,但一些特殊的用法还是鲜有文章涉及,本文给出了多个具体的实例,每个实例均附…...

前端将页面转化为图片---进行下载导出、打印等功能
1.需要实现一个将div页面的东西导出,使用到了html2canvas 官网: 配置型 | HTML2CANVAS 中文文档 (allenchinese.github.io) 2.下载html2canvas npm install --save html2canvas 3.导入使用到的页面 import html2canvas from html2canvas 4.创建图片基础应用 …...

docker安装code-service在线开发vscode工具及node版本过低问题
docker安装code-service 拉去镜像 docker pull codercom/code-server创建项目存放映射路径 mkdir /data/code-service/project运行 这里不唯一,但注意密码 docker run -itd --name code-service -u root -p 1024:8080 -v /data/code-service/project:/home/cod…...

C++ 多态深入解析
文章目录 前言一、什么是多态二、如何实现多态三、代码讲解四、静态联编,动态联编总结 前言 在C编程中,多态性(Polymorphism)是一种重要的概念,它允许基于对象的实际类型来调用不同的函数。多态性提供了灵活性和可扩展…...

C#使用EmguCV播放视频
目录 一、前言 1、简介 2、测试工程代码下载链接 3、EmguCV 库文件下载链接 二、工程环境配置 1、EmguCV控件添加引用 (1)窗口控件添加 (2)相关Dll文件添加添加引用 (3)工程运行基础文件夹添加 &a…...

LeetCode150道面试经典题-买卖股票的最佳时机(简单)
1、题目 给定一个数组 prices ,它的第 i 个元素 prices[i] 表示一支给定股票第 i 天的价格。 你只能选择 某一天 买入这只股票,并选择在 未来的某一个不同的日子 卖出该股票。设计一个算法来计算你所能获取的最大利润。 返回你可以从这笔交易中获取的…...

【积水成渊】CSS磨砂玻璃效果和渐变主题色文字
大家好,我是csdn的博主:lqj_本人 lqj_本人_python人工智能视觉(opencv)从入门到实战,前端,微信小程序-CSDN博客 最新的uniapp毕业设计专栏也放在下方了: https://blog.csdn.net/lbcyllqj/category_12346639.html?spm1…...

JVM、JRE、JDK三者之间的关系
JVM、JRE和JDK是与Java开发和运行相关的三个重要概念。 再了解三者之前让我们先来了解下java源文件的执行顺序: 使用编辑器或IDE(集成开发环境)编写Java源文件.即demo.java程序必须编译为字节码文件,javac(Java编译器)编译源文件为demo.class文件.类文…...

input 标签的 type 属性有哪些值?分别表示什么意思?
聚沙成塔每天进步一点点 ⭐ 专栏简介⭐ type值以及作用⭐ 写在最后 ⭐ 专栏简介 前端入门之旅:探索Web开发的奇妙世界 记得点击上方或者右侧链接订阅本专栏哦 几何带你启航前端之旅 欢迎来到前端入门之旅!这个专栏是为那些对Web开发感兴趣、刚刚踏入前端…...

(十五)大数据实战——hive的安装部署
前言 Hive是由Facebook开源,基于Hadoop的一个数据仓库工具,可以将结构化的数据文件映射为一张表,并提供类SQL查询功能。本节内容我们主要介绍一下hive的安装与部署的相关内容。 正文 上传hive安装包到hadoop101服务器/opt/software目录 解…...

MySQL安装和卸载
1.MySQL概述 MySQL概述 MySQL是一个[关系型数据库管理系统],由瑞典MySQL AB 公司开发,2008年被sun公司收购, 2009sun又被oracle收购,所以属于 Oracle 旗下产品。MySQL 是最流行的关系型数据库管理系统之一,在 WEB 应用…...

ELK、ELFK日志分析系统
菜单一、ELK简介1.1 ELK组件说明1.1.1 ElasticSearch1.1.2 Kiabana1.1.3 Logstash 1.2 可以添加的其它组件1.2.1 Filebeat1.2.2 缓存/消息队列(redis、kafka、RabbitMQ等)1.2.3 Fluentd 1.3 为什么要用ELK1.4 完整日志系统的基本特征1.5 ELK 的工作原理 …...

JVM基础篇-StringTable
StringTable 特性 常量池中的字符串仅是符号,第一次用到时才变为对象 利用串池的机制,来避免重复创建字符串对象 字符串变量拼接的原理是 StringBuilder (1.8) 字符串常量拼接的原理是编译期优化 可以使用 intern 方法&#…...

探秘手机隐藏的望远镜功能:开启后,观察任何你想看的地方
当今的智能手机不仅仅是通信工具,它们蕴藏着各种隐藏的功能,其中之一就是让你拥有望远镜般的观察能力。是的,你没有听错!今天我们将探秘手机中隐藏的望远镜功能,这项神奇的功能可以让你打开后,轻松观察任何…...

正运动亮相2023半导体设备材料与核心部件展示会,助力半导体产业高速高精应用
■展会名称: 第11届(2023)半导体设备材料与核心部件展示会 ■展会日期 2023年8月9日-11日 ■展馆地点 无锡太湖国际博览中心A6馆 ■展位号 A6-A361 正运动技术,作为国内领先的运动控制企业,将于2023年8月9日参加…...

如何在MongoDB中添加新用户
如何在MongoDB中添加新用户? MongoDB是一款流行的NoSQL数据库,它的可扩展性强,可进行分布式部署,且具有高可用性。其许多优势使得越来越多的企业和组织选择MongoDB作为其数据库系统。本文将介绍如何在MongoDB中添加新用户。 第一步…...

幻读怎么复现
大家好,我是想想。 很久没有给大家分享技术了,主要在计划一些事情,几乎没什么时间爽文了。 今天从实操上实现了MySQL事务隔离复现问题,就记录分享给大家吧。 正文 我们知道,著名的四大事务特性ACID特性 Atomicity…...

无脑入门pytorch系列(二)—— torch.mean
本系列教程适用于没有任何pytorch的同学(简单的python语法还是要的),从代码的表层出发挖掘代码的深层含义,理解具体的意思和内涵。pytorch的很多函数看着非常简单,但是其中包含了很多内容,不了解其中的意思…...

ansible-kubeadm在线安装高可用K8S集群v1.19-v1.20版本
ansible可以安装的KS8版本如下: 请按照此博客中的内容操作后,才可以通过下面的命令查询到版本。 [rootk8s-master01 ~]# yum list kubectl --showduplicates | sort -r kubectl.x86_64 1.20.0-0 kubern…...

Cesium entity 渐隐渐显、闪烁
点entity function f2(){var x1;var flogtrue;//闪烁//var x0;var flogfalse;//渐显viewer.entities.add({name:"圆点point闪烁",position:Cesium.Cartesian3.fromDegrees(116.200.03,39.530.03,0),point : {show : true, // defaultcolor :new Cesium.CallbackProp…...

LISA:通过大语言模型进行推理分割
论文:https://arxiv.org/pdf/2308.00692 代码:GitHub - dvlab-research/LISA 摘要 尽管感知系统近年来取得了显著的进步,但在执行视觉识别任务之前,它们仍然依赖于明确的人类指令来识别目标物体或类别。这样的系统缺乏主动推理…...

opencv基础40-礼帽运算(原始图像减去其开运算)cv2.MORPH_TOPHAT
礼帽运算是用原始图像减去其开运算图像的操作。礼帽运算能够获取图像的噪声信息,或者得到比原始图像的边缘更亮的边缘信息。 例如,图 8-22 是一个礼帽运算示例,其中: 左图是原始图像。中间的图是开运算图像。右图是原始图像减开运…...

php中的array_filter()函数
php中的array_filter()函数用于筛选数组中的元素,并返回一个新的数组,新数组的元素是所有返回值为true的原数组元素。 array_filter()函数的使用语法如下: array_filter ( array $array [, callable $callback [, int $flag 0 ]] ) : array…...

ArcGIS Pro基础:【按顺序编号】工具实现属性字段的编号自动赋值
本次介绍一个字段的自动排序编号赋值工具,基于arcgis 的字段计算器工具也可以实现类似功能,但是需要自己写一段代码实现, 相对而言不是很方便。 如下所示,该工具就是【编辑】下的【属性】下的【按顺序编号】工具。 其操作方法是…...

neo4j终端操作
1】进入容器 (base) xiaokkkxiaokkkdeMacBook-Pro ~ % docker exec -it 77ed5fe2b52e /bin/bash 2】启动、停止neo4j root77ed5fe2b52e:/var/lib/neo4j/bin# ./neo4j start Neo4j is already running (pid:7). Run with --verbose for a more detailed error message.root7…...

【深度学习】在 MNIST实现自动编码器实践教程
一、说明 自动编码器是一种无监督学习的神经网络模型,主要用于降维或特征提取。常见的自动编码器包括基本的单层自动编码器、深度自动编码器、卷积自动编码器和变分自动编码器等。 其中,基本的单层自动编码器由一个编码器和一个解码器组成,编…...

SpringBoot3基础用法
技术和工具「!喜新厌旧」 一、背景 最近在一个轻量级的服务中,尝试了最新的技术和工具选型; 即SpringBoot3,JDK17,IDEA2023,Navicat16,虽然新的技术和工具都更加强大和高效,但是适应采坑的过程…...

6、移除链表元素
方法1:原链表删除元素 伪代码: 首先判断头节点是否是待删除元素。(头节点和其他节点的删除方法不一样) while(head ! null && head->value target) //如果链表为 1 1 1 1 1,要删除元素1时用if就会失效 {h…...

大厂容器云实践之路(一)
1-华为CCE容器云实践 华为企业云 | CCE容器引擎实践 ——从IaaS到PaaS到容器集群 容器部署时代的来临 IaaS服务如日中天 2014-2015年,大家都在安逸的使用IaaS服务; 亚马逊AWS的部署能力方面比所有竞争对手…...

《合成孔径雷达成像算法与实现》Figure3.1
代码复现如下: clc close all clear all%参数设置 B 5.80e6; %信号带宽 T 7.26e-6; %脉冲持续时间 K B/T; %线性调频频率 alpha 5; %过采样率 F alpha*B; %采样频率 N F*T; %采样点数 dt T/N; …...