一文解析-通过实例讲解 Linux 内存泄漏检测方法
一、mtrace分析内存泄露
mtrace(memory trace),是 GNU Glibc 自带的内存问题检测工具,它可以用来协助定位内存泄露问题。它的实现源码在glibc源码的malloc目录下,其基本设计原理为设计一个函数 void mtrace (),函数对 libc 库中的 malloc/free 等函数的调用进行追踪,由此来检测内存是否存在泄漏的情况。mtrace是一个C函数,在<mcheck.h>里声明及定义,函数原型为:
void mtrace(void);
mtrace原理
mtrace()
函数会为那些和动态内存分配有关的函数(譬如 malloc()、realloc()、memalign() 以及 free())安装 “钩子(hook)” 函数,这些 hook 函数会为我们记录所有有关内存分配和释放的跟踪信息,而 muntrace() 则会卸载相应的 hook 函数。
基于这些 hook 函数生成的调试跟踪信息,我们就可以分析是否存在 “内存泄露” 这类问题了。
设置日志生成路径
mtrace 机制需要我们实际运行一下程序,然后才能生成跟踪的日志,但在实际运行程序之前还有一件要做的事情是需要告诉 mtrace (即前文提到的 hook 函数)生成日志文件的路径。
设置日志生成路径有两种,一种是设置环境变量:export MALLOC_TRACE=./test.log // 当前目录下
另一种是在代码层面设置:setenv("MALLOC_TRACE", "output_file_name", 1);``output_file_name
就是储存检测结果的文件的名称。
测试实例
#include <mcheck.h>
#include <stdlib.h>
#include <stdio.h>int main(int argc, char **argv)
{mtrace(); // 开始跟踪char *p = (char *)malloc(100);free(p);p = NULL;p = (char *)malloc(100);muntrace(); // 结束跟踪,并生成日志信息return 0;
}
从上述代码中,我们希望能够在程序开始到结束检查内存是否泄漏的问题,例子简单,一眼就能看出存在内存泄漏的问题,所以我们需要验证 mtrace 是否能够检查出来内存泄漏问题,且检查的结果如何分析定位。 gcc -g test.c -o test
生成可执行文件。
日志
程序运行结束,会在当前目录生成 test.log 文件,打开可以看到一下内容:
= Start
@ ./test:[0x400624] + 0x21ed450 0x64
@ ./test:[0x400634] - 0x21ed450
@ ./test:[0x400646] + 0x21ed450 0x64
= End
从这个文件中可以看出中间三行分别对应源码中的 malloc -> free -> malloc 操作。解读:./test 指的是我们执行的程序名字,[0x400624] 是第一次调用 malloc 函数机器码中的地址信息,+ 表示申请内存( - 表示释放),0x21ed450 是 malloc 函数申请到的地址信息,0x64 表示的是申请的内存大小。由此分析第一次申请已经释放,第二次申请没有释放,存在内存泄漏的问题。
泄露分析
使用addr2line工具定位源码位置
通过使用 "addr2line" 命令工具,得到源文件的行数(通过这个可以根据机器码地址定位到具体源码位置)
# addr2line -e test 0x400624
/home/test.c:9
使用mtrace工具分析日志信息
mtrace + 可执行文件路径 + 日志文件路径 mtrace test ./test.log
执行,输出如下信息:
Memory not freed:
-----------------Address Size Caller
0x00000000021ed450 0x64 at /home/test.c:14
二、Valgrind分析内存泄露
Valgrind工具介绍
Valgrind是一套Linux下,开放源代码(GPL V2)的仿真调试工具的集合。Valgrind由内核(core)以及基于内核的其他调试工具组成。内核类似于一个框架(framework),它模拟了一个CPU环境,并提供服务给其他工具;而其他工具则类似于插件 (plug-in),利用内核提供的服务完成各种特定的内存调试任务。Valgrind的体系结构如下图所示
1、Memcheck
最常用的工具,用来检测程序中出现的内存问题,所有对内存的读写都会被检测到,一切对malloc() / free() / new / delete 的调用都会被捕获。
所以,它能检测以下问题:对未初始化内存的使用;读/写释放后的内存块;读/写超出malloc分配的内存块;读/写不适当的栈中内存块;内存泄漏,指向一块内存的指针永远丢失;不正确的malloc/free或new/delete匹配;memcpy()相关函数中的dst和src指针重叠。
2、Callgrind
和 gprof 类似的分析工具,但它对程序的运行观察更是入微,能给我们提供更多的信息。和 gprof 不同,它不需要在编译源代码时附加特殊选项,但加上调试选项是推荐的。
Callgrind 收集程序运行时的一些数据,建立函数调用关系图,还可以有选择地进行 cache 模拟。在运行结束时,它会把分析数据写入一个文件。callgrind_annotate 可以把这个文件的内容转化成可读的形式。
3、Cachegrind
Cache 分析器,它模拟 CPU 中的一级缓存 I1,Dl 和二级缓存,能够精确地指出程序中 cache 的丢失和命中。如果需要,它还能够为我们提供 cache 丢失次数,内存引用次数,以及每行代码,每个函数,每个模块,整个程序产生的指令数。这对优化程序有很大的帮助。
4、Helgrind
它主要用来检查多线程程序中出现的竞争问题。Helgrind 寻找内存中被多个线程访问,而又没有一贯加锁的区域,这些区域往往是线程之间失去同步的地方,而且会导致难以发掘的错误。
Helgrind 实现了名为“Eraser”的竞争检测算法,并做了进一步改进,减少了报告错误的次数。不过,Helgrind 仍然处于实验阶段。
5、Massif
堆栈分析器,它能测量程序在堆栈中使用了多少内存,告诉我们堆块,堆管理块和栈的大小。
Massif 能帮助我们减少内存的使用,在带有虚拟内存的现代系统中,它还能够加速我们程序的运行,减少程序停留在交换区中的几率。
此外,lackey 和 nulgrind 也会提供。Lackey 是小型工具,很少用到;Nulgrind 只是为开发者展示如何创建一个工具。
资料直通车:Linux内核源码技术学习路线+视频教程内核源码
学习直通车:Linux内核源码内存调优文件系统进程管理设备驱动/网络协议栈
Memcheck原理
本文的重点是在检测内存泄露,所以对于valgrind的其他工具不做过多的说明,主要说明下Memcheck的工作。Memcheck检测内存问题的原理如下图所示:
Memcheck 能够检测出内存问题,关键在于其建立了两个全局表。
- Valid-Value 表 对于进程整个地址空间中的每一个字节(byte),都有与之对应的 8个bits;对于 CPU 的每个寄存器,也有一个与之对应的 bit 向量。这些 bits 负责记录该字节或者寄存器值是否具有有效的、已初始化的值。
- Valid-Address 表 对于进程整个地址空间中的每一个字节(byte),还有与之对应的1个 bit,负责记录该地址是否能够被读写。
- 检测原理:当要读写内存中某个字节时,首先检查这个字节对应的Valid-Address 表中的 A bit。如果该 A bit显示该位置是无效位置,memcheck 则报告读写错误。内核(core)类似于一个虚拟的 CPU 环境,这样当内存中的某个字节被加载到真实的 CPU 中时,该字节对应的Valid-Value 表中的 V bit 也被加载到虚拟的 CPU 环境中。一旦寄存器中的值,被用来产生内存地址,或者该值能够影响程序输出,则 memcheck 会检查对应的V bits,如果该值尚未初始化,则会报告使用未初始化内存错误。
内存泄露类型
valgrind 将内存泄漏分成 4 类:
- 确立泄露(definitely lost):运行内存还没有释放出来,但早已沒有表针偏向运行内存,运行内存早已不能浏览。确立泄露的运行内存是强烈要求修补的。
- 间接性泄露(indirectly lost):泄露的运行内存表针储存在确立泄露的运行内存中,伴随着确立泄露的运行内存不能浏览,造成间接性泄露的运行内存也不能浏览。例如:
struct list {struct list *next;
};int main(int argc, char **argv)
{struct list *root;root = (struct list *)malloc(sizeof(struct list));root->next = (struct list *)malloc(sizeof(struct list));printf("root %p roop->next %p\n", root, root->next);root = NULL;return 0;
}
这里遗失的是 root 表针(是确立泄露类型),造成 root 储存的 next 表针变成了间接性泄露。间接性泄露的运行内存毫无疑问也要修补的,但是一般会伴随着 确立泄露 的修补而修补。
- 很有可能泄露(possibly lost):表针并不偏向运行内存头详细地址,只是偏向运行内存內部的部位。valgrind 往往会猜疑很有可能泄露,是由于表针早已偏位,并沒有偏向运行内存头,只是有运行内存偏位,偏向运行内存內部的部位。有一些情况下,这并并不是泄露,由于这种程序流程便是那么设计方案的,比如为了更好地完成内存对齐,附加申请办理运行内存,回到两端对齐后的内存地址。
- 仍可访达(still reachable):表针一直存有且偏向运行内存头顶部,直到程序流程撤出时运行内存还没有释放出来。
Valgrind参数设置
- --leak-check=<no|summary|yes|full> 如果设为 yes 或 full,在被调程序结束后,valgrind 会详细叙述每一个内存泄露情况 默认是summary,只报道发生了几次内存泄露
- --log-fd= [default: 2, stderr] valgrind 打印日志转存到指定文件或者文件描述符。如果没有这个参数,valgrind 的日志会连同用户程序的日志一起输出,会显得非常乱。
- --trace-children=<yes | no> [default: no] 是否跟踪子进程,若是多进程的程序,则建议使用这个功能。不过单进程使能了也不会有多大影响。
- --keep-debuginfo=<yes | no> [default: no] 如果程序有使用 动态加载库(dlopen),在动态库卸载时(dlclose),debug信息都会被清除。使能这个选项后,即使动态库被卸载,也会保留调用栈信息。
- --keep-stacktraces=<alloc | free | alloc-and-free | alloc-then-free | none> [default: alloc-and-free] 内存泄漏不外乎申请和释放不配对,函数调用栈是只在申请时记录,还是在申请释放时都记录 如果我们只关注内存泄漏,其实完全没必要申请释放都记录,因为这会占用非常多的额外内存和更多的 CPU 损耗,让本来就执行慢的程序雪上加霜。
- --freelist-vol= 当客户程序用 free 或 delete 释放一个内存块时,这个内存块不会立即可用于再分配,它只会被放在一个freed blocks的队列中(freelist)并被标记为不可访问,这样有利于探测到在一段很重要的时间后,客户程序又对被释放的块进行访问的错误。这个选项规定了队列所占的字节块大小,默认是20MB。增大这个选项的会增大memcheck的内存开销,但查这类错的能力也会提升。
- --freelist-big-blocks= 当从 freelist 队列中取可用内存块用于再分配时,memcheck 将会从那些比 number 大的内存块中按优先级取出一个块出来用。这个选项就防止了 freelist 中那些小的内存块的频繁调用,这个选项提高了 查到针对小内存块的野指针错误的几率。若这个选项设为0,则所有的块将按先进先出的原则用于再分配。默认是1M。参考:valgrind 简介(内存检查工具)
编译参数推荐
为了更好地在出难题时要详尽打印出出去栈信息内容,实际上大家最好是在编译程序时加上 -g 选择项。如果有动态性载入的库,必须再加上 --keep-debuginfo=yes
,不然假如发觉是动态性载入的库发生泄露,因为动态库被卸载掉了,造成找不到符号表。编码编译程序提升,不建议应用 -O2既之上。-O0很有可能会造成运作变慢,建议使用-O1。
检测实例说明
申请不释放内存
#include <stdlib.h>
#include <stdio.h>
void func()
{//只申请内存而不释放void *p=malloc(sizeof(int));
}
int main()
{func();return 0;
}
使用valgrind命令来执行程序同时输出日志到文件
valgrind --log-file=valReport --leak-check=full --show-reachable=yes --leak-resolution=low ./a.out
参数说明:
- –log-file=valReport 是指定生成分析日志文件到当前执行目录中,文件名为valReport
- –leak-check=full 显示每个泄露的详细信息
- –show-reachable=yes 是否检测控制范围之外的泄漏,比如全局指针、static指针等,显示所有的内存泄露类型
- –leak-resolution=low 内存泄漏报告合并等级
- –track-origins=yes表示开启“使用未初始化的内存”的检测功能,并打开详细结果。如果没有这句话,默认也会做这方面的检测,但不会打印详细结果。执行输出后,报告解读,其中54017是指进程号,如果程序使用了多进程的方式来执行,那么就会显示多个进程的内容。
==54017== Memcheck, a memory error detector
==54017== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==54017== Using Valgrind-3.15.0 and LibVEX; rerun with -h for copyright info
==54017== Command: ./a.out
==54017== Parent PID: 52130
第二段是对堆内存分配的总结信息,其中提到程序一共申请了1次内存,其中0次释放了,4 bytes被分配(1 allocs, 0 frees, 4 bytes allocated
)。
在head summary中,有该程序使用的总heap内存量,分配内存次数和释放内存次数,如果分配内存次数和释放内存次数不一致则说明有内存泄漏。
==54017== HEAP SUMMARY:
==54017== in use at exit: 4 bytes in 1 blocks
==54017== total heap usage: 1 allocs, 0 frees, 4 bytes allocated
第三段的内容描述了内存泄露的具体信息,其中有一块内存占用4字节(4 bytes in 1 blocks
),在调用malloc分配,调用栈中可以看到是func函数最后调用了malloc,所以这一个信息是比较准确的定位了我们泄露的内存是在哪里申请的。
==54017== 4 bytes in 1 blocks are definitely lost in loss record 1 of 1
==54017== at 0x4C29F73: malloc (vg_replace_malloc.c:309)
==54017== by 0x40057E: func() (in /home/oceanstar/CLionProjects/Share/src/a.out)
==54017== by 0x40058D: main (in /home/oceanstar/CLionProjects/Share/src/a.out)
最后这一段是总结,4字节为一块的内存泄露。
==54017== LEAK SUMMARY:
==54017== definitely lost: 4 bytes in 1 blocks // 确立泄露
==54017== indirectly lost: 0 bytes in 0 blocks // 间接性泄露
==54017== possibly lost: 0 bytes in 0 blocks // 很有可能泄露
==54017== still reachable: 0 bytes in 0 blocks // 仍可访达
==54017== suppressed: 0 bytes in 0 blocks
读写越界
#include <stdio.h>
#include <iostream>
int main()
{int len = 5;int *pt = (int*)malloc(len*sizeof(int)); //problem1: not freedint *p = pt;for (int i = 0; i < len; i++){p++;}*p = 5; //problem2: heap block overrunprintf("%d\n", *p); //problem3: heap block overrun// free(pt);return 0;
}
problem1: 指针pt申请了空间,但是没有释放; problem2: pt申请了5个int的空间,p经过5次循环已达到p[5]的位置, *p = 5
时,访问越界(写越界)。(下面valgrind报告中 Invalid write of size 4)
==58261== Invalid write of size 4
==58261== at 0x400707: main (main.cpp:12)
==58261== Address 0x5a23054 is 0 bytes after a block of size 20 alloc'd
==58261== at 0x4C29F73: malloc (vg_replace_malloc.c:309)
==58261== by 0x4006DC: main (main.cpp:7)
problem1: 读越界 (下面valgrind报告中 Invalid read of size 4 )
==58261== Invalid read of size 4
==58261== at 0x400711: main (main.cpp:13)
==58261== Address 0x5a23054 is 0 bytes after a block of size 20 alloc'd
==58261== at 0x4C29F73: malloc (vg_replace_malloc.c:309)
==58261== by 0x4006DC: main (main.cpp:7)
重复释放
#include <stdio.h>
#include <iostream>
int main()
{int *x;x = static_cast<int *>(malloc(8 * sizeof(int)));x = static_cast<int *>(malloc(8 * sizeof(int)));free(x);free(x);return 0;
}
报告如下,Invalid free() / delete / delete[] / realloc()
==59602== Invalid free() / delete / delete[] / realloc()
==59602== at 0x4C2B06D: free (vg_replace_malloc.c:540)
==59602== by 0x4006FE: main (main.cpp:10)
==59602== Address 0x5a230a0 is 0 bytes inside a block of size 32 free'd
==59602== at 0x4C2B06D: free (vg_replace_malloc.c:540)
==59602== by 0x4006F2: main (main.cpp:9)
==59602== Block was alloc'd at
==59602== at 0x4C29F73: malloc (vg_replace_malloc.c:309)
==59602== by 0x4006E2: main (main.cpp:8)
申请释放接口不匹配
申请释放接口不匹配的报告如下,用malloc申请空间的指针用free释放;用new申请的空间用delete释放(Mismatched free() / delete / delete []
):
==61950== Mismatched free() / delete / delete []
==61950== at 0x4C2BB8F: operator delete[](void*) (vg_replace_malloc.c:651)
==61950== by 0x4006E8: main (main.cpp:8)
==61950== Address 0x5a23040 is 0 bytes inside a block of size 5 alloc'd
==61950== at 0x4C29F73: malloc (vg_replace_malloc.c:309)
==61950== by 0x4006D1: main (main.cpp:7)
内存覆盖
int main()
{char str[11];for (int i = 0; i < 11; i++){str[i] = i;}memcpy(str + 1, str, 5);char x[5] = "abcd";strncpy(x + 2, x, 3);
}
问题出在memcpy上, 将str指针位置开始copy 5个char到str+1所指空间,会造成内存覆盖。strncpy也是同理。报告如下,Source and destination overlap
:
==61609== Source and destination overlap in memcpy(0x1ffefffe31, 0x1ffefffe30, 5)
==61609== at 0x4C2E81D: memcpy@@GLIBC_2.14 (vg_replace_strmem.c:1035)
==61609== by 0x400721: main (main.cpp:11)
==61609==
==61609== Source and destination overlap in strncpy(0x1ffefffe25, 0x1ffefffe23, 3)
==61609== at 0x4C2D453: strncpy (vg_replace_strmem.c:552)
==61609== by 0x400748: main (main.cpp:14)
三、总结
内存检测方式无非分为两种:
1、维护一个内存操作链表,当有内存申请操作时,将其加入此链表中,当有释放操作时,从申请操作从链表中移除。如果到程序结束后此链表中还有内容,说明有内存泄露了;如果要释放的内存操作没有在链表中找到对应操作,则说明是释放了多次。使用此方法的有内置的调试工具,Visual Leak Detecter,mtrace, memwatch, debug_new。2、模拟进程的地址空间。仿照操作系统对进程内存操作的处理,在用户态下维护一个地址空间映射,此方法要求对进程地址空间的处理有较深的理解。因为Windows的进程地址空间分布不是开源的,所以模拟起来很困难,因此只支持Linux。采用此方法的是 valgrind。
原文作者:一起学嵌入式
相关文章:
一文解析-通过实例讲解 Linux 内存泄漏检测方法
一、mtrace分析内存泄露 mtrace(memory trace),是 GNU Glibc 自带的内存问题检测工具,它可以用来协助定位内存泄露问题。它的实现源码在glibc源码的malloc目录下,其基本设计原理为设计一个函数 void mtrace ()&#x…...
Spring Boot常用的参数验证技巧和使用方法
简介 Spring Boot是一个使用Java编写的开源框架,用于快速构建基于Spring的应用程序。在实际开发中,经常需要对输入参数进行验证,以确保数据的完整性和准确性。Spring Boot提供了多种方式来进行参数验证,并且可以很方便地集成到应…...
手机+卫星的科技狂想
最近硬件圈最火热的话题之一,应该就是突然上线、遥遥领先的华为Mate 60 Pro了。 其中,CPU和类5G网速是怎么实现的,是大家特别关注的问题。相比之下,卫星通话这个功能,讨论度就略低一些(没有说不火的意思&am…...
便捷查询中通快递,详细物流信息轻松获取
在如今快节奏的生活中,快递已成为人们生活中不可或缺的一部分。然而,快递查询却常常让人头疼,因为需要分别在不同的快递公司官网上进行查询,耗费时间和精力。为了解决这个问题,固乔科技推出了一款便捷的快递查询助手&a…...
ARM接口编程—Interrupt(exynos 4412平台)
CPU与硬件的交互方式 轮询 CPU执行程序时不断地询问硬件是否需要其服务,若需要则给予其服务,若不需要一段时间后再次询问,周而复始中断 CPU执行程序时若硬件需要其服务,对应的硬件给CPU发送中断信号,CPU接收到中断信号…...
适用于Linux的Windows子系统(PHP搭建lmap、redis、swoole环境)
目录 前言 一、Windows安装Linux子系统 二、Ubuntu搭建PHP开发环境 1.PHP 安装 2.Apache2 安装 3.MySQL安装 4.Redis安装 5.Swoole安装 总结 前言 系列分为三章(从安装到项目使用): 一、适用于Linux的Windows子系统(系统安装步骤…...
Vue3+Ts+Vite项目(第十二篇)——echarts安装与使用,vue3项目echarts组件封装
概述 技术栈:Vue3 Ts Vite Echarts 简介: 图文详解,教你如何在Vue3项目中引入Echarts,封装Echarts组件,并实现常用Echarts图例 文章目录 概述一、先看效果1.1 静态效果1.2 动态效果 二、话不多数,引入 …...
hive location更新hive元数据表详解
1.hive location更新方式 一、通过修改表DDL: alter table table_name set location hdfs://nm:8020/table_path 二、直接修改hive 的meta info: update DBS set DB_LOCATION_URI replace(DB_LOCATION_URI,"oldpath","newpath")update SDS…...
【SpringBoot】统一功能处理
目录 🎃1 拦截器 🎀1.1 拦截器的代码实现 🎨1.2 拦截器的实现原理 🧶2 拦截器应用——登录验证 🦺3 异常统一处理 🎭4 统一数据返回格式 🧤4.1 为什么需要统一数据返回格式 🧣4.2 统…...
分布式数据库-架构真题(二十六)
构件组装成软件系统的过程分为三个不同的层次()。(2018年) 初始化、互连和集成连接、集成和演化定制、集成和扩展集成、扩展和演化 答案:C (2018年)CORBA服务端构件模型中,&#x…...
MyWebServer开发日记-socket
打算把 tinyWebServer 重写成跨平台(Windows and Linux)的。 这里首先需要跨平台的 sokcet,主要参考 尹圣雨 的 TCP/IP 网络编程 来着: 代码写的有些笨,欢迎批评: 首先是一个 socket 类,主要…...
图书管理信息系统分析与设计
一、系统开发的可行性分析 (一)系统背景.必要性及意义 随着社会经济的迅速发展和科学技术的全面进步,计算机事业的飞速发展,以计算机与通信技术为基础的信息系统正处于蓬勃发展的时期。随着经济文化水平的显著提高,人…...
Charles基础使用指南
##Charles 基本使用指南 Charles 在本地构建一个HTTP代理服务器,可以实现对HTTP、HTTPS请求的抓取,也就是我们常说的抓包,以及对请求响应的修改等。 Charles 官网地址 https://www.charlesproxy.com/ ###一、移动端的抓包实现 1. PC端开启…...
Android12之/proc/pid/status参数含义(一百六十五)
简介: CSDN博客专家,专注Android/Linux系统,分享多mic语音方案、音视频、编解码等技术,与大家一起成长! 优质专栏:Audio工程师进阶系列【原创干货持续更新中……】🚀 人生格言: 人生…...
UMA 2 - Unity Multipurpose Avatar☀️三.给UMA设置默认服饰Recipes
文章目录 🟥 项目基础配置🟧 给UMA配置默认服饰Recipes🟨 设置服饰Recipes属性🟥 项目基础配置 将 UMA_DCS 预制体放到场景中创建空物体,添加DynamicCharacterAvatar 脚本,选择 HumanMaleDCS作为我们的基本模型配置默认Animator 🟧 给UMA配置默认服饰Recipes 服饰Re…...
uniapp-小程序登录授权框
微信官方文档 不弹出授权框原因 因为版本问题,目前的最新的版本是不支持 wx.getUserInfo 去主动弹出授权框 只能引导用户去点击 butten 去授权 解决方法 我的思路是参考了其他的微信微信小程序, 就是跳转到我的页面的时候 在钩子函数内去触发一个封装的模态框,状…...
Unity 性能优化Shader分析处理函数:ShaderUtil.GetShaderGlobalKeywords用法
Unity 性能优化Shader分析处理函数:ShaderUtil.GetShaderGlobalKeywords用法 点击封面跳转下载页面 简介 Unity 性能优化Shader分析处理函数:ShaderUtil.GetShaderGlobalKeywords用法 在Unity开发中,性能优化是一个非常重要的方面。一个常见…...
第一百四十一回 如何添加程序启动页
文章目录 概念介绍使用方法示例代码 我们在上一章回中介绍了如何解决BLE包中的错误的内容,本章回中将介绍如何添加程序启动页.闲话休提,让我们一起Talk Flutter吧。 概念介绍 程序启动页就是点击手机桌面上的程序启动图标后显示的页面,也叫s…...
从零开始的PICO教程(4)--- UI界面绘制与响应事件
从零开始的PICO教程(4)— UI界面绘制与响应事件 文章目录 从零开始的PICO教程(4)--- UI界面绘制与响应事件一、前言1、大纲2、教程示例 二、具体步骤1、PICO VR环境配置2、XR的UI Canvas画布创建与调整(1)C…...
IntelliJ IDEA 远程调试 Tomcat
准备工作 明确远程服务器的 IP 地址,比如我是:192.168.92.128 关掉服务器防火墙:service iptables stop 本地 Remote Server 配置 添加 Remote Server,如下图 复制 Remote Server 自动生成的 JVM 参数,等下有用&…...
谷粒商城----认证服务
一、短信验证码(阿里云短信服务) Data ConfigurationProperties(prefix "spring.cloud.alicloud.sms") Component public class SmsComponent {private String host;private String path;private String skin;private String sign;private S…...
Mediasoup源码介绍
一、Mediasoup 整体结构 整个Mediasoup库通过Nodejs管理,比如整体逻辑、worker、router、producer、consumer...都是通过JS进行管理的。 其底层的数据传输是通过C部分进行控制的,通过NodeJs来控制C部分,以实现整体的数据传输效 二、Mediasou…...
GIS入门,WKT格式详解
WKT介绍 WKT是Well-known Text的缩写,它是一种用于描述地理空间几何对象的文本格式。 WKT是一种开放的国际标准,由Open Geospatial Consortium(OGC)定义和维护。 WKT是一种标准的表示方法,可以用来描述点、线、面等地理空间对象的形状和位置。通过使用一系列的坐标点和关…...
Qt之postEvent
基本介绍 postEvent方法所属类为QCoreApplication,完整声明如下: [static] void QCoreApplication::postEvent(QObject *receiver, QEvent *event, int priority Qt::NormalEventPriority) 该方法的作用是将要发送的事件推送到对应线程的事件队列中&…...
1976~2020年青藏高原典型冰川及冰湖遥感监测数据集
冰川面积是反应气候变化最直接的指标之一。在全球变暖的大背景下,对于评估冰川融化造成的生态、全球气候变化和水资源价值评价等问题十分重要。本文针对受西风和印度洋夏季风影响下的青藏高原冰川及其末端冰湖的变化特征,制作了近44年来时相相对连续的冰…...
时序预测 | MATLAB实现LSSVM最小二乘支持向量机时间序列预测未来
时序预测 | MATLAB实现LSSVM最小二乘支持向量机时间序列预测未来 目录 时序预测 | MATLAB实现LSSVM最小二乘支持向量机时间序列预测未来预测效果基本介绍程序设计参考资料 预测效果 基本介绍 1.Matlab实现LSSVM时间序列预测未来(最小二乘支持向量机); 2.运行环境Mat…...
windows10 使用WSL2安装原生docker
1.升级WSL2 我的 win10 wsl默认版本是1,先要升级WSL2不然不支持systemd(后台守护进程) 双击直接安装就行,安装包网上都能找到: Microsoft.WSL_1.3.17.0_x64_ARM64.msixbundle 执行 wsl --version 显示这样成功了: C:\Users\xx>wsl --version WSL …...
jupylab pandas按条件批量处理xls数据
批量处理xls表数据 引入相关包 import pandas as pd import xlrd import numpy as np# 去掉jupyleb警告 import warnings warnings.filterwarnings("ignore")from IPython.core.interactiveshell import InteractiveShell InteractiveShell.ast_node_interactivity…...
RJ45水晶头网线顺序出错排查
线序 网线水晶头RJ45常用的线序标准ANSI / TIA-568定义了T568A与T568B两种线序,一般使用T568B,水晶头8个孔对应的8条线颜色如下图: 那1至8的编号,是从水晶头哪一面为参考呢,如下图,是水晶头金手指一面&am…...
【洛谷 P1115】最大子段和 题解(贪心算法)
最大子段和 题目描述 给出一个长度为 n n n 的序列 a a a,选出其中连续且非空的一段使得这段和最大。 输入格式 第一行是一个整数,表示序列的长度 n n n。 第二行有 n n n 个整数,第 i i i 个整数表示序列的第 i i i 个数字 a i …...
有赞微商城官网登录/关键词seo公司推荐
异常处理汇总-前端系列 http://www.cnblogs.com/dunitian/p/4523015.html 调节文字间的距离,发现》word-spacing汉字不起作用 研究发现,使用:letter-spacing才可以(可以这样理解:word-spacing>单词间隔,…...
拉萨营销型网站建设/怎么用手机创建网站
本文章给大家介绍在php中类和对象的protected与const属性用法,有需要了解的朋友可参考参考。const属性用const属性定义的字段是一个常量,类中的常量和静态变量类似,不同之处就是常量的值一旦赋值不能被改变。const定义常量不需要加$符号&…...
做网站资源/ 今日头条
为什么需要技术KPI 在业务技术团队,有一个不好的趋势,就是团队越来越业务,越来越没有技术味道。每个人都在谈业务,技术大会上在谈业务,周会上在聊业务,周报里写的是业务项目...... 唯独少被谈及的是技术本身…...
域名注册和网站建设/百度首页纯净版怎么设置
Polldaddy 是全球领先的在线投票系统服务商,目前每月访问量达到2亿,多数是通过从外部网站调用其饰件(widget)或其 API 进行的。今天 Polldaddy 正式公布其投票系统 API,第三方开发商可以直接使用该 API 在自己的程序中…...
哪些企业网站做的好/企业官网首页设计
知识回顾:当鼠标在窗口内移动,点击或者释放时都会产生WM_NCHITTEST消息,响应函数OnNcHitTest会返回一个枚举值,系统会根据这个枚举值进行相应的处理。 当返回值为HTCAPTION时,系统会认为此时鼠标位于标题栏上,因而当鼠标按下并移动时就会执行拖动操作。我们需要做的就是响应这个…...
精通网站建设电子档/国内军事新闻最新消息
程序员如何表白,看了才知道程序员如何表白,看了才知道https://www.zhihu.com/video/1228451050107457536程序代码:import turtle import matht turtle.pen() t turtle t.up() t.goto(0, 150) t.down() t.color(red) t.begin_fill() t.fillc…...