浅谈 Linux 中的 core dump 分析方法

文章目录

  • 一、什么是 core dump
  • 二、发生 core dump 的原因
    • 1. 空指针或非法指针引起 core dump
    • 2. 数组越界或指针越界引起的 core dump
    • 3. 数据竞争导致 core dump
    • 4. 代码不规范
  • 三、core dump 分析方法
    • 1. 启用 core dump
    • 2. 触发 core dump
      • 2-1. 因空指针解引用而崩溃
      • 2-2. 通过 SIGSEGV 信号触发 core dump
    • 3. gdb 分析 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 文件是二进制文件,可以使用 gdbelfdumpobjdump 或者 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. 对空指针进行解引用等操作;

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

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

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

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

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

#include <stdio.h>int main()
{int i;int array[6];for (i = 0; i < 8; i++) {array[i] = 0;printf("Grayson Zheng\n");}return 0;
}

问:以上代码中的 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;int main()
    {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);return 0;
    }
    

    [!CAUTION]

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

    执行代码后如下:

    0x6447cc49a010 = 0x11223344
    0x6447cc49a014 = 0x55
    0x6447cc49a015 = 0x66
    0x6447cc49a010 = 0xCDEF3344
    0x6447cc49a014 = 0xFFFFFFAB
    0x6447cc49a015 = 0x9
    

    从执行结果来看,global_vsrisble_bglobal_vsrisble_c 的值被破环。

    举这个例子是为了说明,如果通过调试工具定位到是因为 global_vsrisble_b 的值被破坏了,很可能不是操作 global_vsrisble_b 的代码有问题,而是操作 global_vsrisble_a 或者 global_vsrisble_c 失误,导致了 global_vsrisble_b 的出错,进而引发 core dump。

  2. 内存变量的值莫名其妙出现奇怪的值。跟上面的情况有点类似,也是因为有些变量相邻问题被覆盖原有的值。例如,执行了 memcpystrcpy 等函数(string.h 涉及到复制功能的函数,在复制过程中是不会检查是否有越界的风险的)引起的 core dump。对于这类问题,肯定是代码走到了某个特殊的逻辑里面,代码处理缺少必要的保护而引起的。

    此类 core dump 可以通过复现 bug,对比前后两次的 core 文件,找出内存变量存在的某种共性特征,根据这个特征来分析解决问题。

    [!NOTE]

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

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>void func()
{int *p = NULL;*p = 13;
}int main()
{func();return 0;
}

编译并运行程序:

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;int main()
{while(1) {printf("global_num = %d\n", global_num++);sleep(1);}return 0;
}

编译并运行程序:

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 是空指针,不能进行解引用操作。

输入 quitexit 可以退出 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 命令查看堆栈情况时,有出现了两个变量,分别是 reqrem。使用过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,可以详细分析程序在崩溃时的状态,并确定具体的崩溃原因。确保在信号触发时,检查程序的变量和内存状态,能够帮助你更好地理解和解决程序中的问题。

之后如果遇到一个实际工作中产生的 core dump,且具有学习价值,我一定会总结这个分析过程,并输出成文档的形式,分享给大家,共勉,respect~

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

图形编辑器基于Paper.js教程06:鼠标画圆与椭圆

绘制椭圆与圆形&#xff1a;利用Paper.js进行交互式图形设计 在Web应用中实现交互式图形绘制功能&#xff0c;对于提高用户体验至关重要&#xff0c;尤其是在设计和艺术相关的应用中。Paper.js是一款强大的JavaScript库&#xff0c;专门用于处理矢量图形&#xff0c;它提供了一…

智能语音门锁:置入NV170D语音芯片ic 打造便捷生活新体验

一、智能门锁语音芯片开发背景 随着科技的飞速发展&#xff0c;传统门锁的局限性日益凸显&#xff0c;无法满足现代人对高效、安全生活的需求。在这样的时代背景下&#xff0c;智能门锁应运而生&#xff0c;它不仅继承了传统门锁的基本功能&#xff0c;更通过融入先进的科技元素…

商标的近似分辩,商标起名称时注意!

曾有过网友发来商标名称&#xff0c;普推知商标老杨说有近似&#xff0c;然后网友起过新名称还是存有近似&#xff0c;或者加字&#xff0c;后面加的通用词&#xff0c;与先有商标名称也是近似。 “良信健康”这个名称健康是行业通用词&#xff0c;加成健康后变成四个字&#x…

HTTP协议深入

1.了解web和网络基础 有客户端和服务端双方参与交互 客户端发送请求:request 服务端根据请求给出响应:response 请求通过URL来指定要获取都得资源 响应内容可以是HTML网页&#xff0c;或者用json表示的数据或者其他二进制文件内容 Web使用一种名为HTTP的协议作为规范&…

AI与大模型工程师证书研修班报名啦!

人工智能大模型是指拥有超大规模参数&#xff08;通常在十亿个以上&#xff09;、超强计算资源的机器学习模型&#xff0c;能够处理海量数据&#xff0c;完成各种复杂任务&#xff0c;如自然语言处理、图像识别等。计算机硬件性能不断提升&#xff0c;深度学习算法快速优化&…

ESP32CAM物联网教学03

ESP32CAM物联网教学03 物联网小车 小智突发奇想&#xff1a;要是我在点灯物联APP中多增加几个按钮&#xff0c;控制小车的行驶方向&#xff0c;不就可以做成遥控小车了吗&#xff1f; 点灯物联控制小车的行驶方向 我们可以重新编辑点灯物联APP中的设备控件界面&#xff0c;如…

开关电源中强制连续FCCM模式与轻载高效PSM,PFM模式优缺点对比笔记

文章目录 前言一、连续FCCM模式优点&#xff1a;缺点&#xff1a; 二,轻载高效PSM&#xff0c;PFM优点&#xff1a;缺点: 总结 前言 今天我们来学习下开关电源中&#xff0c;强制连续FCCM模式与轻载高效PSM&#xff0c;PFM模式优缺点对比 一、连续FCCM模式 优点&#xff1a; …

mac中如何恢复因为破解脚本导致的IDEA无法启动的问题

问题 为了在mac中安装免费的2024版idea&#xff0c;导致下载了一个脚本&#xff0c;使用这个脚本后&#xff0c;但是发现idea还没有破解&#xff0c;相反导致idea无法启动&#xff0c;每次点击&#xff0c;都会弹出“cannot start IDE…” 问题排查 在访达中点击mac的应用程…

docker -run hello-world超时

主要原因就是尝试拉取库的时候没有从阿里云镜像里拉&#xff0c;所以设置一下就好了 这里使用的是ubuntu系统&#xff08;命令行下逐行敲就行了&#xff09; sudo mkdir -p /etc/docker sudo tee /etc/docker/daemon.json <<-EOF {"registry-mirrors": [&quo…

Adobe Acrobat添加时间戳服务器

文章目录 前言一、Adobe Acrobat添加时间戳服务器1.打开Adobe Acrobat软件2.点击【菜单】→ 【首选项】3.点击【安全性】→【更多】4.点击【新建】5.输入【名称】→【服务器URL】 前言 一、Adobe Acrobat添加时间戳服务器 1.打开Adobe Acrobat软件 2.点击【菜单】→ 【首选项…

模拟退火算法1——简介

模拟退火算法来源于固体退火原理&#xff0c;将固体加温至充分高&#xff0c;再让其徐徐冷却&#xff0c;加温时&#xff0c;固体内部粒子随温升变为无序状&#xff0c;内能增大&#xff0c;而徐徐冷却时粒子渐趋有序&#xff0c;在每个温度都达到平衡态&#xff0c;最后在常温…

[C++][设计模式][访问器]详细讲解

目录 1.动机2.模式定义3.要点总结4.代码感受1.代码一2.代码二 1.动机 在软件构件过程中&#xff0c;由于需求的变化&#xff0c;某些类层次结构中常常需要增加新的行为(方法)&#xff0c;如果直接在基类中做这样的更改&#xff0c; 将会给子类带来很繁重的变更负担&#xff0c…

数据恢复篇:5 款最佳 Mac 数据恢复软件

说到保护我们的数字生活&#xff0c;数据恢复软件的重要性怎么强调都不为过。无论您是意外删除了假期照片的普通用户&#xff0c;还是面临硬盘损坏的专业人士&#xff0c;随之而来的恐慌都是普遍存在的。幸运的是&#xff0c;数据恢复工具可以缓解这些压力。在Mac用户可用的众多…

zabbix小白入门:从SNMP配置到图形展示——以IBM服务器为例

作者 乐维社区&#xff08;forum.lwops.cn&#xff09;许远 在运维实践中&#xff0c;Zabbix作为一款强大的开源监控工具&#xff0c;被广泛应用于服务器、网络设备和应用程序的监控&#xff0c;成为保障业务连续性和高效运行的关键。然而&#xff0c;对于Zabbix的初学者来说&a…

人工智能--循环神经网络

个人主页&#xff1a;欢迎来到 Papicatch的博客 课设专栏 &#xff1a;学生成绩管理系统 专业知识专栏&#xff1a; 专业知识 文章目录 &#x1f349;引言 &#x1f349;概述 &#x1f348;基本概念 &#x1f34d;定义 &#x1f34d;结构 &#x1f34c;输入层 &#…

行业模板|DataEase旅游行业大屏模板推荐

DataEase开源数据可视化分析工具于2022年6月发布模板市场&#xff08;https://templates-de.fit2cloud.com&#xff09;&#xff0c;并于2024年1月新增适用于DataEase v2版本的模板分类。模板市场旨在为DataEase用户提供专业、美观、拿来即用的大屏模板&#xff0c;方便用户根据…

开关电源的电路组成原理

一、 开关电源的电路组成 开关电源的主要电路是由输入电磁干扰滤波器&#xff08;EMI&#xff09;、整流滤波电路、功率变换电路、PWM控制器电路、输出整流滤波电路组成。辅助电路有输入过欠压保护电路、输出过欠压保护电路、输出过流保护电路、输出短路保护电路等。 开关电源…

基于Hadoop平台的电信客服数据的处理与分析④项目实现:任务18: 数据展示

任务描述 接下来我们需要将根据业务需求将某人按照不同维度查询出来的结果&#xff0c;展示到Web页面上。 任务指导 数据展示模块流程图&#xff1a; 数据展示使用Java的SSM框架&#xff0c;需要实现的代码包括&#xff1a; 1. 实体类 2. 数据库操作 3. 业务逻辑操作 4.…

springboot旅游管理系统-计算机毕业设计源码16021

摘 要 本文旨在设计和实现一个基于Spring Boot框架的旅游管理系统。该系统通过利用Spring Boot的快速开发特性和丰富的生态系统&#xff0c;提供了一个高效、可靠和灵活的解决方案。系统将实现旅游景点信息的管理、线路规划、跟团游玩、旅游攻略、酒店信息管理、订单管理和用户…

MySQL—创建和修改数据表结构

创建表 实例&#xff1a; CREATE TABLE user (id INT,name VARCHAR(255),password VARCHAR(255),birthday DATE) CHARACTER SET utf8 COLLATE utf8_bin ENGINE INNODB; 显示数据库中的表 show tables from hsp; 显示表结构 desc dept; 修改表 实例&#xff1a; 代码&…