当前位置: 首页 > news >正文

Linux 内存管理之page cache

文章目录

  • 一、page cache
    • 1.1 File-backed pages和Anonymous pages
    • 1.2 page cache/slab cache
    • 1.3 读/写路径
    • 1.4 脏页回写
    • 1.5 drop_caches
    • 1.6 时间局部性与空间局部性
    • 1.7 Page Cache 的两种类型
    • 1.8 关键数据结构
  • 二、Page Cache 的产生
    • 2.1 Buffered I/O(标准 I/O)
    • 2.2 Memory-Mapped I/O(mmap)
  • 三、page cache回收
    • 3.1 回收模式
      • zone:内存回收的基本单位
    • 3.2 回收过程
      • 3.2.1页面状态转换机制:晋升(Promotion)
      • 3.2.2页面状态转换机制:降级(Demotion)
    • 3.3 ANON/FILE LRU 链表
  • 参考资料

一、page cache

Page Cache(页缓存) 是 Linux 内核中用于缓存文件数据(包括普通文件、块设备文件等)的核心机制,它以内存页(通常为 4KB)为单位,将磁盘中的文件数据存储在物理内存中,从而减少对磁盘的 I/O 访问,提升系统整体性能。

对磁盘的数据进行缓存从而提高性能主要是基于两个因素:
(1)磁盘访问的速度比内存慢好几个数量级(毫秒和纳秒的差距)
(2)被访问过的数据,有很大概率会被再次访问。
如下图所示:
在这里插入图片描述

1.1 File-backed pages和Anonymous pages

并不是所有 page 都被组织为 Page Cache。Linux 系统上供用户可访问的内存分为两个类型,即:File-backed pages和Anonymous pages。

// v5.15/source/include/linux/mm_types.hstruct page {unsigned long flags;		/* Atomic flags, some possibly* updated asynchronously *//** Five words (20/40 bytes) are available in this union.* WARNING: bit 0 of the first word is used for PageTail(). That* means the other users of this union MUST NOT use the bit to* avoid collision and false-positive PageTail().*/union {struct {	/* Page cache and anonymous pages *//*** @lru: Pageout list, eg. active_list protected by* lruvec->lru_lock.  Sometimes used as a generic list* by the page owner.*/struct list_head lru;/* See page-flags.h for PAGE_MAPPING_FLAGS */struct address_space *mapping;pgoff_t index;		/* Our offset within mapping. *//*** @private: Mapping-private opaque data.* Usually used for buffer_heads if PagePrivate.* Used for swp_entry_t if PageSwapCache.* Indicates order in the buddy system if PageBuddy.*/unsigned long private;};......
}

(1)File-backed pages:文件备份页也就是 Page Cache 中的 page,对应于磁盘上的若干数据块;对于这些页最大的问题是脏页回盘;
关联磁盘文件:内容对应于磁盘上的文件(如程序.text段等)。
缓存机制:属于 Page Cache 的一部分,由内核自动缓存以加速文件访问。

struct page中mapping字段最低位为 0 表示文件页。mapping指向该文件页关联文件的struct address_space(被文件的 inode 所持有),pgoff_t index字段表示该文件页在struct address_space中的索引。内核会通过 index 字段从 struct address_space 中查找该文件页。

File-backed pages(Page Cache)的内存回收代价较低。Page Cache 通常对应于一个文件上的若干顺序块,因此可以通过顺序 I/O 的方式落盘。另一方面,如果 Page Cache 上没有进行写操作(所谓的没有脏页),甚至不会将 Page Cache 回盘,因为数据的内容完全可以通过再次读取磁盘文件得到。

(2)Anonymous pages:匿名页不对应磁盘上的任何磁盘数据块,它们是进程的运行时内存空间(例如进程的堆,栈空间等属性);
无磁盘关联:存储进程运行时动态分配的数据(如堆、栈、共享内存等),没有对应的磁盘文件,内容仅存在于内存。

struct page中mapping字段最低位为 1 表示匿名页。mapping指向该匿名页在进程虚拟内存空间中唯一对应的匿名映射区 struct anon_vma 结构体,用于物理内存到虚拟内存的反向映射。

// v5.15/source/include/linux/page-flags.h#define PAGE_MAPPING_ANON	0x1static __always_inline int PageAnon(struct page *page)
{page = compound_head(page);return ((unsigned long)page->mapping & PAGE_MAPPING_ANON) != 0;
}

Anonymous pages 的内存回收代价较高。这是因为 Anonymous pages 通常随机地写入持久化交换设备。另一方面,无论是否有更操作,为了确保数据不丢失,Anonymous pages 在 swap 时必须持久化到磁盘。

Anonymous pages不属于Page Cache。

在这里插入图片描述
swappiness是 Linux 内核控制 匿名页(Anonymous Pages) 与 文件缓存(File-backed Pages) 回收平衡的关键参数。

$ cat /proc/sys/vm/swappiness
60

定义:控制内核在内存不足时,优先回收文件缓存还是将匿名页换出到 Swap。
取值范围:0(禁用 Swap 倾向)到 100(积极使用 Swap,linux 5.8内核版本最大值改为200)。
默认值:60(大多数发行版的默认设置)。

// v5.8/source/mm/vmscan.c/** From 0 .. 200.  Higher means more swappy.*/
int vm_swappiness = 60;

Linux内存回收业务当中更加偏向对文件页的回收,通过增大swappiness可以提高匿名页的扫描比例,进一步促进系统回收更多的匿名页。

1.2 page cache/slab cache

page cache 和 slab cache是两种cache。
文件 = 数据 + 元数据。
page cache对应的是文件系统中的文件数据(userdata),而inode cache对应的是文件系统中文件的元数据(metadata)。
在这里插入图片描述

如下图所示:
在这里插入图片描述

page cache缓冲硬盘中的内容,dcache、icache缓存文件系统的数据。这些内容是为了提升性能而设计的,还可以再次从硬盘中重新读取来构建对象,这部分内容可以在内存紧张的时候可以直接释放。

inode 缓存则主要用于缓存文件系统的元数据,比如文件名、文件权限、文件大小、文件的创建时间和修改时间等 。当我们要访问一个文件时,首先需要通过 inode 缓存找到文件的元数据,然后才能进一步读取文件的内容 。例如,当我们在命令行中输入ls命令查看目录下的文件时,系统会先在 inode 缓存中查找该目录下所有文件的元数据,然后将这些信息显示出来。如果没有 inode 缓存,每次执行ls命令都需要从磁盘中读取这些元数据,这将大大降低系统的响应速度 。inode 缓存通过将常用的元数据存储在内存中,减少了对磁盘的访问次数,提高了文件系统的性能 。

Page Cache 一般都是用户空间的程序在使用,但是是属于内核空间的内存,dcache、icache是内核空间在使用。
用户数据 Page Cache — reclaimable memory。
内核数据 Slab — reclaimable kernel memory。

1.3 读/写路径

(1)应用程序请求读取文件数据
内核首先检查 page cache 中是否存在所需数据
命中:直接从内存返回数据(快速路径)
未命中:发起磁盘 I/O,将数据读入 page cache 后再返回

当内核发起一个读请求时(例如进程发起read()请求),首先会检查请求的数据是否缓存到了Page Cache中。
如果有,那么直接从内存中读取,不需要访问磁盘,这被称为cache命中(cache hit);
如果cache中没有请求的数据,即cache未命中(cache miss),就必须从磁盘中读取数据。然后内核将读取的数据缓存到cache中,这样后续的读请求就可以命中cache了。
page可以只缓存一个文件部分的内容,不需要把整个文件都缓存进来。

(2)应用程序写入文件数据
数据首先被写入 page cache
写入方式:
Write-back(默认):延迟写入磁盘,定期或内存压力时同步。
而在Write Back模式下,数据更新仅在缓存中进行,不会立即写入主存储器。只有当缓存中的数据需要被新数据替换时,被修改的数据才会写回主存储器。这种方法的优点是提高了CPU执行的效率,因为不需要每次都写入慢速的主存储器。但缺点是实现起来技术较为复杂,且如果在更新后的数据未被写入主存储器时系统掉电,那么数据可能会丢失。
当内核发起一个写请求时(例如进程发起write()请求),同样是直接往cache中写入,后备存储中的内容不会直接更新(当服务器出现断电关机时,存在数据丢失风险)。
内核会将被写入的page标记为dirty,并将其加入dirty list中。内核会周期性地将dirty list中的page写回到磁盘上,从而使磁盘上的数据和内存中缓存的数据一致。

Write-through:同步写入磁盘和缓存。
在Write Through模式下,每当缓存中的数据被更新时,更改同时也会写入主存储器。这种方法的优点是简单且数据一致性较好,因为缓存和主存储器中的数据始终保持同步。但缺点是性能较低,因为每次写操作都需要访问主存储器,这会导致速度变慢。例如,如果一个程序频繁地修改一个局部变量,即使其他进程或线程不需要这些数据,CPU也会频繁地在缓存和主存储器之间交换数据,造成不必要的带宽损失。

1.4 脏页回写

当满足以下三个条件之一将触发脏数据刷新到磁盘操作:
(1)dirty_expire_centisecs
数据存在的时间超过了dirty_expire_centisecs时间;

cat /proc/sys/vm/dirty_expire_centisecs
3000

这个值的单位为 百分之一秒(centiseconds),因此 3000 表示 30 秒。

定义 脏页(被修改但未写入磁盘的数据) 在内存中的最长存活时间。
超过这个时间后,内核的 flush 线程(如 kworker 或旧版 pdflush)会将这些脏页写入磁盘(即使未达到 dirty_background_ratio 或 dirty_ratio 阈值)。
适用于 Writeback(写回)缓存策略(Linux 默认的文件写入方式)。

(2)dirty_background_ratio
脏数据所占内存 > dirty_background_ratio,也就是说当脏数据所占用的内存占总内存的比例超过dirty_background_ratio的时候会触发pdflush刷新脏数据。

cat /proc/sys/vm/dirty_background_ratio
10

表示当系统内存中的 脏页(Dirty Pages) 达到总内存的 10% 时,内核会 异步 启动后台进程(如 kworker 或 flush 线程)将脏页写入磁盘,而不会阻塞应用程序的 I/O 操作。

定义 触发后台异步刷脏的内存脏页比例(单位:百分比)。
默认值 10 表示当脏页占用超过 10% 的可用内存 时,内核开始 后台写入磁盘。
不影响应用程序性能,因为刷盘是异步进行的。

(3)dirty_ratio
脏数据所占内存 > dirty_ratio,也就是说当脏数据所占用的内存占总内存的比例超过dirty_ratio的时候,内核会 强制同步 将脏页写入磁盘,阻塞 所有新的写 I/O 操作,直到脏页比例降低。

cat /proc/sys/vm/dirty_ratio
20

dirty_ratio=20 是 Linux 内核控制 同步刷脏(Sync Writeback) 的关键参数,表示当系统内存中的 脏页(Dirty Pages) 达到总内存的 20% 时,内核会 强制同步 将脏页写入磁盘,阻塞 所有新的写 I/O 操作,直到脏页比例降低。

定义 触发同步刷脏的内存脏页比例(单位:百分比)。
默认值 20 表示当脏页占用超过 20% 的可用内存 时,内核会 强制同步刷盘,导致应用程序的写入操作被阻塞。
直接影响 I/O 性能,因为刷盘是同步进行的(相比 dirty_background_ratio 的异步刷盘)。

与 dirty_background_ratio 的关系:
dirty_background_ratio=10 异步刷盘(后台线程处理) 不影响应用性能。
dirty_ratio=20 同步刷盘(阻塞应用 I/O) 可能导致写入延迟增加。

还有参数dirty_writeback_centisecs:

cat /proc/sys/vm/dirty_writeback_centisecs
500

dirty_writeback_centisecs=500 是 Linux 内核控制 脏页回写(Writeback)线程唤醒频率 的参数,单位是 百分之一秒(centiseconds),默认值 500 表示 每 5 秒 唤醒一次 flush 线程(如 kworker)检查并回写脏页到磁盘。

控制内核 定期检查脏页 的频率,决定刷盘的 及时性。

默认 500(5 秒)表示:
每隔 5 秒,内核唤醒 flush 线程检查是否有脏页需要写入磁盘。
如果发现脏页 超过 dirty_expire_centisecs(默认 30 秒),则触发回写。
注意:该参数仅控制 检查频率,实际刷盘还受 dirty_background_ratio 和 dirty_ratio 影响。

在这里插入图片描述

查看脏页情况:

# 查看当前脏页大小(KB)
cat /proc/meminfo | grep Dirty# 查看刷盘线程状态
grep -E 'dirty|writeback' /proc/vmstat# 查看当前刷盘策略
sysctl -a | grep dirty
$ cat /proc/meminfo | grep Dirty
Dirty:                 8 kB$ grep -E 'dirty|writeback' /proc/vmstat
nr_dirty 2
nr_writeback 0
nr_writeback_temp 0
nr_dirty_threshold 3056636
nr_dirty_background_threshold 1526452$ sudo /sbin/sysctl -a | grep dirty
vm.dirty_background_bytes = 0
vm.dirty_background_ratio = 10
vm.dirty_bytes = 0
vm.dirty_expire_centisecs = 3000
vm.dirty_ratio = 20
vm.dirty_writeback_centisecs = 500
vm.dirtytime_expire_seconds = 43200

1.5 drop_caches

/proc/sys/vm/drop_caches 是一个用于 手动清理 Linux 内核缓存 的特殊文件,可以通过写入不同的值来释放 Page Cache、Slab 缓存等。

To free pagecache:echo 1 > /proc/sys/vm/drop_caches
To free reclaimable slab objects (includes dentries and inodes):echo 2 > /proc/sys/vm/drop_caches
To free slab objects and pagecache:echo 3 > /proc/sys/vm/drop_caches

在这里插入图片描述
仅在调试/测试时使用,生产环境优先依赖内核自动管理。
如需使用,务必先执行 sync 并评估性能影响。
这是一个调试工具,而非生产环境调优手段。内核自动回收机制比手动操作高效得多。

注意:比如使用 echo 2 > /proc/sys/vm/drop_caches reclaimable slab objects,回收了 inode slab obeject,那么 inode 对应的Page Cache也都会被回收掉,所以如果业务进程读取的文件对应的inode被回收了,那么该文件所有的Page Cache都会被释放掉。

进程会通过inode来找到文件的地址空间(address_space),然后结合文件偏移(会转换成page index)来找具体的Page。如果该Page存在,那就说明文件内容已经被读取到了内存;如果该Page不存在那就说明不在内存中,需要到磁盘中去读取。你可以理解为inode是Pagecache Page(页缓存的页)的宿主(host),如果inode不存在了,那么PageCache Page也就不存在了。

1.6 时间局部性与空间局部性

(1)时间局部性与 Page Cache
原理
定义:最近访问的数据很可能在短期内被再次访问。

Page Cache 实现:
内核保留最近访问的磁盘数据在内存中,后续重复访问可直接命中缓存。
LRU(最近最少使用)算法管理缓存回收,优先保留热点数据。

(2)空间局部性与预读(Readahead)
原理
定义:访问某一数据时,其相邻数据很可能被连续访问。

Page Cache 实现:
预读算法:检测到顺序访问模式时,异步提前读取后续磁盘块(如 readahead(2) 系统调用)。
预读窗口动态调整:根据应用访问模式自适应扩展/收缩。

1.7 Page Cache 的两种类型

(1) Discardable Pages(可丢弃页)
对应内容:只读文件段(如代码段 .text)
特点:
内容一致性:内存副本与磁盘完全一致,无修改可能。
回收代价:直接清空页表项(PTE)即可,无需 I/O 操作。
内核标记:PG_clean(无脏数据)。

(2) Syncable Pages(需同步页)
对应内容:可读写文件数据(如数据段 .data)

特点:
脏页标记:通过 SetPageDirty(page) 设置 PG_dirty 标志,同时 CPU 硬件会设置 PTE 的 Dirty 位。

回写触发:
周期性地由 flush 线程(如 kworker)扫描。
达到 dirty_expire_centisecs(默认 30 秒)或内存压力时触发。

1.8 关键数据结构

struct address_space {struct inode *host;      // 所属的inodestruct radix_tree_root page_tree; // 基数树存储page cache页// ...
};struct page {unsigned long flags;     // 页面标志位struct address_space *mapping; // 所属的address_spacepgoff_t index;          // 在文件中的偏移(页为单位)// ...
};

一个address_space管理了一个文件在内存中缓存的所有pages,这部分缓存也是页高速缓存。

每个进程打开一个文件的时候,都会生成一个表示这个文件的struct file,但是文件的struct inode只有一个,inode才是文件的唯一标识,指向address_space的指针就是内嵌在inode结构体中的。在page cache中,每个page都有对应的文件,这个文件就是这个page的owner,address_space将属于同一owner的pages联系起来,将这些pages的操作方法与文件所属的文件系统联系起来。

二、Page Cache 的产生

在 Linux 中,Page Cache 主要通过两种机制产生:Buffered I/O(标准 I/O) 和 Memory-Mapped I/O(存储映射 I/O)。
如下图所示:
在这里插入图片描述

2.1 Buffered I/O(标准 I/O)

系统调用:通过 read()/write() 等标准文件操作接口。

磁盘文件 → Page Cache → 用户态缓冲区(如 `fread()` 的 buffer)→ 应用程序

内核参与:数据需在 用户态缓冲区 和 Page Cache 之间显式拷贝。

标准I/O是写的(write(2))用户缓冲区(Userpace Page对应的内存),然后再将用户缓冲区里的数据拷贝到内核缓冲区(Pagecache Page对应的内存);如果是读的(read(2))话则是先从内核缓冲区拷贝到用户缓冲区,再从用户缓冲区读数据,也就是buffer和文件内容不存在任何映射关系。

如下图所示:
在这里插入图片描述
这个过程大致可以描述为:首先往用户缓冲区buffer(这是Userspace Page)写入数据,然后buffer中的数据拷贝到内核缓冲区(这是Pagecache Page),如果内核缓冲区中还没有这个Page,就会发生Page Fault会去分配一个Page,拷贝结束后该Pagecache Page是一个Dirty Page(脏页),然后该Dirty Page中的内容会同步到磁盘,同步到磁盘后,该Pagecache Page变为Clean Page并且继续存在系统中。

2.2 Memory-Mapped I/O(mmap)

系统调用:通过 mmap() 直接将文件映射到进程地址空间。

磁盘文件 → Page Cache → 进程虚拟内存(直接映射,无拷贝)

内核参与:通过 缺页异常(Page Fault) 按需加载数据,对应用程序透明。

对于存储映射I/O而言,则是直接将Pagecache Page给映射到用户地址空间,用户直接读写Pagecache Page中内容。

三、page cache回收

page cache回收用于在系统内存不足时释放被缓存的文件数据所占用的内存。
page cache 中的页面和 anonymous page 都是可以被回收的。

对于内存管理,我们回收的目的主要是基于用户空间进行回收,其主要回收的策略如下:
(1)用户空间内存:原则上应该都可以参与内存回收,除非那些被进程锁定(mlock())的页。
(2)内核空间内存:一般内核代码段,数据段,内核kmalloc()/vmalloc()出来的内存,内核线程占用的内存等都是不可以回收的,除此之外的内存都是我们要回收,所以大致为磁盘高速缓存(如索引节点,目录项高速缓存),页面高速缓存(访问文件时系统生成的页面cache),mmap()文件时所用的有名映射所使用的物理内存。

3.1 回收模式

在这里插入图片描述
如下如所示:
在这里插入图片描述

zone:内存回收的基本单位

在这里插入图片描述
内存回收是以zone为单位进行的,zone的内存回收,它针对三样东西进程回收:slab、lru链表中的页、buffer_head。而系统判断一个zone需不需要进行内存回收,如上面所说,为zone设置一条线,当此zone的空闲页框不足以到达这条线时,就会对此zone进行内存回收,实际上一个zone有三条线,这三条线分别是最小阀值(WMARK_MIN),低阀值(WMARK_LOW),高阀值(WMARK_HIGH),它们都保存在zone的watermark[NR_WMARK]数组中,这个数组中保存的是各个阀值要求的页框数量,而每个阀值都会对内存回收造成影响。

内核为每个物理内存区域(zone)画了三条水位线:WMARK_MIN(页最小阈值), WMARK_LOW(页低阈值)和 WMARK_HIGH(页高阈值)。定义在 zone_watermarks 枚举中。

enum zone_watermarks {WMARK_MIN,WMARK_LOW,WMARK_HIGH,NR_WMARK
};

内存水位线是按区域(zone)维护的三个阈值,用于监控和管理系统内存使用状态:
(1)WMARK_MIN(最低水位线)
当可用内存低于此阈值时,系统进入紧急状态
触发直接内存回收(Direct Reclaim),可能导致应用程序短暂卡顿
优先保证关键内核操作的内存需求

(2)WMARK_LOW(低水位线)
当可用内存低于此阈值时,系统开始后台异步回收内存
唤醒 kswapd 内核线程进行页面回收(Page Reclaim)
维持系统内存的 “健康状态”

(3)WMARK_HIGH(高水位线)
当可用内存高于此阈值时,系统内存充足
停止后台内存回收,允许更宽松的内存分配策略
通常作为系统内存使用的 “安全上限”

内存回收策略
kswapd 后台回收:异步进行,优先回收不常用的匿名页和文件缓存。
直接内存回收:同步阻塞应用程序,强制回收内存以满足紧急需求。
内存不足(OOM):当所有回收手段无效时,触发 OOM 杀手选择进程终止。

阈值计算
水位线值通常基于区域总页数的百分比动态计算,例如:

min = zone->managed_pages * (sysctl_vm_min_free_kbytes / totalram_pages);
low = min * 5/4;
high = min * 3/2;

WMARK_MIN、WMARK_LOW和WMARK_HIGH 水位线都是通过内核参min_free_kbytes分别计算得到,使用sysctl可以动态设置这个参数,达到动态控制水位线的目的。

cat /proc/sys/vm/min_free_kbytes
67584

min_free_kbytes 是 Linux 内核中的一个关键参数,用于控制系统预留的最低空闲内存量。这个参数直接影响内存水位线机制的行为,对系统性能和稳定性有重要影响。

基本功能
min_free_kbytes 定义了系统中每个内存区域(zone)必须保留的最低空闲内存量(以千字节为单位)。
根据每个区域容量大小比例,从min_free_kbytes划分每个区域的 WMARK_MIN 水位线。
这个值直接决定了内存水位线中的 WMARK_MIN 阈值,而 WMARK_LOW 和 WMARK_HIGH 则基于 WMARK_MIN 按比例计算。

3.2 回收过程

page cache 中的页面和 anonymous page 都是可以被回收的。
在回收page cache文件页时,系统会先判断文件页的状态。如果文件页保存的内容与磁盘中文件对应内容一致,即该文件页是干净的,那么无需进行回写操作,可直接将其作为空闲页框释放到伙伴系统;反之,如果文件页保存的数据和磁盘中文件对应的数据不一致,这样的文件页被称为脏页,就需要先将其回写到磁盘中对应数据所在的位置,确保数据的一致性,然后才能作为空闲页框释放 。

anonymous page用于存储进程的堆、栈数据等 。当系统需要回收匿名页时,会筛选出那些访问频率较低、不经常使用的匿名页,将它们写入到 swap 分区中。swap 分区就像是内存的 “临时仓库”,当内存空间紧张时,把暂时不用的数据存放到这里,等需要时再取回来。写入 swap 分区后,这些匿名页就可以作为空闲页框释放到伙伴系统,供其他进程申请使用,从而有效缓解内存压力。

上面说到zone的内存回收,它针对三样东西进程回收:slab、lru链表中的页、buffer_head。这里只讨论内存回收针对lru链表中的页是如何进行回收的。lru链表主要用于管理进程空间中使用的内存页。

在 Linux 内核的内存管理中,LRU(Least Recently Used)链表用于高效管理内存页的回收。Linux 采用的方法是维护 2 个双向链表,一个是包含了最近使用页面的 active list,另一个是包含了最近不使用页面的 inactive list。并且在 struct page 的 page flags 中使用了 PG_referenced 和 PG_active 两个标志位来标识页面的活跃程度。

Active List 与 Inactive List 的作用:
在这里插入图片描述

双向链表结构
active list:存储近期活跃页面(PG_active=1),新页面从头部插入,尾部页面可能降级。
inactive list:存储非活跃页面(PG_active=0),尾部页面优先被回收。

PG_active:标记页面所属链表(1=active,0=inactive)。
PG_referenced:标记页面近期是否被访问(1 = 已访问,0 = 未访问)。

// v5.15/source/include/linux/mm_types.hstruct page {unsigned long flags;		/* Atomic flags, some possibly* updated asynchronously */......
}

页描述符页描述符中对内存回收来说非常必要的标志:
PG_lru:表示页在lru链表中
PG_referenced: 表示页最近被访问(只有文件页使用)
PG_dirty:页为脏页,文件页被修改,以及非文件页加入到swap cache后,就会被标记为脏页。在此页回写前会被清除,但是回写失败时又会被置位
PG_active:页为活动页,配合PG_lru就可以得出页是处于非活动页lru链表还是活动页lru链表PG_private:页描述符中的page->private保存有数据
PG_writeback:页正在进行回写
PG_swapbacked:此页可写入swap分区,一般用于表示此页是非文件页
PG_swapcache:页已经加入到了swap cache中(只有非文件页使用)
PG_reclaim:页正在进行回收,只有在内存回收时才会对需要回收的页进行此标记
PG_mlocked:页被锁在内存中

3.2.1页面状态转换机制:晋升(Promotion)

晋升(Promotion):从 inactive 到 active

触发条件:inactive list 中的页面被访问 2 次(即 PG_referenced=1 时再次被访问)。

操作:
通过activate_page()将页面移至 active list 头部。
设置 PG_active=1,清 0 PG_referenced。

当页面被访问时,内核调用 mark_page_accessed():

// v5.15/source/mm/swap.c/** Mark a page as having seen activity.** inactive,unreferenced	->	inactive,referenced* inactive,referenced		->	active,unreferenced* active,unreferenced		->	active,referenced** When a newly allocated page is not yet visible, so safe for non-atomic ops,* __SetPageReferenced(page) may be substituted for mark_page_accessed(page).*/
void mark_page_accessed(struct page *page)
{page = compound_head(page);if (!PageReferenced(page)) {SetPageReferenced(page);} else if (PageUnevictable(page)) {/** Unevictable pages are on the "LRU_UNEVICTABLE" list. But,* this list is never rotated or maintained, so marking an* evictable page accessed has no effect.*/} else if (!PageActive(page)) {/** If the page is on the LRU, queue it for activation via* lru_pvecs.activate_page. Otherwise, assume the page is on a* pagevec, mark it active and it'll be moved to the active* LRU on the next drain.*/if (PageLRU(page))activate_page(page);else__lru_cache_activate_page(page);ClearPageReferenced(page);workingset_activation(page);}if (page_is_idle(page))clear_page_idle(page);
}
EXPORT_SYMBOL(mark_page_accessed);

mark_page_accessed() 函数负责管理页面的活跃状态标记(PG_referenced 和 PG_active)。
标记页面为“被访问过”,参与 LRU 链表的活跃度管理。
关键行为:
(1)若页面首次被访问,设置 PG_referenced 标志。

if (!PageReferenced(page)) {SetPageReferenced(page);
}

对应状态转换:inactive,unreferenced → inactive,referenced。

(2)若页面已被访问过(PG_referenced 已设置)且可回收,则将其激活(移到 Active List)。

    if (PageLRU(page))activate_page(page);else__lru_cache_activate_page(page);ClearPageReferenced(page);workingset_activation(page);
}

逻辑分支:
页面在 LRU 链表上:调用 activate_page() 将其从 Inactive List 移到 Active List。
页面在 PageVec 缓存中:通过 __lru_cache_activate_page() 标记,待后续批量处理。

状态操作:
清除 PG_referenced(因已激活,无需重复标记)。
调用 workingset_activation() 更新工作集统计(用于内存压力评估)。

对应状态转换:inactive,referenced → active,unreferenced。

(3)处理不可回收页面(Unevictable)和空闲页面(Idle)的特殊情况。

if (page_is_idle(page))clear_page_idle(page);

关键设计思想
(1) 二次机会算法(Second Chance)
规则:页面需被访问 两次(首次设 PG_referenced,二次激活)才能晋升到 Active List。
优势:避免短暂访问的页面长期占用 Active List。

(2) 批处理优化
PageVec 机制:不在 LRU 上的页面(如刚分配)暂存到每 CPU 缓存,减少锁争用。
性能权衡:延迟激活操作,提升并发性能。

3.2.2页面状态转换机制:降级(Demotion)

内核定期扫描 Active List,将未被引用的页面降级。

shrink_active_list() 函数扫描 Active LRU 链表,根据页面访问情况决定将其降级到 Inactive List 或保留在 Active List。

// v5.15/source/mm/vmscan.c/** shrink_active_list() moves pages from the active LRU to the inactive LRU.** We move them the other way if the page is referenced by one or more* processes.** If the pages are mostly unmapped, the processing is fast and it is* appropriate to hold lru_lock across the whole operation.  But if* the pages are mapped, the processing is slow (page_referenced()), so* we should drop lru_lock around each page.  It's impossible to balance* this, so instead we remove the pages from the LRU while processing them.* It is safe to rely on PG_active against the non-LRU pages in here because* nobody will play with that bit on a non-LRU page.** The downside is that we have to touch page->_refcount against each page.* But we had to alter page->flags anyway.*/
static void shrink_active_list(unsigned long nr_to_scan,struct lruvec *lruvec,struct scan_control *sc,enum lru_list lru)
{unsigned long nr_taken;unsigned long nr_scanned;unsigned long vm_flags;LIST_HEAD(l_hold);	/* The pages which were snipped off */LIST_HEAD(l_active);LIST_HEAD(l_inactive);struct page *page;unsigned nr_deactivate, nr_activate;unsigned nr_rotated = 0;int file = is_file_lru(lru);struct pglist_data *pgdat = lruvec_pgdat(lruvec);lru_add_drain();spin_lock_irq(&lruvec->lru_lock);nr_taken = isolate_lru_pages(nr_to_scan, lruvec, &l_hold,&nr_scanned, sc, lru);__mod_node_page_state(pgdat, NR_ISOLATED_ANON + file, nr_taken);if (!cgroup_reclaim(sc))__count_vm_events(PGREFILL, nr_scanned);__count_memcg_events(lruvec_memcg(lruvec), PGREFILL, nr_scanned);spin_unlock_irq(&lruvec->lru_lock);while (!list_empty(&l_hold)) {cond_resched();page = lru_to_page(&l_hold);list_del(&page->lru);if (unlikely(!page_evictable(page))) {putback_lru_page(page);continue;}if (unlikely(buffer_heads_over_limit)) {if (page_has_private(page) && trylock_page(page)) {if (page_has_private(page))try_to_release_page(page, 0);unlock_page(page);}}if (page_referenced(page, 0, sc->target_mem_cgroup,&vm_flags)) {/** Identify referenced, file-backed active pages and* give them one more trip around the active list. So* that executable code get better chances to stay in* memory under moderate memory pressure.  Anon pages* are not likely to be evicted by use-once streaming* IO, plus JVM can create lots of anon VM_EXEC pages,* so we ignore them here.*/if ((vm_flags & VM_EXEC) && page_is_file_lru(page)) {nr_rotated += thp_nr_pages(page);list_add(&page->lru, &l_active);continue;}}ClearPageActive(page);	/* we are de-activating */SetPageWorkingset(page);list_add(&page->lru, &l_inactive);}/** Move pages back to the lru list.*/spin_lock_irq(&lruvec->lru_lock);nr_activate = move_pages_to_lru(lruvec, &l_active);nr_deactivate = move_pages_to_lru(lruvec, &l_inactive);/* Keep all free pages in l_active list */list_splice(&l_inactive, &l_active);__count_vm_events(PGDEACTIVATE, nr_deactivate);__count_memcg_events(lruvec_memcg(lruvec), PGDEACTIVATE, nr_deactivate);__mod_node_page_state(pgdat, NR_ISOLATED_ANON + file, -nr_taken);spin_unlock_irq(&lruvec->lru_lock);mem_cgroup_uncharge_list(&l_active);free_unref_page_list(&l_active);trace_mm_vmscan_lru_shrink_active(pgdat->node_id, nr_taken, nr_activate,nr_deactivate, nr_rotated, sc->priority, file);
}

关键操作:
(1)从 Active List 隔离指定数量的页面(isolate_lru_pages)。

lru_add_drain();  // 确保所有待处理的LRU操作完成
spin_lock_irq(&lruvec->lru_lock);
nr_taken = isolate_lru_pages(nr_to_scan, lruvec, &l_hold, &nr_scanned, sc, lru);
spin_unlock_irq(&lruvec->lru_lock);

lru_add_drain():清空每 CPU 的 LRU 缓存(pagevec),确保后续操作基于最新状态。
隔离页面:从 Active List 取出 nr_to_scan 个页面到临时链表 l_hold,避免长期持有锁。

(2)检查页面的访问状态(page_referenced)。

while (!list_empty(&l_hold)) {page = lru_to_page(&l_hold);if (page_referenced(page, ...)) {if ((vm_flags & VM_EXEC) && page_is_file_lru(page)) {nr_rotated++;list_add(&page->lru, &l_active);  // 保留在Activecontinue;}}ClearPageActive(page);list_add(&page->lru, &l_inactive);  // 降级到Inactive
}

page_referenced():检查页面近期是否被访问(通过反向映射遍历 PTE 的 ACCESSED 位)。
特殊处理可执行文件页:若文件页具有 VM_EXEC 标志(如二进制代码),给予额外活跃周期,提升程序运行性能。
降级逻辑:未被引用的页面清除 PG_active 标志,准备移入 Inactive List。

(3)将页面重新分类到 Active 或 Inactive 链表(move_pages_to_lru)。

spin_lock_irq(&lruvec->lru_lock);
nr_activate = move_pages_to_lru(lruvec, &l_active);
nr_deactivate = move_pages_to_lru(lruvec, &l_inactive);
spin_unlock_irq(&lruvec->lru_lock);

3.3 ANON/FILE LRU 链表

内核维护了四种主要的 LRU 链表,分别针对不同类型的页面进行优化:

LRU 链表描述典型页面类型
LRU_ACTIVE_ANON活跃的匿名页(Anonymous Pages)进程堆、栈、共享内存等动态分配的内存
LRU_INACTIVE_ANON非活跃的匿名页近期未使用的匿名页,可能被换出到 Swap
LRU_ACTIVE_FILE活跃的文件页(File-backed Pages)频繁访问的文件缓存(如代码、数据文件)
LRU_INACTIVE_FILE非活跃的文件页近期未使用的文件缓存,可直接丢弃

链表定义(mmzone.h):

// v5.15/source/include/linux/mmzone.h
/** We do arithmetic on the LRU lists in various places in the code,* so it is important to keep the active lists LRU_ACTIVE higher in* the array than the corresponding inactive lists, and to keep* the *_FILE lists LRU_FILE higher than the corresponding _ANON lists.** This has to be kept in sync with the statistics in zone_stat_item* above and the descriptions in vmstat_text in mm/vmstat.c*/
#define LRU_BASE 0
#define LRU_ACTIVE 1
#define LRU_FILE 2enum lru_list {LRU_INACTIVE_ANON = LRU_BASE,LRU_ACTIVE_ANON = LRU_BASE + LRU_ACTIVE,LRU_INACTIVE_FILE = LRU_BASE + LRU_FILE,LRU_ACTIVE_FILE = LRU_BASE + LRU_FILE + LRU_ACTIVE,LRU_UNEVICTABLE,NR_LRU_LISTS
};struct lruvec {struct list_head		lists[NR_LRU_LISTS];......
}typedef struct pglist_data {....../* Fields commonly accessed by the page reclaim scanner *//** NOTE: THIS IS UNUSED IF MEMCG IS ENABLED.** Use mem_cgroup_lruvec() to look up lruvecs.*/struct lruvec		__lruvec;......
}

(1) 查看 LRU 链表大小

cat /proc/meminfo | grep -iE 'active|inactive'
Active:          4477068 kB
Inactive:       10577744 kB
Active(anon):       1964 kB
Inactive(anon):  5480488 kB
Active(file):    4475104 kB
Inactive(file):  5097256 kB

(2)链表在 node 的各个 zone 上的分布

cat /proc/zoneinfo
Node 0, zone      DMAper-node statsnr_inactive_anon 1370098nr_active_anon 491nr_inactive_file 1274314nr_active_file 1119114nr_unevictable 143

参考资料

Linux内核技术实战课
https://zhuanlan.zhihu.com/p/70964195

相关文章:

Linux 内存管理之page cache

文章目录 一、page cache1.1 File-backed pages和Anonymous pages1.2 page cache/slab cache1.3 读/写路径1.4 脏页回写1.5 drop_caches1.6 时间局部性与空间局部性1.7 Page Cache 的两种类型1.8 关键数据结构 二、Page Cache 的产生2.1 Buffered I/O(标准 I/O&…...

uniApp实战四:网络请求封装

文章目录 1.最终效果预览2.请求封装3.创建config配置文件4.创建api请求5.页面调用 说明:当前笔记基于Vue3开发,HbuilderX版本4.66 1.最终效果预览 2.请求封装 在util/request.js下创建js文件,代码如下 import config from /configconst tim…...

sentinel 自定义 dashboard 用户名密码

默认情况下,sentinel dashboard 用户名密码为 sentinel / sentinel ,这里我使用重写 镜像的方式: // 定义 Dockerfile $ cat Dockerfile # 基于现有 Sentinel Dashboard 镜像 FROM bladex/sentinel-dashboard:1.8.4# 重新定义 ENTRYPOINT&…...

Fisco Bcos学习 - 搭建星形拓扑组网

文章目录 一、前言二、环境准备与依赖安装2.1 系统要求2.2 依赖安装 三、星形拓扑设计与节点规划四、使用build_chain.sh构建星形拓扑4.1 创建操作目录并获取脚本4.2 生成星形拓扑配置文件4.3 执行构建命令4.4 查看生成的节点文件 五、启动节点与共识验证5.1 启动所有节点5.2 查…...

深度学习入门--(二)感知机

一.感知机是什么 简单的输入和输出,感觉(输入),知道(输出,作出反应) 二.简单逻辑电路 2.1与门 import numpy as np #AND def AND(X1,X2):w1,w2,thera0.5,0.5,0.7tmpX1*w1X2*w2if tmp>the…...

LeetCode 3298.统计重新排列后包含另一个字符串的子字符串数目2

给你两个字符串 word1 和 word2 。 如果一个字符串 x 重新排列后,word2 是重排字符串的 前缀 ,那么我们称字符串 x 是 合法的 。 请你返回 word1 中 合法 子字符串 的数目。 注意 ,这个问题中的内存限制比其他题目要 小 ,所以你…...

【nRF52832】【环境搭建 1】【ubuntu下搭建nRF52832开发环境】

本文讲述如何在 ubuntu 22.04 下开发 nRF52832. host 环境说明: $ uname -a Linux leo 6.8.0-60-generic #63~22.04.1-Ubuntu SMP PREEMPT_DYNAMIC Tue Apr 22 19:00:15 UTC 2 x86_64 x86_64 x86_64 GNU/Linux1. 安装软件 sudo apt install gcc-arm-none-eabisudo apt-get i…...

Django

1. Django 和 Tornado 的关系 Django 是一个高级 Python Web 框架,它鼓励快速开发和干净、实用的设计。Django 遵循 MVC(模型-视图-控制器)设计模式的一个变种,称为 MTV(模型-模板-视图)。Django 框架提供…...

51c嵌入式~CAN~合集2

我自己的原文哦~ https://blog.51cto.com/whaosoft/14016935 一、CAN总线常见信号干扰问题 定位干扰原因 当总线有干扰时,有经验的工程师能够迅速定位,但是对于新手来说却很麻烦。 造成总线干扰的原因有很多,比如通过电磁辐射耦合到通…...

【iOS】iOS崩溃总结

【iOS】iOS崩溃总结 一、前言 之前写了一篇博文《【Flutter】程序报错导致的灰屏总结》,浏览量、收藏率和点赞量还挺高,还被收录了,就想着总结一下iOS崩溃,这个也是在iOS面试中经常被问到的。 在 iOS 开发过程中,导致…...

npm 报错:“无法加载文件 ...npm.ps1,因为在此系统上禁止运行脚本” 解决方案(附执行策略说明)

在使用 npm 命令时,部分 Windows 用户可能会遇到如下错误: npm : 无法加载文件 D:\nvm4w\nodejs\npm.ps1,因为在此系统上禁止运行脚本。有关详细信息,请参阅 https://go.microsoft.com/fwlink/?LinkID135170 中的 about_Executi…...

AES加密:为你的PDF文档加上一道钢铁防线

在数字化时代,确保敏感数据的安全性至关重要。加密技术在保护信息免受未经授权访问方面起着关键作用。而在众多加密标准中,AES(高级加密标准)因其强大的安全性和广泛的应用而脱颖而出。那么,AES加密如何应用到PDF文档中…...

2025学年湖北省职业院校技能大赛 “信息安全管理与评估”赛项 样题卷(一)

2025学年湖北省职业院校技能大赛 “信息安全管理与评估”赛项 样题卷(一) 第一部分:网络平台搭建与设备安全防护任务书DCRS:DCFW:DCWS:WAF: 第二部分:网络安全事件响应、数字取证调查、应用程序安全任务书任务 1:应急响…...

1688商品发布API:自动化上架与信息同步

一、1688商品发布API的核心功能与技术架构 1.1 API功能全景 1688商品发布API是1688开放平台的核心组件之一,支持商品信息的自动化发布、编辑、上下架及库存同步。其核心功能包括: 商品信息管理:支持商品标题、描述、价格、库存、SKU&#…...

鸿蒙ArkUI---基础组件Tabs(Tabbar)

基础页面组件 Tabs 作用: 快速创建Tabbar 个人理解: 快速的创建Tabar。 效果图: 代码:interface TabItem {icon: Resource; //未选中activeIcon: Resource; //选中name: string; //文字 }Entry Component struct Index {// st…...

50. Pow(x, n)快速幂算法

实现 pow(x, n) ,即计算 x 的整数 n 次幂函数(即,xn )。此函数应将 x 作为浮点数(意味着它可以是十进制数)和 n 作为整数(可以是正数、负数或零)一起使用。 快速幂(Expo…...

Python函数

三.函数进阶 0.定义 函数三要素:函数名,参数,返回值,其中只有函数名是必须要的,参数,返回值可以没有 语法: def 函数名(参数): 函数体 return 返回值 1.…...

7.Spring框架

# spring框架Spring3.0开启了纯注解开发模式,使用Java类替代配置文件,开启了Spring快速开发赛道## 为什么要使用 **Spring** 框架?​ Spring 是一个轻量级应用框架,它提供了 IoC 和 AOP 这两个核心的功能。它的核心目的是为了…...

计算机网络-----详解HTTP协议

✏️1. 什么是HTTP HTTP (全称为 “超⽂本传输协议”) 是⼀种应⽤⾮常⼴泛的应⽤层协议(所谓 “超⽂本” 的含义, 就是传输的内容不仅仅是⽂本(⽐如 html, css 这个就是⽂本), 还可以是⼀些其他的资源, ⽐如图⽚, 视频, ⾳频等⼆进制的数据)。 HTTP 诞⽣…...

解决npm安装依赖报错ERESOLVE unable to resolve dependency tree

在使用 npm 安装项目依赖时,有时会遇到错误信息 “npm ERR! code ERESOLVE”,该错误通常发生在依赖版本冲突或者依赖解析问题时。本文将详细介绍出现这个错误的原因,并提供解决方法,确保正确安装项目依赖并避免该错误的发生。 一…...

微信小程序安卓手机输入框文字飘出输入框

最近在开发微信小程序遇到一个问题,安卓手机输入框文字飘出输入框,但是ios系统的手机则正常。 使用情景:做了一个弹窗,弹窗内是表单,需要填写一些信息,但是在填写信息时光标不显示,输入的内容飘…...

python网络自动化-数据格式与数据建模语言

数据格式 在Python网络运维自动化最基本是JSON、YAML和XML这3种数据格式。除了这3种常用的数据格式,还有一种深受网络工程师喜爱且在网络运维自动化中常用的数据承载方式——表格 需要注意的是JSON的键必须用双引号包裹,JSON的对象数据键值对的值和数组…...

C++ 中的 atan2 函数:深入解析与应用

在 C 编程中,数学计算是许多应用场景的核心,例如几何问题、物理模拟和游戏开发等。atan2 函数作为数学库中的一个重要工具,提供了比普通反正切函数更强大的功能。本文将深入解析 atan2 函数的原理、使用方法以及实际应用场景,并通…...

云计算-Azure Functions :构建事件驱动的云原生应用报告

云计算导论 课程研究报告 Azure Functions :构建事件驱动的云原生应用 摘要: Azure Functions 是一种无服务器解决方案,是由微软 Azure 平台提供的,可以使用户专注于业务逻辑,减少代码的编写,减少需要维护…...

【笔记——李沐动手学深度学习】2.3 线性代数

2.3.1 标量 标量由只有一个元素的张量表示。 下面的代码将实例化两个标量,并执行一些熟悉的算术运算,即加法、减、乘法、除法和指数。 2.3.2 向量 人们通过一维张量表示向量。一般来说,张量可以具有任意长度,取决于机器的内存限…...

多个 Job 并发运行时共享配置文件导致上下文污染,固化 Jenkins Job 上下文

基于 context.py 固化 Jenkins Job 上下文的完整方案,适用于你当前的工作流(Python Jenkins Pipeline),解决: 多个 Job 并发运行时共享配置文件导致上下文污染;读取环境变量或 JSON 文件时被其他 Job 修改…...

github 上的php项目

github 上的php项目 项目的网址 (Loong1996/LikeGirlSite: 情侣网站、情侣网页、恋爱记录网站) # 修改 # admin/Config_DB.php//localhost 为数据库地址 一般使用默认的即可 或(127.0.0.1) $db_address "mysql_php";/…...

防火墙快速管理软件,66K超小巧

软件介绍 今天为大家推荐一款轻量级的Windows防火墙管理工具,这款工具能帮助用户快速开启或关闭系统防火墙功能,操作比系统原生设置更加便捷高效。 软件优势 相比通过系统设置层层点击的操作方式,这款仅66KB大小的微型工具只需单击按钮…...

入门级STM32F103C8T6无人机遥控(原理图)

一、STM32主控电路 一、STM32 主控电路 把 STM32 想象成 “机器人的大脑”,核心电路是 “大脑的基础保障”:让大脑有电、有心跳(时钟 )、能复活(复位 )。 1. 电源引脚(VDD、VDDA、VSS 等 &#…...

无人机灯光驱动模块技术解析

一、运行方式 1. 核心流程: 指令接收:灯光控制模块通过无线通信链路(如WiFi, 数传电台,或专用的表演控制链路)接收来自地面站或中央控制系统的灯光指令。指令包含:颜色(RGB/RGBW值&#xff0…...

React + Umi(Umijs/Max) 搭建项目及配置

文章标题 01 环境准备02 快速构建2.1 参数选项2.2 umix 还是 umijs/max2.3 使用 pnpm (推荐)2.4 使用 npm 和 yarn2.5 启动项目2.6 启用 Prettier(可选)2.7 打包部署发布 03 Tailwind CSS 插件(可选)3.1 安…...

React 第六十四节Router中HashRouter的使用详细介绍及案例分析

前言 HashRouter 是 React Router 提供的一种路由实现方案,它使用 URL 的 hash 部分(# 后面的内容)来实现客户端路由功能。 一、HashRouter 的核心用途 客户端路由:在不刷新页面的情况下管理应用导航兼容性:支持不支…...

Linux RDMA网络配置手册

一、配置前准备工作 在进行 RDMA 网络配置之前,请确保以下准备工作已完成: 硬件环境 确保服务器支持 RDMA 功能,例如支持 InfiniBand 或 RoCE(RDMA over Converged Ethernet)的网卡。确保网络交换设备支持 RDMA 协议…...

sentinel与seata组件在微服务中的基本作用

微服务基础内容: 在微服务中,首先学习了微服务的横向拆分与纵向拆分,纵向拆分指按照功能拆分模块,横向拆分指将高复用的模块单独拆分,使纵向拆分的模块去调用这部分内容。 学习了基本拆分后,需要知道微服…...

Springboot 集成多数据源pgSql+mysql,启动报错

一.错误信息: 2025-06-25 20:25:50.870 ERROR [ai-manage-center,,] --- [ruid-ConnectionPool-Create-1057240219] DruidDataSource : create connection SQLException, url: jdbc:postgresql://10.10.60.227:5432/ai_dify1?sslmodedisable&currentSchemapub…...

南宫28NG相信品牌力量/Vue 3 中的组合式 API(Composition API)进阶实战

南宫28NG相信品牌力量【罔丨止:MGTY.PW】 点击此处复制到浏览器打开 随着 Vue 3 的普及,Composition API 已成为现代 Vue 开发的主流。本节我们将深入掌握组合式 API 的进阶用法,涵盖响应式工具、生命周期钩子封装、自定义逻辑抽离等关键技术…...

实战使用 Docker Compose 搭建 Redis Cluster 集群

文章目录 前言技术积累Docker Compose简介Redis Cluster简介Redis Cluster 解决的问题 实战演示部署环境创建目录编写Redis配置文件编写Docker-Compose.yml执行yml文件,启动容器查看容器状态创建集群验证集群集群数据验证 总结 前言 随着互联网技术的发展&#xff…...

Tauri(2.5.1)+Leptos(0.8.2)开发自用桌面小程序--DeepSeek辅助编程(俄罗斯方块)

在之前工作基础上(Tauri(2.5.1)Leptos(0.8.2)开发自用桌面小程序-CSDN博客),继续进行自用桌面小程序的开发,这次完全使用DeepSeek辅助编程做一个俄罗斯方块游戏,大部分代码由DeepSeek自主完成,Bug扔给DeepS…...

flex布局实例:把色子放进盒子里

目录 一、flex布局实例:把色子放进盒子里 1、基础样式 二、justify-content 属性 三、flex-direction 属性 四、align-items 属性 五、flex-wrap 属性 二、flex布局应用到常见场景 非常详细的讲解flex布局,看一看,练一练! …...

【启发式算法】RRT*算法详细介绍(Python)

📢本篇文章是博主人工智能(AI)领域学习时,用于个人学习、研究或者欣赏使用,并基于博主对相关等领域的一些理解而记录的学习摘录和笔记,若有不当和侵权之处,指出后将会立即改正,还望谅…...

基于R语言的亚组分析与森林图绘制1

亚组分析是临床研究中的重要分析方法,其核心是通过将研究对象按基线特征(如年龄、性别或吸烟状况等)划分为不同亚组,进而评估干预措施或暴露因素在各亚组中对结局影响的差异性。 在亚组分析中,交互作用(P for interaction)是关键指标,用于判断干预措施或暴露因素与亚组…...

idea, CreateProcess error=206, 文件名或扩展名太长

idea, CreateProcess error206, 文件名或扩展名太长 解决 “CreateProcess error206, 文件名或扩展名太长” 错误 CreateProcess error206 是 Windows 系统特有的错误,表示命令行参数超出了 Windows 的 32767 字符限制。这个问题在 Java 开发中尤其常见&#xff0c…...

aspose.word在IIS后端DLL中高并发运行,线程安全隔离

aspose.word在IIS后端DLL中运行,加载很慢,如何为全部用户加载,再每个用户访问时在各自线程中直接可以打开WORD文件处理 Aspose.Words 在 IIS 中优化加载性能方案 针对 Aspose.Words 在 IIS 后端 DLL 中加载缓慢的问题,我们可以通过单例模式预加载组件并结合线程安…...

day042-负载均衡与web集群搭建

文章目录 0. 老男孩思想-面试官问:你对加班的看法?1. 负载均衡2. 搭建负载均衡的WordPress集群2.1 负载均衡服务器2.2 配置web服务器2.3 测试 踩坑记录1. /var/cache/nginx权限问题 0. 老男孩思想-面试官问:你对加班的看法? 互联网公司没有不加班的&a…...

DuDuTalk | 武汉赛思云科技有限公司通过武汉市人工智能企业认定!

近日,2025年武汉市人工智能企业名单正式公布!武汉赛思云科技有限公司(以下简称赛思云科技)凭借卓越的技术实力与创新成果,成功入选武汉市人工智能企业。这是对公司长期深耕AI语音智能领域、推动数字化转型的高度认可&a…...

Tita CRM飞书协同版:解锁企业销售与交付管理新效能

数字化转型的破局之道 在数字经济加速发展的今天,传统管理模式正面临前所未有的挑战: • 销售过程缺乏可视化管控手段 • 项目执行存在严重的信息孤岛 • 跨部门协作效率持续低下 • 绩效考核缺乏客观数据支撑 Tita CRM作为专业的智能化管理平台&#x…...

web安全之h2注入系统学习

起初是在N1 Junior 2025 上面碰到一题,考点是h2的sql注入。由于之前没有见过,趁此机会系统学习一番 实验代码 public class H2Inject {public static void main(String[] args) throws Exception{JdbcDataSource dataSource new JdbcDataSource();dataS…...

LVS-DR负载均衡群集深度实践:高性能架构设计与排障指南

目录 一、核心原理与理论 二、背景与架构设计 三、全流程部署步骤 1. NFS共享存储配置(192.168.7.100) 2. Real Server节点配置(四台服务器) 3. Director服务器配置 四、常见问题解决方案 五、生产环境总结 拓扑示意图&am…...

Java如何导出word(根据模板生成),通过word转成pdf,放压缩包

<!-- 导出word文档所需依赖--><dependency><groupId>com.deepoove</groupId><artifactId>poi-tl</artifactId><version>1.10.0-beta</version></dependency><dependency><groupId>org.apache.poi</gr…...

.NET 7.0 EF Core:一、创建Web API 项目基础框架和用户表的增删改查

demo 地址: https://github.com/iotjin/Jh.Admin.NETCore 代码不定时更新&#xff0c;请前往github查看最新代码 .NET 7.0 EF Core&#xff1a;一、创建Web API项目 官方教程序一、项目目录结构各层职责说明1️⃣ Admin.NETCore.API&#xff08;接口层&#xff09;2️⃣ Admin.…...