【Linux初阶】进程间通信介绍 管道
🌟hello,各位读者大大们你们好呀🌟
🍭🍭系列专栏:【Linux初阶】
✒️✒️本篇内容:进程间通信介绍,管道概述,匿名管道+应用,命名管道+应用
🚢🚢作者简介:计算机海洋的新进船长一枚,请多多指教( •̀֊•́ ) ̖́-
文章目录
- 一、进程间通信介绍
- 1.进程间通信目的
- 2.进程间通信发展
- 3.进程间通信分类
- 管道
- System V IPC
- POSIX IPC
- 二、管道概述
- 1.什么是管道
- 2.进程间通信的原理
- 3.站在文件描述符角度-深度理解管道
- 三、匿名管道
- 1.匿名管道概述及代码实现
- 2.管道的读写特征(4种情况)
- 3.管道的特征(5种特征)
- 四、匿名管道应用
- 1.匿名管道在命令行中的应用
- 2.基于匿名管道的进程池设计
- 五、命名管道
- 1.命名管道概述
- 2.创建一个命名管道
- (1)命令行创建命名管道
- (2)程序创建命名管道
- 3.匿名管道与命名管道的区别
- 4.命名管道的打开规则
- 六、用命名管道实现server&client通信
- 结语
一、进程间通信介绍
1.进程间通信目的
数据传输
:一个进程需要将它的数据发送给另一个进程(进程具有独立性,通信成本不低)。资源共享
:多个进程之间共享同样的资源。通知事件
:一个进程需要向另一个或一组进程发送消息,通知它(它们)发生了某种事件(如进程终止时要通知父进程)。进程控制
:有些进程希望完全控制另一个进程的执行(如Debug进程),此时控制进程希望能够拦截另一个进程的所有陷入和异常,并能够及时知道它的状态改变。
2.进程间通信发展
- 管道
- System V进程间通信(聚焦本地通信)
- POSIX进程间通信(让通信过程可以跨主机)
3.进程间通信分类
管道
- 匿名管道pipe
- 命名管道
System V IPC
- System V 消息队列
- System V 共享内存
- System V 信号量
POSIX IPC
- 消息队列
- 共享内存
- 信号量
- 互斥量
- 条件变量
- 读写锁
二、管道概述
1.什么是管道
- 管道是Unix中最古老的进程间通信的形式。
- 我们把从一个进程连接到另一个进程的一个数据流称为一个“管道"。
- 管道分
匿名管道
和命名管道
。
2.进程间通信的原理
我们知道,进程间通信需要一定的成本,那么这个成本是怎么产生的呢?这就要我们理解通信的本质了:1.OS直接或间接给通信双方的进程提供“内存空间”
;2.要通信的进程,必须看到同一份公共的资源
。
这个很好理解,因为进程具有独立性,所以需要有一个公共的地方让两个进程完成信息或资源的传输。我们所学的不同的通信种类,就是以操作系统提供的不同模块进行区别分类的!
任何一个文件(struct file)都有 file的操作方法
、属于自己的内核缓冲区
、strcut Page[]
。当我们进行 fork创建子进程的时候,我们会将父进程的 task_strcut
和 文件描述符表
拷贝一份,子进程文件描述符表指向的文件在不做修改的情况下和父进程文件描述符表指向的文件相同。此时,我们就能让父子两个不同的进程看到同一份资源了。
在能看到同一份资源的基础上,如果父进程可以向文件缓冲区中写入,子进程能从文件缓冲区中读取,不就完成了进程间的通信
了吗。我们将操作系统提供的这个内核级文件,我们称之为管道文件
。
进程间通信的数据不会刷新到磁盘,而是在OS内部完成
,因为刷新到磁盘会大大降低通信的速度。管道文件是一个内存级文件,通信只需要在OS内部完成,因此我们可以不关心它在磁盘的路径,甚至这个文件可以是虚拟出来的。
3.站在文件描述符角度-深度理解管道
下面我们一起来认识一下管道通信的基本原理:
上图的步骤执行完之后,就可以实现将父进程的数据传输到子进程中去了。通过对上面图解的学习,我们不难知道:管道通常只能进行单向数据通信!
单向通信相比于双向通信更简单且更具有依赖性。
最终,设计者们根据这个方案的特性(有出入口、单向传输),为这个方案/方法取名为 - 管道
。
三、匿名管道
1.匿名管道概述及代码实现
我们知道通信的实现需要两个步骤:1.让不同的进程拥有一份共享资源
;2.通信
。在文章之前的内容中,我们已经对第一点的原理进行了讲解,下面我将带着大家重一起学习管道的具体实现(开辟共享资源+通信)。
匿名管道:目前能用来进行父子进程间的通信
。
#include <unistd.h>
功能:创建一无名管道
原型
int pipe(int fd[2]);
参数
fd:文件描述符数组,其中fd[0]表示读端, fd[1]表示写端
返回值:成功返回0,失败返回错误代码
因为文件描述符0,1,2已经被占用,因此在理想情况下也需要用3,4代表读取和写入端,由于我们不清楚系统申请读写的顺序,所以我们习惯上直接使用fd[0]表示读端, fd[1]表示写端。
读取和写入端巧记:[0]: 读取,嘴巴,读书的;[1]: 写入,钢笔,写的;
代码示例:
#include <iostream>
#include <cstdio> //snprintf
#include <cstring> //write
#include <string>
#include <cassert>
#include <unistd.h> //sleep wait
#include <sys/types.h>
#include <sys/wait.h>using namespace std;// 父进程进行读取,子进程进行写入
int main()
{// 第一步:创建管道文件,打开读写端int fds[2];int n = pipe(fds);assert(n == 0);// 第二步: forkpid_t id = fork();assert(id >= 0);if (id == 0){// 子进程进行写入 - 关闭读close(fds[0]);// 子进程的通信代码const char* s = "我是子进程,我正在给你发消息";int cnt = 0;while (true){cnt++;char buffer[1024]; // 只有子进程能看到!snprintf(buffer, sizeof buffer, "child->parent say: %s[%d][%d]", s, cnt, getpid());//格式化输出// 写端写满的时候,再写会阻塞,等对方进行读取!write(fds[1], buffer, strlen(buffer));cout << "count: " << cnt << endl;//sleep(2); - 用于测试写端阻塞}// 子进程close(fds[1]); // 子进程关闭写端fdcout << "子进程关闭自己的写端" << endl;exit(0);}// 父进程进行读取close(fds[1]);// 父进程的通信代码while (true){//sleep(2); - 用于测试读端阻塞char buffer[1024];// 如果管道中没有了数据,读端在读,默认会直接阻塞当前正在读取的进程!ssize_t s = read(fds[0], buffer, sizeof(buffer) - 1); //buffer如果满了,s代表下一个未被使用的字节数if (s > 0){buffer[s] = 0;cout << "Get Message# " << buffer << " | my pid: " << getpid() << endl;}else if (s == 0){//读到文件结尾 - 读完之后s为0 - 跳出循环结束读取cout << "read: " << s << endl;break;}break;}close(fds[0]);cout << "父进程关闭读端" << endl;int status = 0;n = waitpid(id, &status, 0);assert(n == id);cout << "pid->" << n << " : " << (status & 0x7F) << endl;return 0;
}
———— 我是一条知识分割线 ————
2.管道的读写特征(4种情况)
读写特征:
-
管道是一个固定大小的缓冲区
。 -
读慢、写快
:写端将缓冲区写满的时候,再写(写端)会阻塞,需要等对方进行读取! -
读快、写慢
:如果管道中没有了数据,读端在读,默认会直接阻塞当前正在读取的进程! -
写关闭、读到0
:根据自己代码的判断,进行不同的操作;
-
读关闭、一直写
:为避免资源浪费,OS会关闭写端,即给写进程发送信号,终止写端。
———— 我是一条知识分割线 ————
3.管道的特征(5种特征)
- 管道的生命周期随进程;
- 管道可以用来实现具有血缘关系(父子、兄弟)进程之间的通信,常用于父子进程通信;
- 管道是面向字节流的(网路);
- 一般而言,内核会对管道操作进行同步与互斥,同步与互斥机制是一种对共享资源的一种保护方案;
- 管道是半双工的,即数据只能向一个方向流动;需要双方通信时,需要建立起两个管道;
四、匿名管道应用
1.匿名管道在命令行中的应用
我们在命令行中经常会使用这样的指令
cat file.txt | grep "hello"
它实质上就是通过树划线对指令进行了分割,让操作系统 fork两个子进程对分割好的两条指令进行实现,再通过兄弟管道之间的进程通信,最终变成我们看到的样子的!
2.基于匿名管道的进程池设计
我们可以用匿名管道设计一个简易的进程池
,即用一个辅助进程控制其他进程完成工作。在父进程(写端)我们通过给不同的子进程(读端)传输不同的命令操作码(commandCode),让对应的子进程根据命令操作码实现不同的功能,我们让命令操作码为 4字节。
场景示例:我们可以给 2号进程发送命令操作码和对应的运算数据,唤醒 2号进程之后,让进程对命令操作码对应的功能(运算)进行实现。
我们希望:我们可以将我们的任务均衡的下发给每一个子进程,让子进程执行 - 负载均衡(单机)。
代码示例
- Makefile
processpool:processPool.cc
g++ - o $@ $ ^ -std = c++11
.PHONY:clean
clean :
rm - rf processpool
- ProcessPool.cc
#include <iostream>
#include <string>
#include <vector>
#include <cstdlib>
#include <cassert>
#include <ctime>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>#define MakeSeed() srand((unsigned long)time(nullptr) ^ getpid() ^ 0x171237 ^ rand() % 1234)//随机数种子#define PROCSS_NUM 10///子进程要完成的某种任务 -- 模拟一下/
// 函数指针 类型
typedef void (*func_t)();void downLoadTask()
{std::cout << getpid() << ": 下载任务\n"<< std::endl;sleep(1);
}void ioTask()
{std::cout << getpid() << ": IO任务\n"<< std::endl;sleep(1);
}void flushTask()
{std::cout << getpid() << ": 刷新任务\n"<< std::endl;sleep(1);
}void loadTaskFunc(std::vector<func_t>* out) //加载方法表
{assert(out);out->push_back(downLoadTask);out->push_back(ioTask);out->push_back(flushTask);
}/下面的代码是一个多进程程序//
class subEp // Endpoint
{
public:subEp(pid_t subId, int writeFd): subId_(subId), writeFd_(writeFd){char nameBuffer[1024]; //定义对象名称snprintf(nameBuffer, sizeof nameBuffer, "process-%d[pid(%d)-fd(%d)]", num++, subId_, writeFd_);name_ = nameBuffer;}public:static int num; //用于区别不同对象(整体)std::string name_; //管道+子进程的整体名称pid_t subId_; //子进程pidint writeFd_; //父进程写端的文件描述符
};int subEp::num = 0;int recvTask(int readFd) //读端接口
{int code = 0;ssize_t s = read(readFd, &code, sizeof code);if (s == 4) return code;else if (s <= 0) return -1;else return 0;
}void sendTask(const subEp& process, int taskNum)
{std::cout << "send task num: " << taskNum << " send to -> " << process.name_ << std::endl;int n = write(process.writeFd_, &taskNum, sizeof(taskNum));assert(n == sizeof(int));(void)n;
}void createSubProcess(std::vector<subEp>* subs, std::vector<func_t>& funcMap)
{std::vector<int> deleteFd; //管道+子进程整体选择(可以选择不同整体)for (int i = 0; i < PROCSS_NUM; i++){int fds[2];int n = pipe(fds);assert(n == 0);(void)n;// 父进程打开的文件,是会被子进程共享的// 你试着多想几轮 - 子进程会继承上一个子进程的写端pid_t id = fork();if (id == 0){for (int i = 0; i < deleteFd.size(); i++) close(deleteFd[i]); //关闭从上一个子进程继承而来的写端// 子进程, 进行处理任务close(fds[1]);while (true){// 1. 获取命令码,如果没有发送,我们子进程应该阻塞int commandCode = recvTask(fds[0]);// 2. 完成任务if (commandCode >= 0 && commandCode < funcMap.size())funcMap[commandCode]();else if (commandCode == -1) break;}exit(0);}close(fds[0]);subEp sub(id, fds[1]); //构造一个整体对象subs->push_back(sub); //将新整体链接到vector中deleteFd.push_back(fds[1]); //保存文件描述符,方便以后关闭从上一个子进程继承而来的写端}
}void loadBlanceContrl(const std::vector<subEp>& subs, const std::vector<func_t>& funcMap, int count)
{int processnum = subs.size();int tasknum = funcMap.size();bool forever = (count == 0 ? true : false);while (true){// 1. 选择一个子进程 --> std::vector<subEp> -> index - 随机数(负载均衡)int subIdx = rand() % processnum;// 2. 选择一个任务 --> std::vector<func_t> -> indexint taskIdx = rand() % tasknum;// 3. 任务发送给选择的进程sendTask(subs[subIdx], taskIdx);sleep(1); //防止发送过快if (!forever){count--;if (count == 0) break;}}// write quit -> read 0for (int i = 0; i < processnum; i++) close(subs[i].writeFd_); // waitpid();
}void waitProcess(std::vector<subEp> processes)
{int processnum = processes.size();for (int i = 0; i < processnum; i++){waitpid(processes[i].subId_, nullptr, 0);std::cout << "wait sub process success ...: " << processes[i].subId_ << std::endl;}
}int main()
{MakeSeed(); //生成随机数种子// 1. 建立子进程并建立和子进程通信的信道, 有bug的,但是不影响我们后面编写// 1.1 加载方法表std::vector<func_t> funcMap;loadTaskFunc(&funcMap);// 1.2 创建子进程,并且维护好父子通信信道std::vector<subEp> subs;createSubProcess(&subs, funcMap);// 2. 走到这里就是父进程, 控制子进程,负载均衡的向子进程发送命令码int taskCnt = 3; // 0: 永远进行,>0: 父进程发送几次loadBlanceContrl(subs, funcMap, taskCnt);// 3. 回收子进程信息waitProcess(subs);return 0;
}
五、命名管道
1.命名管道概述
- 管道应用的一个限制就是只能在具有共同祖先(具有亲缘关系)的进程间通信。
- 如果我们想在不相关(不具有亲缘关系)的进程之间交换数据,可以使用FIFO文件来做这项工作,它经常被称为
命名管道
。 - 命名管道是一种特殊类型的文件。
2.创建一个命名管道
(1)命令行创建命名管道
$ mkfifo filename
———— 我是一条知识分割线 ————
(2)程序创建命名管道
命名管道也可以从程序里创建,相关函数有:
#include <sys/types.h>
#include <sys/stat.h>
int mkfifo(const char *filename,mode_t mode);
3.匿名管道与命名管道的区别
- 匿名管道由pipe函数创建并打开。它通过文件指针的方式,让子进程继承父进程指向的文件,再通过文件描述符的方式看到同一份资源。
- 命名管道由mkfifo函数创建,打开用open。它通过让不同进程看到同一个路径标定的文件,是它们能看到同一份资源。
- FIFO(命名管道)与pipe(匿名管道)之间唯一的区别在它们创建与打开的方式不同,一但这些工作完 成之后,它们具有相同的语义。
4.命名管道的打开规则
- 只有读端打开FIFO,写端没打开:阻塞直到有相应进程为写而打开该FIFO。
- 只有写端打开FIFO,读端没打开:阻塞直到有相应进程为读而打开该FIFO。
六、用命名管道实现server&client通信
这里我们先要搞清楚一个概念,命名管道如何让不同的进程,看到同一份资源?
可以让不同的进程打开指定名称(路径+文件名)的同一文件
。
- comm.hpp
#include <iostream>
#include <string>
#include <cstring>
#include <cerrno>
#include <cassert>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>#define NAMED_PIPE "/tmp/mypipe"bool createFifo(const std::string& path)
{umask(0);int n = mkfifo(path.c_str(), 0600);if (n == 0)return true;else{std::cout << "errno: " << errno << " err string: " << strerror(errno) << std::endl;return false;}
}void removeFifo(const std::string& path)
{int n = unlink(path.c_str()); //删除管道assert(n == 0); // debug , release 里面就没有了(void)n;
}
- client.cc(写端)
#include "comm.hpp"int main()
{std::cout << "client begin" << std::endl;int wfd = open(NAMED_PIPE, O_WRONLY);std::cout << "client end" << std::endl;if (wfd < 0) exit(1);//writechar buffer[1024];while (true){std::cout << "Please Say# ";fgets(buffer, sizeof(buffer), stdin); // abcd\nif (strlen(buffer) > 0) buffer[strlen(buffer) - 1] = 0; //去掉输入的\nssize_t n = write(wfd, buffer, strlen(buffer));assert(n == strlen(buffer));(void)n;}close(wfd);return 0;
}
- server.cc(读端)
#include "comm.hpp"int main()
{bool r = createFifo(NAMED_PIPE);assert(r);(void)r;std::cout << "server begin" << std::endl;int rfd = open(NAMED_PIPE, O_RDONLY);std::cout << "server end" << std::endl;if (rfd < 0) exit(1);//readchar buffer[1024];while (true){ssize_t s = read(rfd, buffer, sizeof(buffer) - 1);if (s > 0){buffer[s] = 0;std::cout << "client->server# " << buffer << std::endl;}else if (s == 0){std::cout << "client quit, me too!" << std::endl;break;}else{std::cout << "err string: " << strerror(errno) << std::endl;break;}}close(rfd);// sleep(10);removeFifo(NAMED_PIPE);return 0;
}
注意:命名管道文件必须读端、写端都打开才能开始运行
注意:当我们关闭写端,读端会跟着关闭(代码实现的!)
结语
🌹🌹 进程间通信介绍 & 管道 的知识大概就讲到这里啦,博主后续会继续更新更多C++ 和 Linux的相关知识,干货满满,如果觉得博主写的还不错的话,希望各位小伙伴不要吝啬手中的三连哦!你们的支持是博主坚持创作的动力!💪💪
相关文章:

【Linux初阶】进程间通信介绍 管道
🌟hello,各位读者大大们你们好呀🌟 🍭🍭系列专栏:【Linux初阶】 ✒️✒️本篇内容:进程间通信介绍,管道概述,匿名管道应用,命名管道应用 🚢&#…...
App 在macOS Catalina下提示已损坏无法打开解决办法:
App 在macOS Catalina下提示已损坏无法打开解决办法: 打开终端; 输入以下命令,回车; sudo xattr -d com.apple.quarantine /Applications/xxxx.app注意:/Applications/xxxx.app 换成你的App路径(推荐直接…...

ad+硬件每日学习十个知识点(26)23.8.6 (DCDC的降压电路、升压电路、降压-升压电路,同步整流,选型考虑同步、隔离)
文章目录 1.DCDC的降压原理2.DCDC的升压原理3.DCDC的升压和降压原理4.什么是肖特基二极管造成的死区电压?5.MOS管有死区电压么?6.DCDC的同步整流(用MOS管取代整流二极管,避免死区电压的影响)7.DCDC选型——同步与非同步…...
Elasticsearch3节点集群配置账号密码安全验证
Elasticsearch3节点集群配置账号密码安全验证 ES配置文件 rootnode1:~# grep -Ev "^#|^$" /etc/elasticsearch/elasticsearch.yml cluster.name: es-pre node.name: node1 node.master: true node.data: true path.data: /data/elk/es/data path.logs: /data/elk/…...

ffmepg滤镜
视频按顺时针方向旋转90度 ffplay -vf transpose1 -i juren-30s.mp4 ffplay -f lavfi -i testsrc -vf transpose1 -f lavfi -i testsrc这个滤镜是ffmpeg给用户的一个测试使用的视频 视频水平翻转(左右翻转) -vf hflip 实现慢速播放,声音速度是原始速度的50% ffpla…...
Linux 基础(六)常用命令 - find locate which whereis gzip gunzip tar
find & locate & which & whereis & gzip & gunzip & tar findlocatewhichwhereisgzipgunzipzip/unziptar find 在指定目录下查找文件或目录 find --help Usage: find [-H] [-L] [-P] [-Olevel] [-D help|tree|search|stat|rates|opt|exec] [path...…...
【Ajax】回调地狱解决方法
回调地狱(Callback Hell)是指在异步编程中,特别是在嵌套的回调函数中,代码变得深度嵌套、难以阅读和维护的现象。这通常发生在处理多个异步操作时,每个操作都依赖于前一个操作的结果。回调地狱使代码变得难以理解、扩展…...
解决Vue根组件设置transition失效的问题
解决Vue根组件设置transition失效的问题 1.代码 <div id"app"><!-- :name"$route.meta.transitionName" --><transition :name"animation" mode"out-in"><router-view /></transition></div>&…...
【剑指 Offer 40】最小的k个数
题目: 输入整数数组 arr ,找出其中最小的 k 个数。例如,输入 4、5、1、6、2、7、3、8 这 8 个数字,则最小的 4 个数字是 1、2、3、4。 示例: 输入:arr [3,2,1], k 2 输出:[1,2] 或者 [2,1] …...

vue3+vite在main.ts文件中引入./App.vue报错(./App.vue不是模块)
问题 如下图: 方法一 下载TypeScript Vue Plugin (Volar)插件就不报红了,看它的描述应该就是ts文件可以识别vue文件。 方法二 在src文件夹下添加env.d.ts文件,添加以下代码: declare module *.vue {import type { DefineC…...

【LeetCode】102. 二叉树的层序遍历、107. 二叉树的层序遍历 II
作者:小卢 专栏:《Leetcode》 喜欢的话:世间因为少年的挺身而出,而更加瑰丽。 ——《人民日报》 102. 二叉树的层序遍历 102. 二叉树的层序遍历 给你二叉树的根节点 root ,返回其节…...

HTML详解连载(2)
HTML详解连载(2) 专栏链接 [link](http://t.csdn.cn/xF0H3)下面进行专栏介绍 开始喽超链接作用代码示例解释经验分享 音频标签代码示例注意强调 视频标签代码示例注意强调 列表作用:布局内容排列整齐的区域。分类:无序列表&#x…...

qt事件系统源码-----定时器
qt定时器的使用一般有以下几种方式: 1、直接使用QTimer对象,绑定定时器的timeout信号; 2、使用QTimer的静态方法singleshot方法,产生一个一次性的定时事件 3、在QObject子类中,调用startTimer方法,产生定…...
【Android】ViewBinding+DataBinding+MVVM新手快速上手
为什么写这篇博客 网上大部分博客,代码量都比较大,把实际的业务都代入进去了 这篇博客的目的,就是为了讲解基本原理和使用思路,然后给出一个最简单的Demo 这里不讲解具体用法,那样篇幅会太长,直接看Demo…...

生成式人工智能模型:提升营销分析用户体验
使用生成式人工智能来改善分析体验,使业务用户能够询问有关我们数据平台中可用数据的任何信息。 在本文中,我们将解释如何使用新的生成式人工智能模型 ( LLM ) 来改善业务用户在我们的分析平台上的体验。假设我们为零售销售经理提供 Web 应用程序或移动应…...

【并发编程】无锁环形队列Disruptor并发框架使用
Disruptor 是苹国外厂本易公司LMAX开发的一个高件能列,研发的初夷是解决内存队列的延识问顾在性能测试中发现竟然与10操作处于同样的数量级),基于Disruptor开发的系统单线程能支撑每秒600万订单,2010年在QCn演讲后,获得了业界关注…...

【C语言】初阶指针详解
大家好,我是苏貝,本篇博客带大家了解C语言中令人头疼的指针,如果大家觉得我写的不错的话,可以给我一个赞👍吗,感谢❤️ 使用的是VS2019编译器,默认为32位平台 文章目录 ①指针是什么②指针定义与…...

ElasticSearch:项目实战(1)
es环境搭建参考:ElasticSearch:环境搭建步骤_Success___的博客-CSDN博客 需求: 用户输入关键可搜索文章列表 关键词高亮显示 文章列表展示与home展示一样,当用户点击某一篇文章,可查看文章详情 思路: …...
React 实现文件分片上传和下载
React 实现文件分片上传和下载 在开发中,文件的上传和下载是常见的需求。然而,当面对大型文件时,直接的上传和下载方式可能会遇到一些问题,比如网络传输不稳定、文件过大导致传输时间过长等等。为了解决这些问题,我们…...
2023.8.13
atcoder_abc\AtCoder Beginner Contest 310\E_NAND_repeatedly //题意:给定一个n长度的01串,计算f(l,r)(l<r,l在1~n,r在1~n)的和,f的计算(ai,a(i1))运算,有0就为1,11为0 //若f(l,r)1,则f(l,r-1)为0或sr为0,即只取决于上一位的情况和当前位ÿ…...
web vue 项目 Docker化部署
Web 项目 Docker 化部署详细教程 目录 Web 项目 Docker 化部署概述Dockerfile 详解 构建阶段生产阶段 构建和运行 Docker 镜像 1. Web 项目 Docker 化部署概述 Docker 化部署的主要步骤分为以下几个阶段: 构建阶段(Build Stage):…...

【人工智能】神经网络的优化器optimizer(二):Adagrad自适应学习率优化器
一.自适应梯度算法Adagrad概述 Adagrad(Adaptive Gradient Algorithm)是一种自适应学习率的优化算法,由Duchi等人在2011年提出。其核心思想是针对不同参数自动调整学习率,适合处理稀疏数据和不同参数梯度差异较大的场景。Adagrad通…...
蓝桥杯 2024 15届国赛 A组 儿童节快乐
P10576 [蓝桥杯 2024 国 A] 儿童节快乐 题目描述 五彩斑斓的气球在蓝天下悠然飘荡,轻快的音乐在耳边持续回荡,小朋友们手牵着手一同畅快欢笑。在这样一片安乐祥和的氛围下,六一来了。 今天是六一儿童节,小蓝老师为了让大家在节…...

基于当前项目通过npm包形式暴露公共组件
1.package.sjon文件配置 其中xh-flowable就是暴露出去的npm包名 2.创建tpyes文件夹,并新增内容 3.创建package文件夹...
【Web 进阶篇】优雅的接口设计:统一响应、全局异常处理与参数校验
系列回顾: 在上一篇中,我们成功地为应用集成了数据库,并使用 Spring Data JPA 实现了基本的 CRUD API。我们的应用现在能“记忆”数据了!但是,如果你仔细审视那些 API,会发现它们还很“粗糙”:有…...
LLM基础1_语言模型如何处理文本
基于GitHub项目:https://github.com/datawhalechina/llms-from-scratch-cn 工具介绍 tiktoken:OpenAI开发的专业"分词器" torch:Facebook开发的强力计算引擎,相当于超级计算器 理解词嵌入:给词语画"…...

佰力博科技与您探讨热释电测量的几种方法
热释电的测量主要涉及热释电系数的测定,这是表征热释电材料性能的重要参数。热释电系数的测量方法主要包括静态法、动态法和积分电荷法。其中,积分电荷法最为常用,其原理是通过测量在电容器上积累的热释电电荷,从而确定热释电系数…...
动态 Web 开发技术入门篇
一、HTTP 协议核心 1.1 HTTP 基础 协议全称 :HyperText Transfer Protocol(超文本传输协议) 默认端口 :HTTP 使用 80 端口,HTTPS 使用 443 端口。 请求方法 : GET :用于获取资源,…...

在Mathematica中实现Newton-Raphson迭代的收敛时间算法(一般三次多项式)
考察一般的三次多项式,以r为参数: p[z_, r_] : z^3 (r - 1) z - r; roots[r_] : z /. Solve[p[z, r] 0, z]; 此多项式的根为: 尽管看起来这个多项式是特殊的,其实一般的三次多项式都是可以通过线性变换化为这个形式…...
腾讯云V3签名
想要接入腾讯云的Api,必然先按其文档计算出所要求的签名。 之前也调用过腾讯云的接口,但总是卡在签名这一步,最后放弃选择SDK,这次终于自己代码实现。 可能腾讯云翻新了接口文档,现在阅读起来,清晰了很多&…...