Linux malloc内存分配实现原理

目录

 

一、用户进程虚拟内存空间布局

二、malloc工作原理

2.1 malloc实现流程

2.1.1 brk方式申请内存

2.1.2 mmap方式分配内存

2.2 核心代码

2.3 malloc分配物理内存的时机

2.4 malloc分配的实际内存大小

三、虚拟内存与物理内存

3.1 如何建立映射

3.2 分配物理内存

3.3 物理内存访问

四、new和malloc区别与联系

一、用户进程虚拟内存空间布局

以Linux 64位系统为例。理论上,64bit内存地址可寻址空间为0x0000000000000000 ~ 0xFFFFFFFFFFFFFFFF,这是个相当庞大的地址空间,Linux实际上只用了其中一小部分(256T)。Linux64位操作系统仅使用低47位,高17位做扩展(全0或全1)。因此,实际用到的地址为空间为0x0000000000000000 ~ 0x00007FFFFFFFFFFF和0xFFFF800000000000 ~ 0xFFFFFFFFFFFFFFFF,其中前者为用户空间(User Space),后者为内核空间(Kernel Space)。

同理,32位的系统,0x00000000 ~ 0xBFFFFFFF为用户空间地址(3G),0xC0000000 ~ 0xFFFFFFFF为内核空间地址(1G)。

64位和32位的内存布局,如下:

用户空间的内存空间通常包括以下几个部分,每个部分用于存放不同类型的数据:

  • Text(代码段)

存放程序的机器指令(即编译后的代码)。这部分内存是只读的,包含程序的执行代码。

  • Data(数据段)

存放已初始化的全局变量和静态变量。这些变量在程序启动时就已经有了初始值。

  • BSS(Block Started by Symbol)段

存放未初始化的全局变量和静态变量。这些变量在程序启动时默认初始化为零或空。

  • Heap(堆)

用于动态内存分配,如通过 malloc、calloc、realloc等函数分配的内存。堆的大小可以通过 brk或 sbrk系统调用进行调整。

  • Stack(栈)

用于存放局部变量、函数调用的参数和返回地址。栈是后进先出(LIFO)的数据结构,用于管理函数调用和返回。

mmap(内存映射区域):

用于内存映射文件、共享库、匿名映射等。通过 mmap系统调用,可以将文件或其他对象映射到进程的地址空间。

  • 保留区

保留区是一些未明确分配用途的虚拟地址空间,这些区域可能用于未来的扩展或其他特殊用途。

由上图可以看出,进程的虚拟地址空间,由多个虚拟内存区域构成。虚拟内存区域是进程的虚拟地址空间中的一个同质区间,即具有同样特性的连续地址范围。上图中所示的text数据段、Data段、Bss数据段、堆、栈、mmap区域,都是一个独立的虚拟内存区域。linux 内核使用的vm_area_struct 结构来表示一个独立的虚拟内存区域,由于每个不同质的虚拟内存区域功能和内部机制不同;因此同一个进程使用多个vm_area_struct 结构来分别表示不同类型的虚拟内存区域。各个vm_area_struct 结构使用链表或者树形结构链接,方便进程快速访问。如下图所示:

二、malloc工作原理

2.1 malloc实现流程

malloc 实现通常会使用 brk 或 mmap 系统调用来向内核请求内存。

对于小块内存(<= 128kB)分配,malloc 通常使用 brk 系统调用扩展数据段。

对于大块内存(> 128kB)分配,malloc 通常使用 mmap 系统调用创建新的匿名映射。

最终通过伙伴系统分配物理内存。

2.1.1 brk方式申请内存

如果用户分配的内存小于或等于 128 KB,通过 brk() 申请内存。brk()的实现方式很简单,就是通过 brk() 函数将堆顶指针向高地址移动,获得新的内存空间。使用brk()分配了一段新的虚拟内存区域,但这并不会立即分配物理内存。实际的物理内存分配通常是在访问新分配的虚拟内存区域时,如果发生了缺页异常(Page Fault),操作系统才会开始分配并映射相应的物理内存页面。如下所示:

malloc 通过 brk() 方式申请的内存,free 释放内存的时候,并不一定会把内存归还给操作系统,而是缓存在 malloc 的内存池中,待下次使用,这样就可以重复使用。

brk()系统调用的优点:

可以减少缺页异常的发生,提高内存访问效率。

brk()系统调用的缺点:

由于申请的内存没有归还系统,在内存工作繁忙时,频繁的内存分配和释放会造成内存碎片。brk()方式之所以会产生内存碎片,是由于brk通过移动堆顶的位置来分配内存,并且使用完不会立即归还系统,重复使用,如果高地址的内存不释放,低地址的内存是得不到释放的。

正是由于使用brk()会出现内存碎片,所以在申请大块内存的时候才会使用mmap()方式,mmap()是以页为单位进行内存分配和管理的,释放后就直接归还系统了,所以不会出现这种小碎片的情况。

2.1.2 mmap方式分配内存

mmap是在进程的虚拟地址空间中的文件映射区域找一块空闲的虚拟内存,内存分配器通常使用这个系统调用来创建私有匿名映射,以分配内存,但不会立即分配物理内存。

实现这样的映射关系后,进程就可以采用指针的方式读写操作这一段内存,而系统会自动回写脏页面到对应的文件磁盘上,即完成了对文件的操作而不必调用read,write等系统调用函数。相反,内核空间的这段区域的修改也直接反映用户空间,从而可以实现不同进程的文件共享。如下:

2.2 核心代码

  • brk

用户程序通过malloc进行内存分配会系统调用brk

SYSCALL_DEFINE1(brk, unsigned long, brk)
{unsigned long newbrk, oldbrk, origbrk;struct mm_struct *mm = current->mm;struct vm_area_struct *brkvma, *next = NULL;unsigned long min_brk;bool populate;bool downgraded = false;LIST_HEAD(uf);MA_STATE(mas, &mm->mm_mt, 0, 0);if (mmap_write_lock_killable(mm))return -EINTR;origbrk = mm->brk;#ifdef CONFIG_COMPAT_BRK/** CONFIG_COMPAT_BRK can still be overridden by setting* randomize_va_space to 2, which will still cause mm->start_brk* to be arbitrarily shifted*/if (current->brk_randomized)min_brk = mm->start_brk;elsemin_brk = mm->end_data;
#elsemin_brk = mm->start_brk;
#endifif (brk < min_brk)goto out;/** Check against rlimit here. If this check is done later after the test* of oldbrk with newbrk then it can escape the test and let the data* segment grow beyond its set limit the in case where the limit is* not page aligned -Ram Gupta*/if (check_data_rlimit(rlimit(RLIMIT_DATA), brk, mm->start_brk,mm->end_data, mm->start_data))goto out;newbrk = PAGE_ALIGN(brk);oldbrk = PAGE_ALIGN(mm->brk);if (oldbrk == newbrk) {mm->brk = brk;goto success;}/** Always allow shrinking brk.* do_brk_munmap() may downgrade mmap_lock to read.*/if (brk <= mm->brk) {int ret;/* Search one past newbrk */mas_set(&mas, newbrk);brkvma = mas_find(&mas, oldbrk);if (!brkvma || brkvma->vm_start >= oldbrk)goto out; /* mapping intersects with an existing non-brk vma. *//** mm->brk must be protected by write mmap_lock.* do_brk_munmap() may downgrade the lock,  so update it* before calling do_brk_munmap().*/mm->brk = brk;ret = do_brk_munmap(&mas, brkvma, newbrk, oldbrk, &uf);if (ret == 1)  {downgraded = true;goto success;} else if (!ret)goto success;mm->brk = origbrk;goto out;}if (check_brk_limits(oldbrk, newbrk - oldbrk))goto out;/** Only check if the next VMA is within the stack_guard_gap of the* expansion area*/mas_set(&mas, oldbrk);next = mas_find(&mas, newbrk - 1 + PAGE_SIZE + stack_guard_gap);if (next) {vma_start_write(next);if (newbrk + PAGE_SIZE > vm_start_gap(next))goto out;}brkvma = mas_prev(&mas, mm->start_brk);/* Ok, looks good - let it rip. */if (do_brk_flags(&mas, brkvma, oldbrk, newbrk - oldbrk, 0) < 0)goto out;mm->brk = brk;success:// 如果标记有VM_LOCKED的flag,立刻进行物理内存分配populate = newbrk > oldbrk && (mm->def_flags & VM_LOCKED) != 0;if (downgraded)mmap_read_unlock(mm);elsemmap_write_unlock(mm);userfaultfd_unmap_complete(mm, &uf);if (populate)mm_populate(oldbrk, newbrk - oldbrk);return brk;out:mmap_write_unlock(mm);return origbrk;
}

进程的堆边界:指进程虚拟地址空间中堆区域的结束地址,可动态调整。

内存动态分配:应用程序调用 malloc等函数请求内存时,如果当前堆空间不足,操作系统会通过调整堆边界来分配更多的内存。相反,当调用 free函数释放内存时,堆边界通常不会立即调整,而是由内存管理器在后续的内存分配请求中决定是否收缩堆。

  • do_brk_flags

根据给定的地址和长度,扩展或创建一个新的虚拟内存区域(VMA),并将其添加到当前进程的内存管理结构中。

static int do_brk_flags(struct ma_state *mas, struct vm_area_struct *vma,unsigned long addr, unsigned long len, unsigned long flags)
{struct mm_struct *mm = current->mm;validate_mm_mt(mm);/** Check against address space limits by the changed size* Note: This happens *after* clearing old mappings in some code paths.*/flags |= VM_DATA_DEFAULT_FLAGS | VM_ACCOUNT | mm->def_flags;if (!may_expand_vm(mm, flags, len >> PAGE_SHIFT))return -ENOMEM;if (mm->map_count > sysctl_max_map_count)return -ENOMEM;if (security_vm_enough_memory_mm(mm, len >> PAGE_SHIFT))return -ENOMEM;if (vma)vma_start_write(vma);/** Expand the existing vma if possible; Note that singular lists do not* occur after forking, so the expand will only happen on new VMAs.*/if (vma && vma->vm_end == addr && !vma_policy(vma) &&can_vma_merge_after(vma, flags, NULL, NULL,addr >> PAGE_SHIFT, NULL_VM_UFFD_CTX, NULL)) {mas_set_range(mas, vma->vm_start, addr + len - 1);if (mas_preallocate(mas, vma, GFP_KERNEL))return -ENOMEM;/* Set flags first to implicitly lock the VMA before updates */vm_flags_set(vma, VM_SOFTDIRTY);vma_adjust_trans_huge(vma, vma->vm_start, addr + len, 0);if (vma->anon_vma) {anon_vma_lock_write(vma->anon_vma);anon_vma_interval_tree_pre_update_vma(vma);}vma->vm_end = addr + len;mas_store_prealloc(mas, vma);if (vma->anon_vma) {anon_vma_interval_tree_post_update_vma(vma);anon_vma_unlock_write(vma->anon_vma);}khugepaged_enter_vma(vma, flags);goto out;}/* create a vma struct for an anonymous mapping */// 创建一个新的VMAvma = vm_area_alloc(mm);if (!vma)goto vma_alloc_fail;vma_set_anonymous(vma);vma->vm_start = addr;vma->vm_end = addr + len;vma->vm_pgoff = addr >> PAGE_SHIFT;vm_flags_init(vma, flags);vma->vm_page_prot = vm_get_page_prot(flags);vma_start_write(vma);mas_set_range(mas, vma->vm_start, addr + len - 1);// 新的VMA(虚拟内存区域)添加到当前进程的内存管理结构if (mas_store_gfp(mas, vma, GFP_KERNEL))goto mas_store_fail;mm->map_count++;
out:perf_event_mmap(vma);mm->total_vm += len >> PAGE_SHIFT;mm->data_vm += len >> PAGE_SHIFT;if (flags & VM_LOCKED)mm->locked_vm += (len >> PAGE_SHIFT);vm_flags_set(vma, VM_SOFTDIRTY);validate_mm(mm);return 0;mas_store_fail:vm_area_free(vma);
vma_alloc_fail:vm_unacct_memory(len >> PAGE_SHIFT);return -ENOMEM;
}

  • mm_populate

为VMA分配物理内存。

int __mm_populate(unsigned long start, unsigned long len, int ignore_errors)
{...// 查询VMAvma = find_vma_intersection(mm, nstart, end);...ret = populate_vma_page_range(vma, nstart, nend, &locked);...return ret; 
}long populate_vma_page_range(struct vm_area_struct *vma,unsigned long start, unsigned long end, int *locked)
{struct mm_struct *mm = vma->vm_mm;unsigned long nr_pages = (end - start) / PAGE_SIZE;int gup_flags;long ret;...ret = __get_user_pages(mm, start, nr_pages, gup_flags,NULL, NULL, locked);lru_add_drain();return ret;
}static long __get_user_pages(struct mm_struct *mm,unsigned long start, unsigned long nr_pages,unsigned int gup_flags, struct page **pages,struct vm_area_struct **vmas, int *locked)
{long ret = 0, i = 0;struct vm_area_struct *vma = NULL;struct follow_page_context ctx = { NULL };...page = follow_page_mask(vma, start, foll_flags, &ctx);if (!page || PTR_ERR(page) == -EMLINK) {// 缺页异常处理ret = faultin_page(vma, start, &foll_flags,PTR_ERR(page) == -EMLINK, locked);...}             return i ? i : ret;
}

这块代码量较大,有兴趣可以自己跟读后续的代码。

2.3 malloc分配物理内存的时机

触发缺页异常,会去分配物理内存。

2.4 malloc分配的实际内存大小

如果malloc内存分配函数最终通过linux伙伴系统(以页为单位)分配器进行物理内存分配,申请的内存4KB,一律分配4KB.

三、虚拟内存与物理内存

3.1 如何建立映射

虚拟内存和物理内存建立映射的关键步骤:

1) 页表的创建

当一个进程被创建时,操作系统会为该进程分配一个页表。页表的初始状态通常是空的,因为此时进程的虚拟地址空间还没有与物理内存建立任何映射。

2)页表条目的填充

随着进程的运行,操作系统会根据需要将虚拟页映射到物理页框。这通常发生在以下几种情况:

- 进程首次访问某个虚拟地址时,如果对应的虚拟页还没有映射到物理内存,会发生缺页异常,操作系统会处理这个异常,将所需的页从磁盘加载到物理内存,并更新页表。

- 操作系统主动为进程分配内存时,例如通过 `malloc` 或 `mmap` 等系统调用,操作系统会在物理内存中找到可用的页框,并更新页表,建立虚拟页到物理页框的映射。

3)页表的维护

操作系统需要持续地维护页表,确保虚拟地址空间和物理内存之间的映射是正确的。这包括在内存不足时进行页面置换(将不常用的页写回磁盘,释放物理页框),以及在进程结束时清理页表,释放相关的物理内存。

4)页表的更新

当物理内存中的页框发生变化时(例如,由于页面置换或内存回收),操作系统需要更新相关的页表条目,以反映最新的映射关系。

总结来说,虚拟内存和物理内存之间的映射是通过页表的创建、填充、维护和更新来建立和管理的。

3.2 分配物理内存

分配物理内存的基本步骤:

1)虚拟内存分配

当应用程序调用 malloc 或其他内存分配函数时,操作系统会在进程的虚拟地址空间中分配一块内存。

2)缺页异常

当进程首次尝试访问新分配的虚拟内存时,由于这块内存尚未映射到物理内存,会发生缺页异常。

3)物理内存分配

内核在处理缺页异常时,会检查是否有可用的物理内存页。如果有可用页,内核会通过伙伴系统或其他内存分配机制分配一个物理内存页。

4)建立映射

内核随后会将这个新分配的物理内存页映射到发生缺页异常的虚拟地址,并更新页表以反映这种映射关系。

一旦映射建立完成,进程就可以访问刚刚分配的内存。

3.3 物理内存访问

一旦虚拟内存与物理内存之间的映射建立完成,进程访问这块物理内存的过程是通过CPU的内存管理单元(MMU)和页表自动完成的。以下是进程访问物理内存的具体步骤:

1)虚拟地址生成

当进程中的指令需要访问内存时,它会生成一个虚拟地址。这个虚拟地址是进程在其虚拟地址空间中看到的地址。

2)虚拟地址到物理地址的转换

CPU中的MMU负责将虚拟地址转换为物理地址。MMU使用进程的页表来进行这种转换。页表中包含了虚拟页号到物理页帧号的映射信息。

3)物理地址计算

MMU首先查找虚拟地址的页表条目。如果页表条目中的present bit(存在位)被设置,表示该虚拟页已经映射到了物理内存中。如果在页表查找过程中发现present bit没有被设置,这意味着对应的虚拟页当前不在物理内存中,MMU会产生一个缺页异常。内核会处理这个异常,可能会从磁盘上的交换空间加载缺失的页,并更新页表和TLB。

MMU使用页表条目中的物理页帧号和虚拟地址中的页内偏移量来计算出最终的物理地址。

为了加速虚拟地址到物理地址的转换,CPU通常有一个TLB(Translation Lookaside Buffer),它是一个小型的硬件缓存,存储了最近使用的虚拟页到物理页的映射。如果TLB中有所需的映射,MMU可以直接从TLB中获取物理地址,而不需要访问页表,这样可以显著提高内存访问速度。

4)内存访问

计算出物理地址,CPU就可以直接访问物理内存中的数据。这个访问可以是读取操作,也可以是写入操作,具体取决于进程的指令。

整个过程对进程来说是透明的,进程只需要使用虚拟地址,而不需要知道物理内存的具体位置。内核和MMU共同确保了进程能够正确地访问其请求的内存。

四、new和malloc区别与联系

new分配内存:

int *p;
p = new int; //返回类型为int *类型,分配的大小为sizeof(int)
p = new int[100]; //返回类型为int *类型,分配的大小为sizeof(int) * 100

malloc分配内存:

int *p;
p = (int *)malloc(sizeof(int));

主要区别有3点:

1)new返回指定类型的指针,并且可以自动计算所需要的大小;而malloc则必须由我们计算字节数,并且在返回的是void*,需强转成实际指定类型的指针

2)malloc的实参是sizeof(int),用于指明一个整形数据需要的大小,如果我们写成p = (int *)malloc(1), 只是申请了一个字节的空间,如果向里面存放了一个整数的话,将会占用额外的3个字节,可能会改变原有内存空间中的数据

3)malloc只管分配内存,并不能对其进行初始化,所以得到的一片新内存中,其值将是随机的,可以用memset将其初始化为0

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

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

相关文章

大数据采集与分析实训室解决方案

随着信息技术的飞速发展&#xff0c;大数据已成为推动产业升级、社会进步的重要力量。为了培养适应未来社会需求的大数据专业人才&#xff0c;构建一套科学、先进的大数据采集与分析实训室解决方案显得尤为重要。为此&#xff0c;唯众特推出全面升级的大数据采集与分析实训室解…

使用实例:xxl-job应用在spring cloud微服务下

1、首先下载&#xff0c;从github上下载&#xff0c;选择zip然后直接解压 https://github.com/xuxueli/xxl-job/releases 2、解压完后用idea启动。 启动这个启动类&#xff0c;然后按照路径访问 http://localhost:8080/xxl-job-admin/ 3、在你的项目里编写一个单独的微服务&a…

mac的使用

mac使用python的问题 对于python的虚拟环境&#xff0c;其实是基于已经安装到本地的python来安装不同的包。&#xff08;之前我的mac上只安装了python3.9.6 &#xff0c;安装的位置为/usr/bin/python3&#xff09;然后我在vscode里怎么找都找不到如何弄一个python3.7.6 的版本…

Nginx部署前端vue项目操作步骤和方法~小皮

部署前端Vue.js项目到Nginx上&#xff0c;是开发流程中至关重要的一步&#xff0c;它意味着将静态文件托管在Web服务器上&#xff0c;使应用程序能够被用户访问和交互。下面将详细介绍如何使用Nginx部署前端Vue项目的操作步骤和方法&#xff1a; 准备构建Vue项目 安装Node.js和…

k8s集群环境搭建(一主二从--kubeadm安装)

前置条件 版本&#xff1a;CentOS Linux release 7.5.1804 (Core) 内存&#xff1a;2G CPU&#xff1a;2 主机名解析 vim /etc/hosts 192.168.109.100 master 192.168.109.101 node1 192.168.109.102 node2时间同步&#xff0c;这里直接使用chronyd服务从网络同步时间syste…

2024.9.2

还没写完 #include <iostream> #include <cstring> using namespace std;class myString { private:char *str; //字符串int size; //实际字符长度int len; //字符串容量 public:myString():size(10) //无参构造函数{len siz…

ES6语法详解

以下是ES6常用的一些语法和特性&#xff1a; 声明变量的关键字变化&#xff1a;使用let和const、var来声明变量。 箭头函数&#xff1a;使用箭头&#xff08;>&#xff09;定义函数&#xff0c;简化函数的写法。 模板字符串&#xff1a;使用反引号&#xff08;&#xff0…

学习大数据DAY52 Docker中的Mysql主从配置

Mysql 数据库主从同步 上机练习 1 容器生成命令 压缩包获取镜像 docker image load -i /root/mysql.tar 创建并开启两个镜像&#xff1a; docker run --name mysql1 -d -p 3333:3306 \ -v /opt/mysql1/conf:/etc/mysql/conf.d/ \ -v /opt/mysql1/data:/var/lib/mysql \…

★ 算法OJ题 ★ 力扣1089 - 复写零

Ciallo&#xff5e;(∠・ω< )⌒☆ ~ 今天&#xff0c;我将和大家一起做一道双指针算法题--复写零~ 目录 一 题目 二 算法解析 2.1 算法思路 2.2 算法流程 三 编写算法 一 题目 1089. 复写零 - 力扣&#xff08;LeetCode&#xff09; 二 算法解析 2.1 算法思路 …

国内独家首发 | OpenCSG开源中文版fineweb edu数据集

01 背景 近年来&#xff0c;人工智能&#xff08;AI&#xff09;技术&#xff0c;特别是自然语言处理&#xff08;NLP&#xff09;的飞速发展深刻影响着各个行业。从智能客服到内容生成&#xff0c;从语音识别到翻译工具&#xff0c;NLP的应用已经无处不在。在这一领域中&…

【Datawhale X 李宏毅苹果书 AI夏令营】深度学习自适应学习率(AdaGrad/RMSProp/Adam)及其调度

1、自适应学习率 理论上&#xff1a;在训练一个网络&#xff0c;训练到现在参数在临界点附近&#xff0c;再根据特征值的正负号判断该 临界点是鞍点还是局部最小值实际上&#xff1a;①在训练的时候&#xff0c;要走到鞍点或局部最小值非常困难&#xff1b;②多数还未走到临界…

一次性了解Neo4j图形数据库

Neo4j高性能的NoSQL图形数据库 它将结构化数据存储在网络&#xff08;从数学角度叫做图&#xff09;上而不是传统的表格中。 Neo4j是一个嵌入式的、基于磁盘的、具备完全事务特性的Java持久化引擎。 但它在数据表示上采用了图形模型&#xff0c;即数据以节点&#xff08;Nod…

基于Yolov5_6.1、LPRNet、PySide6开发的车牌识别系统

项目概述 项目背景 随着车辆数量的不断增加&#xff0c;车牌识别系统在交通管理、停车场自动化等领域变得越来越重要。本项目利用先进的深度学习技术和现代图形用户界面框架来实现高效的车牌识别功能。 项目特点 高效识别&#xff1a;采用 YOLOv5_6.1 进行车牌定位&#xff…

【Day08】

目录 MySQL-多表查询-概述 MySQL-多表查询-内连接 MySQL-多表查询-外连接 MySQL-多表查询-[标量、列]子查询 MySQL-多表查询-[行、表]子查询 MySQL-多表查询-案例 MySQL-事务-介绍与操作 MySQL-事务-四大特性 MySQL-索引-介绍 MySQL-索引-结构 MySQL-索引-操作语法 …

Datawhale X 李宏毅苹果书 AI夏令营-深度学习入门task3:实践方法论

在应用机器学习算法时&#xff0c;实践方法论能够帮助我们更好地训练模型。 1.模型偏差 模型偏差可能会影响模型训练。举个例子&#xff0c;假设模型过于简单&#xff0c;即使找到的最好的函数也不能满足需求。这种情况就是想要在大海里面捞针&#xff08;一个损失低的函数&am…

2023 ICPC 江西省赛K. Split

K. Split time limit per test: 3 seconds memory limit per test: 512 megabytes You are given a positive integer n and a non-increasing sequence ai of length n , satisfying ∀i∈[1,n−1],. Then, you are given a positive integer m, which represents the tot…

传统CV算法——背景建模算法介绍

帧差法 由于场景中的目标在运动&#xff0c;目标的影像在不同图像帧中的位置不同。该类算法对时间上连续的两帧图像进行差分运算&#xff0c;不同帧对应的像素点相减&#xff0c;判断灰度差的绝对值&#xff0c;当绝对值超过一定阈值时&#xff0c;即可判断为运动目标&#xf…

HarmonyOS开发实战( Beta5版)小程序场景性能优化开发指导

简介 小程序是一种轻量级的应用&#xff0c;它不需要下载、安装即可使用&#xff0c;用户可以通过扫描二维码或者搜索直接打开使用。小程序运行在特定的平台上&#xff0c;平台提供了小程序的运行环境&#xff08;运行容器&#xff09;和一些基础服务&#xff08;小程序API&am…

Linux学习笔记5 值得一读,Linux(ubuntu)软件管理,搜索下载安装卸载全部搞定!(上)

本文记录Ubuntu操作系统的软件包管理。 一、背景 整个Linux系统就是大大小小的软件包构成的&#xff0c;在linux系统中&#xff0c;软件的管理非常重要&#xff0c;与其他操作系统不同&#xff0c;linux的软件包管理比较复杂&#xff0c;有时还需要处理软件包之间的冲突。本文…

【电池专题】软包电池封装工序

铝塑膜成型工序冲坑 铝塑膜成型工序,软包电芯可以根据客户的需求设计成不同的尺寸,当外形尺寸设计好后,就需要开具相应的模具,使铝塑膜成型。 成型工序也叫作冲坑,顾名思义,就是用成型模具在加热的情况下,在铝塑膜上冲出一个能够装卷芯的坑,具体的见下图。 …