Linux源码分析: 12 内核是如何实现内存管理的

Linux内核源码分析之内存管理 本文站的角度更底层,基本都是从Linux内核出发,会更深入。所以当你都读完,然后再次审视这些功能的实现和设计时,我相信你会有种豁然开朗的感觉。 1、页 内核把物理页作为内存管理的基本单元。 尽管处理器的最小处理单位是字(或者字节),但是MMU(内存管理单元,管理内存并把虚拟地址转换为物理地址的硬件)通常以页为单位进行处理。所以从虚拟内存看,页也是最小单元。 体系不同,支持的页大小不同。大多数32位体系结构支持4KB的页,而64位体系结构一般会支持8KB的页。 内核用struct page结构体表示 阅读详情

Linux内核设计与实现 12—— 内存管理

前言:内核内存管理存在的困难

在内核里分配内存存在着以下这些困难:

  • 从根本上讲,内核本身不能像用户空间那样奢侈地使用内存;
  • 内核不支持简单便捷的内存分配方式;
  • 内核难以处理内存分配错误。

因此,内存管理十分重要,本文通过研究Linux内核源代码以及相关资料,对内核如何进行内存管理给出了详细介绍。

12.1 数据结构介绍:页与区

12.1.1 页

内核把物理页(page)作为内存管理的基本单位。从虚存的角度来看,页就是最小单位。

内核中用struct page结构表示系统中的每个物理页(<linux/mm_types.h>):

struct page {
    unsigned long flags;			// 存放页的状态,是否为脏页,是否被锁存等
    atomic_t _count;	    		// 存放页的引用次数,为-1代表未被引用
    atomic_t _mapcount;				// 被映射引用的次数
    unsigned long private;			// 作为私有数据
    struct address_space *mapping;   // 当一个页被页缓存使用时,mapping域指向和这个页相关的地址空间
    pgoff_t index;
    struct list_head lru;
    void *virtual;				    // 页在虚拟内存中的地址
}

12.1.2 区

有些页位于内存中特定的物理地址上,所以不能将其用于一些特定的任务。因此,内核把页划分为不同的区(zone),并利用区对具有相似特性的页进行分组。Linux必须处理如下两种由于硬件缺陷所引发的内存寻址问题:

  • 一些硬件只能用某些特定的内存地址来执行DMA(直接内存访问);
  • 一些体系结构中的内存物理寻址范围远大于虚拟寻址范围,这导致内存不能永久地映射在内核空间上。

因为存在这些制约条件,Linux主要使用了四种区(<linux/mmzone.h>)来解决问题:

  • ZONE_DMA——这个区包含的页能用来执行DMA操作;
  • ZONE_DMA32——与前一个类似,不同点在于该区中的页面只能被32位设备访问;
  • ZONE_NORMAL——这个区包含能够正常映射的页;
  • ZONE_HIGHEM——“高端内存”,又称动态映射页,其中的页不能永久映射到内核地址空间中。

每个区都用struct zone表示(<linux/mmzone.h>),该结构体很大,以下展示的是最重要的三个域:

struct zone {
    ...
    /* lock域是一个自旋锁,它防止该结构被并发访问 */
    spinlock_t lock;
    /* watermark数组持有该区的最小值、最低和最高水位值,内核使用水位为每个内存区设置合适的内存消耗基准 */
    unsigned long watermark[NR_WMARK];
    /* name域是一个以NULL结束的字符串表示这个区的名字,它在内核启动期间被初始化 */
    const char *name;
    ...
}

12.2 内核管理内存的接口:以页为单位

12.2.1 如何获得页

内核提供了一种请求内存的底层机制,并提供了对它进行访问的几个接口。所有接口都以页为单位分配内存,核心函数是(<linux/gfp.h>):

static inline struct page *__alloc_pages(gfp_t gfp_mask, unsigned int order,
		struct zonelist *zonelist)
{
	return __alloc_pages_nodemask(gfp_mask, order, zonelist, NULL);
}

该函数分配2order(1 << order)个连续的物理页,并返回一个指向第一个页的page结构体的指针;如果出错,则返回NULL。

其他的页分配方法列表如下:

标志描述
alloc_page(gfp_mask)只分配一页,返回指向页结构的指针
alloc_pages(gfp_mask, order)分配2^order个页,返回指向第一页页结构的指针
__get_free_page(gfp_mask)只分配一页,返回指向其逻辑地址的指针
__get_free_pages(gfp_mask, order)分配2^order个页,返回指向第一页逻辑地址的指针
get_zeroed_page(gfp_mask)只分配一页,同时让内容填充为0(报障系统安全),返回指向其逻辑地址的指针

12.2.2 如何释放页

我们可以根据需要,调用不同的函数来释放页:

void __free_pages(struct page *page, unsigned int order)	// 根据指针进行删除
void free_pages(unsigned long addr, unsigned int order)		// 根据逻辑地址进行删除(多页)
void free_page(unsigned long addr)						  // 根据逻辑地址进行删除(单页)

需要注意的是,释放页的时候需要谨慎,如果传递了错误的page指针、地址或者order值,都可能导致系统的崩溃。

12.3 内核管理内存的接口:以字节为单位

12.3.1 如何分配字节单位的内存:kmalloc()

如果需要以字节为单位分配内存,内核提供的是kmalloc()接口(<linux/slab.h>):

void * kmalloc(size_t size,gfp_t flags)

它与malloc()函数非常类似,唯一的区别是多了一个flags参数,该函数返回一个指向内存块的指针,该内存在物理上是连续的,如果出错,则返回NULL。

与malloc()类似,在调用之后,我们必须检查返回的是不是NULL,如果是,要适当地处理错误,举例如下:

struct node *p;

p = kmalloc(sizeof(struct node), GFP_KERNEL);
if (!p) {
	/* 处理错误…… */
}

12.3.2 如何释放字节单位的内存:kfree()

正如malloc()与free()对应,kmalloc()与kfree()也一一对应,而在使用kfree()时,也需要注意,必须释放的是kmalloc()分配的内存,如果想要释放的内存已经被释放了,或者释放了属于内核其他部分的内存,这同样会导致相当严重的后果。

void kfree(const void *ptr)

12.3.3 gfp_mask标志

在前面,我们注意到在各种分配函数中,都出现了分配器标志,这些标志可以分成三类:

  • 行为修饰符:表示内核应当如何分配所需内存;
  • 区修饰符:表示从哪儿分配内存(从前面介绍的四个区中选择);
  • 类型:组合了行为修饰符和区修饰符,所以指定一个类型标志就可以了。

而在实际使用中,绝大多数情况都是使用GFP_KERNEL或者GFP_ATOMIC,二者的描述如下:

  • GFP_ATOMIC:用在中断处理程序、下半部、持有自旋锁以及其他不能睡眠的地方;
  • GFP_KERNEL:一种尽力而为的常规分配方式,常用于获得调用者所需的内存。

12.3.4 更接近malloc()的分配方式:vmalloc()

内核还提供了一种分配方式,vmalloc(),它相对于kmalloc(),更接近于malloc()的分配方式,因为它分配的内存虚拟地址是连续的,而物理地址则无需连续。同样,它有一个对应的释放函数vfree()(mm/vmalloc.c):

void *vmalloc(unsigned long size)
void vfree(const void *)

实际上使用的更多的还是kmalloc(),这是出于性能的考虑,因为vmalloc()分配的空间在物理上是不连续的,所以必须对这些页进行一对一地映射,这会导致比直接内存映射大得多的TLB抖动。因此,vmalloc()仅在不得已的时候使用,一般是为了获得大块内存的时候。

12.4 对空闲链表的改进:slab层

空闲链表是一个包含可供使用的、和以及分配好的数据结构块。当代码需要一个新的数据结构实例是,就可以从空闲链表中抓取一个,而不需要分配内存,再把数据放进去。当不需要这个数据结构的实例时,再把它放回空闲链表。而不是释放它。这使得空闲链表相当于对象的高速缓存,即快速地存储频繁使用的对象类型。

但是内核,空闲链表存在一个问题,那就是不能进行全局控制。为了弥补这一缺陷,使代码更加健壮,Linux内核提供了slab层。

12.4.1 slab层的设计思路

可以把slab层划分为三级结构,高速缓存——slab——对象,上层对应多个下层:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FMqRTsMa-1655262639950)(C:\Users\ASUS\AppData\Roaming\Typora\typora-user-images\image-20220615110942852.png)]

slab描述符struct slab用来描述slab(mm/slab.c):

/*
 * struct slab
 *
 * Manages the objs in a slab. Placed either at the beginning of mem allocated
 * for a slab, or allocated from an general cache.
 * Slabs are chained into three list: fully used, partial, fully free slabs.
 */
struct slab {
	struct list_head list;	/* 满、部分满或空链表 */
	unsigned long colouroff;	/* slab着色的偏移量 */
	void *s_mem;		/* including colour offset */
	unsigned int inuse;	/* num of objs active in slab */
	kmem_bufctl_t free;	/* slab中第一个空闲对象 */
	unsigned short nodeid;
};

slab描述符要么在slab之外另外分配,要么就放在slab自身开始的地方。如果slab狠下,或者slab内部有足够的空间容纳slab描述符,那么描述符就存放在slab里面。

slab分配器可以创建新的slab,创建函数如下:

/*
 * Interface to system's page allocator. No need to hold the cache-lock.
 *
 * If we requested dmaable memory, we will get it. Even if we
 * did not request dmaable memory, we might get it, but that
 * would be relatively rare and ignorable.
 */
// 第一个参数指向高速缓存
// 第二个参数是传给__get_free_pages()的标志
static void *kmem_getpages(struct kmem_cache *cachep, gfp_t flags, int nodeid)
{
	struct page *page;
	int nr_pages;
	int i;

#ifndef CONFIG_MMU
	/*
	 * Nommu uses slab's for process anonymous memory allocations, and thus
	 * requires __GFP_COMP to properly refcount higher order allocations
	 */
	flags |= __GFP_COMP;
#endif
	// 把高速缓存需要的缺省标志加到flags参数上
	flags |= cachep->gfpflags;
	if (cachep->flags & SLAB_RECLAIM_ACCOUNT)
		flags |= __GFP_RECLAIMABLE;

    // 通过alloc_pages_exact_node为告诉缓存分配足够的内存
	page = alloc_pages_exact_node(nodeid, flags | __GFP_NOTRACK, cachep->gfporder);
	if (!page)
		return NULL;

	nr_pages = (1 << cachep->gfporder);
	if (cachep->flags & SLAB_RECLAIM_ACCOUNT)
		add_zone_page_state(page_zone(page),
			NR_SLAB_RECLAIMABLE, nr_pages);
	else
		add_zone_page_state(page_zone(page),
			NR_SLAB_UNRECLAIMABLE, nr_pages);
	for (i = 0; i < nr_pages; i++)
		__SetPageSlab(page + i);

	if (kmemcheck_enabled && !(cachep->flags & SLAB_NOTRACK)) {
		kmemcheck_alloc_shadow(page, cachep->gfporder, flags, nodeid);

		if (cachep->ctor)
			kmemcheck_mark_uninitialized_pages(page, nr_pages);
		else
			kmemcheck_mark_unallocated_pages(page, nr_pages);
	}

	return page_address(page);
}

忽略掉与NUMA相关的代码,核心的函数如下:

// 第一个参数指向高速缓存
// 第二个参数是传给__get_free_pages()的标志
static void *kmem_getpages(struct kmem_cache *cachep, gfp_t flags, int nodeid)
{
	struct page *page;
	/*
	 * Nommu uses slab's for process anonymous memory allocations, and thus
	 * requires __GFP_COMP to properly refcount higher order allocations
	 */
	flags |= __GFP_COMP;
	// 把高速缓存需要的缺省标志加到flags参数上
	flags |= cachep->gfpflags;
	if (cachep->flags & SLAB_RECLAIM_ACCOUNT)
		flags |= __GFP_RECLAIMABLE;

    // 通过alloc_pages_exact_node为告诉缓存分配足够的内存
	page = alloc_pages_exact_node(nodeid, flags | __GFP_NOTRACK, cachep->gfporder);
	if (!page)	// 无效页,直接返回NULL
		return NULL;
	...
	return page_address(page);
}

当然,slab层的关键就是避免频繁分配和释放页,所以slab层只会在极少数的情况下执行上述操作。

12.4.2 slab分配器的接口

一个新的高速缓存通过以下函数创建:

struct kmem_cache * kmem_cache_create {
        const char *name,           // 高速缓存名称
        size_t size,                // 每个元素大小
        size_t align,               // 第一个对象的偏移,确保页内对齐
        unsigned long flags,        // 可选设置项
        void (*ctor)(void *);      // 构造函数,一般为null
}

同样的,在创建了一个高速缓存之后,我们也可以通过下面这对函数进行高速缓存对象的分配以及释放:

/**
 * kmem_cache_alloc - Allocate an object
 * @cachep: The cache to allocate from.
 * @flags: See kmalloc().
 *
 * Allocate an object from this cache.  The flags are only relevant
 * if the cache has no available objects.
 */
void *kmem_cache_alloc(struct kmem_cache *cachep, gfp_t flags)
{
	void *ret = __cache_alloc(cachep, flags, __builtin_return_address(0));

	trace_kmem_cache_alloc(_RET_IP_, ret,
			       obj_size(cachep), cachep->buffer_size, flags);

	return ret;
}

/**
 * kmem_cache_free - Deallocate an object
 * @cachep: The cache the allocation was from.
 * @objp: The previously allocated object.
 *
 * Free an object which was previously allocated from this
 * cache.
 */
void kmem_cache_free(struct kmem_cache *cachep, void *objp)
{
	unsigned long flags;

	local_irq_save(flags);
	debug_check_no_locks_freed(objp, obj_size(cachep));
	if (!(cachep->flags & SLAB_DEBUG_OBJECTS))
		debug_check_no_obj_freed(objp, obj_size(cachep));
	__cache_free(cachep, objp);
	local_irq_restore(flags);

	trace_kmem_cache_free(_RET_IP_, objp);
}

12.5 内存管理中存在的问题与解决方案

12.5.1 问题1:内核栈的静态分配不足

不同于用户空间的内存分配,内存的静态分配在内核中显得比较困难,因为内核栈很小,而且固定。栈溢出时悄无声息,但势必会引起严重的问题,因为内核对于栈的管理能力较弱,一旦溢出,就有可能覆盖掉紧邻堆栈末端的东西。

解决这个问题,主要有以下两种措施:

  • 给每个进程分配一个固定大小的小栈,这样不但可以减少内存的消耗,而且内核也无须负担太重的栈管理任务。

  • 减少静态分配,多采用动态分配。

12.5.2 问题2:高端内存页无法永久映射

根据定义,高端内存中的页不能永久地映射到内核地址空间上。因此,通过alloc_pages()函数以__GFP_HIGHMEM标志获得的页不会获得逻辑地址。

12.5.2.1 永久映射

要映射一个给定的page结构到内核地址空间,可以使用kmap()(<linux/highmem.h>),和它对应的解除映射的函数为kunmap(),因为允许永久映射的数量有限,所以在使用过程我们应该及时解除映射:

static inline void *kmap(struct page *page)
{
	might_sleep();
	return page_address(page);
}

static inline void kunmap(struct page *page)
{
}
12.5.2.2 临时映射

kmap()函数因为可以睡眠,所以只能用在进程上下文中。

当必须创建一个映射而当前上下文又不能睡眠时,内核提供了临时映射,也叫原子映射:

static inline void *kmap_atomic(struct page *page, enum km_type idx)
{
	pagefault_disable();	// 使页错误失效,保证操作的原子性
	return page_address(page);
}

参数type是下列枚举类型之一,这些枚举类型描述了临时映射的目的(<asm/kmap_types.h>):

// enum类型指示了建立临时映射的目的
enum km_type {
KMAP_D(0)	KM_BOUNCE_READ,
KMAP_D(1)	KM_SKB_SUNRPC_DATA,
KMAP_D(2)	KM_SKB_DATA_SOFTIRQ,
KMAP_D(3)	KM_USER0,
KMAP_D(4)	KM_USER1,
KMAP_D(5)	KM_BIO_SRC_IRQ,
KMAP_D(6)	KM_BIO_DST_IRQ,
KMAP_D(7)	KM_PTE0,
KMAP_D(8)	KM_PTE1,
KMAP_D(9)	KM_IRQ0,
KMAP_D(10)	KM_IRQ1,
KMAP_D(11)	KM_SOFTIRQ0,
KMAP_D(12)	KM_SOFTIRQ1,
KMAP_D(13)	KM_SYNC_ICACHE,
KMAP_D(14)	KM_SYNC_DCACHE,
/* UML specific, for copy_*_user - used in do_op_one_page */
KMAP_D(15)	KM_UML_USERCOPY,
KMAP_D(16)	KM_IRQ_PTE,
KMAP_D(17)	KM_NMI,
KMAP_D(18)	KM_NMI_PTE,
KMAP_D(19)	KM_TYPE_NR
};

这个函数不会阻塞,因此可以用在中断上下文和其他不能重新调度的地方。

12.5.3 问题3:CPU数据的分配问题

对于多处理机的操作系统,不同的处理机访问内存页的数据是唯一的,而在访问过程中有可能出现互斥和抢占的问题。

为了方便创建和操作每个CPU数据,内核采用了percpu的操作接口,这简化了创建和操作每个CPU的数据。

在运行时实现对每个CPU变量的动态分配(<linux/percpu.c>):

/**
 * __alloc_percpu - allocate dynamic percpu area
 * @size: size of area to allocate in bytes
 * @align: alignment of area (max PAGE_SIZE)
 *
 * Allocate percpu area of @size bytes aligned at @align.  Might
 * sleep.  Might trigger writeouts.
 *
 * CONTEXT:
 * Does GFP_KERNEL allocation.
 *
 * RETURNS:
 * Percpu pointer to the allocated area on success, NULL on failure.
 */
// 第一个参数指分配的实际字节数
// 第二个参数指分配时要按多少字节对齐
void __percpu *__alloc_percpu(size_t size, size_t align)
{
	return pcpu_alloc(size, align, false);
}
EXPORT_SYMBOL_GPL(__alloc_percpu);

同样的,可以相应的调用free_percpu()来释放处理器上指定的percpu数据。

__alloc_percpu()返回了一个指针,通过对指针进行操作,可以进一步修改处理器的数据:

void *percpu_ptr;
unsigned long *foo;

percpu_ptr = alloc_percpu(unsigned long);
if (!percpu_ptr)
	/* 内存分配错误 */

foo = get_cpu_var(percpu_ptr);	// 返回一个指向处理器的指针的拷贝
/* 操作foo ... */
put_cpu_var(percpu_ptr);	// 完成操作:重新激活内核抢占

get_cpu_var()和put_cpu_var()是两个宏,其中get_cpu_var()会禁止内核抢占;而put_cpu_var()会重新激活内核抢占。

Linux内核内存管理总结 微信公众号:奔跑吧linux社区 加奔跑吧微信群请先加微信:runninglinuxkernel欢迎订阅奔跑吧配套旗舰篇视频节目:https://weidian.com/?userid=1352872851 这篇对《奔跑吧Linux内核》第一版的内存管理 做了很棒的笔记和总结。《奔跑吧Linux内核》第一版各大书店有售。 这篇文章作者是:Arnold Lu@南京 原文:https://www.cnblogs.com/arnoldlu/p/8051674.html?from=groupmessage . 阅读详情

相关推荐

Linux内核5.0版本五大模块及内核书籍推荐

时间已经来到8月份,现在还在迷茫的同学真的要好好想一下今年的秋招了。这里就给大家分享一份嵌入式岗位学习Linux内核的最强攻略,突击一下吧,少年!

weixin_71478434的博客 3274

深入分析Linux内核源代码6-Linux 内存管理(1)

每天十五分钟,熟读一个技术点,水滴石穿,一切只为渴望更优秀的你! ————零声学院 存储器是一种必须仔细管理的重要资源。在理想的情况下,每个程序员都喜欢无穷大、 快速并且内容不易变(即掉电后内容不会丢失)的存储器,同时又希望它是廉价的。但不幸 的是,当前技术没有能够提供这样的存储器,因此大部分的计算机都有一个存储器层次结构, 即少量、快速、昂贵、易变的高速缓存(cache);若干兆字节的中等速度、中等价格、易变 的主存储器(RAM);数百兆或数千兆的低速、廉价、不易变的磁盘。如图 6.1 所示,这些资 源.

资料qun832218493 3024

Linux 4.19内核中的内存管理:x86_64架构下的实现与源码解析

通过上述文件和模块,Linux 内核在 x86_64 架构下实现了高效的内存管理,支持多任务、多用户的复杂操作系统功能。内存管理是一个广泛且复杂的子系统,其多个方面在内核源码的不同文件中得以实现。理解这些文件有助于深入掌握内核如何管理系统中的内存资源。在 Debian 10 所使用的 Linux 4.19 内核中,这些机制的实现不仅保证了系统的高性能和稳定性,还为开发者提供了丰富的工具和接口,用于优化和调试内存使用。

数字人生 879

Linux源码剖析——物理内存管理

Linux的内存管理可分为物理内存管理和虚拟内存管理。所谓的虚拟内存就是指程序中使用的地址(32位系统可以使用4GB的虚拟内存地址),而物理地址是指计算机上真实安装的内存(例如我们安装了一条256MB的内存条,那么物理内存就只有256MB,但虚拟内存地址依然有4GB)。Linux内核需要通过一个映射关系才能把虚拟地址转换成物理地址,下面就来介绍Linux内存管理的细节。

白话机器学习 439

深入Linux 0.11内核:源码分析与下载

简介:Linux 0.11作为Linux发展史上的关键版本,展示了从个人项目到全球开源项目的转变。该版本内核具备操作系统的基本功能,如进程管理、内存管理、设备驱动、文件系统和网络支持。详细探讨了这些核心组件的源码构成及工作原理,以及如何编译和构建Linux 0.11源码。通过学习这个早期版本的Linux,可以获得操作系统原理和内核发展历史的深入理解。

weixin_33298352的博客 1159

Linux内核深度解析与实战指南

Linux内核包括SELinux、AppArmor等安全模块,以及内核完整性保护机制如KASAN和SMAP。

weixin_36483050的博客 873

Linux内存管理源码剖析(三)

从fork系统调用开始讲述小块内存分配机制的原理 。同时简单介绍了分配小块内存的slub allocator技术如何维护一个缓存块,即当空间足够时如何管理,不够时如何申请。

Tryturned 463

NVIDIA Jetson Orin Nano Super刷机避坑指南:从虚拟机配置到NVMe烧录实战

本文详细介绍了NVIDIA Jetson Orin Nano Super的完整刷机流程,涵盖虚拟机配置、SDK Manager安装、硬件连接、固件下载、rpcbind服务冲突解决以及NVMe烧录等关键步骤。重点解析了Super模式下的NVMe烧录命令参数和操作时机,帮助开发者高效完成刷机并避免常见错误。

y2z3a4b5c的博客 752

Linux内存管理和源码分析——概述

对Linux的内存进行一个整体的介绍,为后面的分析打基础。

fpcc的专栏 860

VMware Workstation 17 Pro 保姆级教程:手把手教你安装银河麒麟 Kylin V10 SP3 国产系统

本文提供VMware Workstation 17 Pro安装银河麒麟Kylin V10 SP3的详细教程,涵盖环境准备、虚拟机配置、系统安装及优化全流程。通过分步指导和实用技巧,帮助用户高效搭建国产操作系统开发测试环境,特别适合技术自主可控需求场景下的虚拟化实践。

weixin_30879169的博客 645

简话 RAG 本地部署(DeepSeek + RAGFlow)

RAG = DeepSeek + OLlama + Docker + RAGFlow + Windows 11 简话 RAG 本地部署

捉虫客 de 博客 1万+

Linux的内存管理

转载请注明出处,并保留以上所有对文章内容、图片、表格的来源的描述。 Linux的内存管理 Linux的内存管理是一个非常复杂的过程,主要分成两个大的部分:内核的内存管理和进程虚拟内存。内核的内存管理是Linux内存管理的核心,所以我们先对内核的内存管理进行简介。 一、物理内存模型 物理内存模型主要分为两种:UMA(Uniform Memory Acce...

weixin_30604651的博客 642

初学者入门YOLO目标检测:从懵圈到上手,这篇文章讲透所有核心(附学习路径+避坑指南)

目标:比如训练一个“检测苹果和橘子”的模型,或者“检测人脸”的模型;任务:学会用LabelImg标注数据,制作YOLO格式的数据集,用命令行训练模型;关键:不用追求高精度,能训练出一个能检测出目标的模型就行。很多初学者卡在入门阶段,不是因为智商不够,而是因为“想太多,做太少”——总觉得要把所有原理搞懂才能动手,结果越想越怕,最后放弃。先动手,再动脑。先跑通一个案例,获得成就感;再制作自定义数据集,训练简单模型;最后再回头去理解原理,优化模型。

专注于Python爬虫开发,分享爬虫技巧、项目实战与反爬经验,使用Scrapy、BeautifulSoup等工具,解决数据抓取难题。 690

内存管理(Linux内核源码分析)

背景本篇博客试图通过linux内核源码分析linux的内存管理机制,并且对比内核提供的几个分配内存的接口函数。然后聊下slab层的用法以及接口函数。内核分配内存与用户态分配内存内核分配内存与用户态分配内存显然是不同的,内核不可以像用户态那样奢侈的使用内存,内核使用内存一定是谨小慎微的。并且,在用户态如果出现内存溢出因为有内存保护机制,可能只是一个报错或警告,而在内核态若出现内存溢出后果就会严重的多(

hty46565的博客 1万+

【Spring Cloud Alibaba】Linux安装RocketMQ以及RocketMQ Dashboard可视化工具

在当今分布式架构的开发中,消息队列成为了一种常见的解决方案。RocketMQ是阿里巴巴开源的分布式消息中间件,具有高吞吐量、高可用性和强大的扩展性,因此在构建大规模、高可靠性的应用系统时备受推崇。而在使用RocketMQ时,为了更好地了解消息的流动和监控,RocketMQ Dashboard成为了一个不可或缺的可视化工具。本篇博客将带大家一步步在Linux系统中安装RocketMQ,并介绍如何配置和使用RocketMQ Dashboard。

编码人生 3383

第 2 章:PassThrough 直通滤波

通过设置某个维度(如 x、y、z 或 rgb)的取值范围,仅保留该范围内的点,从而达到**点云裁剪(裁边)**或过滤异常值的目的。所有在该范围之外的点会被移除(可选是否保留);点云中包含背景、地面或传感器噪声,需要过滤;通过 z 轴过滤,只保留桌面上方的物体;多轴组合过滤(如同时过滤 x、y、z);可用于地面剔除、车载点云预处理等场景。不支持一次过滤多个字段,可多次调用;仅处理某一层或某一区域的点云数据。原始点云中包含地面、墙面、背景等;在区域裁剪、前景提取中广泛使用;设定一个轴(如z轴);

yumeiguo的博客 337

Linux内核手帐之内存管理源码分析(一)

最近再阅读LINUX内核内存部分源码,边读便记录,就当知识积累了。(这里分析源码版本是linux 5.7-rc4) 在linux源码中内存无论是以UMA还是NUMA的计算机类型,都是以【NODE->ZONE->PAGE】的数据结构形式管理。这篇文章主要分享的是NODE数据结构中一个重要的组成元素zones[MAX_NR_ZONES]的大小,话不多说,先附上相关部分...

Little_Baby24的博客 1033

第五章 TTM 后备管理:5.6系统内存域

系统内存域由 TTM 内置资源管理器管理,通过 ttm_tt 承载页信息,依赖 ttm_pool 提供物理页。ttm_tt 管理页数组、缓存属性与状态标志,支持外部页与换出机制。初始化分 ttm_tt_init 与 ttm_sg_tt_init,生命周期包括创建、填充、释放与销毁。页池针对 write-combined、uncached 及一致性 DMA 分配的页进行缓存,提升性能并减少 TLB 刷新。分配时高阶优先、批量设置缓存属性,回收时清零后归还池中,确保高效复用。

图形/计算GPU的驱动与虚拟化技术分享园地 2567

linux内核源码1.1进程管理和内存管理

linux内核源码进程管理和内存管理 我报一个班学习linux内核源码, 经过学习我觉得有必要整理一哈我学的知识点。 如果你有兴趣一起学习,可以找我为你推荐,老学员推荐新学员,新学员有折扣,老学员也会获得对应的福利, 加我QQ哈:85289765. 我准备用三个博文来书写,每天写一个博文,正好三天可以更新完成。 ...

tan1666的博客 1446

【反封禁策略】集成 Undetected-Chromedriver:让 Browser-Use 绕过 Cloudflare 五秒盾

摘要:AI Agent如何突破Cloudflare检测的实战指南 本文针对AI Agent在Cloudflare检测下频繁受阻的问题,深入分析了undetected-chromedriver的工作原理与局限性。文章指出,Cloudflare 2026年的检测体系已升级为"浏览器指纹+IP信誉+协议指纹+行为模式"的多维交叉验证,单一伪装方案难以奏效。 核心内容包括: undetected-chromedriver的三层补丁体系解析 v2与v3版本的适用场景对比 两种Browser-Use集成方案:Selen

ZDQ58818的博客 284

ssm框架搭建

这个是一个在idea上成功跑起来的ssm框架项目,保存在这里方便以后使用直接下载

[Linux源码分析]Linux内核架构

1、Linux内核在整个操作系统中的地位:Linux操作系统由四大组成部分组成(四层依赖关系–>上层依赖下层) 用户进程:用户应用程序是运行在Linux操作系统最高层的软件集合。 系统调用接口:系统调用时运行在内核态,或者叫系统态,一般情况运行在用户态。系统调用时内核代码的一部分。 Linux内核:内核是整个操作系统的灵魂。它负责管理磁盘文件、内存、负责启动运行程序,负责在网络上接受和发送数据包等。 硬件设备:Linux安装时需要所有可能的物理设备,例如:CPU、硬盘、网络硬件。 2.Linux内核的作..

「鸿渐之翼」的博客 3362

计算机控制技术及应用

《计算机控制技术及应用》通过总结作者的科研成果和吸收国内外的先进理论、方法和技术,以计算机控制系统的网络化、开放化、智能化和集成化发展趋势为主线索,主要介绍计算机控制系统的基本概念和计算机控制系统的典型形式,计算机控制系统的数学描述与基本分析方法,以及数字PID控制器、最少拍控制系统、达林算法、状态变量设计法等设计方法及其各种改进算法。重点突出计算机控制系统输入输出通道接口的设计方法和典型控制系统的设计案例与实现方法等。

Linux内核源代码分析一(Linux0.12)

Linux 内核主要由 5 个模块构成,它们分别是:进程调度模块、内存管理模块、文件系统模块、进程间通信模块和网络接口模块。首先说一下pc机的启动流程,在加电后,80X86会先进入实模式并进入地址0XFFFF0开始自动执行代码,这个地址一般带表了BIOS的代码地址,PC的BIOS将会执行硬件检测和诊断功能,并在0地址进行中断向量的初始化,最后BIOS会将启动设备的第一个扇区读入地址0X7C00位置,并从这个位置开始执行代码。boot目录下包含三个汇编代码文件bootsect文件是在记录在磁盘的第一个扇区中,

qq_43147121的博客 3023

文档:亲测可用解决vnc灰屏,黑屏,鼠标大黑叉情况

主要解决vnc-viewer灰屏,黑屏,鼠标大黑叉等现象,详细见文档。

win10 跳过U8环境检测的IE WebControls.rar

win10 安装u8的时候 提示IE WebControls 不通过,这个工具可以欺骗u8的检查。 跳过U8环境检测的IE WebControls。提示导入失败后,重新检测通过。

[Linux源码分析]内存管理

页page(是内核空间管理基本单位)源代码分析如下:/include/linux/mm_types.h 内存管理单元(MMU,把虚拟地址转换为物理地址的硬件设备)通常是以页为单位处理。内核用struct page结构体表示每个物理页,struct page占用40个字节。 2. 区(zone,内核把页划分在不同的区) 共记3个区: ZONE_DMA(DMA使用的页,物理内存...

「鸿渐之翼」的博客 3201

Linux内核学习路线 ,应从入门到深入,学内核就来零声教育

学习linux内核,这个可不像学一门语言,c或者java一个月或者3月你就能精通掌握。学习linux内核是需要一步一步循序渐进,掌握正确的linux内核学习路线对学习至关重要,本篇文章就来分享学习linux内核的一些建议吧。 1. 了解操作系统基本概念。如果不会,可以学习《操作系统:设计与实现》Andrew S.Tanenbaum 写的那本。以MINIX为例子讲解操作系统的概念。非常推荐。 2. 有了操作系统的基本概念以后,可以了解Linux的机制了。推荐《Linux内核设计与实现》Robert Lov

csbmww的博客 5740

Linux内核移植(源码分析、配置与编译)

Linux内核移植(源码分析、配置与编译)

qq_52479948的博客 3005

Linux内核内存管理源码分析之init-mm.c(2)

Linux内核内存管理源码分析之init-mm.c(2)

phmatthaus的专栏 1025
上一篇: 365天挑战LeetCode1000题——Day 016 旋转数组
下一篇: 365天挑战LeetCode1000题——Day 017 动态规划 15
ShowM3TheCode
博客等级 码龄5年 29粉丝 173原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值