GDB调试无行号,报dwarf error问题解决

news2024/11/17 9:44:57

背景

近期我开发的一个C程序,在生产环境产生了coredump,但是在调试该core文件时,打出的debug信息并不全。

这种debug信息丢失,其实说白了,就是符号表丢失。一般由两种情况造成,一种是编译的时候没有加-g参数,另一种是dwarf版本不对。
首先排除第一种可能,因为编译脚本是我自己写的,-g参数是有的。而唯一可能出问题的地方,就是dwarf版本不对。
而之所以出现dwarf版本不对,还是编译环境的问题。我为了兼容编译C++17标准的另外一个cpp项目,就对编译环境做了容器化处理,在镜像里安装了gcc11.3,而在生产环境使用的时候,gdb版本仍然是4.8.5,由于gcc版本和gdb版本不匹配,就造成了该问题的出现。
为了验证这一点,我在物理机上重现了这种现象:

[root@ck08 ctest]# gcore `pidof flow`
Dwarf Error: wrong version in compilation unit header (is 5, should be 2, 3, or 4) [in module /root/chenyc/src/flow/flow]
[New LWP 3048]
[New LWP 3047]
[New LWP 3046]
[New LWP 3045]
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib64/libthread_db.so.1".
0x00007f50dfd850e3 in epoll_wait () from /lib64/libc.so.6
warning: target file /proc/3044/cmdline contained unexpected null characters
Saved corefile core.3044
[Inferior 1 (process 3044) detached]

我的物理机的gdb版本也是4.8.5, 我使用gcore命令生成core文件的时候,出现了下面的警告:Dwarf Error: wrong version in compilation unit header (is 5, should be 2, 3, or 4),这句话从字面意思很好理解,就是说,gdb支持的dwarf版本应该是23,或者4,但是当前二进制文件的dwarf版本是5,无法调试。
那么,何为dwarf?什么又是dwarf版本呢?

何为dwarf

所谓的dwarf,它是一种文件调试的格式。你可以将其简单理解为调试信息的组织模式。除了dwarf之外,常见的调试格式还有stabsCOFFpdb等。
除了pdb这种windows专用的调试格式外,绝大多数的调试格式都是支持Unix系统的。但随着时间的推移,逐渐被dwarf一统江山,被各大主流编译器所支持。其他的一些调试格式虽然还零星存在,但也是苟延残喘,名存实亡。
说到dwarf自身的发展,也是经历了好几个阶段,从1992年推出至今,已经迭代了5个版本。其中,dwarf1作为第一个版本,结构不紧凑,功能不成熟,很多编译器都已经不支持。dwarf2是1993年PLSIG机构在初版的基础上做了一些优化,减少了调试信息的大小,但只是有一个草案,并没有正式发布。
第一个正式发布的dwarf版本是Free Standards Group于2005年发布的dwarf3,该机构并于2010年发布了dwarf4。目前最新的dwarf版本是2017年发布的dwarf5
官方说法是这样的:

Produce debugging information in DWARF format (if that is supported). The value of version may be either 2, 3, 4 or 5; the default version for most targets is 5 (with the exception of VxWorks, TPF and Darwin/Mac OS X, which default to version 2, and AIX, which defaults to version 4).

Note that with DWARF Version 2, some ports require and always use some non-conflicting DWARF 3 extensions in the unwind tables.

Version 4 may require GDB 7.0 and -fvar-tracking-assignments for maximum benefit. Version 5 requires GDB 8.0 or higher.

GCC no longer supports DWARF Version 1, which is substantially different than Version 2 and later. For historical reasons, some other DWARF-related options such as -fno-dwarf2-cfi-asm) retain a reference to DWARF Version 2 in their names, but apply to all currently-supported versions of DWARF.

关于dwarf的调试文件格式,本文就不多做介绍了,如果展开来说,一个专题远远不够。但需要明白的是,各个dwarf版本之间,数据格式也是有所区别的,这也就造成了彼此之间的不兼容,因此才会出现文章开头出现的问题。

如何指定dwarf版本

那么,原因定位到了,我们如何解决这个问题呢?
难不成,我需要降级gcc版本?总不能逼着客户去升级生产环境的gdb版本吧?这明显都是不现实的。
不过好在gcc编译器提供了指定dwarf版本的选项。我们只需要在编译时,增加-gdwarf-version选项即可。
为了演示指定dwarf版本,我在这里准备了一个demo
C程序如下:

//hello.c
#include <stdio.h>

int main(void){
        char *p = "hello";
        printf("p = %s\n", p);
        p[3] = 'M';
        printf("p = %s\n", p);
        return 0;
}

容器内gcc版本如下:

[root@5b2c03891f42 tmp]# gcc -v
Using built-in specs.
COLLECT_GCC=gcc
COLLECT_LTO_WRAPPER=/usr/local/libexec/gcc/x86_64-pc-linux-gnu/11.3.0/lto-wrapper
Target: x86_64-pc-linux-gnu
Configured with: ./configure --enable-languages=c,c++
Thread model: posix
Supported LTO compression algorithms: zlib
gcc version 11.3.0 (GCC) 

在容器内编译:

gcc -o hello hello.c -g

该程序一定会产生core文件。我们在容器外运行,此时,这个core文件是无法调试的:

[root@ck08 ctest]# ulimit -c unlimited
[root@ck08 ctest]# ./hello 
p = hello
Segmentation fault (core dumped)
[root@ck08 ctest]# gdb ./hello core.30856 
GNU gdb (GDB) Red Hat Enterprise Linux 7.6.1-120.el7
Copyright (C) 2013 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-redhat-linux-gnu".
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>...
Reading symbols from /root/chenyc/src/ctest/hello...Dwarf Error: wrong version in compilation unit header (is 5, should be 2, 3, or 4) [in module /root/chenyc/src/ctest/hello]
(no debugging symbols found)...done.
[New LWP 30856]
Core was generated by `./hello'.
Program terminated with signal 11, Segmentation fault.
#0  0x0000000000401164 in main ()
Missing separate debuginfos, use: debuginfo-install glibc-2.17-326.el7_9.x86_64
(gdb) bt
#0  0x0000000000401164 in main ()
(gdb) 

我们尝试指定dwarf版本编译:

gcc -gdwarf-4 -gstrict-dwarf -fvar-tracking-assignments -o hello hello.c

其中:

  • -gdwarf-4 指定dwarf版本为4
  • -fvar-tracking-assignments 在编译的早期对用户变量的赋值进行注释,并尝试在整个编译过程中将注释一直延续到最后,以尝试在优化的同时改进调试信息。
  • -gstrict-dwarf 禁用更高版本的的dwarf扩展,转而使用指定的dwarf版本的扩展
    此时我们可以看到,能够正常调试了。
    在这里插入图片描述

通过上述的演示,理论上我们只需要在项目编译时,指定dwarf版本,就可以正常调试了。
然而,如果问题如此简单就能解决,那似乎没有必要专门写一篇文章的必要,事实上,我在使用的时候,又遇到了比较玄学的问题。

玄之又玄

截取部分编译输出,可以看到,我的确使用了dwarf-4版本:
在这里插入图片描述

但是我们在运行时,发现仍然报Dwarf Error:

[root@ck08 flow]# gdb ./flow core.10772 
GNU gdb (GDB) Red Hat Enterprise Linux 7.6.1-120.el7
Copyright (C) 2013 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-redhat-linux-gnu".
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>...
Reading symbols from /root/chenyc/src/flow/flow...Dwarf Error: wrong version in compilation unit header (is 5, should be 2, 3, or 4) [in module /root/chenyc/src/flow/flow]
(no debugging symbols found)...done.
[New LWP 10773]
[New LWP 10774]
[New LWP 10775]
[New LWP 10776]
[New LWP 10772]
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib64/libthread_db.so.1".
Core was generated by `./flow'.
#0  0x00007f13b9ae7a35 in pthread_cond_wait@@GLIBC_2.3.2 () from /lib64/libpthread.so.0
Missing separate debuginfos, use: debuginfo-install glibc-2.17-326.el7_9.x86_64
(gdb) bt
#0  0x00007f13b9ae7a35 in pthread_cond_wait@@GLIBC_2.3.2 () from /lib64/libpthread.so.0
#1  0x00000000004117d5 in nxlog_worker_thread ()
#2  0x000000000040cdd5 in _thread_helper ()
#3  0x00007f13b9ae3ea5 in start_thread () from /lib64/libpthread.so.0
#4  0x00007f13b9400b0d in clone () from /lib64/libc.so.6
(gdb) 

那么,问题出在哪呢?为什么设置了dwarf版本,但是不生效?
为了实锤我们设置的dwarf版本确实生效了,我使用objdump命令查看了一下:

[root@ck08 flow]# objdump --dwarf=info ./flow|more

./flow:     file format elf64-x86-64

Contents of the .debug_info section:

  Compilation Unit @ offset 0x0:
   Length:        0x3e07 (32-bit)
   Version:       4
   Abbrev Offset: 0x0
   Pointer Size:  8
 <0><b>: Abbrev Number: 1 (DW_TAG_compile_unit)
    <c>   DW_AT_producer    : (indirect string, offset: 0x31f): GNU C17 11.3.0 -mtune=generic -march=x86-64 -g -gdwarf-4 -gstrict-dwa
rf -O2 -fPIC
    <10>   DW_AT_language    : 12       (ANSI C99)
    <11>   DW_AT_name        : (indirect string, offset: 0x16ac): src/core/protocol.c
    <15>   DW_AT_comp_dir    : (indirect string, offset: 0x1c15): /tmp
    <19>   DW_AT_low_pc      : 0x4090c0
    <21>   DW_AT_high_pc     : 0x127c
    <29>   DW_AT_stmt_list   : 0x0

这里,能看到src/core/protocol.c文件编译出来的二进制文件,dwarf版本确实是4。那么,为什么gdb调试仍然会报dwarf版本是5呢?
那么,会不会是程序依赖的第三方库使用了dwarf-5
带着疑问,我查看了一下所有的version
在这里插入图片描述

发现确实有部分二进制文件使用到了dwarf-5版本。
先把dwarf.debug-info导出来:

objdump --dwarf=info ./flow > dwarf.info

直接定位到754527行:
在这里插入图片描述

可以定位到,是在编译bzip2库的时候,出现了dwarf-5的版本。
为了验证我的猜想,我直接到容器里找到了libbz2,果然它就是罪魁祸首。

[root@5703f261ff2b lib]# objdump --dwarf=info libbz2.a|grep Version
   Version:       5
   Version:       5
   Version:       5
   Version:       5
   Version:       5
   Version:       5
   Version:       5
    <1760>   DW_AT_name        : (indirect string, offset: 0x650): BZ2_bzlibVersion
[root@5703f261ff2b lib]# 

那么问题来了,我是在容器里编译第三方依赖的,在编译之前统一设置过CC环境变量:

[root@5703f261ff2b tmp]# echo $CC
gcc -gdwarf-4 -gstrict-dwarf -fvar-tracking-assignments

截取部分Dockerfile内容:
在这里插入图片描述

Dockerfile可知,我们先设置了CC,然后依次编译openssllibaprbzip2,那为什么其他的依赖都没有问题,单单bzip2没有生效呢?

[root@5703f261ff2b lib]# objdump --dwarf=info libssl.a|grep Version
   Version:       4
   Version:       4
   Version:       4
   Version:       4
   Version:       4
   Version:       4
   Version:       4
   Version:       4
   Version:       4
   Version:       4

所以似乎还要到bzip2源码本身去找原因。于是我重新解压了bzip2的源码包,发现它是没有configure文件的,只有一个Makefile,打开Makefile,发现了端倪:
在这里插入图片描述

虽然我们在外面设置了CC的值,但是在Makefile里又将其覆盖掉了,使用的是gcc的默认dwarf版本,而我们的gcc11.3,所以默认使用了dwarf-5版本。
这里,明显看到bzip2开发者省了个懒,其实比较安全一点的写法应该是:

CC ?= gcc

我们将Makefile修改一下,重新编译,发现结果正确了:

[root@5703f261ff2b bzip2-1.0.8]# objdump --dwarf=info libbz2.a|grep Version
   Version:       4
   Version:       4
   Version:       4
   Version:       4
   Version:       4
   Version:       4
   Version:       4
    <1482>   DW_AT_name        : (indirect string, offset: 0x60c): BZ2_bzlibVersion

我使用新的bzip2库编译了一下程序,这时使用gcore生成core文件,已经不会报Dwarf Error了:

[root@ck08 flow]# gcore `pidof flow`
[New LWP 25963]
[New LWP 25962]
[New LWP 25961]
[New LWP 25960]
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib64/libthread_db.so.1".
0x00007f704555fb43 in select () from /lib64/libc.so.6
warning: target file /proc/25959/cmdline contained unexpected null characters
Saved corefile core.25959
[Inferior 1 (process 25959) detached]

使用gdb调试这个core文件也能拿到详细的调试信息:

[root@ck08 flow]# gdb ./flow core.25959
GNU gdb (GDB) Red Hat Enterprise Linux 7.6.1-120.el7
Copyright (C) 2013 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-redhat-linux-gnu".
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>...
Reading symbols from /root/chenyc/src/flow/flow...done.
[New LWP 25960]
[New LWP 25961]
[New LWP 25962]
[New LWP 25963]
[New LWP 25959]
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib64/libthread_db.so.1".
Core was generated by `./flow'.
#0  0x00007f7045c52efd in open64 () from /lib64/libpthread.so.0
Missing separate debuginfos, use: debuginfo-install glibc-2.17-326.el7_9.x86_64
(gdb) bt
#0  0x00007f7045c52efd in open64 () from /lib64/libpthread.so.0
#1  0x000000000049b731 in apr_file_open (new=0x7f7034003320, 
    fname=0x7f7034002ad0 "/root/chenyc/test/dc/mave/probes/itoa-flow/data/utf-8_nolb.log", flag=1, perm=<optimized out>, 
    pool=0x7f7034003288) at file_io/unix/open.c:176
#2  0x000000000041c1b9 in im_file_ext_input_open (module=0x2313a00, file=0x7f7045253fd8, finfo=0x7f704524eaa0, readfromlast=false, 
    existed=true) at src/modules/input/fileExt/im_fileExt.c:976
#3  0x000000000041f51f in im_file_ext_check_file (module=<optimized out>, file=<optimized out>, fname=<optimized out>, 
    pool=<optimized out>) at src/modules/input/fileExt/im_fileExt.c:1315
#4  0x0000000000420294 in im_file_ext_check_files (module=0x2313a00, active_only=<optimized out>)
    at src/modules/input/fileExt/im_fileExt.c:1475
#5  0x000000000042076b in im_file_ext_read (module=0x2313a00) at src/modules/input/fileExt/im_fileExt.c:2981
#6  0x00000000004208f8 in im_file_ext_event (module=0x2313a00, event=0x7f702c0008c0) at src/modules/input/fileExt/im_fileExt.c:3583
#7  0x00000000004118da in nxlog_worker_thread (thd=0x22f1c08, data=<optimized out>) at src/core/nxlog.c:552
#8  0x000000000040cdd5 in _thread_helper (thd=0x22f1c08, d=0x7ffc646c4050) at src/core/core.c:85
#9  0x00007f7045c4bea5 in start_thread () from /lib64/libpthread.so.0
#10 0x00007f7045568b0d in clone () from /lib64/libc.so.6
(gdb) 

总结

dwarf error的问题,网上很多资料说得很含糊,大多也都一知半解,真要深入研究,还是有很多坑的。反正总之从以下几个思路进行切入,基本都能找到解决方向:

  • dwarf error 一般出现在gcc编译环境版本与gdb调试环境版本不匹配导致,一般可以通过编译时指定dwarf版本解决
  • 除了我们自身的源码需要指定dwarf版本,程序所依赖的第三方库也需要使用指定的dwarf版本进行编译

推荐一个零声学院免费教程,个人觉得老师讲得不错,分享给大家:[Linux,Nginx,ZeroMQ,MySQL,Redis,
fastdfs,MongoDB,ZK,流媒体,CDN,P2P,K8S,Docker,
TCP/IP,协程,DPDK等技术内容,点击立即学习: C/C++Linux服务器开发/高级架构师

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

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

相关文章

EasyRecovery16适用于Windows和Mac的专业硬盘恢复软件

无论你对数据恢复了解多少&#xff0c; 我们将为您处理所有复杂的流程并简化恢复!适用于Windows和Mac的 专业硬盘恢复软件 硬盘数据无法保证绝对安全。有时会发生数据丢失&#xff0c;需要使用硬盘恢复工具。支持恢复不同存储介质数据&#xff1a;硬盘、光盘、U盘/移动硬盘、数…

类似于ChatGPT的优秀应用notion

notion 是一款流行的笔记应用。不过功能实际远超笔记&#xff0c;官方自己定义是&#xff1a;“将笔记、知识库和任务管理无缝整合的协作平台”。其独特的 block 概念&#xff0c;极大的扩展了笔记文档的作用&#xff0c;一个 block 可以是个数据库、多媒体、超链接、公式等等。…

如何把Docker容器变成物理机系统

如何把容器变成物理机 本文的主题是把容器变成物理机&#xff0c;根据所学的知识。以及通过各种搜索引擎。他们都告诉我们&#xff0c;这是不可能的。这真的是不可能的吗&#xff1f;我不信&#xff0c;那我就要创造奇迹。请继续往下看。本文将教你如何把容器变成物理机。作品…

PBDF8WN、FPBJXDN、FPBMXDN插装式比例阀放大器

PBHB8WN、PBFB8WN、PBDB8WN、PBHF8WN、PBFF8WN、PBDF8WN、PBJB8WN、RPEILAN、RBAPXAN、RBANXAN、FPBGXDN、FPBDXDN、FPBJXDN、FPBMXDN、FPBFXDN、FPBIXDN、FREPXAN比例插装阀一种高精度液压控制元件&#xff0c;其采用了先进的比例控制技术&#xff0c;可以根据控制信号快速地调…

linux介绍

/ 是所有目录的源点目录结构整体是一棵倒挂的树bin&#xff1a;存放二进制可执行文件boot&#xff1a;存放系统引导时使用的各类文件dev&#xff1a;存放设备文件etc&#xff1a;存放系统配置文件home&#xff1a;存放系统用户的文件lib&#xff1a;存放系统运行所需的共享库和…

传输层:TCP协议

传输层中有两个重要的协议&#xff1a;TCP协议和UDP协议。本博文分享的是TCP协议&#xff0c;不仅分享其协议格式&#xff0c;特点等等&#xff0c;还有应答机制、超时传送机制、连接管理机制、滑动窗口、阻塞控制等等。 TCP协议 TCP全称为 "传输控制协议(Transmission C…

Python中的自定义函数创建方法和应用举例

Python中的自定义函数创建方法和应用举例 在Python语言中&#xff0c;函数是一组能够完成特定任务的语句模块&#xff0c;可分为内置函数、第三方模块函数和自定义函数。其中&#xff0c;内置函数是Python系统自带的函数&#xff1b;模块函数是NumPy等库中的函数。 1.自定义函…

Java【网络原理2】TCP 协议的三次握手和四次挥手到底是什么意思?

文章目录 前言一、三次握手三次握手的作用 二、四次挥手总结 前言 各位读者好, 我是小陈, 这是我的个人主页, 希望我的专栏能够帮助到你: &#x1f4d5; JavaSE基础: 基础语法, 类和对象, 封装继承多态, 接口, 综合小练习图书管理系统等 &#x1f4d7; Java数据结构: 顺序表, 链…

mysqlbinlog delete恢复成insert

不小心把数据删掉了 首先要拿到binlog文件 执行以下命令行 /usr/local/mysql/bin/mysqlbinlog --base64-outputdecode-rows --start-datetime“2023-05-19 09:01:32” --stop-datetime“2023-05-19 09:01:35” -v /Users/zylong/Downloads/mysql-bin.003178 --result-file/Use…

第一章 数据库操作

一、 数据库操作 1.1 创建数据库 创建数据库是指在数据库系统中划分一块空间&#xff0c;用来存储相应的数据&#xff0c;这是进行表操作的基础&#xff0c;也是数据库管理的基础在MySQL中创建数据库之前&#xff0c;可以使用show语句来显示当前已经存在的数据库&#xff0c;…

java基于springboot协同过滤算法的网上图书商城推荐系统m44xq2

开发环境 开发语言&#xff1a;Java 框架&#xff1a;springboot 技术&#xff1a;JSP JDK版本&#xff1a;JDK1.8 数据库工具&#xff1a;Navicat11 开发软件&#xff1a;eclipse/myeclipse/idea Maven包&#xff1a;Maven3.3.9 浏览器&#xff1a;谷歌浏览器功能介绍 Spring…

微服务—Redis实用篇-黑马头条项目用户签到功能(使用bitmap实现)与UV统计

微服务—Redis实用篇-黑马头条项目用户签到功能&#xff08;使用bitmap实现&#xff09;与UV统计 1、用户签到 1.1、用户签到-BitMap功能演示 我们针对签到功能完全可以通过mysql来完成&#xff0c;比如说以下这张表 用户一次签到&#xff0c;就是一条记录&#xff0c;假如有…

npm 账户的创建、测试、登录

目录 1、账户的创建 2、测试创建的账户 3、通过电子邮件接收一次性密码 3.1 使用一次性密码登录&#xff1a; 3.2 启用双因素身份验证&#xff1a; 3.3 无法访问电子邮件 1、账户的创建 如果您还没有npm用户帐户&#xff0c;您可以创建一个帐户&#xff0c;以便在公共注…

MATLAB 之 二维图形绘制的基本函数和辅助操作

文章目录 一、绘制二维曲线的基本函数1. plot 函数的基本用法2. 含多个输入参数的 plot 函数3. 含选项的 plot 函数4. 双纵坐标函数 plotyy 二、绘制绘制图像的辅助操作1. 图形标注2. 坐标控制3. 图形保持4. 图形窗口的分割 二维图形是将平面坐标上的数据点连接起来的平面图形。…

C++的cout详解

2023年5月20日&#xff0c;周六早上&#xff1a; 我发现我找不到非常详细的cout类的成员函数&#xff0c;只好自己写了。 不定期更新。 cout的继承关系 cout类继承自ostream类&#xff0c;ostream类继承自ios类&#xff0c;ios类继承自ios_base类 cout类拥有的所有成员函数 …

包图的画法

包图 1.包图 1.1概念 包图是UML中用来组织模型元素的模型元素。 可以把包图比作一个存放模型元素的箱子或者是容器&#xff0c;里面可以可以存放各种各样的模型元素。 包图中可以包含的信息主要有&#xff1a;类 构件 用例 结点 活动 状态 等其他的相关的模型元素。 1.2包…

秒杀场景checklist

在面试中经常碰到的问题&#xff0c;列下来备忘。

车载以太网 - SomeIP - 协议用例 - Messages_01

目录 Service Discovery Messages 1、验证Instance ID为0xFFFF时DUT需要返回该Serveice ID包含的所有Instance ID

chatgpt赋能Python-python3去掉空格

Python3去掉空格——让你的代码更整洁高效 在Python3编程中&#xff0c;去掉无用的空格可以使代码更具可读性和高效性。因此&#xff0c;在本文中&#xff0c;我们将介绍如何使用Python3去掉空格&#xff0c;包括字符串中的空格和代码文件中的空格。 字符串中的空格 在Pytho…

学系统集成项目管理工程师(中项)系列23a_信息系统集成及服务管理(上)

1. 提供的专业信息技术咨询服务、系统集成服务、技术支持服务、运行维护服务等工作 2. 信息技术咨询服务 2.1. 信息系统集成及服务的前端环节 2.2. 为企业提供信息化建设规划和解决方案 3. 原因 3.1. 不具备技术实力的系统集成商搅乱信息系统集成及服务市场 3.2. 一些建设…