​浅谈 Linux 中的 core dump 分析方法

在 Linux 系统开发领域中,core dump(核心转储)是一个不可或缺的工具,它为我们提供了在程序崩溃时分析程序状态的重要线索。当程序因为某种原因(如段错误、非法指令等)异常终止时,Linux 系统会尝试将程序在内存中的映像、程序计数器、寄存器状态等信息写入到一个名为 core 的文件中,这个文件就是所谓的 core dump。

对于开发者而言,core dump 文件如同一块宝藏,其中蕴含着程序崩溃时的现场信息。通过对 core dump 文件的分析,我们可以了解到程序在崩溃时的内存布局、函数调用栈、变量值等重要信息,从而帮助我们快速定位问题原因,优化代码,提高程序的健壮性。

在本文中,我们将探讨 Linux 中 core dump 的分析方法。通过一些简单的案例来演示 core dump 分析的实际应用,帮助读者更好地理解和掌握这一技术。

一、什么是 core dump

核心转储(core dump),在汉语中有时戏称为吐核,是操作系统在进程收到某些信号而终止运行时,将此时进程地址空间的内容以及有关进程状态的其他信息写出的一个磁盘文件。这种信息往往用于调试。 在 UNIX 系统中,常将“主内存称为核心(core),因为在使用半导体作为内存材料之前,便是使用核心(core)。而核心映像(core image)就是 “进程”(process)执行当时的内存内容。当进程发生错误或收到 “信号”(signal)而终止执行时,系统会将核心映像写入一个文件,以作为调试之用,这就是所谓的核心转储(core dump)。 有时程序并未经过彻底测试,这使得它在执行的时候一不小心就会找到破坏。这可能会导致核心转储(core dump)。幸好,现行的 UNIX 系统极少会面临这样的问题。即使遇到,程序员可以通过核心映像(core image)调试程序来找到错误原因。 ——引用:核心转储_百度百科 (baidu.com)

可以这样去理解,core dump 是程序运行时在突然崩溃的那一刻的一个内存快照。操作系统在程序发生异常而异常在进程内部又没有被捕获的情况下,会把进程此刻内存、寄存器状态、运行堆栈等信息转储保存在一个 core 文件里。这个 core 文件是二进制文件,可以使用 gdb、elfdump、objdump 或者 Windows 下的 windebug 进行打开此文件,并分析里面的具体内容,找出 core dump 的具体原因,并解决问题。

[!NOTE] core 是在半导体作为内存材料前的线圈,当时用线圈当做内存材料,线圈叫做 core。用线圈做的内存叫做 core memory。故 core dump 也可称为 core memory dump,真是个充满历史味道的词。

在 Linux 系统下开发,时常会遇到程序突然崩溃了,且没有留下任何日志的情况,这时就可以查看 core 文件。从 core 文件中分析原因,通过 gdb 看出程序挂在哪里,分析前后的变量,找出问题的原因。

二、发生 core dump 的原因

C/C++ 程序员遇到的比较常见的一个问题,就是自己编写的代码, 在运行过程中出现了意想不到的 core dump。程序发生 core dump 的原因是多方面的,不同的 core dump 问题有着不同的解决办法。同时,不同的 core dump 问题解决的难易程度也存在很大的区别。有些在短短几秒钟内就可以定位问题,但是也有一些可能需要花费数天时间才能解决。这种问题是对软件开发人员的极大的挑战。笔者从事 C/C++ 语言的软件开发工作多年,前后解决了许多此类问题,久而久之积累了一定的经验,现把常见 core dump 总结一下。

1. 空指针或非法指针引起 core dump

空指针或非法指针(野指针、悬空指针)引起 core dump 是一种最常见的核心转储,大致可以有 3 种原因导致程序出现异常:

  1. 1. 对空指针进行解引用等操作;

  2. 2. 声明指针变量后未进行初始化,并直接进行操作,极大概率引发 core dump,此类未经初始化的指针,统称野指针;

  3. 3. 对某个指针,调用了 free 函数或者 delet 函数,该指针指向的空间已经被释放,但未将该指针重新指向 NULL,此类指针成为悬空指针。对悬空指针再次操作,也会引发 core dump;

此类问题通常是代码编写时的疏漏造成的,属于低级 bug,也比较容易解决的问题。Linux 平台常用的 core dump 文件分析工具是 gdb,调试一下产生的 core 文件,对照代码定位问题出现的原因,可以轻松解决问题。

2. 数组越界或指针越界引起的 core dump

提到这个,笔者不由得想起互联网大厂百度的第一道 C 语言面试题,如下代码:

#include <stdio.h>

intmain()
{
int i;
intarray[6];

for(i =0; i <8; i++){
array[i]=0;
printf("Grayson Zheng\n");
}

return0;
}

问:以上代码中的 printf 函数会执行多少次?

这个问题的答案在不同操作系统下有不同的答案,当下只讨论 Linux 系统的结果,执行该程序,结果如下:

可以看出,在打印了 8 次之后,程序结束,但这并不是一次正常的结束,而是一次 core dump。不难看出这是数组越界导致的内存踩踏,数组定义了 6 个元素,遍历完 6 个元素之后,还对数组之外的内存进行了操作,从而引发了这次的 core dump。

这种情况还相对简单,而指针越界引发的 core dump,有的是就比较简单,有的就属于一种隐藏比较深的 core dump 了。遇到这种问题时,在调试 core 文件,尽管也能定位到代码行,但是有可能呗定位到的那行代码本身并没有什么问题,它只是一个 “被陷害者”。

根据经验,这种 core dump 问题很可能是其他代码处理过程中的内存越界造成的(亲身经历:一个指针越界导致内存踩踏,让 7.5 万台机器拆包重流,经济损失估计超过 40 w。当然,我不是那个写 bug 的人,哈哈),通常由以下两个原因引起:

1. 假如有以下三个全局变量:int global_vsrisble_a; char global_vsrisble_b; char global_vsrisble_c;在不同操作系统中,这个三个全局变量在内存的位置可能不一样,以 Ubuntu 为例,三个全局变量的内存位置分布如下图所示:

假设在某些做了如下代码所作的事:#include <stdio.h>

int  global_vsrisble_a =0x11223344;
char global_vsrisble_b =0x55;
char global_vsrisble_c =0x66;

intmain()
{
printf("%p = 0x%X\n%p = 0x%X\n%p = 0x%X\n",&global_vsrisble_a,
           global_vsrisble_a,&global_vsrisble_b, global_vsrisble_b,
&global_vsrisble_c, global_vsrisble_c);

char*p_1 =(char*)(&global_vsrisble_a);
    p_1 +=2;
int*p_2 =(int*)p_1;
*p_2 =0x09ABCDEF;

printf("%p = 0x%X\n%p = 0x%X\n%p = 0x%X\n",&global_vsrisble_a,
           global_vsrisble_a,&global_vsrisble_b, global_vsrisble_b,
&global_vsrisble_c, global_vsrisble_c);

return0;
}

[!CAUTION] 以上代码只是为了示范,现实情况并不可能如此。

执行代码后如下:0x6447cc49a010 =0x11223344 0x6447cc49a014=0x55 0x6447cc49a015=0x66 0x6447cc49a010=0xCDEF3344 0x6447cc49a014=0xFFFFFFAB 0x6447cc49a015= 0x9从执行结果来看,global_vsrisble_b 和 global_vsrisble_c 的值被破环。举这个例子是为了说明,如果通过调试工具定位到是因为 global_vsrisble_b 的值被破坏了,很可能不是操作 global_vsrisble_b 的代码有问题,而是操作 global_vsrisble_a 或者 global_vsrisble_c 失误,导致了 global_vsrisble_b 的出错,进而引发 core dump。

2. 内存变量的值莫名其妙出现奇怪的值。跟上面的情况有点类似,也是因为有些变量相邻问题被覆盖原有的值。例如,执行了 memcpy、strcpy 等函数(string.h 涉及到复制功能的函数,在复制过程中是不会检查是否有越界的风险的)引起的 core dump。对于这类问题,肯定是代码走到了某个特殊的逻辑里面,代码处理缺少必要的保护而引起的。此类 core dump 可以通过复现 bug,对比前后两次的 core 文件,找出内存变量存在的某种共性特征,根据这个特征来分析解决问题。

[!NOTE] 曾经在工作中遇到过一个 core bump,起因是对一段未初始化的缓冲存储区做字符串搜索(搜索并不会引发 core dump)。但是代码流程走了很长一段之后,对一个与缓冲存储区相邻的变量执行了操作,导致了 core dump。

linux c/c++服务器开发视频教程

【腾讯T9推荐】2024最新linux c/c++后端服务器开发教程,通俗易懂深入底层讲解,多项目实战,1V1指导,学完轻松拿下大厂offer!!!icon-default.png?t=N7T8https://www.bilibili.com/video/BV1XZ421q7UD/

Linux C/C++开发(后端/音视频/游戏/嵌入式/高性能网络/存储/基础架构/安全)

需要C/C++ Linux服务器架构师学习资料加qun812855908获取(资料包括C/C++,Linux,golang技术,Nginx,ZeroMQ,MySQL,Redis,fastdfs,MongoDB,ZK,流媒体,CDN,P2P,K8S,Docker,TCP/IP,协程,DPDK,ffmpeg等),免费分享

3. 数据竞争导致 core dump

多线程访问全局变量,如果不进行适当的同步保护,确实可能导致内存值异常,从而引发不可预测的行为,甚至可能导致程序崩溃并生成核心转储文件(core dump)。这种问题通常称为 “数据竞争” 或 “竞态条件”(race condition)。

竞态条件是指两个或多个线程同时访问共享数据,并且至少有一个线程在修改数据时未进行适当的同步。这可能导致以下问题:

1. 数据不一致:多个线程读取和修改全局变量时,可能会导致数据处于不一致的状态。

2. 程序崩溃:未同步的访问可能导致非法的内存访问,从而引发段错误(segmentation fault),导致程序崩溃并生成核心转储文件。

4. 代码不规范

初学者有时候编译一个程序,出现了一整页的编译错误,其实这种情况也不用担心,很可能就是某一行代码多了几个字符,当把这些代码删去再编译,几百个编译错误全都消失了。

有些时候,程序发生 core dump 的根本原因还是程序员自己进行程序设计时的编码失误造成的,这种代码失误绝大多数都是因为没有严格遵守相应的代码编写规范(比如用 0 做为除数等)。所以,要从根本上杜绝或者减少程序 core dump 的发生,还是要从严格遵守代码编写规范来做起。

三、core dump 分析方法

1. 启用 core dump

默认情况下,程序运行崩溃导致 core dump,是不会生成 core 文件的,因为系统的 RLIMIT_CORE(核心文件大小)资源限制,默认情况下设置为 0。

使用 ulimit -c 命令可以查看 core 文件的大小,其中 -c 的含义是 core file size,单位是 blocks 也就是 KB 的意思。ulimit -c 命令后面可以写整数,表示生成写入值大小的 core 文件。如果使用 ulimit -c unlimited 设置无限大,则任意情况下都会产生 core 文件。

以下命令可在用户进程触发信号时启用 core dump 生成,并使用合理的名称将核心文件位置设置为 /tmp/。请注意,这些设置不会永久存储。

ulimit -c unlimited
echo 1 > /proc/sys/kernel/core_uses_pid
echo "/tmp/core-%e-%s-%u-%g-%p-%t" > /proc/sys/kernel/core_pattern

[!IMPORTANT] 后面两条命令在运行时,即使是加了 sudo 执行,也可能会被提示权限不足。这可能是由于 shell 的重定向在命令前已经处理完成,因此重定向操作并没有被提升到超级用户权限,这就导致了 “Permission denied” 的错误。可以通过以下命令来解决这个问题: echo 1 | sudo tee /proc/sys/kernel/core_uses_pid echo "/tmp/core-%e-%s-%u-%g-%p-%t" | sudo tee /proc/sys/kernel/core_pattern

顺便解释一下 "/tmp/core-%e-%s-%u-%g-%p-%t" 的各个参数的含义:

  • • %e:导致 core dump 的程序的可执行文件名。

  • • %s:导致 core dump 的信号编号。

  • • %u:导致 core dump 的程序的实际用户 ID。

  • • %g:导致 core dump 的程序的实际组 ID。

  • • %p:导致 core dump 的程序的进程 ID。

  • • %t:core dump 发生时的时间戳(自 epoch 时间以来的秒数)。

因此,/tmp/core-%e-%s-%u-%g-%p-%t 会生成包含如下信息的 core 文件:

/tmp/core-<executable>-<signal>-<uid>-<gid>-<pid>-<timestamp>

举个例子,如果一个进程名为 my_program,用户 ID 为 1000,组 ID 为 1000,进程 ID 为 12345,并且在 1617701234 时间点崩溃于信号 11,则生成的 core 文件名将是:

/tmp/core-my_program-11-1000-1000-12345-1617701234

2. 触发 core dump

我们使用两个简单的 C 程序作为示例。

2-1. 因空指针解引用而崩溃

文件名为 example.c:

#include <stdio.h>

voidfunc()
{
int*p =NULL;
*p =13;
}

intmain()
{
    func();
return0;
}

编译并运行程序:

gcc -g -o example example.c
./example

运行程序时后,会在 /tmp/ 文件夹下生成一个 core 文件。

2-2. 通过 SIGSEGV 信号触发 core dump

文件名为 example2.c:

#include <stdio.h>
#include <unistd.h>

int global_num;

intmain()
{
while(1){
printf("global_num = %d\n", global_num++);
        sleep(1);
}

return0;
}

编译并运行程序:

gcc -g -o example2 example2.c
./example2

运行程序时后,在另一个终端查找进程的 PID,并用 kill -11 加上 PID,向进程发送段错误信号,结束掉进程。之后会在 /tmp/ 文件夹下生成一个 core 文件。

3. gdb 分析 core dump

两个例子都是段错误导致的 core dump,所以用 gdb 调试的方法也是一样的,命令格式如下:

gdb <program_name> <core_dump_file>

比如先调试第一个例子的 core 文件,则输入 gdb example,再加上 core 文件名,命令如下(建议先提前复制 core 文件名,不知道为什么,按 Tab 键不给补齐):

gdb example /tmp/core-example-11-1000-1000-88496-1719910934

随后可以看到,gdb 提示在代码第 6 行的地方出现了段错误,如下图:

如果函数关系调用关系很复杂,可以用 bt 命令(全称 backtrace,堆栈的意思)查看调用堆栈(where 命令也有同样功能),如下图可知是在调用 func 函数时产生的段错误,可用 list 命令查看,具体就是 list 加函数名,如下图。找到提示错误的那一行代码,print 命令可以打出 p 的值,由下图可知,p 是空指针,不能进行解引用操作。

输入 quit 或 exit 可以退出 gdb。

第二个例子,也是同样用 gdb 打开 core 文件:

gdb example2 /tmp/core-example2-11-1000-1000-88552-1719911473

执行结果如下图:

虽然这个段错误是因为我们人为地发送了 SIGSEGV 信号,导致了程序地段错误,而在打开 core 文件后,可以看出在执行 __GI___clock_nanosleep 函数时,遇到了段错误。

[!NOTE] 通常情况下,分析 core dump 问题,除了 core 文件之外,还会结合程序的 log 信息和系统的 log 信息(包括 kernel log、systemd log 等)一起分析。 当然人为故意制造出来的 core dump,有时候是分析不出来的。所以这个例子的作用在于分析的过程,也顺便告诉大家,不是所有的 core dump 都可以分析出具体原因。

如果我们不事先知道是由 SIGSEGV 信号导致段错误的,首先要用 bt 命令找到函数的调用关系链:

由上图可知,先是在 main 函数调用了 __sleep 函数,接着 __sleep 函数调用了 __GI___nanosleep 函数,__GI___nanosleep 函数调用了 __GI___clock_nanosleep 函数,到这里,执行到了 __GI___clock_nanosleep 函数的第 78 行时,发生了段错误,使程序崩溃。

此时,我们是没办法通过 list 命令去找出问题的,因为栈区的那三个函数是封装后的库函数,根本看不到源码:

在输入 bt 命令查看堆栈情况时,有出现了两个变量,分别是 req 和 rem。使用过nanosleep 函数的小伙伴可能会很眼熟这两个变量,因为这个两个变量是 nanosleep 函数的形参,原型是 int nanosleep(const struct timespec *req, struct timespec *rem)。

用 print 命令打印出两个变量的地址:

使用 info registers 命令查看寄存器状态,检查程序在崩溃时的上下文:

从寄存器状态来看,没有明显的错误迹象,函数的栈帧空间没什么问题,形参的位置和值也没什么问题,所有值看起来都在正常范围内。

当下是没办法直接了当的判断为人为干预造成 core dump,如果此时想到了信号会引发段错误,可以用 info signals 命令查看信号情况:

从 info signals 的输出中可以看出,SIGSEGV(Segmentation fault)信号是设置为在程序接收到该信号时停止执行并打印信息的。也就说,可以人为地使用 kill -11 发送了 SIGSEGV 信号来终止程序并生成 core dump。

总结

分析 core dump 的具体原因不可能仅凭两个案例就学会,本文只是提供一个基本的排除思路和方法。通过查看调用堆栈、源代码和变量的值,可以逐步确定程序崩溃的原因。通过向程序发送 SIGSEGV 信号来生成 core 文件是一个有效的调试手段。通过 gdb,可以详细分析程序在崩溃时的状态,并确定具体的崩溃原因。确保在信号触发时,检查程序的变量和内存状态,能够帮助你更好地理解和解决程序中的问题。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/772902.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

spring boot + vue3+element plus 项目搭建

一、vue 项目搭建 1、创建 vue 项目 vue create vue-element说明:创建过程中可以选择路由,也可也可以不选择,可以通过 npm install 安装 vue 项目目录结构 说明:api 为自己创建的文件夹,router 选择路由模块会自动创建 router下的index.js文件(配置路由的文件) im…

泰国内部安全行动司令部数据泄露

BreachForums 论坛的一名成员宣布发生一起重大数据泄露事件&#xff0c;涉及泰国内部安全行动司令部 (ISOC)&#xff0c;该机构被称为泰国皇家武装部队的政治部门。 目前&#xff0c;我们无法准确确认此次泄露的真实性&#xff0c;因为该组织尚未在其网站上发布有关该事件的任…

微信开发者工具报错 Error: module ‘xxx.js‘ is not defined, require args is ‘xxx.js‘

背景 报错如下 检查 代码逻辑和写法都是ok的重新打开项目又是可以的 解决方案 先确保微信开发者工具和uniapp的将js编译成es5都开着&#xff08;这个是默认开的&#xff09; 然后把微信开发者工具关了重开 一般做这一步就会好了&#xff0c;但是只是临时解决 &#xff08…

如何使用 3D 建模库在 C# 中将 3DS 转换为 USDZ?

USDZ/USD是一种 3D 文件格式&#xff0c;被广泛用于跨平台共享 3D 资产。另一方面&#xff0c;3DS是另一种以块形式存储数据的 3D 文件格式。在某些情况下&#xff0c;您需要将3DS 文件转换为 USDZ/USD文件格式。因此&#xff0c;本篇博文介绍了一个功能丰富的3D 建模库&#x…

记录一下简单导入导出excel二级表头

数据库导入导出表头 之前的工具类GenerateExcelToFile新增两个导出这种二级表头方法 package com.njry.utils;import cn.hutool.core.util.IdUtil; import com.njry.config.FileProperties; import com.njry.exception.BadRequestException; import org.apache.poi.hssf.user…

《Winodws API每日一练》8.2 static控件

在 Windows 编程中&#xff0c;"Static" 控件是一种常见的用户界面元素&#xff0c;用于显示静态文本或图像&#xff0c;而无法进行用户交互。它通常用于显示标签、标题、说明文本或静态图像等信息。Static 控件是一种静态的、只读的显示元素&#xff0c;不接受用户的…

JAVA 快递100wms工具类

快递wms工具类 import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; import com.google.gson.Gson; import com.kuaidi100.sdk.api.QueryTrack; import com.kuaidi100.sdk.api.Subscribe; import com.kuaidi100.sdk.contant.ApiInfoConstant; import c…

11.SQL注入-盲注基于(base on boolian)

SQL注入-盲注基于boolian案例利用 首先总结一下sql语句中的函数意思 #查看当前所在的数据库 mysql> select database(); ------------ | database() | ------------ | pikachu | ------------ 1 row in set (0.00 sec)#函数substr里1是从第几位开始取字符&#xff0c;2…

mybatis-使用自动生成(根据数据库反向生成pojo、映射文件,映射接口)

1.在pom.xml中导入依赖和插件 <dependencies> <!-- 导入自动生成依赖--><dependency><groupId>org.mybatis.generator</groupId><artifactId>mybatis-generator-maven-plugin</artifactId><version>1.4.0</version>&…

【PCIe】P2P DMA

PCIe P2P (peer-to-peer communication)是PCIe的一种特性&#xff0c;它使两个PCIe设备之间可以直接传输数据&#xff0c;而不需要使用主机RAM作为临时存储。如下图3的走向 比如EP1要发送和数据给EP2,操作流程如下&#xff1a; 1. 打开EP1的dma控制器&#xff1b;--client侧 …

go开源webssh终端源码main.go分析

1.地址: https://github.com/Jrohy/webssh.git 2.添加中文注释地址: https://github.com/tonyimax/webssh_cn.git main.go分析 主包名&#xff1a;main package main //主包名 依赖包加载 //导入依赖包 import ("embed" //可执行文件…

密码学复习

目录 基础 欧拉函数 欧拉函数φ(n)定义 计算方法的技巧 当a=a_1*a_2*……*a_n时 欧拉定理 剩余系 一些超简单密码 维吉尼亚 密钥fox 凯撒(直接偏移) 凯特巴氏(颠倒字母表) 摩斯密码(字母对应电荷线) 希尔(hill)密码 一些攻击 RSA 求uf+vg=1 快速幂模m^…

苹果获得OpenAI董事会观察员职位、Runway最新估值40亿美元

ChatGPT狂飙160天&#xff0c;世界已经不是之前的样子。 更多资源欢迎关注 据知情人士透露&#xff0c;苹果应用商店&#xff08;App Store&#xff09;负责人、前营销主管Phil Schiller被选中担任这一职位。这位知情人士说&#xff0c;作为董事会观察员&#xff0c;他不会以正…

二次搭建无人车平台遇到的问题(mid360+joy游戏手柄操纵无人车)

joy节点启动 报错&#xff1a; ROS path [0]/opt/ros/noetic/share/ros ROS path [1]/home/jetson/yahboomcar_ws/src/yahboomcar_autodrive ROS path [2]/home/jetson/yahboomcar_ws/src/yahboomcar_bringup ROS path [3]/home/jetson/yahboomcar_ws/src/yahboomcar_ctrl ROS…

【2024版】Microsoft Azure 管理员培训课程招生简章(8月有开班)

课程介绍 本课程专为希望深入了解和精通Microsoft Azure管理的IT专业人员设计。在为期三天的培训中&#xff0c;学员将全面学习如何管理Azure订阅&#xff0c;保护标识&#xff0c;配置虚拟网络&#xff0c;以及实现存储解决方案和虚拟机。此外&#xff0c;课程还涵盖了实现We…

传感器标定(一)摄像头内参标定

一、使用ROS进行手动标定安装 1、安装 image-view &usb_cam ⽤于驱动相机 sudo apt-get install ros-melodic-image-view sudo apt-get install ros-melodic-usb-cam2、查看系统视频设备 v4l2- ctl -d /dev/video0 --all 查询所有相机具体的参数包括width和height ls /…

Excel数据截取及合并多行多列数据

公式一&#xff1a;RIGHT(A2,LEN(A2)-FINDB(")",A2)) 公式二&#xff1a;PHONETIC(C2:D19) 详情可以看附件。

JMeter--定时执行的方法

原文网址&#xff1a;JMeter--定时执行的方法_IT利刃出鞘的博客-CSDN博客 简介 本文介绍JMeter如何使用定时器定时执行测试任务。 Java技术星球&#xff1a;way2j.com 方法 第一步&#xff1a;新建定时器 右键测试任务> Add > Timer> Constant Timer 如下图所示…

【python数据处理】— “2020-01-01 05:20:15“日期格式数据

文章目录 一、数据说明及目标二、实现方式1.提取date2.提取hour3.提取weekday4.提取month 一、数据说明及目标 数据说明 数据表有一列名为"datetime"表示时间数据&#xff0c;该列的数据格式是"2020-01-01 05:20:15"。 import pandas as pd datapd.read_e…

云动态摘要 2024-07-04

给您带来云厂商的最新动态&#xff0c;最新产品资讯和最新优惠更新。 最新优惠与活动 数据库上云优选 阿里云 2024-07-04 RDS、PolarDB、Redis、MongoDB 全系产品新用户低至首年6折起&#xff01; [免费体验]智能助手ChatBI上线 腾讯云 2024-07-02 基于混元大模型打造&…