glibc内存管理ptmalloc

news2024/9/20 4:54:27

1、前言

今天想谈谈ptmalloc如何为应用程序分配释放内存的,基于以下几点原因才聊它:

  1. C/C++ 70%的问题是内存问题。了解一点分配器原理对解决应用程序内存问题肯定有帮助。
  2. C++也在用ptmalloc. 当你在C++中new一个对象时,底层还是依赖glibc中的ptmalloc.
  3. 虽然市面上还有jemalloc/tcmalloc, 但ptmalloc被glibc内嵌,用的最广.

2、初识ptmalloc

ptmalloc是glibc(GNU C库)中使用的内存分配器,它基于dlmalloc(Doug Lea’s Malloc)的设计。ptmalloc的主要目标是为多线程应用程序提供高效的内存分配和释放,其名称中的“pt”代表“pthreads”,即POSIX线程库。

ptmalloc所有版本的源代码可在Index of /gnu/glibc下载,其中2.26为了增强多线程情况下的性能引入了tcache, 不过为了使讲解简单,我们还是以2.26之前的版本2.17来分析其原理。

相信很多程序员都思考过一个问题而且也知道答案:

free函数释放内存时只有一个参数 -- 要释放内存的指针,那要释放多大的内存哪???

答案就是这块内存前面的8个字节(64bit下,之后默认都是64bit)存了这块内存大小,让我们看个例子快速入门一下:

void *p1= malloc(10);
memset(p1,'a',10);

 就像大大小小不同种类的卡车都有个车斗拉东西,ptmalloc的卡车叫malloc chunk用来拉内存, 它有自重,0x602000~0x602010便是自重(大小:0x10字节,有个特例,以后再说),对用户来说浪费掉了不能用来拉东西,还剩0x20-0x10=0x10个字节可以放用户数据,比我们申请的10个字节多一点点,这是故意预留的。

ptmalloc本质就是一个内存缓冲池,缓存的最小单位就是malloc chunk. 之后我们提到chunk或者malloc chunk都是一个东西。即使free的内存实际可能并没有free,而是被ptmalloc管理了起来。

3、卡车的定义 malloc_chunk

通过上面的例子我们看到了卡车的一个零部件:size(卡车大小)。那它有其它零部件吗?让我们看下它的庐山真面目:

struct malloc_chunk
{

  INTERNAL_SIZE_T prev_size; /* 上一个malloc_chunk的size(如果它是free状态).  */
  INTERNAL_SIZE_T size;      /*  本malloc_chunk的size,包括16个字节的自重 */

  struct malloc_chunk *fd; /* 用来构造双向链表 -- 仅仅用在free的chunk上. */
  struct malloc_chunk *bk; /* 用来构造双向链表 -- 仅仅用在free的chunk上. */

  /* 以下两个字段用在large chunk上,以后介绍.  */
  struct malloc_chunk *fd_nextsize; 
  struct malloc_chunk *bk_nextsize;
};

 正如代码组织所暗示,两两一组, prev_size & size, fd & bk, fd_nextsize & bk_nextsize. 

后两组只有在本chunk free的状态下才有意义,不然就存储用户数据。

3.1、prev_size & size 物理相邻的chunk

prev_size表示上个chunk的大小,只有上个chunk是free时才有此意义。size表示本chunk大小,但是最后三位从最不重要的位开始由特别意义分别表示:

  1. 物理上上个chunk在用(非free, PREV_INUSE)
  2. 本chunk是由mmap()分配的(IS_MMAPPED)
  3. 本chunk是属于非主arena的(NON_MAIN_ARENA)

我们着重讲下第一个PREV_INUSE:

所有的chunk在物理上都是相邻的,就像火车车厢一样连在一起,

不过与直觉相反的是:不是自己表示自己空不空,而是由下一个chunk表示上一个chunk空不空(原因在下面的总结中)。

下面看个例子理解下PREV_INUSE、prev_size:

int main(int argc, char* argv[])
{
        void *p1= malloc(192); //192是故意的,较小的数字free后会到fastbin中
                               //PREV_INUSE依然保持1,那样达不到演示PREV_INUSE的效果
        memset(p1,'a',192);    //原因在fastbin中介绍

        void *p2= malloc(20);
        memset(p2,'b',20);

        free(p1);
}

调试到free但还没free,通过下图理解下p2所在malloc chunk前两个字段的意义。

(gdb) p *(mchunkptr)(p2-16)
$2 = {prev_size = 0, size = 33, fd = 0x6262626262626262, bk = 0x6262626262626262, fd_nextsize = 0x62626262,
  bk_nextsize = 0x20f11}

然后把p1释放,再看下p2 malloc chunk.size:

 

(gdb) p *(mchunkptr)(p2-16)
$3 = {prev_size = 208, size = 32, fd = 0x6262626262626262, bk = 0x6262626262626262, fd_nextsize = 0x62626262,
  bk_nextsize = 0x20f11}

这里眼尖的同学可能已经注意到prev_size只有在上个chunk free的状态下有意义,如果上个chunk分配给用户了则什么也没存,浪费掉了!ptmalloc的作者早就想到了这一点:prev_size这8个字节还真可以让渡给上个chunk用来存用户数据,我们把上面程序中的192改成194或者200看一看

  

总结一下: 

1. prev size, prev inuse中的prev指的是物理上相邻的两个chunk的前一个(内存地址小的那个)

2. size字段指本chunk自己的size大小,包括自重(overhead)。size的最不重要的三位(bit)是三个flag,其中最后那个位(bit)表示上个chunk是否free,如果上个chunk是free则prev_size有意义且表示上个chunk的size。为什么要在一个chunk里存上一个chunk的size哪?这是为了方便两者都是free时好合并成一个大的chunk, p - prev_size就指向了上一个chunk。

3. malloc_chunk虽然有这么多数据成员,但只有size永远有意义,为了提高负载率其它字段在某种情况下会被用来放用户数据:a. 本chunk假如分配出去了(malloc),则fd、bk、fd_nextsize、bk_nextsize都无意义,可以用来放用户数据; b. 上个chunk假如分配出去了(malloc)且malloc的大小%8<=8, 则本chunk的prev_size字段会被用来存储上个chunk的用户数据。

3.2、fd & bk 逻辑上把free的chunk串起来

ptmalloc中有个bin的概念,正如字面意思就是回收垃圾用的垃圾桶(放free掉的chunk),bin有如下几类:

fastbin - 单向链表,放小chunk,默认为小到0x20大到0x80大小的,以16字节递进。比如main_arena.fastbinsY[0]指向大小为0x20的chunk链表,main_arena.fastbinsY[1]指向大小为0x30的chunk链表...

unsortedbin - 双向链表,临时垃圾桶,里面的chunk的size不一致。

smallbin - 双向链表,也是放小chunk,但上限到0x3F0, 以16字节递进, 共62个smallbin。

largebin - 双向链表,放大chunk, 63个.(largebin本节不具体展开)

main_arena是一个全局变量,它是一个很好的入口去找到这些bin,如下图示:

4、fastbin

fastbin, 正如它的名字,是用的最频繁的bin。当一块小内存被释放后,极大可能会被放到对应大小的fastbin链条上的,以加快下次分配同样大小的内存。

void* p1 = malloc(10);
free(p1);
void* p2 = malloc(10);

上面这段代码,p1 p2应该是相等的。 

fastbin是单链表,fd指向下一个;先释放的在链尾,后释放的放在链头。也就是说那些最近别使用过得内存更容易被再次使用;那些很久以前free掉最近都没用过的内存,有较大的概率被swap out了,重新使用的代价可能较大。

下面看个例子:

int main(int argc, char* argv[])
{
        void *p1= malloc(10);
        memset(p1,'a',10);

        void *p2= malloc(10);
        memset(p2,'b',10);

        void *p3= malloc(30);
        memset(p3,'c',10);

        void *p4= malloc(30);
        memset(p4,'d',10);

        free(p1);
        free(p2);
        free(p3);
        free(p4);

        getchar();
#链接我自己编译出来的glibc
[mzhai]$ gcc fastbin.c -I/usr/local/glibc-2.17/include -L/usr/local/glibc-2.17/lib -Wl,--rpath=/usr/local/glibc-2.17/lib -Wl,--rpath=/lib64 -Wl,--dynamic-linker=/usr/local/glibc-2.17/lib/ld-2.17.so -g
[mzhai]$ gdb ./a.out

运行到getchar, 看下fastbinsY[0] & [1],

(gdb) p main_arena
$2 = {mutex = 0, flags = 0, fastbinsY = {0x602020, 0x602070, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, top = 0x6020a0,
  last_remainder = 0x0, bins = {0x7ffff7dd7678 <main_arena+88>, 0x7ffff7dd7678 <main_arena+88>,

#chunk大小为0x20的链表. 
(gdb) p /x *(mchunkptr)0x602020
$7 = {prev_size = 0x0, size = 0x21, fd = 0x602000, bk = 0x6262, fd_nextsize = 0x0, bk_nextsize = 0x31}
(gdb) p /x *(mchunkptr)0x602000
$8 = {prev_size = 0x0, size = 0x21, fd = 0x0, bk = 0x6161, fd_nextsize = 0x0, bk_nextsize = 0x21}

#chunk大小为0x30的链表。
(gdb) p /x *(mchunkptr)0x602070
$9 = {prev_size = 0x0, size = 0x31, fd = 0x602040, bk = 0x6464, fd_nextsize = 0x0, bk_nextsize = 0x0}
(gdb)  p /x *(mchunkptr)0x602040
$10 = {prev_size = 0x0, size = 0x31, fd = 0x0, bk = 0x6363, fd_nextsize = 0x0, bk_nextsize = 0x0}

 

fastbin共有最多10个链表 ,已由arena.fastbinY[10]限制死,默认7个。用户能通过mallopt(M_MXFAST, value)个性化设置链表个数,但意义不大,最多也就10个。

 5、unsortedbin 

双向链表,临时垃圾桶,里面的chunk的size不一致。

是第二常用的bin,free时没进fastbin则大概率要进unsortedbin. 之后的malloc可能会把unsortedbin上的chunk挪到smallbin.

int main(int argc, char* argv[])
{
        void *p1= malloc(200);
        memset(p1,'a',200);

        void *temp1= malloc(10); //把p1 p2隔开,防止合并
        memset(temp1,'b',10);

        void *p2= malloc(300);
        memset(p2,'a',300);

        void *temp2= malloc(10); //把p2 top 隔开,防止合并
        memset(temp2,'c',10);

        free(p1);
        free(p2);

        getchar();

调试到getchar, 

(gdb) p main_arena
$1 = {mutex = 0, flags = 1, fastbinsY = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, top = 0x602250,
  last_remainder = 0x0, bins = {0x6020f0, 0x602000

(gdb) p *(mchunkptr)0x6020f0
$4 = {prev_size = 0, size = 321, fd = 0x602000, bk = 0x7ffff7dd7678 <main_arena+88>, fd_nextsize = 0x6161616161616161,
  bk_nextsize = 0x6161616161616161}
(gdb) p *(mchunkptr)0x602000
$5 = {prev_size = 0, size = 209, fd = 0x7ffff7dd7678 <main_arena+88>, bk = 0x6020f0, fd_nextsize = 0x6161616161616161,
  bk_nextsize = 0x6161616161616161}

6、smallbin

 双向链表,也是放小chunk,但上限到0x3F0, 以16字节递进, 共62个smallbin。

smallbin与unsortedbin相似,都是双向链表,不同的是:smallbin每个链上chunk大小相等,这一点与fastbin一致。

free时不会直接往smallbin里扔chunk,而是malloc时把unsortedbin里的chunk整理到对应的smallbin链上。

请看下面的例子:

int main(int argc, char* argv[])
{
        void *p1= malloc(30);
        void *temp1= malloc(10);

        void *p2= malloc(30);
        void *temp2= malloc(10);

        void *p3= malloc(30);
        void *temp3= malloc(10);

        free(p1);
        free(p2);
        free(p3); //fastbin 0x30链表上p3->p2->p1

        void* large = malloc(1024); //fastbin->unsortedbin->smallbin
        getchar();

free(p3)后

(gdb) p main_arena
$1 = {mutex = 0, flags = 0, fastbinsY = {0x0, 0x6020a0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, top = 0x6020f0,
  last_remainder = 0x0, bins = {0x7ffff7dd7678 <main_arena+88>, 0x7ffff7dd7678 <main_arena+88>,
    0x7ffff7dd7688 <main_arena+104>, 0x7ffff7dd7688 <main_arena+104>, 0x7ffff7dd7698 <main_arena+120>,

(gdb) p *(mchunkptr)0x6020a0
$2 = {prev_size = 0, size = 49, fd = 0x602050, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x0}
(gdb) p *(mchunkptr)0x602050
$3 = {prev_size = 0, size = 49, fd = 0x602000, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x0}
(gdb) p *(mchunkptr)0x602000
$4 = {prev_size = 0, size = 49, fd = 0x0, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x0} 

之后要分配1024字节,加上自重共1040个字节,借助这个malloc我们过一下malloc的大体流程:

1. 首先从fastbin中寻找。因为1040大于fastbin的范围(0x20~0x80/0xa0),找不到。

if ((unsigned long)(nb) <= (unsigned long)(get_max_fast ())) 

2. 从smallbin中寻找。1040已超过smallbin的范围。

3315      if (in_smallbin_range(nb))
3355      else {                                                                                                      
3356        idx = largebin_index(nb);                                                                                 
3357        if (have_fastchunks(av))                                                                                  
3358          malloc_consolidate(av); //fastbin -> unsortedbin   
              //只有要申请的字节数>=1024 且 fastbin不空 才触发 fastbin -> unsortedbin                                                                          
              //这才是我们的例子中要申请1024字节的原因
3359      }               

(gdb) p main_arena
$7 = {mutex = 1, flags = 1, fastbinsY = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, top = 0x6020f0,
  last_remainder = 0x0, bins = {0x602000, 0x6020a0, 0x7ffff7dd7688 <main_arena+104>, 0x7ffff7dd7688 <main_arena+104>,
    0x7ffff7dd7698 <main_arena+120>, 0x7ffff7dd7698 <main_arena+120>, 0x7ffff7dd76a8 <main_arena+136>,

(gdb) p *(mchunkptr)0x602000
$8 = {prev_size = 0, size = 49, fd = 0x602050, bk = 0x7ffff7dd7678 <main_arena+88>, fd_nextsize = 0x0, bk_nextsize = 0x0}
(gdb) p *(mchunkptr)0x602050
$9 = {prev_size = 0, size = 49, fd =
0x6020a0, bk = 0x602000, fd_nextsize = 0x0, bk_nextsize = 0x0}
(gdb) p *(mchunkptr)0x6020a0
$10 = {prev_size = 0, size = 49, fd =
0x7ffff7dd7678 <main_arena+88>, bk = 0x602050, fd_nextsize = 0x0, bk_nextsize = 0x0}

fastbin上的3个chunk 已被move到 unsortedbin上

3. 整理unsortedbin上的chunk到smallbin/largebin上

3377        while ( (victim = unsorted_chunks(av)->bk) != unsorted_chunks(av)) { 
//从unsortedbin的最后一个chunk开始,unsorted_chunks(av)是那个虚拟chunk

3422          /* 把victic从unsortedbin上摘下来 */ 
3423          unsorted_chunks(av)->bk = bck;
3424          bck->fd = unsorted_chunks(av); 

3439         /* 把victim放到相应的bin上(smallbin、largebin) */                       
3440                                                        
3441          if (in_smallbin_range(size)) {                
3442            victim_index = smallbin_index(size);        
3443            bck = bin_at(av, victim_index);             
3444            fwd = bck->fd;                              
3445          }                                             
3446          else {                                        
3447            victim_index = largebin_index(size);        
3448            bck = bin_at(av, victim_index);             
3449            fwd = bck->fd;               

4. 搜索largebin

5. 从top chunk割一块下来

6. 如果连top chunk都满足不了,调用sysmalloc从系统中分配

 chunk move的过程,fastbin -> unsortedbin -> smallbin/largebin

虽然这个例子很好的演示了chunk move的过程,但并没有诠释move的意义:正如malloc_consolidate名字所暗示的,它会合并fastbin中相邻的chunk以减少碎片,要知道fastbin的哲学是期盼同样小的内存申请很快到来,所以它不会合并相邻的chunk,这样时间长了碎片就会很多。看下面这个例子:

int main(int argc, char* argv[])
{
        void *p1= malloc(30);
        void *temp1= malloc(10);

        void *p2= malloc(30);
        void *temp2= malloc(10);

        void *p3= malloc(30);
        void *temp3= malloc(10);

        free(p1);
        free(p2);
        free(p3);

        void* large = malloc(1024);

(gdb) p main_arena
$1 = {mutex = 0, flags = 1, fastbinsY = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, top = 0x6024d0,
  last_remainder = 0x0, bins = {0x7ffff7dd7678 <main_arena+88>, 0x7ffff7dd7678 <main_arena+88>,
    0x7ffff7dd7688 <main_arena+104>, 0x7ffff7dd7688 <main_arena+104>, 0x7ffff7dd7698 <main_arena+120>,
    0x7ffff7dd7698 <main_arena+120>, 0x7ffff7dd76a8 <main_arena+136>, 0x7ffff7dd76a8 <main_arena+136>,
    0x7ffff7dd76b8 <main_arena+152>, 0x7ffff7dd76b8 <main_arena+152>, 0x7ffff7dd76c8 <main_arena+168>,
    0x7ffff7dd76c8 <main_arena+168>, 0x7ffff7dd76d8 <main_arena+184>, 0x7ffff7dd76d8 <main_arena+184>,
    0x7ffff7dd76e8 <main_arena+200>, 0x7ffff7dd76e8 <main_arena+200>, 0x602000, 0x602000,

(gdb) p *(mchunkptr)0x602000
$2 = {prev_size = 0,
size = 145, fd = 0x7ffff7dd76f8 <main_arena+216>, bk = 0x7ffff7dd76f8 <main_arena+216>,
  fd_nextsize = 0x0, bk_nextsize = 0x0}

三个相邻的小碎片被 合体了!

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

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

相关文章

基于SpringBoot精品在线试题库系统

采用技术 基于SpringBoot精品在线试题库系统的设计与实现~ 开发语言&#xff1a;Java 数据库&#xff1a;MySQL 技术&#xff1a;SpringBootMyBatis 工具&#xff1a;IDEA/Ecilpse、Navicat、Maven 页面展示效果 系统功能结构图 学生管理 教师管理 专业管理 试卷管理 …

【阿里魔搭】modelscope包下载安装

最终解决方案&#xff1a;使用源码安装modelscope 这里写目录标题 问题描述&#xff1a;pip安装包冲突安装步骤 问题描述&#xff1a;pip安装包冲突 一开始的是在3.11的虚拟环境下使用命令行pip install "modelscope[nlp]" -f https://modelscope.oss-cn-beijing.al…

基于时空上下文(STC)的运动目标跟踪算法,Matlab实现

博主简介&#xff1a; 专注、专一于Matlab图像处理学习、交流&#xff0c;matlab图像代码代做/项目合作可以联系&#xff08;QQ:3249726188&#xff09; 个人主页&#xff1a;Matlab_ImagePro-CSDN博客 原则&#xff1a;代码均由本人编写完成&#xff0c;非中介&#xff0c;提供…

MyBatis:XML操作

&#x1f451;专栏内容&#xff1a;MyBatis⛪个人主页&#xff1a;子夜的星的主页&#x1f495;座右铭&#xff1a;前路未远&#xff0c;步履不停 目录 一、MyBatis XML方式1、配置数据库2、指明XML路径3、写持久层代码 二、基础操作1、新增2、删除3、更新4、查找Ⅰ、开启驼峰命…

权限提升-Web权限提升篇划分获取资产服务后台系统数据库管理相互转移

知识点 1、权限提升转移-分类&高低&场景 2、Web权限提升及转移-后台&数据库 3、后台权限及转移-转移对象&后台分类 章节点&#xff1a; 1、Web权限提升及转移 2、系统权限提升及转移 3、宿主权限提升及转移 4、域控权限提升及转移 基础点 0、为什么我们要学…

Spring AI Embeddings 和 Vector 入门

在前面 Spring AI Chat 简单示例 中介绍了 Chat 的基本用法&#xff0c;本文在此基础&#xff08;主要是pom.xml&#xff09;上继续探索 Embedding 和 Vector。 官方文档&#xff1a; embeddings: https://docs.spring.io/spring-ai/reference/api/embeddings/openai-embedding…

java算法第30天 | ● 332.重新安排行程 ● 51. N皇后 ● 37. 解数独

今天是三道困难题&#xff0c;一刷可以先跳过。 332.重新安排行程 本题的重点是使用Map<String,Map<String,Integer>>存储路径以便递归中根据上一个地点检索接下来的选择。注意&#xff0c;因为要求按字母顺序排列优先级&#xff0c;所以内层的Map要用TreeMap<…

Jupyter Notebook介绍、安装及使用教程

Jupyter Notebook介绍、安装及使用教程 一、什么是Jupyter Notebook&#xff1f;简介组成部分网页应用文档Jupyter Notebook的主要特点 二、安装Jupyter Notebook先试用&#xff0c;再决定安装安装前提使用Anaconda安装使用pip命令安装 三、运行Jupyter Notebook帮助启动默认端…

html5cssjs代码 032 边框属性示例

html5&css&js代码 032 边框属性示例 一、代码二、解释 该HTML文件定义了一个网页页面&#xff0c;主要介绍了HTML5中CSS边框属性的用法。 一、代码 <!DOCTYPE html> <html lang"zh-cn"><head><title>编程笔记 html5&css&j…

图Graph及相关算法(Dijkstra,Kruskal)

目录 无向图 有向图 邻接矩阵 邻接表 图的bfs&#xff0c;dfs 二部图&#xff08;二分图&#xff09; 有向无环图&#xff08;DAG&#xff09; 拓扑排序&#xff08;Topological Sort&#xff09; AOV网 迪杰斯特拉Dijkstra 最小生成树 克鲁斯卡尔&#xff1a;Krus…

回归预测 | Matlab基于SAO-BiLSTM雪融算法优化双向长短期记忆神经网络的数据多输入单输出回归预测

回归预测 | Matlab基于SAO-BiLSTM雪融算法优化双向长短期记忆神经网络的数据多输入单输出回归预测 目录 回归预测 | Matlab基于SAO-BiLSTM雪融算法优化双向长短期记忆神经网络的数据多输入单输出回归预测效果一览基本介绍程序设计参考资料 效果一览 基本介绍 1.Matlab基于SAO-B…

Linux系统——Mysql索引补充知识

目录 一、索引介绍 1.索引的优点 2.索引的分类 3.索引的技术名词 3.1回表 3.2覆盖索引 3.3最左匹配 3.4索引下推 4.索引匹配方式 4.1全值匹配 4.2最左前缀匹配 4.3匹配列前缀 4.4匹配一个范围值 4.5精确匹配某一列并范围匹配另一列 4.6只访问索引的查询 一、索引…

银行OA系统|基于SpringBoot架构+ Mysql+Java+ B/S结构的银行OA系统设计与实现(可运行源码+数据库+设计文档)

推荐阅读100套最新项目 最新ssmjava项目文档视频演示可运行源码分享 最新jspjava项目文档视频演示可运行源码分享 最新Spring Boot项目文档视频演示可运行源码分享 2024年56套包含java&#xff0c;ssm&#xff0c;springboot的平台设计与实现项目系统开发资源&#xff08;可…

【Charles如何对手机APP进行抓包和弱网测试】

一、Charles对APP抓包 1、前提条件&#xff1a; 1&#xff09;电脑上必须安装Charles工具&#xff0c;如没有安装可参考&#xff1a;【Charles抓包工具下载安装详细操作步骤】-CSDN博客 2&#xff09;手机和电脑必须在同一个局域网内&#xff08;连接同一个WiFi&#xff09;…

Linux信号补充——信号捕捉处理

一、信号的捕捉处理 ​ 信号保存后会在合适的时间进行处理&#xff1b; 1.1信号处理时间 ​ 进程会在操作系统的调度下处理信号&#xff0c;操作系统只管发信号&#xff0c;即信号处理是由进程完成的&#xff1b; ​ 1.信号处理首先进程得检查是否有信号&#xff1b;2.进程…

异地组网需求如何实现?

在当今信息化时代&#xff0c;越来越多的企业和个人在不同地区之间需要进行远程通信和数据传输。由于网络环境的差异和网络隔离等原因&#xff0c;异地组网的需求愈发迫切。本文将介绍一款名为【天联】的异地组网内网穿透产品&#xff0c;它的出现有效解决了不同地区间电脑、设…

基于Arduino IDE 野火ESP8266模块WIiFi开发

一、函数介绍 头文件 #include <ESP8266WiFi.h> #include <ESP8266WiFiMulti.h>ESP8266WiFi.h库主要用于连接单个WiFi网络。如果需要连接到多个WiFi网络&#xff0c;例如在需要切换不同网络或者备用网络时&#xff0c;可以使用ESP8266WiFiMulti.h头文件&#xff…

供应链投毒预警 | 恶意Py组件tohoku-tus-iot-automation开展窃密木马投毒攻击

概述 上周&#xff08;2024年3月6号&#xff09;&#xff0c;悬镜供应链安全情报中心在Pypi官方仓库&#xff08;https://pypi.org/&#xff09;中捕获1起新的Py包投毒事件&#xff0c;Python组件tohoku-tus-iot-automation 从3月6号开始连续发布6个不同版本恶意包&#xff0c…

SAP前台处理:物料计价方式:价格控制与价格确定 - 01

一、背景&#xff1a; 物料主数据中我们讲解到物料的计价方式&#xff0c;SAP应用到的主要计价方式有移动平均价和标准价格方式两种&#xff0c;但也有按照批次计价等方式&#xff0c;我们主要介绍最常用的V2移动平均价和S3的标准价格&#xff1b; 二、不同组合下的差异&…

知识蒸馏——深度学习的简化之道 !!

文章目录 前言 1、什么是知识蒸馏 2、知识蒸馏的原理 3、知识蒸馏的架构 4、应用 结论 前言 在深度学习的世界里&#xff0c;大型神经网络因其出色的性能和准确性而备受青睐。然而&#xff0c;这些网络通常包含数百万甚至数十亿个参数&#xff0c;使得它们在资源受限的环境下&…