坑惨啦!!!——符号冲突案例分析

 背景

        前段时间在北汽项目中,遇到了一个奇怪现象:程序启动之后,偶现运行一段时间后,crash,复现频率较高。困扰了大家较长时间。最终在和同事的不懈努力下,找到的根因,并找到了解决方法。过程中也学习到了很多。在此,记录并分享,希望能够帮助大家。

问题描述

         作为OTA服务的提供方,我们提供方式一般为将自己的代码编译成动态库(libsysi4dpc.so),提供给设备厂家,让他们进行集成,并调试。

         从控和主控之间通过MQTT协议实现进程间通信。MQTT client相关接口被封装在libupc.a静态库中。也就是说,libsysi4dpc.so 库中其实是包含libupc.a 静态库的所有代码段和数据段(需要的都会加载进去,以.o为单位),并不再依赖其他MQTT 动态库。如下图所示:

         但是当客户集成libsysi4dpc.so 库,生成可执行程序hal-process。当出现crash时,通过gdb进行函数调用栈跟踪。会发现,虽然出错线程的底层调用函数是我们的adm_ups_run 接口(进行MQTT 重连的线程),但是其栈进入到了libmqtt-paho.so库中(这应该也是支持MQTT协议的库)。这就让我们很奇怪,因为我们代码中的MQTT协议接口应该是在libupc.a 中,为什么会走到libmqtt-paho.so 中呢?

         通过查询hal-process 动态库依赖,如下:

        由上可知,libmqtt-paho.so 是hal-process进行链接的。(细心的同事可能会觉得奇怪,为什么没有看到libsysi4dpc.so,那是因为hal-process并不直接依赖我们的库,而是通过libqzm_dpc.so 间接依赖)。

        到此,问题应该就明确了,crash 的原因:是因为adm_ups_run 接口,并没有走到我们自己的mqtt库中,而是运行到了客户提供的mqtt库中,由于mqtt版本的不同,其中兼容问题,导致了crash

项目的模块依赖关系大致如下:

        上述的问题其实有一个专业的名词,叫做同名符号冲突

符号冲突

        符号冲突,作为开发人员,我们应该并不少见。比如编译器提示如下错误:

multiple definition of `xxx';

        其原因,就是同一作用域内,存在相同的符号。可参考C语言中弱符号与弱引用的实际应用_c语言中的弱引用_谢艺华的博客-CSDN博客文章。

        但是为什么上面的场景中,客户在编译hal-process程序时,编译器没有提示相关错误呢?因为符号冲突存在两种形式:显式符号冲突隐式符号冲突

显示符号冲突

        显示符号冲突,就是我们常见的一种错误,它可以在编译阶段直接报错。比如:

yihua@ubuntu:~/symbol$ cat strong_symbol.c
#include<stdio.h>
int symbol()
{
        printf("strong symbol\n");
}
yihua@ubuntu:~/symbol$ cat week_symbol.c
#include<stdio.h>
int symbol()
{
        printf("week symbol\n");
}
yihua@ubuntu:~/symbol$ cat main.c
#include<stdio.h>
#include<stdlib.h>
extern int symbol();
int main()
{
        symbol();
        return 0;
}
yihua@ubuntu:~/symbol$ gcc main.c strong_symbol.c week_symbol.c -o main
/tmp/ccSU1PtA.o: In function `symbol':

week_symbol.c:(.text+0x0): multiple definition of `symbol'
/tmp/cc1gLIic.o:strong_symbol.c:(.text+0x0): first defined here
collect2: error: ld returned 1 exit status

        但有时也存在这样的场景。两个静态库拥有相同的函数符号symbol,并且主函数也调用了symbol,编译阶段会提示错误吗?

还是上述的代码,我们进行如下验证

  1. 生成libstrong.alibweek.a静态库

C
yihua@ubuntu:~/symbol$ gcc -c strong_symbol.c
yihua@ubuntu:~/symbol$ gcc -c week_symbol.c
yihua@ubuntu:~/symbol$ ar -rcs libstrong.a strong_symbol.o
yihua@ubuntu:~/symbol$ ar -rcs libweek.a week_symbol.o

     2. main.c链接两个静态库,生成可执行程序

        由上可知,并没有提示错误,并且输出week symbol

分析:

        我们可以在编译选项中增加-Wl,-–verbose选项(将编译过程中的详细信息进行输出)。输出如下:

yihua@ubuntu:~/symbol$ gcc main.c -L./ -Wl,--verbose -lweek -lstrong -o main
GNU ld (GNU Binutils) 2.30
  Supported emulations:
   elf_x86_64
   elf32_x86_64
   elf_i386
   elf_iamcu
   i386linux
   elf_l1om
   elf_k1om
using internal linker script:
==================================================
/* Script for -pie -z combreloc -z now -z relro: position independent executable, combine & sort relocs */
/* Copyright (C) 2014-2018 Free Software Foundation, Inc.
   Copying and distribution of this script, with or without modification,
   are permitted in any medium without royalty provided the copyright
   notice and this notice are preserved.  */
OUTPUT_FORMAT("elf64-x86-64", "elf64-x86-64",
              "elf64-x86-64")
OUTPUT_ARCH(i386:x86-64)
ENTRY(_start)
SEARCH_DIR("/usr/local/x86_64-pc-linux-gnu/lib64"); SEARCH_DIR("/usr/local/lib64"); SEARCH_DIR("/lib64"); SEARCH_DIR("/usr/lib64"); SEARCH_DIR("/usr/local/x86_64-pc-linux-gnu/lib"); SEARCH_DIR("/usr/local/lib"); SEARCH_DIR("/lib"); SEARCH_DIR("/usr/lib");
SECTIONS
{
  /* Read-only sections, merged into text segment: */
  PROVIDE (__executable_start = SEGMENT_START("text-segment", 0)); . = SEGMENT_START("text-segment", 0) + SIZEOF_HEADERS;
  .interp         : { *(.interp) }
  .note.gnu.build-id : { *(.note.gnu.build-id) }
  .hash           : { *(.hash) }
  .gnu.hash       : { *(.gnu.hash) }
  .dynsym         : { *(.dynsym) }
  .dynstr         : { *(.dynstr) }
  .gnu.version    : { *(.gnu.version) }
  .gnu.version_d  : { *(.gnu.version_d) }
  .gnu.version_r  : { *(.gnu.version_r) }
  .rela.dyn       :
    {
      *(.rela.init)
      *(.rela.text .rela.text.* .rela.gnu.linkonce.t.*)
      *(.rela.fini)
      *(.rela.rodata .rela.rodata.* .rela.gnu.linkonce.r.*)
      *(.rela.data .rela.data.* .rela.gnu.linkonce.d.*)
      *(.rela.tdata .rela.tdata.* .rela.gnu.linkonce.td.*)
      *(.rela.tbss .rela.tbss.* .rela.gnu.linkonce.tb.*)
      *(.rela.ctors)
      *(.rela.dtors)
      *(.rela.got)
      *(.rela.bss .rela.bss.* .rela.gnu.linkonce.b.*)
      *(.rela.ldata .rela.ldata.* .rela.gnu.linkonce.l.*)
      *(.rela.lbss .rela.lbss.* .rela.gnu.linkonce.lb.*)
      *(.rela.lrodata .rela.lrodata.* .rela.gnu.linkonce.lr.*)
      *(.rela.ifunc)
    }
  .rela.plt       :
    {
      *(.rela.plt)
      PROVIDE_HIDDEN (__rela_iplt_start = .);
      *(.rela.iplt)
      PROVIDE_HIDDEN (__rela_iplt_end = .);
    }
  .init           :
  {
    KEEP (*(SORT_NONE(.init)))
  }
  .plt            : { *(.plt) *(.iplt) }
.plt.got        : { *(.plt.got) }
.plt.sec        : { *(.plt.sec) }
  .text           :
  {
    *(.text.unlikely .text.*_unlikely .text.unlikely.*)
    *(.text.exit .text.exit.*)
    *(.text.startup .text.startup.*)
    *(.text.hot .text.hot.*)
    *(.text .stub .text.* .gnu.linkonce.t.*)
    /* .gnu.warning sections are handled specially by elf32.em.  */
    *(.gnu.warning)
  }
  .fini           :
  {
    KEEP (*(SORT_NONE(.fini)))
  }
  PROVIDE (__etext = .);
  PROVIDE (_etext = .);
  PROVIDE (etext = .);
  .rodata         : { *(.rodata .rodata.* .gnu.linkonce.r.*) }
  .rodata1        : { *(.rodata1) }
  .eh_frame_hdr : { *(.eh_frame_hdr) *(.eh_frame_entry .eh_frame_entry.*) }
  .eh_frame       : ONLY_IF_RO { KEEP (*(.eh_frame)) *(.eh_frame.*) }
  .gcc_except_table   : ONLY_IF_RO { *(.gcc_except_table
  .gcc_except_table.*) }
  .gnu_extab   : ONLY_IF_RO { *(.gnu_extab*) }
  /* These sections are generated by the Sun/Oracle C++ compiler.  */
  .exception_ranges   : ONLY_IF_RO { *(.exception_ranges
  .exception_ranges*) }
  /* Adjust the address for the data segment.  We want to adjust up to
     the same address within the page on the next page up.  */
  . = DATA_SEGMENT_ALIGN (CONSTANT (MAXPAGESIZE), CONSTANT (COMMONPAGESIZE));
  /* Exception handling  */
  .eh_frame       : ONLY_IF_RW { KEEP (*(.eh_frame)) *(.eh_frame.*) }
  .gnu_extab      : ONLY_IF_RW { *(.gnu_extab) }
  .gcc_except_table   : ONLY_IF_RW { *(.gcc_except_table .gcc_except_table.*) }
  .exception_ranges   : ONLY_IF_RW { *(.exception_ranges .exception_ranges*) }
  /* Thread Local Storage sections  */
  .tdata          : { *(.tdata .tdata.* .gnu.linkonce.td.*) }
  .tbss           : { *(.tbss .tbss.* .gnu.linkonce.tb.*) *(.tcommon) }
  .preinit_array     :
  {
    PROVIDE_HIDDEN (__preinit_array_start = .);
    KEEP (*(.preinit_array))
    PROVIDE_HIDDEN (__preinit_array_end = .);
  }
  .init_array     :
  {
    PROVIDE_HIDDEN (__init_array_start = .);
    KEEP (*(SORT_BY_INIT_PRIORITY(.init_array.*) SORT_BY_INIT_PRIORITY(.ctors.*)))
    KEEP (*(.init_array EXCLUDE_FILE (*crtbegin.o *crtbegin?.o *crtend.o *crtend?.o ) .ctors))
    PROVIDE_HIDDEN (__init_array_end = .);
  }
  .fini_array     :
  {
    PROVIDE_HIDDEN (__fini_array_start = .);
    KEEP (*(SORT_BY_INIT_PRIORITY(.fini_array.*) SORT_BY_INIT_PRIORITY(.dtors.*)))
    KEEP (*(.fini_array EXCLUDE_FILE (*crtbegin.o *crtbegin?.o *crtend.o *crtend?.o ) .dtors))
    PROVIDE_HIDDEN (__fini_array_end = .);
  }
  .ctors          :
  {
    /* gcc uses crtbegin.o to find the start of
       the constructors, so we make sure it is
       first.  Because this is a wildcard, it
       doesn't matter if the user does not
       actually link against crtbegin.o; the
       linker won't look for a file to match a
       wildcard.  The wildcard also means that it
       doesn't matter which directory crtbegin.o
       is in.  */
    KEEP (*crtbegin.o(.ctors))
    KEEP (*crtbegin?.o(.ctors))
    /* We don't want to include the .ctor section from
       the crtend.o file until after the sorted ctors.
       The .ctor section from the crtend file contains the
       end of ctors marker and it must be last */
    KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .ctors))
    KEEP (*(SORT(.ctors.*)))
    KEEP (*(.ctors))
  }
  .dtors          :
  {
    KEEP (*crtbegin.o(.dtors))
    KEEP (*crtbegin?.o(.dtors))
    KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .dtors))
    KEEP (*(SORT(.dtors.*)))
    KEEP (*(.dtors))
  }
  .jcr            : { KEEP (*(.jcr)) }
  .data.rel.ro : { *(.data.rel.ro.local* .gnu.linkonce.d.rel.ro.local.*) *(.data.rel.ro .data.rel.ro.* .gnu.linkonce.d.rel.ro.*) }
  .dynamic        : { *(.dynamic) }
  .got            : { *(.got.plt) *(.igot.plt) *(.got) *(.igot) }
  . = DATA_SEGMENT_RELRO_END (0, .);
  .data           :
  {
    *(.data .data.* .gnu.linkonce.d.*)
    SORT(CONSTRUCTORS)
  }
  .data1          : { *(.data1) }
  _edata = .; PROVIDE (edata = .);
  . = .;
  __bss_start = .;
  .bss            :
  {
   *(.dynbss)
   *(.bss .bss.* .gnu.linkonce.b.*)
   *(COMMON)
   /* Align here to ensure that the .bss section occupies space up to
      _end.  Align after .bss to ensure correct alignment even if the
      .bss section disappears because there are no input sections.
      FIXME: Why do we need it? When there is no .bss section, we don't
      pad the .data section.  */
   . = ALIGN(. != 0 ? 64 / 8 : 1);
  }
  .lbss   :
  {
    *(.dynlbss)
    *(.lbss .lbss.* .gnu.linkonce.lb.*)
    *(LARGE_COMMON)
  }
  . = ALIGN(64 / 8);
  . = SEGMENT_START("ldata-segment", .);
  .lrodata   ALIGN(CONSTANT (MAXPAGESIZE)) + (. & (CONSTANT (MAXPAGESIZE) - 1)) :
  {
    *(.lrodata .lrodata.* .gnu.linkonce.lr.*)
  }
  .ldata   ALIGN(CONSTANT (MAXPAGESIZE)) + (. & (CONSTANT (MAXPAGESIZE) - 1)) :
  {
    *(.ldata .ldata.* .gnu.linkonce.l.*)
    . = ALIGN(. != 0 ? 64 / 8 : 1);
  }
  . = ALIGN(64 / 8);
  _end = .; PROVIDE (end = .);
  . = DATA_SEGMENT_END (.);
  /* Stabs debugging sections.  */
  .stab          0 : { *(.stab) }
  .stabstr       0 : { *(.stabstr) }
  .stab.excl     0 : { *(.stab.excl) }
  .stab.exclstr  0 : { *(.stab.exclstr) }
  .stab.index    0 : { *(.stab.index) }
  .stab.indexstr 0 : { *(.stab.indexstr) }
  .comment       0 : { *(.comment) }
  /* DWARF debug sections.
     Symbols in the DWARF debugging sections are relative to the beginning
     of the section so we begin them at 0.  */
  /* DWARF 1 */
  .debug          0 : { *(.debug) }
  .line           0 : { *(.line) }
  /* GNU DWARF 1 extensions */
  .debug_srcinfo  0 : { *(.debug_srcinfo) }
  .debug_sfnames  0 : { *(.debug_sfnames) }
  /* DWARF 1.1 and DWARF 2 */
  .debug_aranges  0 : { *(.debug_aranges) }
  .debug_pubnames 0 : { *(.debug_pubnames) }
  /* DWARF 2 */
  .debug_info     0 : { *(.debug_info .gnu.linkonce.wi.*) }
  .debug_abbrev   0 : { *(.debug_abbrev) }
  .debug_line     0 : { *(.debug_line .debug_line.* .debug_line_end ) }
  .debug_frame    0 : { *(.debug_frame) }
  .debug_str      0 : { *(.debug_str) }
  .debug_loc      0 : { *(.debug_loc) }
  .debug_macinfo  0 : { *(.debug_macinfo) }
  /* SGI/MIPS DWARF 2 extensions */
  .debug_weaknames 0 : { *(.debug_weaknames) }
  .debug_funcnames 0 : { *(.debug_funcnames) }
  .debug_typenames 0 : { *(.debug_typenames) }
  .debug_varnames  0 : { *(.debug_varnames) }
  /* DWARF 3 */
  .debug_pubtypes 0 : { *(.debug_pubtypes) }
  .debug_ranges   0 : { *(.debug_ranges) }
  /* DWARF Extension.  */
  .debug_macro    0 : { *(.debug_macro) }
  .debug_addr     0 : { *(.debug_addr) }
  .gnu.attributes 0 : { KEEP (*(.gnu.attributes)) }
  /DISCARD/ : { *(.note.GNU-stack) *(.gnu_debuglink) *(.gnu.lto_*) }
}


==================================================
attempt to open /usr/lib/gcc/x86_64-linux-gnu/7/../../../x86_64-linux-gnu/Scrt1.o succeeded
/usr/lib/gcc/x86_64-linux-gnu/7/../../../x86_64-linux-gnu/Scrt1.o
attempt to open /usr/lib/gcc/x86_64-linux-gnu/7/../../../x86_64-linux-gnu/crti.o succeeded
/usr/lib/gcc/x86_64-linux-gnu/7/../../../x86_64-linux-gnu/crti.o
attempt to open /usr/lib/gcc/x86_64-linux-gnu/7/crtbeginS.o succeeded
/usr/lib/gcc/x86_64-linux-gnu/7/crtbeginS.o
attempt to open /tmp/ccipcMTK.o succeeded
/tmp/ccipcMTK.o

attempt to open .//libweek.so failed
attempt to open .//libweek.a succeeded
(.//libweek.a)week_symbol.o
attempt to open .//libstrong.so failed
attempt to open .//libstrong.a succeeded
attempt to open .//libgcc.so failed
attempt to open .//libgcc.a failed
attempt to open /usr/lib/gcc/x86_64-linux-gnu/7/libgcc.so failed
attempt to open /usr/lib/gcc/x86_64-linux-gnu/7/libgcc.a succeeded
attempt to open .//libgcc_s.so failed
attempt to open .//libgcc_s.a failed
attempt to open /usr/lib/gcc/x86_64-linux-gnu/7/libgcc_s.so succeeded
opened script file /usr/lib/gcc/x86_64-linux-gnu/7/libgcc_s.so
opened script file /usr/lib/gcc/x86_64-linux-gnu/7/libgcc_s.so
attempt to open libgcc_s.so.1 failed
attempt to open .//libgcc_s.so.1 failed
attempt to open /usr/lib/gcc/x86_64-linux-gnu/7/libgcc_s.so.1 succeeded
libgcc_s.so.1 (/usr/lib/gcc/x86_64-linux-gnu/7/libgcc_s.so.1)
attempt to open .//libgcc.so failed
attempt to open .//libgcc.a failed
attempt to open /usr/lib/gcc/x86_64-linux-gnu/7/libgcc.so failed
attempt to open /usr/lib/gcc/x86_64-linux-gnu/7/libgcc.a succeeded
attempt to open .//libc.so failed
attempt to open .//libc.a failed
attempt to open /usr/lib/gcc/x86_64-linux-gnu/7/libc.so failed
attempt to open /usr/lib/gcc/x86_64-linux-gnu/7/libc.a failed
attempt to open /usr/lib/gcc/x86_64-linux-gnu/7/../../../x86_64-linux-gnu/libc.so succeeded
opened script file /usr/lib/gcc/x86_64-linux-gnu/7/../../../x86_64-linux-gnu/libc.so
opened script file /usr/lib/gcc/x86_64-linux-gnu/7/../../../x86_64-linux-gnu/libc.so
attempt to open /lib/x86_64-linux-gnu/libc.so.6 succeeded
/lib/x86_64-linux-gnu/libc.so.6
attempt to open /usr/lib/x86_64-linux-gnu/libc_nonshared.a succeeded
(/usr/lib/x86_64-linux-gnu/libc_nonshared.a)elf-init.oS
attempt to open /lib/x86_64-linux-gnu/ld-linux-x86-64.so.2 succeeded
/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2
/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2
attempt to open .//libgcc.so failed
attempt to open .//libgcc.a failed
attempt to open /usr/lib/gcc/x86_64-linux-gnu/7/libgcc.so failed
attempt to open /usr/lib/gcc/x86_64-linux-gnu/7/libgcc.a succeeded
attempt to open .//libgcc_s.so failed
attempt to open .//libgcc_s.a failed
attempt to open /usr/lib/gcc/x86_64-linux-gnu/7/libgcc_s.so succeeded
opened script file /usr/lib/gcc/x86_64-linux-gnu/7/libgcc_s.so
opened script file /usr/lib/gcc/x86_64-linux-gnu/7/libgcc_s.so
attempt to open libgcc_s.so.1 failed
attempt to open .//libgcc_s.so.1 failed
attempt to open /usr/lib/gcc/x86_64-linux-gnu/7/libgcc_s.so.1 succeeded
libgcc_s.so.1 (/usr/lib/gcc/x86_64-linux-gnu/7/libgcc_s.so.1)
attempt to open .//libgcc.so failed
attempt to open .//libgcc.a failed
attempt to open /usr/lib/gcc/x86_64-linux-gnu/7/libgcc.so failed
attempt to open /usr/lib/gcc/x86_64-linux-gnu/7/libgcc.a succeeded
attempt to open /usr/lib/gcc/x86_64-linux-gnu/7/crtendS.o succeeded
/usr/lib/gcc/x86_64-linux-gnu/7/crtendS.o
attempt to open /usr/lib/gcc/x86_64-linux-gnu/7/../../../x86_64-linux-gnu/crtn.o succeeded
/usr/lib/gcc/x86_64-linux-gnu/7/../../../x86_64-linux-gnu/crtn.o
ld-linux-x86-64.so.2 needed by /lib/x86_64-linux-gnu/libc.so.6
found ld-linux-x86-64.so.2 at /lib/x86_64-linux-gnu/ld-linux-x86-64.so.2

我们知道静态其实就是目标文件.o的集合体,只有当可执行程序需要时,才会从静态库中提取出相关依赖的.o文件,进行编译。这也是为什么hello world程序依赖libc.a,但是最终可执行程序却比libc.a小的多的原因,因为可执行程序仅将其中的printf.o进行编译。

attempt to open .//libweek.so failed
attempt to open .//libweek.a succeeded
(.//libweek.a)week_symbol.o
attempt to open .//libstrong.so failed
attempt to open .//libstrong.a succeeded

从上述的红色字体中,我们可以知道编译过程:

  1. 加载了libweek.a静态库,并将其中的week_symbol.o提取出来
  2. 加载了libstrong.a静态库,但是没有加载任何目标文件

因此最终输出week symbol这就会导致程序运行最终结果与预期不一致。该现象也称为符号抢占

在如今IT现状下,主进程可能依赖多个模块,各个模块提供的集成方式,很有可能就是以静态库或动态库形式。那么如何确保避免上述问题呢

        链接器提供了一个链接参数-Wl,--whole-archive,该参数告诉链接器,将该参数后面的共享库强制编译到目标文件中,即将共享库中的代码段,数据段强制加入到可执行程序中。对应-Wl,--no-whole-archive关闭该属性。命令行如下:

发现这时提示错误了。打开-Wl,-–verbose参数可知:

attempt to open .//libweek.so failed
attempt to open .//libweek.a succeeded
(.//libweek.a)week_symbol.o
attempt to open .//libstrong.so failed
attempt to open .//libstrong.a succeeded
(.//libstrong.a)strong_symbol.o

此时,加载libstrong.a时,也将strong_symbol.o加载了。

隐式符号冲突

        如上可知,我们遇到的问题并不是显示符号冲突,因为显示符号冲突是在编译阶段就已经确认,并且会一直存在。和我们问题现象不符合。隐式符号冲突会导致程序运行时跳转到错误的符号,进而执行错误的代码段,最终造成错误。

隐式符号冲突其实就是动态库符号重定位导致的问题。可参考我的另一篇博客。

解决方式

        通过上述分析,该问题是隐式符号冲突导致的。最终在编译libsysi4dpc.so动态库时,添加链接属性-Wl,Bsymbolic。要求动态库采用本地的符号。

总结

        通过以上分析,我觉得在项目中,我们可以从预防和解决两个方面规避符号冲,导致程序非预期情况。

  1. 预防,提前识别到冲突符号。使用-Wl,--whole-archive-Wl,--no-whole-archive链接参数。在链接第三方库时,强制将共享库的符号加载到目标文件中。
  2. 若动态库依赖本地的文件时,设置-Wl,Bsymbolic参数。非必要不要使用,会导致目标文件很大

        其实客户编译阶段应该没有设置-Wl,--whole-archive-Wl,--no-whole-archive链接参数,否则在编译阶段应该就会发现该问题。

参考文档

https://blog.csdn.net/weixin_45449806/article/details/129114860

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

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

相关文章

影视行业如何远程完整快速传输大文件?

影视行业是一个充满创意和协作的领域。在影视制作中&#xff0c;涉及到多个环节和部门&#xff0c;包括编剧、导演、摄影、剪辑、配音、视效等。这些环节和部门通常分布在不同的地点&#xff0c;甚至不同的国家。因此&#xff0c;影视制作过程中需要频繁进行远程传输&#xff0…

科技赋能,创新发展!英码科技受邀参加2023中国创新创业成果交易会

11月17日至19日&#xff0c;2023中国创新创业成果交易会&#xff08;简称&#xff1a;创交会&#xff09;在广州市广交会展馆圆满举行。英码科技受邀参加本届创交会&#xff0c;并在会场展示了创新性的AIoT产品、深元AI引擎和行业热门解决方案。 据介绍&#xff0c;本届创交会由…

vscode编写verilog的插件【对齐、自动生成testbench文件】

vscode编写verilog的插件&#xff1a; 插件名称&#xff1a;verilog_testbench,用于自动生成激励文件 安装教程&#xff1a;基于VS Code的Testbench文件自动生成方法——基于VS Code的Verilog编写环境搭建SP_哔哩哔哩_bilibili 优化的方法&#xff1a;https://blog.csdn.net…

CMake 判断操作系统类型

上回的CMakeLists.txt里面有一句,if (WIN32)......endif(WIN32); 根据资料,这是判断操作系统是否是Windows; 下面单独看一下; 一个CMakeLists.txt文件如下; if(WIN32)# 如果是 Windowsmessage("当前操作系统为 Windows") elseif(UNIX AND NOT APPLE)# 如果…

Go 语言学习:了解 const 关键字及常量声明

如果一个变量应该有一个固定的、不能改变的值&#xff0c;你可以使用const关键字。 const关键字将变量声明为"常量"&#xff0c;这意味着它是不可改变和只读的。 语法 const CONSTNAME type value 声明常量 声明常量的示例&#xff1a; package main import (…

【MATLAB基础绘图第17棒】绘制玫瑰图

MATLAB绘制玫瑰图 玫瑰图&#xff08;Nightingale Rose Chart&#xff09;风玫瑰图&#xff08;WindRose&#xff09;准备工作&#xff1a;WindRose工具包下载案例案例1&#xff1a;基础绘图 参考 玫瑰图&#xff08;Nightingale Rose Chart&#xff09; 玫瑰图&#xff08;Ni…

Java_异常详解

前言 异常是什么,异常如何抛出,如何抛出自定义异常,异常处理主要的五个关键字&#xff1a;throw,try,catch,finally,throws ,异常的处理流程 异常是什么 在Java中&#xff0c;将程序执行过程中发生的不正常行为称为异常。比如之前写代码时经常遇到的&#xff1a; 1. 算数异…

app小程序定制开发的优势|企业软件网站建设

app小程序定制开发的优势|企业软件网站建设 小程序定制开发是目前互联网行业中备受关注的领域之一。随着智能手机的普及和移动互联网的迅猛发展&#xff0c;越来越多的企业和个人开始重视小程序的潜力&#xff0c;并积极寻求定制开发的服务。那么&#xff0c;为什么小程序定制开…

php字符串处理函数的使用

php字符串处理函数的使用 trim() trim()函数的功能用于去除字符串首尾的空白字符(包括空格、制表符、换行符等&#xff09;。它可以用于清理用户输入的数据或去除字符串中的多余空格。 <?php $char" holle world! ";echo trim($char) ?>str_repl…

【LeetCode】104. 二叉树的最大深度

104. 二叉树的最大深度 难度&#xff1a;简单 题目 给定一个二叉树 root &#xff0c;返回其最大深度。 二叉树的 最大深度 是指从根节点到最远叶子节点的最长路径上的节点数。 示例 1&#xff1a; 输入&#xff1a;root [3,9,20,null,null,15,7] 输出&#xff1a;3示例 …

JVM--Java虚拟机

0. java代码的执行过程 了解Java虚拟机&#xff08;JVM&#xff09;首先需要了解一下一段Java代码的具体执行过程。 Java代码的具体执行过程如下&#xff1a; 执行 javac 命令编译源代码为字节码执行 java 命令&#xff0c;二进制字节码通过解释器翻译为机器码 创建 JVM&…

通过css设置元素隐藏和显示

背景&#xff1a;鼠标悬浮时显示删除&#xff0c;放开后显示组件名 解决&#xff1a;通过display:none和display:block显示和隐藏元素&#xff1b; 使用 div p选择器选择当前div的下一个紧跟的p元素 <div v-if"!preview" class"name">{{propertyDa…

(数据库管理系统)DBMS与(数据库系统)DBS的区别

数据库管理系统&#xff08;DBMS&#xff0c;Database Management System&#xff09;和数据库系统&#xff08;DBS&#xff0c;Database System&#xff09;是两个相关但不同的概念。 DBS是一个更广泛的概念&#xff0c;指的是计算机系统引入数据库后的系统&#xff0c;包括数…

Spring接入Metric+Graphite+Grafana搭建监控系统

环境搭建 Metric 主要是记录操作记录&#xff0c;把数据传给Graphite&#xff0c;这个只需要引入依赖就可以了 日志收集系统&#xff0c;可以支持很多的监控系统一般在Spring项目中用其收集数据&#xff0c;可以发送到Graphite等监控系统中一般使用Merter和Timer分别记录成功…

C# 图解教程 第5版 —— 第15章 事件

文章目录 15.1 发布者和订阅者15.2 源代码组件概览15.3 声明事件15.4 订阅事件15.5 触发事件15.6 标准事件的用法15.6.1 通过扩展 EventArgs 来传递数据15.6.2 移除事件处理程序 15.7 事件访问器 15.1 发布者和订阅者 ​ 发布者 / 订阅者模式&#xff1a;发布者定义了一系列事…

Lifecyle的原理

1、Lifecycle是典型的观察者模式&#xff0c;被观察者的继承关系如上图所示。 2、LifeCycleRegistry是Lifecycle的子类。 3、观察者通过LifeCycle对象的addObserver注册监听生命周期的变化&#xff0c;通过removeObserver移除监听生命周期的变化。 4、Activity或Fragment的生命…

【MATLAB源码-第84期】基于matlab的802.11a标准的OFDM系统误码仿真对比QPSK,16QAM。

操作环境&#xff1a; MATLAB 2022a 1、算法描述 基于802.11a标准的OFDM&#xff08;正交频分复用&#xff09;系统是一种高效的无线通信技术&#xff0c;特点如下&#xff1a; 频带与信道&#xff1a; 802.11a工作在5 GHz频段&#xff0c;这个频段相对于2.4 GHz&#xff08…

目标检测 详解SSD原理,数据处理与复现

原理详解 前言 今天我们要读的这篇VGGNet&#xff08;《Very Deep Convolutional Networks For Large-Scale Image Recognition》&#xff09;&#xff0c;就是在AlexNet基础上对深度对网络性能的影响做了进一步的探索。它是ImageNet 2014年亚军&#xff0c;相比于AlexNet&am…

ueditor整合到thinkPHP里

<?phpnamespace app\ueditor\controller;use think\Controller;class Ueditor extends Controller {//首页public function upload(){//header(Access-Control-Allow-Origin: http://www.baidu.com); //设置http://www.baidu.com允许跨域访问//header(Access-Control-Allow…

Motion Plan之搜索算法笔记

背景&#xff1a; 16-18年做过一阵子无人驾驶&#xff0c;那时候痴迷于移动规划&#xff1b;然而当时可学习的资料非常少&#xff0c;网上的论文也不算太多。基本就是Darpa的几十篇无人越野几次比赛的文章&#xff0c;基本没有成系统的文章和代码讲解实现。所以对移动规划的认…