【Linux】进程通信篇Ⅱ:共享内存、消息队列、信号量
文章目录
- 一、共享内存
- 1.1 一些接口
- 1. shmget 函数:申请一个 system v 的共享内存块
- 2. ftok 函数:设置唯一标识码
- 3. shmctl 函数:控制 system v 的共享内存块(可以删除、查看...)
- 4. shmat 函数:将进程与共享内存块 关联\ 挂接(attach)
- 5. shmdt 函数:将进程与共享内存块 去关联(detach)
- 1.2 一些命令
- 1. ipcs - - 查看三种 ipc 资源
- 2. ipcrm -- 删除某种 ipc 资源
- 1.3 结论
- 二、消息队列
- 1. msgget 函数:创建消息队列
- 2. msgctl 函数
- 3. msgsnd 和 msgrcv 函数,发送和接收消息
- 三、信号量
- 1. semget
- 2. semctl 函数:可以获取信号量的相关属性
- 3. semop 函数:对信号量进行操作
- 总结:
一、共享内存
我们知道,进程间通信的本质就是:让不同的进程,看到同一份资源
这里要介绍的同一份资源就是:内存块,即 共享内存(shared memory,简写为 shm)
- 共享内存的原理:
- 1.创建(key 和 共享内存)
- 2.关联进程 和 取消关联
- 3.释放共享内存
内存中的每块共享内存,会有一个 struct shm 结构体
,里面放着共享内存的全部属性,OS 通过这个结构体建立链表关系来对所有的共享内存进行管理,就等于把管理 shm 的问题转化成了管理链表的问题。
故:
共享内存
=
共享内存的内核数据结构 (伪代码:struct shm)
+
真正开辟的内存空间
1.1 一些接口
1. shmget 函数:申请一个 system v 的共享内存块
头文件:
#include <sys/ipc.h>
#include <sys/shm.h>// umask的头文件如下
#include <sys/types.h>
#include <sys/stat.h>
int shmget(key_t key, size_t size, int shmflg);
参数 key:
- 使用
ftok
函数设置的唯一标识码,他虽由用户设置,却是在内核中使用的
参数 size
- 需要申请共享内存块的大小,单位为字节,不足 PAGE 页(4KB)时,会向上对齐到 PAGE 页
参数 shmflg:
选项 IPC_CREAT and IPC_EXCL
单独使用 IPC_CREAT:创建一个共享内存,如果共享内存不存在,就创建,如果已经存在就获取已经存在的共享内存并返回。
IPC_CREAT | IPC_EXCL :IPC_EXCL 必须要配合 IPC_CREAT 使用,创建一个共享内存,如果共享内存不存在,就创建,如果已经存在就出错返回
意味着,一起使用时,如果创建成功,对应的shm,一定是最新的!IPC_CREAT | IPC_EXCL | 0666 :上面的基础上,添加权限(可以配合函数 umask(0) 使用)
返回值:
- 成功会返回一个共享内存标识符,失败返回 -1
2. ftok 函数:设置唯一标识码
头文件
#include <sys/types.h>
#include <sys/ipc.h>
key_t ftok(const char *pathname, int proj_id);
参数 pathname
- 用户设置的路径
参数 proj_id
- 用户设置的项目 id
返回值:
- 根据用户传入的参数,结合一定的算法,返回一个冲突概率很低的值。ket_t 就是一个 32 位的整数,是对 int 的封装
3. shmctl 函数:控制 system v 的共享内存块(可以删除、查看…)
头文件
#include <sys/ipc.h>
#include <sys/shm.h>
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
参数 shmid
- 需要的共享内存块的 shmid
参数 cmd:
- 选项 IPC_STAT:把用户传入 shmid 的相应内核数据结构信息复制到 buf 中(在调用者有读权限的情况下,才能成功)
- 选项 IPC_RMID:删除 shmid 为传入值的共享内存块
输出型参数 buf:
- 需要得到 ipc 信息时传一个相应类型的值用来接收结果
返回值:
- 失败返回 -1,成功则根据 cmd 传入的选项返回对应的值
//The buf argument is a pointer to a shmid_ds structure, defined in <sys/shm.h> as follows:struct shmid_ds {struct ipc_perm shm_perm; /* Ownership and permissions */size_t shm_segsz; /* Size of segment (bytes) */time_t shm_atime; /* Last attach time */time_t shm_dtime; /* Last detach time */time_t shm_ctime; /* Last change time */pid_t shm_cpid; /* PID of creator */pid_t shm_lpid; /* PID of last shmat(2)/shmdt(2) */shmatt_t shm_nattch; /* No. of current attaches */...};//The ipc_perm structure is defined as follows (the highlighted fields are settable using IPC_SET):struct ipc_perm {key_t __key; /* Key supplied to shmget(2) */uid_t uid; /* Effective UID of owner */gid_t gid; /* Effective GID of owner */uid_t cuid; /* Effective UID of creator */gid_t cgid; /* Effective GID of creator */unsigned short mode; /* Permissions + SHM_DEST andSHM_LOCKED flags */unsigned short __seq; /* Sequence number */};
4. shmat 函数:将进程与共享内存块 关联\ 挂接(attach)
头文件
#include <sys/types.h>
#include <sys/shm.h>
void *shmat(int shmid, const void *shmaddr, int shmflg);
参数 shmid
- 需要的共享内存块的 shmid
参数 shmaddr:
- 用户可以选择虚拟地址作为共享内存块的起始地址
- 用户一般不定义,设为 nullptr 让 OS 自主定义即可
参数 shmflg:
- 选项 SHM_RDONLY:只读
- 0:可以读写
返回值:
- 挂接成功,返回共享内存块的虚拟地址的起始地址
5. shmdt 函数:将进程与共享内存块 去关联(detach)
头文件
#include <sys/types.h>
#include <sys/shm.h>
int shmdt(const void *shmaddr);
参数 shmaddr:
- 共享内存块的起始地址
返回值:
- 去关联成功返回 0,失败返回 -1
1.2 一些命令
1. ipcs - - 查看三种 ipc 资源
ipcs 就是进程间通信(ipc)资源
ipcs
:可以查看 消息队列、共享内存亏块、信号量
-m
:查看 共享内存块(memory)
-s
:查看 信号量(semaphore)
perms:权限
nattach:当前 ipc 挂接的进程数
2. ipcrm – 删除某种 ipc 资源
ipcrm
:删除一个 消息队列、共享内存亏块、信号量
-m
:删除一个共享内存块,后接 shmid
1.3 结论
-
两个进程管道通信一次,需要进行两次复制。而共享内存间的通信,可以让进程们直接在自己映射的地址空间中访问,减少了拷贝次数()
-
管道单方面关闭读写端会有相应的保护,而共享内存没有保护机制(同步互斥)。管道通过系统接口通信,共享内存直接通信
-
互斥:任何一个时刻,都只允许一个执行流在进行共享资源的访问,叫做加锁
-
我们把任何一个时刻,都只允许一个执行流在进行访问的共享资源,叫做 临界资源。凡是访问临界资源的代码,叫做临界区,控制进出临界区的手段造就了临界资源。
二、消息队列
1. msgget 函数:创建消息队列
头文件
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgget(key_t key, int msgflg)
参数 key:
- 使用
ftok
函数设置的唯一标识码
参数 msgflg:
选项 IPC_CREAT and IPC_EXCL
单独使用 IPC_CREAT:创建一个共享内存,如果共享内存不存在,就创建,如果已经存在就获取已经存在的共享内存并返回。
IPC_CREAT | IPC_EXCL :IPC_EXCL 必须要配合 IPC_CREAT 使用,创建一个共享内存,如果共享内存不存在,就创建,如果已经存在就出错返回
意味着,一起使用时,如果创建成功,对应的shm,一定是最新的!IPC_CREAT | IPC_EXCL | 0666 :上面的基础上,添加权限(可以配合函数 umask(0) 使用)
返回值:
- 成功则返回消息队列的标识符
2. msgctl 函数
头文件
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgctl(int msqid, int cmd, struct msqid_ds *buf);
参数 msqid
- 需要的消息队列的 msqid
参数 cmd:
- 选项 IPC_STAT:把用户传入 msqid 的相应内核数据结构信息复制到 buf 中(在调用者有读权限的情况下,才能成功)
- 选项 IPC_RMID:删除 msqid 为传入值的共享内存块
输出型参数 buf:
- 需要得到 ipc 信息时传一个相应类型的值用来接收结果
返回值:
- 成功返回 >= 0 的值,失败返回 -1
The msqid_ds data structure is defined in <sys/msg.h> as follows:struct msqid_ds {struct ipc_perm msg_perm; /* Ownership and permissions */time_t msg_stime; /* Time of last msgsnd(2) */time_t msg_rtime; /* Time of last msgrcv(2) */time_t msg_ctime; /* Time of last change */unsigned long __msg_cbytes; /* Current number of bytes inqueue (nonstandard) */msgqnum_t msg_qnum; /* Current number of messagesin queue */msglen_t msg_qbytes; /* Maximum number of bytesallowed in queue */pid_t msg_lspid; /* PID of last msgsnd(2) */pid_t msg_lrpid; /* PID of last msgrcv(2) */};The ipc_perm structure is defined as follows (the highlighted fields are settable using
IPC_SET):struct ipc_perm {key_t __key; /* Key supplied to msgget(2) */uid_t uid; /* Effective UID of owner */gid_t gid; /* Effective GID of owner */uid_t cuid; /* Effective UID of creator */gid_t cgid; /* Effective GID of creator */unsigned short mode; /* Permissions */unsigned short __seq; /* Sequence number */};
3. msgsnd 和 msgrcv 函数,发送和接收消息
头文件
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg);
参数 msqid:
- 发送和接收访问的同一个消息队列
参数 msgp:
- 发送或接收的数据块
参数 msgsz:
- 发送或接收数据块的大小
参数 msgflg:
- 选项,一般填 0 即可
参数 msgtyp:
- msgbuf 里面的 mtype
// The msgp argument is a pointer to caller-defined structure of the following general form:struct msgbuf {long mtype; /* message type, must be > 0 */char mtext[1]; /* message data */
};
三、信号量
信号量 / 信号灯(semaphore),本质 就是一个计数器,是一个描述资源数量的计数器。
举个例子:
-
比如我们任何一个执行流,像访问临界资源中的一个子资源的时候,不能直接访问,需要 先申请信号量资源(P操作),此时 count-- 。只要申请信号量成功,未来就一定能拿到一个子资源。(类似摇号)
-
然后进入进程自己的临界区,访问对应的临界资源。
-
使用完成后,进程释放信号量资源(V操作),只要将计数器增加 count++,就表示将我们对应的资源进行了归还。
至此,进程通过执行代码来申请,意味着,所有进程都得先看到信号量,信号量就是一个共享资源。(信号量保护共享资源,自己却又是一个共享资源)
故,信号量必须保证自己的 ++ - - 是原子的
也,信号量被归类到了进程间通信
信号量部分未完待续~
1. semget
头文件
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
int semget(key_t key, int nsems, int semflg);
参数 key:
- 使用
ftok
函数设置的唯一标识码,他虽由用户设置,却是在内核中使用的
参数 nsems:
- 申请信号量的个数(叫做信号量集)
参数 semflg:
选项 IPC_CREAT and IPC_EXCL
单独使用 IPC_CREAT:创建一个共享内存,如果共享内存不存在,就创建,如果已经存在就获取已经存在的共享内存并返回。
IPC_CREAT | IPC_EXCL :IPC_EXCL 必须要配合 IPC_CREAT 使用,创建一个共享内存,如果共享内存不存在,就创建,如果已经存在就出错返回
意味着,一起使用时,如果创建成功,对应的shm,一定是最新的!IPC_CREAT | IPC_EXCL | 0666 :上面的基础上,添加权限(可以配合函数 umask(0) 使用)
返回值:
- 成功会返回一个信号量计数器标识符,失败返回 -1
2. semctl 函数:可以获取信号量的相关属性
头文件
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
int semctl(int semid, int semnum, int cmd, ...);
参数 semid:
- 需要的信号量的semid
参数 semnum:
- 信号量编号
参数 cmd:
- 选项 IPC_STAT:把用户传入 msqid 的相应内核数据结构信息复制到 buf 中(在调用者有读权限的情况下,才能成功)
- 选项 IPC_RMID:删除 msqid 为传入值的共享内存块
返回值:
- 成功返回 >= 0 的值,失败返回 -1
//This function has three or four arguments, depending on cmd. When there are four, the
//fourth has the type union semun. The calling program must define this union as follows:union semun {int val; /* Value for SETVAL */struct semid_ds *buf; /* Buffer for IPC_STAT, IPC_SET */unsigned short *array; /* Array for GETALL, SETALL */struct seminfo *__buf; /* Buffer for IPC_INFO(Linux-specific) */};//The semid_ds data structure is defined in <sys/sem.h> as follows:struct semid_ds {struct ipc_perm sem_perm; /* Ownership and permissions */time_t sem_otime; /* Last semop time */time_t sem_ctime; /* Last change time */unsigned long sem_nsems; /* No. of semaphores in set */};//The ipc_perm structure is defined as follows (the highlighted fields are settable usingIPC_SET):struct ipc_perm {key_t __key; /* Key supplied to semget(2) */uid_t uid; /* Effective UID of owner */gid_t gid; /* Effective GID of owner */uid_t cuid; /* Effective UID of creator */gid_t cgid; /* Effective GID of creator */unsigned short mode; /* Permissions */unsigned short __seq; /* Sequence number */};
3. semop 函数:对信号量进行操作
头文件
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
int semop(int semid, struct sembuf *sops, unsigned nsops);
参数 sops:
- 需要用户自己定义后传入,设置结构体内容,以此达到对信号量的 PV 操作等等
//Each semaphore in a System V semaphore set has the following associated values:unsigned short semval; /* semaphore value */unsigned short semzcnt; /* # waiting for zero */unsigned short semncnt; /* # waiting for increase */pid_t sempid; /* ID of process that did last op *///semop() performs operations on selected semaphores in the set indicated by semid.
//Each of the nsops elements in the array pointed to by sops specifies an operation to be performed on a single semaphore.
//The elements of this structure are of type struct sembuf, containing the following members:unsigned short sem_num; /* semaphore number */short sem_op; /* semaphore operation */short sem_flg; /* operation flags */
总结:
共享内存、消息队列、信号量 这三种 ipc 都有各自的内核数据结构体,而结构体的第一个成员都是 struct ipc_perm xx_perm
。
可以理解为 OS 将这三种 ipc 都放进了一个 struct ipc_perm* ipc_id_arr[]
指针数组中进行管理(这里只是做理解解释,实际上更复杂)。
OS 通过指针,可以找到每个结构体(同时也是每个结构的第一个成员,即 struct ipc_perm xx_perm),在其中找到 key 值就可以确定。
要访问里面的内容时,以共享内存举例
((struct shmid_ds*)ipc_id_arr[n])->other...
对指针进行强转,就可以访问到其中内容了,这也是一种多态。
相关文章:
【Linux】进程通信篇Ⅱ:共享内存、消息队列、信号量
文章目录 一、共享内存1.1 一些接口1. shmget 函数:申请一个 system v 的共享内存块2. ftok 函数:设置唯一标识码3. shmctl 函数:控制 system v 的共享内存块(可以删除、查看...)4. shmat 函数:将进程与共享…...
8.14 校招 内推 面经
绿泡泡: neituijunsir 交流裙,内推/实习/校招汇总表格 1、半导体芯片一周资讯 - 小米OPPO之后,星纪魅族调整芯片业务,今年应届生或被全部优化,英伟达2024推出比H100更快的芯片 半导体芯片一周资讯 - 小米OPPO之后&…...

阿里云服务器安装部署Docker使用教程
本文阿里云百科分享如何在云服务ECS实例上,部署并使用Docker。Docker是一款开源的应用容器引擎,具有可移植性、可扩展性、高安全性和可管理性等优势。开发者可将应用程序和依赖项打包到一个可移植的容器中,快速发布到Linux机器上并实现虚拟化…...

WebRTC | ICE详解
目录 一、Candidate种类与优先级 二、ICE策略 1. iceServers 2. iceTransportPolicy 三、P2P连接 1.Nat类型 (1)完全锥型NAT (2)IP限制锥型NAT (3)端口限制锥型NAT (4)对称…...

网络设备(防火墙、路由器、交换机)日志分析监控
外围网络设备(如防火墙、路由器、交换机等)是关键组件,因为它们控制进出公司网络的流量。因此,监视这些设备的活动有助于 IT 管理员解决操作问题,并保护网络免受攻击者的攻击。通过收集和分析这些设备的日志来监控这些…...

2023年国赛数学建模思路 - 复盘:人力资源安排的最优化模型
文章目录 0 赛题思路1 描述2 问题概括3 建模过程3.1 边界说明3.2 符号约定3.3 分析3.4 模型建立3.5 模型求解 4 模型评价与推广5 实现代码 建模资料 0 赛题思路 (赛题出来以后第一时间在CSDN分享) https://blog.csdn.net/dc_sinor?typeblog 1 描述 …...

Compute shader SV 理解图
本图转子:【Computeshader】个人总结_蒋伟博的博客-CSDN博客...

生信豆芽菜-多种算法计算免疫浸润
网址:http://www.sxdyc.com/immuneInfiltration 一、使用方法 1、数据准备 一个全编码蛋白的表达谱基因,其中行为基因,列为样本 第一列为基因为行名,不能重复 2、选择计算的方法(这里提供了5种免疫计算的方法&#x…...

逆向破解学习-单机斗地主
试玩 破解思路 9000 是成功的代码 Hook代码 import de.robv.android.xposed.XC_MethodHook; import de.robv.android.xposed.XposedHelpers; import de.robv.android.xposed.callbacks.XC_LoadPackage; public class HookComJuneGameDouDiZhu extends HookImpl{ Override p…...

matplotlib绘制位置-时序甘特图
文章目录 1 前言2 知识点2.1 matplotlib.pyplot.barh2.2 matplotlib.legend的handles参数 3 代码实现4 绘制效果5 总结参考 1 前言 这篇文章的目的是,总结记录一次使用matplotlib绘制时序甘特图的经历。之所以要绘制这个时序甘特图,是因为22年数模研赛C…...

数据库概述、部署MySQL服务、必备命令、密码管理、安装图形软件、SELECT语法 、筛选条件
Top NSD DBA DAY01 案例1:构建MySQL服务器案例2:密码管理案例3:安装图形软件案例4:筛选条件 1 案例1:构建MySQL服务器 1.1 问题 在IP地址192.168.88.50主机和192.168.88.51主机上部署mysql服务练习必备命令的使用 …...

概率论与数理统计:第四章:随机变量的数字特征
文章目录 Ch4. 随机变量的数字特征1. 数学期望E(X)(1)数学期望的概念1.离散型①一维离散型随机变量X的数学期望: E X EX EX②一维离散型随机变量的函数的期望: E [ g ( X ) ] E[g(X)] E[g(X)]③二维离散型随机变量的函数的期望: E [ g ( X , …...
解决饿了么ui的对话框缩放和移动
import Vue from "vue";// v-dialogDrag: 弹窗拖拽水平方向伸缩 /** 使用方法* 将以下代码复制到一个js文件中,然后在入口文件main.js中import引入即可;* 给elementUI的dialog上加上 v-dialogDrag 指令就可以实现弹窗的全屏和拉伸了。* 给…...
Linux 中复制文件并保持修改时间等属性
一、遇到的问题 Linux使用cp命令复制文件备份时,发现文件的修改时间变成当前时间了,想要保留备份文件原有的修改时间及其它文件属性。 二、实现 1、cp命令 在 Linux 中,你可以使用 cp 命令来复制文件,并通过 -p 或 --preserve…...

Hugging News #0814: Llama 2 学习资源大汇总
每一周,我们的同事都会向社区的成员们发布一些关于 Hugging Face 相关的更新,包括我们的产品和平台更新、社区活动、学习资源和内容更新、开源库和模型更新等,我们将其称之为「Hugging News」。本期 Hugging News 有哪些有趣的消息࿰…...
可视化绘图技巧100篇进阶篇(五)-阶梯线图(Step Chart)
目录 前言 图表类型特征 适用场景 图例 绘图工具及代码实现 ECharts SMARTBI...
GPT带我学-设计模式-命令模式
1 你知道设计模式的命令模式吗 是的,我知道设计模式中的命令模式。命令模式是一种行为型设计模式,它将请求封装成一个对象,从而允许使用不同的请求、队列或日志来参数化其他对象。命令模式还支持撤销操作,并且可以提供事务的实现…...

互联网发展历程:跨越远方,路由器的启示
互联网的蓬勃发展,一直在追求更广阔的连接,更遥远的距离。然而,在早期的网络中,人们面临着连接距离有限的问题。一项重要的技术应运而生,那就是“路由器”。 连接受限的问题:距离有限 早期的网络受限于直接…...

postman入门基础 —— 接口测试流程
一、编写接口测试计划 接口测试计划和功能测试计划目标一致,都是为了确认需求、确定测试环境、确定测试方法,为设计测试用例做准备,初步制定接口测试进度方案。一般来说,接口测试计划包括概述、测试资源、测试功能、测试重点、测试…...
springcloud+nacos实现灰度发布
灰度发布 gateway网关实现灰度路由 灰度发布实体 package com.scm.boss.common.bean;import lombok.Data; import lombok.experimental.Accessors;import java.io.Serializable;/*** 灰度发布实体*/ Data Accessors(chain true) public class GrayBean implements Serializ…...
Spring Boot 与 Kafka 的深度集成实践(二)
3. 生产者实现 3.1 生产者配置 在 Spring Boot 项目中,配置 Kafka 生产者主要是配置生产者工厂(ProducerFactory)和 KafkaTemplate 。生产者工厂负责创建 Kafka 生产者实例,而 KafkaTemplate 则是用于发送消息的核心组件&#x…...

fast-reid部署
配置设置: 官方库链接: https://github.com/JDAI-CV/fast-reid# git clone https://github.com/JDAI-CV/fast-reid.git 安装依赖: pip install -r docs/requirements.txt 编译:切换到fastreid/evaluation/rank_cylib目录下&a…...

FMC STM32H7 SDRAM
如何无痛使用片外SDRAM? stm32 已经成功初始化了 STM32H7 上的外部 SDRAM(32MB) 如何在开发中无痛使用SDRAM 使它像普通 RAM 一样“自然地”使用? [todo] 重要 MMT(Memory Management Tool) of STM32CubeMx The Memory Management Tool (MMT) disp…...
机器学习14-迁移学习
迁移学习学习笔记 一、迁移学习概述 迁移学习是机器学习中的一个重要领域,它旨在解决当目标任务的训练数据有限时,如何利用与目标任务相关但不完全相同的源任务数据来提高学习性能的问题。在现实世界中,获取大量高质量的标注数据往往成本高…...
使用阿里云百炼embeddings+langchain+Milvus实现简单RAG
使用阿里云百炼embeddingslangchainMilvus实现简单RAG 注意测试时,替换其中的key、文档等 import os from langchain_community.embeddings import DashScopeEmbeddings from langchain_community.vectorstores import Milvus from langchain_text_splitters impor…...

华为手机开机卡在Huawei界面不动怎么办?
遇到华为手机卡在启动界面(如HUAWEI Logo界面)的情况,可依次尝试以下解决方案,按操作复杂度和风险由低到高排序: 🔧 一、强制重启(优先尝试) 1.通用方法 长按 电源键 音量下键…...

图卷积网络:从理论到实践
图卷积网络(Graph Convolutional Networks, GCNs)彻底改变了基于图的机器学习领域,使得深度学习能够应用于非欧几里得结构,如社交网络、引文网络和分子结构。本文将解释GCN的直观理解、数学原理,并提供代码片段帮助您理…...

免费插件集-illustrator插件-Ai插件-随机填色
文章目录 1.介绍2.安装3.通过窗口>扩展>知了插件4.功能解释5.总结 1.介绍 本文介绍一款免费插件,加强illustrator使用人员工作效率,实现路径随机填色。首先从下载网址下载这款插件https://download.csdn.net/download/m0_67316550/87890501&#…...
day46 python预训练模型补充
目录 一、预训练模型的背景知识 二、实验过程 (一)实验环境与数据准备 (二)预训练模型的选择与适配 (三)训练策略 三、实验结果与分析 四、学习总结与展望 一、预训练模型的背景知识 在传统的神经网…...

基于React + FastAPI + LangChain + 通义千问的智能医疗问答系统
📌 文章摘要: 本文详细介绍了如何在前端通过 Fetch 实现与 FastAPI 后端的 流式响应通信,并支持图文多模态数据上传。通过构建 multipart/form-data 请求,配合 ReadableStream 实时读取 AI 回复内容,实现类似 ChatGPT…...