深入理解Linux虚拟内存管理(三)

news2024/11/17 10:10:32

在这里插入图片描述

系列文章目录


Linux 内核设计与实现
深入理解 Linux 内核(一)
深入理解 Linux 内核(二)
Linux 设备驱动程序(一)
Linux 设备驱动程序(二)
Linux 设备驱动程序(三)
Linux设备驱动开发详解
深入理解Linux虚拟内存管理(一)
深入理解Linux虚拟内存管理(二)
深入理解Linux虚拟内存管理(三)


文章目录

  • 系列文章目录
  • 第12章 共享内存虚拟文件系统
    • 12.1 初始化虚拟文件系统
    • 12.2 使用 shmem 函数
    • 12.3 在 tmpfs 中创建文件
    • 12.4 虚拟文件中的缺页中断
      • 12.4.1 定位交换页面
      • 12.4.2 向交换区写页面
    • 12.5 tmps 中的文件操作
    • 12.6 tmpfs 中的索引节点操作
    • 12.7 建立共享区
    • 12.8 System V IPC
    • 12.9 2.6 中有哪些新特性
  • 第13章 内存溢出管理
    • 13.1 检查可用内存
    • 13.2 确定 OOM 状态
    • 13.3 选择进程
    • 13.4 杀死选定的进程
    • 13.5 是这样吗?
    • 13.6 2.6 中有些新特性
  • 第14章 结束语


第12章 共享内存虚拟文件系统

   共享一个有后援文件或后援设备的内存区域仅需要调用 mmap() 设置 MAP_SHARED 标志位。但是,进程间共享一片匿名区域时存在两种重要情形:其一是在 mmap() 设置 MAP_SHARED 时没有后援文件,这片区域将在 fork() 执行之后由父、子进程共享;其二是该区域明确地利用 shmget() 设置,并由 shmat() 附加到虚拟地址空间中。

   当 VMA 中的页面有磁盘上的后援文件时,所使用的接口很简单。系统在缺页时为了读入一页会调用 vm_area_struct->vm_ops 中的 nopage() 函数。要写一页到后援存储设备时,就使用 inode -> i_mapping -> a_opspage -> mapping→ a_opsaddress_space_operations 中寻找到相应的 writepage() 函数。当进行普通文件操作如 mmap()read()write() 时,系统则使用 inode→i_fop 等找到 struct file_operations 及其相应的函数。这些关系如图 4.2 所示。

   这是一个相当清晰的接口,概念上也很容易理解。但它却无法处理匿名页的情况,因为匿名页没有后援文件。所以,为了保留这个清晰的接口,Linux 引入了一个基于 RAM 文件系统的人造文件,用于后援匿名页面,其中每个 VMA 都由这个文件系统中的一个文件作为后援。此文件系统的每个索引节点都被放置在 shmem_inodes 链表中,这样就很容易确定每个索引节点。通过引入这个概念,Linux 允许使用相同的基于文件的接口,而不是对匿名页作特殊处理。

   这种文件系统有两个变种 :shmtmpfs。它们的核心功能相同,主要不同的地方是用途。内核利用 shm 为匿名页创建后援文件并由 shmget() 生成后援区域。该文件系统由 kern_mount() 加载,所以它在内部加载,并不为用户所见。tmpfs 是一个临时文件系统,它可以有选择地加载到 /tmp/ 以获得一个基于 RAM 的临时文件系统。tmpfs 的另一个用途是被加载到 /dev/shm/ 。这时,在 tmpfs 文件系统中,mmap() 文件的进程之间可以共享信息,这是代替 System V 进程间通信(IPC)机制的一种方法。但是无论哪一种使用情形,tmpfs 必须显式地由系统管理员加载。

   本章首先描述如何实现虚拟文件系统。接着讨论如何建立及销毁共享区域,最后说明怎样使用工具实现 System V IPC 机制。

12.1 初始化虚拟文件系统

   如图 12.1 所示,在系统启动或载入模块时,由函数 init_tmpfs() 初始化虚拟文件系统。 init_tmpfs() 注册两个文件系统 tmpfsshm,并调用 kern_mount() 加载作为内部文件系统的 shm。然后计算文件系统中块和索引节点的最大数量。作为注册过程的一部分,它还调用 shmem_read_super() 作为一个回调函数来生成 super_block 结构,这个结构中保存有更多关于文件系统的信息,如保证块大小等于页面大小。

在这里插入图片描述

   文件系统创建的每一个索引节点都附带有 shmem_inode_info 结构,这个结构含有文件系统的私有信息。SHMEM_I() 函数以索引节点为参数,返回指针到此类型的结构中。<linux/shmem_fs.h> 将其如下定义:

struct shmem_inode_info {
	spinlock_t		lock;
	unsigned long		next_index;
	swp_entry_t		i_direct[SHMEM_NR_DIRECT]; /* for the first blocks */
	void		      **i_indirect; /* indirect blocks */
	unsigned long		swapped;    /* data pages assigned to swap */
	unsigned long		flags;
	struct list_head	list;
	struct inode	       *inode;
};

其中各字段含义如下。

  • lock:为了保护索引节点信息不被并发访问的自旋锁。
  • next_index:文件最后一页的索引。在文件被截断时它与 inode→i_size 是不同的。
  • i_direct:一个直接块,它存放有文件中用到的第一个 SHMEM_NR_DIRECT 交换向量。具体见 12.4.1 小节。
  • i_indirect:指向首个间接块的指针。具体见 12.4.1 小节。
  • swapped:记录文件当前换出的页面数量的计数。
  • flags:目前仅用于记住文件是否属于 shmget() 建立的共享区域。它通过 shmctl() 指定 SHM_LOCK 锁来设置,指定 SHM_UNLOCK 来解锁。
  • list:文件系统中所有索引节点的列表。
  • inode:父索引节点的指针。

12.2 使用 shmem 函数

   包含 shmem 特定函数指针的结构有多种,所有结构中 tmpfsshm 都共享相同的结构。

   Linux 中声明了 hmem_aopsshmem_vm_ops,它们分别具有 struct address_space_operationsstruct vm_operations_struct,分别对应于换页中的缺页处理和写页面到后援存储区。

   地址空间操操作结构 struct shmem_aops 包含有指向一些函数的指针,其中最重要的是
shmem_writepage()),它在从高速缓存移动页面到交换高速缓存时被调用。shmem_remove-
page()在从页面高速缓存中移除页面时被调用,这时系统可以回收存储块。tmpfs 不使用
shmem_readpage(,但 Linux 还是提供了这个函数,这样 tmpfs 文件上的系统调用 sendfile())
才可能被使用。同样 tmpfs 也没有使用 shmem_prepare_write()和 shmem_commit_write(,
但 Linux 提供它们从而 tmpfs 可以被用作回环设备。mm/shmem.c 中,shmem_aops 有 如下
声明:

匿名 VMA 使用 shmem_vm_ops 作为 vm_operations_struct,所以在中断陷入时系统调
用 shmem_nopage()。它如下声明:
1426 static struct vm_operations_struct shmem_vm_ops = (
1427 nopage:shmem_nopage,
1428 };
在文件和索引节点上完成操作时需要有两个结构 : file_operations 和 inode_operations.
file_operations 由 shmem_file_operations 调用,它提供函数实现 mmap(),read(,write()和
fsync()。它如下声明:
1510 static struct file_operations shmem_file_operations =
1511 mmap:shmem_mmap,
1512 # ifdef CONFIG_TMPFS
1513 read:shmem_file_read,
1514 write:shmem_file_write,
1515 fsync:shmem_sync_file,
1516 #・endif
1517 };
Linux 提供了 3 种 inode_operations:第 1 种是shmem_inode_operations,它在文件索引
节点中使用;第 2 种是 shmem_dir_inode_operations,它针对目录;第 3 种是 shmem_symlink_
inline_operations 和 shmem_symlink_inode_operations 的组合,它针对符号链接。
在名为 shmem_inode_operations 的 inode_operations 结构中存放有 Linux 支持的两种文
件操作,即 truncate()和 setattr()。shmem_truncate()用于截断文件。shmem_notify_change()在
文件属性改变时被调用,从而使文件中的其他部分通过 truncate())得以增长并使用全局零页
面作为数据页面。shmem_inode_operations 如下声明:
1519 static struct inode_operations shmem_inode_operations = {
1520 truncate:shmem_truncate,
1521 setattr:shmem_notify_change,
1522 };
目录 inode_operations 提供了诸如 create(),link())和 mkdir()的函数,它们如下声明:

最后一对操作用于符号链接。它们如下声明:
1354 static struct inode_operations shmem_symlink_inline_operations = 《
1355 readlink:shmem_readlink_inline,
1356 follow_link:shmem_follow_link_inline,
1357 );
1358
1359 static struct inode_operations shmem_symlink_inode_operations =
1360 truncate:shmem_truncate,
1361 readlink;shmem_readlink,
1362 follow_link:shmem_follow_link,
1363 );
函数 readlink()和 follow_link()之间的差异与链接信息存放的位置有关。符号链接索引
节点不需要私有索引节点信息结构 shmem_inode_information。如果符号链接名的长度小于
这个结构,则索引节点中的空间用于存放名字,而 shmem_symlink_inline_operations 会变成
索引节点操作结构。否则,shmem_getpage())开辟一个页面,把符号链接复制到 页面中,索引
节点操作结构定义为 shmem_symlink_inode_operations。第 2 个结构中包括 truncate())函数,
可以在删除文件时回收页面。
这些不同的结构保证了与索引节点相关操作等价的 shmem 会在共享区域有后援虚拟文
件时被用到。当用到它们时,VM 的主要部分中不会出现由实际文件作后援的页面和由虚拟
文件作后援的页面之间的差异。

12.3 在 tmpfs 中创建文件

由于 tmpfs作为一种用户可见的文件系统加载,因此它必须支持目录索引节点操作,如
open(),mkdir())和 link()。实现 tmpfs 的函数指针由 shmem_dir_inode_operations 提供 ,详。
见 12.2 节。
这些函数中大部分的实现都不完全,在某种层次上,它们之间的交互如图 12.2 所示。它

们实现虚拟文件系统中的索引节点相关操作的基本原理都是相同的,并且大部分索引节点字
段都由 shmem_get_inode()填充。
shmem_rename )
shmem_rmdir
shmem_empty
shmem_unlink
(shmem_positive )
d_unhashed
dput
dentry_iput
shmem_create )
d free)
shmem_symlinkshmem_mknod
shmem_link
dname_external
iput
shmem_getpage shmem_get_inode
dget
d_instantiate
shrink_dcache_sb
图 12.2 调用图:shmem_create()
在创建一个新文件时,调用的顶层函数是 shmem_create(),它调用 shmem_mknod(),设
置 S_IFREG 标志位,从而创建一个普通文件。shmem_mknod()仅是 shmem_get_inode(()的
一个包装器,可以确定,它创建一个新的索引节点并填充结构字段。主要要填充的 3 个字段是
inode i_mapping a_ops,inode i_op 和 inode→i_fop 字段。在创建索引节点之后,shmem_
mknod()会更新目录索引节点大小并统计参数 mtime,然后实例化这个新的索引节点。
即使各文件系统的功能本质上相同,shm 中文件的创建也各不相同。如何创建这些文件
将在 12.7 中详细讨论。

12.4 虚拟文件中的缺页中断

发生缺页中断时,如果 vma-vm_ops=nopage 操作存在,do_no_page()将会调用该操作。
在虚拟文件系统的情形下,这意味着函数 shmem_nopage()以及它的调用图中函数(如图 12.3
所示)将在缺页时被调用。。
在这 种 情形 下 的 核 心函数是 shmem_get-
shmem_nopage)
pageO,它 负 责 分 配 新 页 或 者 在交 换 区 中找 到
该页。这种对错误类型的重载是不寻常的,因
为 do_swap_page()一般负责使用 PTE 中的编
shmem_getpage mark_page_accessed
址信息来定位那些已经移到交换高速缓存或后
援存储区的页面。在这种情况下,有后援虚拟
图 12.3 调用图:shmem_nopage()
文件的页面在被移到交换高速缓存中时其 PTE
是被设为 0 的,而索引节点的私有文件系统数据储存了直接或间接的关于块的信息,这些信息
将被用来定位页面。这个操作在很多方面都与普通缺页中断处理类似。

12.4.1 定位交换页面

当一个页面被换出后,swp_entry_t就会包含再次定位该页面所需的信息。这些信息存
放在索引节点中作为文件系统特定的私有信息,而不是使用 PTE 来完成这个工作。
在发生缺页时,系统会调用 shmem_alloc_entry())来定位交换项。它的基本任务是进行基
本的检查并保证 shmem_inode_info next_index 始始终 指向虚拟文件末端的页面索引。它 的
主要任务是调用 shmem_swp_entryO)得到索引节点信息,然后查找索引节点信息中的交换向
量,并且在需要时分配新页来存放交换向量。
第一个 SHMEM_NR_DIRECT 项存放在 inode→i_direct 中。这意味着对 x86 而言,小于
64 KB(SHMEM_NR_DIRECT * PAGE_SIZE)的文件将不需要使用间接块。更大的文件则
必须使用从 inode→i_indirect 指向位置开始的间接块。
刚开开始间接块(inode→i_indirect)分为两半。一半包含指向二次间接块的指针,另一半则
包含指向三次间接块的指针。二次间接块由包含交换向量(swp_entry_t)的页面组成。三次
间接块包含由交换向量填充的页面指针。图 12.4 中显示了间接块不同层次之间的关系。这
种关系意味着在一个虚拟文件(SHMEM_MAX_INDEX)中页面的最大数量(在 mm/shmem.
中定义)为:

12.4.2 向交换区写页面

在虚拟文件系统中,由 address_space_operations 的注册函数 shmem_writepageO)向交换
区写页面。这个函数负责把页面从页面高速缓存移到交换高速缓存。过程包括以下几步:
记录当前 page→mapping 和索引节点相关的信息。
调用 get_swap_pageO在后援存储区中分配一空闲槽。
调用函数 shmem_swp_entry(分配 swp_entry_t。
从页面高速缓存中移除该页。
将该页加入到交换高速缓存中。如果有失败,则释放交换槽,将页面加入到页面高速
缓存中并再次进行这步操作。

12.5 tmps 中的文件操作

虚拟文件支持四种操作,mmap(),read(),write()和 fsync()。这些函数的指针都存放
在 shmem_file_operations 中,在 12.2 节中已有叙述。
这些操作的实现都很普通,在代码注释部分也会有详 细 阐述。mmap()操作由 shmem_
mmap()实现,它仅是更新负责那片映射区域的 VMA。read())操作由 shmem_read()实现,它
从虚拟文件中读数据到用户空间的缓冲区中,并在需要时产生缺页中断。write()由 shmem_
write()实现现,本质上与 read()类似。fsync()操作由 shmem_file_sync()实现,但它实际上是一
个 NULL 操作,因为它不做任何事,仅返回 0 表示成功。由于这些文件只存在于 RAM 中,所
以它们不需要与磁盘同步。

12.6 tmpfs 中的索引节点操作

支持索引节点的最复杂的操作是截断,它涉及 4 个不同的阶段。第 1 个阶段是在 shmem_
truncate())中中,它会截断文件末尾的部分页面,并不停地调用 shmem_truncate_indirect()),直到
文件被截断到合适大小。每一次调用 shmem_truncate_indirect() 只能对一个间接块进行处
理,这也是它可能需要多次调用的原因。
第 2 个阶段,是在 shmem_truncate_indirect()中,处理二次和三次间接块。它找到下一个
需要被截断的间接块。这个将被传到第 3 个阶段的直接块将包含含有交换向量的页面指针。
第 3 个阶段,是在在 shmem_truncate_direct()中处理包含交换量的页面。它选出一个需
要要截断的范围并且将这个范围传到最后一个阶段 shmem_swp_free())。最后一个阶段调用
free_swap_and_cache()来释放表项,包括释放交换区表项以及包含数据的页面。
文件的链接和断开操作很简单,因为大部分的工作由文件系统层完成。要链接一个文件,
需要增大目录索引引节点的大小,更新相应索引节点的 ctime 和 mtime 参数,以及增加将要链接
的索引节点的链接节点数量。然后调用 dget()指向新表项,接着调用 d_instantiate()来实例
化新表项。断开操作同样会更新相应索引节点的参数,再减少将要链接的索引节点的链接节
点数量,然后调用 dput()来减少引用。dput()也会调用 iput()在引用计数到 0 时清除该索引
节点。
创建目录的操作将通过调用 shmem_mkdir ()来完成。它调用 shmem_mknod()设置 S_
IFDIR 标志位,然 后 增 加 父 目录 索 引 节 点的 i_nlink 计数。函数 shmem_rmdir()在 调用
shmem_empty()清空目录之后再删除此目录。如果目录是空的,shmem_rmdir()会将父目录

索引节点的 i_nlink 计数减 1,然后调用 shmem_unlink()来移除目录。

12.7 建立共享区

共享区由 shm 创建的文件作后援。存在创建文件的两种情形:一种是在 shmget()建立共
享区时,另一种是在 mmap()设立 MAP_SHARED 标志位来启动匿名共享区时。这两个函数
都使用核心函数 shmem_file_setup(()创建文件。
由于文件系统是在内部的,所以所要创建的文件的名字不需要惟一(文件总是以索引节点
来定位的,不是名字)。因此,shmem_zero_setup((如图 12.5 所示)据说总是创建一个名名为
dev/zero 的文件,在文件/proc/pid/maps 中它就是这样 出 现 的 。shmget()) 创 建 的 文 件 叫 做
SYSVNN,其中 NN 是传递给 shmget()的一个参数。
核心函数 shmem_file_setup()仅创建一个目录项和索引节点,然后填充相关的字段并实。
例化。

12.8 System V IPC

IPC 实现的内部机制超出出了本书的范围。这一节仅集中讨论 shmget()和 shmat()的实现
以及它们如何受到 VM 的影响。如图 12.6 所示,系统调用 shmget()由 sys_shmget()实现。
它进行基本的参数检查,然后建立 IPC 相关的数据结构。为了创建段,它调用 newset(,就是
由这个函数在 shmfs 中调用前面节中讨论过的 shmem_file_setup()来创建文件。
系统调用 shmat(由 sys_shmat(实现。对这个函数没有什么要注意的地方。它获得适
当的描述符,并且保证所有的参数都有效,然后调用 do_mmap()把共享区映射到进程地址空
间。这个函数中仅有两点需要注意。
首先,如果调用者指明了地址,必须保证 VMA 不会被覆盖。其次 shp→shm_nattch 计数
由一个类型为 shm_vm_ops 的 vm_operations_struct()来维护,它分别注册了 open())和 close())
的回调函数 shm_openO和 shm_close。回调函数 shm_close()还负责在指明 SHM_DEST
标志位且 shm_nattch 计数到 0 时销毁共享区。

12.9 2.6 中有哪些新特性

在 2.6 中,文件系统的核心概念和功能保持不变,改变的部分仅是优化或者对文件系统功
能的扩展。如果读者能够很好地理解 2.4 中的实现,那么对 2.6 中的实现的理解就不会构成
很大的障碍。
在 shmem_inode_info 中增加了一个叫做 alloced 的字段。它存储了分配给这个文件的页
数,在 2.4 中这是需要通过 inode→i_blocks 的值在运行时计算的。同时它保存了一些共同操
作的时钟周期,从而使代码更具可读性。
2.4 中的 flags 字段现在使用 VM_ACCOUNT 标志位以及 VM_LOCKED 标志位。系统
总是要设置 VM_ACCOUNT,表明 VM 将仔细地计算所需的内存从而确保分配不会失败。
对文件操作的扩展能通过系统调用 llseek(来定位内容,_llseek 由 generic_file_llseek())
实现。还有虚拟文件上调用 sendfile(),它由 shmem_file_sendfile()实现。还有 2.6 中允许非
线性映射的对 VMA 操作的扩展,它由 shmem_populate()实现。
最后一个主要的改变是文件系统负责分配和回收它自己的索引节点,这涉及到在结构
super_operations 中的 两个回调函数。 它们通过调用 shmem_inode_cache 来创建一个 slab 高
速缓存实现。在这个 slab 分配器上注册一个 init_once()构造函数来初始化每个新索引节点。

第13章 内存溢出管理

现在要讨论的有关 VM 的最后一部分是内存溢出(OOM,Out Of Memory)管理。这一
章写得比较短,因为检查系统是否有足够的内存、是否真的溢出以及在内在溢出时杀死一个进
程等都是比较简单的任务。这部分内容在 VM 中颜具争议,所以很多场合中都会删除这部
分。但我认为无论它在以后的内核中是否会出现,它都是一个需要考虑的重要子系统,因为它
涉及到一系列其他的子系统。。

13.1 检查可用内存

对于某些操作,如扩展堆的 brk())和重 映射地址空间的 mremap(),系统都会检查是否有
足够的空间来满足请求。注意这与下一节中的 out_of_memory() 路径 是有差异 的。 out_of_
memory() 路径用于在可能的时候避免系统处于 OOM 状态。
在进行可用内存检查时,系统将所请求的页面数作为参数传递给 vm_enough_memory(),然
后检查是否可以满足该请求。除非系统管理员指定了系统可以多用内存,否则系统都将进行
可用内存的检查。为了确定系统所有可用的页面数量,Linux 把以下数据相加。
页面高速缓存容量:因为页面高速缓存很容易就可以回收。
空闲页面数:因为它们已经处于可用状态。
空闲交换页面数:因为可以换出用户空间页面。
swapper_space 管理的页面数量:其对空闲交换页面双倍计数。但由于槽要保留起来,而
且不曾用到,因此该方式是比较妥当的。
dentry 高速缓存使用的页面数:因为很容易回收它们。
inode 高速缓存使用的页面数:因为也很容易回收它们。

如果加起来的页面数对请求页面数来说已经足够,则 vm_enough_memory()会返回 true
给调用者。如果调用者获得了 false 的返 回值,则它知道系统当前空闲页面不足,这时它就会
决定是否向用户空间返回-ENOMEM 错误。

13.2 确定 OOM 状态

在机器内存不足时,系统就会回收旧的页面帧(见第 10 章),但是在这个过程中可能会发
现,系统即使是以最高优先级扫描都无法释放足够的页面来满足请求。如果系统不能够释放
页面,就会调用 out_of_memory(),告知系统发生内存溢出,这时需要杀死某个进程。函数调
用图如图 13.1 所示。
不幸的是,系统可能并没有发生内存溢出,
而只是等待 I/O操作完成,或者将页面换出到后
援存储器。这很精糕,不是因为系统没有内存,
而是因为函数被不必要地调用,它将导致进程可
能会被不必要地杀掉。所以在决定杀死某个进
程之前,系统会做如下检查:
out_of_memory
(oom_kill )
• 有足够的换出空间留下吗(nr_swap_pa-
ges > 0)? 如果是,不是 OOM;
select_bad_process
oom_kill_task )
• 自从上次失败已经大于 5 s 了?如果是,
不是 OOM;
(badness
(force_sig )
• 在 上 一 秒 中 失 败 过? 如 果 不 是,不
是 OOM;
如果在至少上 5 s 中没有 10 个错误,不
( int_sqrt)
force_sig_info
是 OOM;
• 在上 5 s 中有进程被杀死?如果是,不
图 13.1 调用图:out_of_memory()
是 OOM。
仅当上面的检查都通过时,系统才会调用 oom_kill()选择一个进程杀死。

13.3 选择进程

函数 select_bad_process()负责选择一个进程杀死。它逐个检查各个正在运行的进程,并
调用函数 badness()计算各个进程是否合适被杀死。badness()的计算方法如下,注意方根是

使用 int_sqrt()计算出来的整型估计数值。
total_vm_for_task
badness_for_task-(cpu_time_in_seconds) (cpu_time_in_minutes)
它所选择的是那个使用了最大量的内存空间而没有生存很久的进程,因为那些已经生存
了很久的进程不像是导致内存不足的原因,所以这个计算选择一个使用了大量的内存而没有
生存很久的进程。如果该进程是一个根进程或者具有 CAP_SYS_ADMIN 能力,数值就会除
以 4,因 为 具 有 根 特权 的 进 程 被 认 为 是 表 现 良 好 的 。 类 似 地,如果 该 进程具有 CAP_SYS

RAWIO(访问源设备的能力),数值会再除以 4,因为杀死一个可以访问硬件的进程是不值
得的。

13.4 杀死选定的进程

一旦选定一个进程,系统会再一次遍历该列表,发信号给所有与该进程共享相同的 mm_
struct 的进程(或它们都是线程)。如果该进程具有 CAP_SYS_RAWIO 能力,系统将会发出
一个 SIGTERM 信号,给它一个完全结束的机会,否则就会发出一个 SIGKILL 信号。

13.5 是这样吗?

是这样的。OOM 管理涉及到很多其他的子系统,但是又没有那么多。

13.6 2.6 中有些新特性

除引入了 VM 占用对象以外,2.6 内核中的 OOM 管理几乎保持不变。在 4.8 节中曾经
提到过 VM_ACCOUNT,在这里用许多 VMA 表示。系统在这些标识空间中对 VMA 进行
操作时,就进行额外的检查以确保有足够的内存。这种复杂操作背后的动机是要争取避免
OOM 终止进程。
含有 VM_ACCOUNT 标志位的区域有:进程,进程堆,mmap()使用 MAP_SHARED
映射的区域,可写的私有区域,以及建立 shmget()的区域,或者说,大部分用户空间区域都含
有 VM_ACCOUNT 标志位。
Linux 所占内存中的 VMA 数量由 vm_acct_memory())确定,vm_acct_memory())会增加
变量 committed_space 的大 小。 释放 VMA 时,vm_unacct_memory()会减少 committed_

第14章 结束语

在这里插入图片描述

123

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

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

相关文章

Stable Diffusion 超详细讲解

Stable Diffusion 超详细讲解 这篇文章是 《Stable Diffusion原理详解》的后续&#xff0c;在《Stable Diffusion原理详解》中我更多的是以全局视角讲解了 Stable Diffusion 的原理和工作流程&#xff0c;并未深入步骤细节。本文将在《Stable Diffusion原理详解》和《Diffusio…

【自用 三层代码结构】「go语言」项目中降低服务内的耦合性使用wire框架或工厂模式进行管理

文章目录 前言一、为什么需要分层&#xff1f;1. Server服务&#xff08;向外暴露gRPC接口&#xff09;&#xff1a;2. API服务&#xff08;向外暴露HTTP接口&#xff09;&#xff1a;3. domain模型的概念&#xff1a;DO&#xff08;Domain Object&#xff09;&#xff1a;DTO&…

Docker安装Mysql教程(linux)

本文主要讲解如何使用Docker去安装mysql 一、搜索镜像 docker search mysql二、拉取镜像 不指定版本&#xff0c;默认为最新版&#xff0c;这里用的5.7 docker pull mysql:5.7三、创建容器&#xff08;运行镜像&#xff09; 1、内外都使用3306端口&#xff08;确保你的宿主机3…

Umi微前端水印踩坑以及解决方案

最近公司需要在管理后台加一个水印方案~ 项目用的umi方案,以为就是改一个配置的问题,后来发现坑点还蛮多~ 希望此稳定能帮助到用umi 的你们. 一. 先来说说心路历程 坑点1 umi的水印适配只能在layout中进行配置,也就是路由配置中layout为false的页面无法配置水印,比如说登录页…

SCS【27】单细胞转录组之识别标记基因 (scran)

桓峰基因公众号推出单细胞生信分析教程并配有视频在线教程&#xff0c;目前整理出来的相关教程目录如下&#xff1a; Topic 6. 克隆进化之 Canopy Topic 7. 克隆进化之 Cardelino Topic 8. 克隆进化之 RobustClone SCS【1】今天开启单细胞之旅&#xff0c;述说单细胞测序的前世…

规则引擎架构-基于easy-rules

目录 概念理解实例和编码抽象出2条规则简单的规则引擎事实1的处理事实2的处理 easy-rules 规则的抽象和执行事实描述规则的抽象默认的规则 动态代理执行规则和动作规则的执行&#xff1a;org.jeasy.rules.core.DefaultRulesEngine#doFirepublic class RuleProxy implements Inv…

Java上进了,JDK21 要来了,并发编程再也不是噩梦了

更丝滑的并发编程模式 如果说之前的 JDK17你还觉得没必要折腾&#xff0c;那 JDK21确实有必要关注一下了。因为 JDK21 引入了一种新型的并发编程模式。 当前 Java 中的多线程并发编程绝对是另我们都非常头疼的一部分&#xff0c;感觉就是学起来难啃&#xff0c;用起来难用。但…

基础篇010.3 STM32驱动RC522 RFID模块之三:STM32软件模拟SPI驱动RC522

目录 1. 实验硬件及原理图 2. 利用STM32CubeMX创建MDK工程 2.1 STM32CubeMX工程创建 2.2 配置调试方式 2.3 配置时钟电路 2.4 配置时钟 2.5 配置GPIO 2.6 配置串口 2.7 项目配置 3. MDK工程驱动代码调试 3.1 按键、LED程序 3.2 SPI软件模拟程序 3.3 RC522驱动程序…

Unity制作二次元卡通渲染角色材质——1、资源分析

Unity制作二次元材质角色 回到目录 大家好&#xff0c;我是阿赵。 开始制作二次元角色材质之前&#xff0c;我觉得应该是先分析一下&#xff0c;我手上拿到的这个角色模型资源&#xff0c;总共有哪些信息是我们能用的。 所以这篇文章我不会分享具体的Shader&#xff0c;但我感觉…

基于RT-Thread快速上手SD NAND 虚拟文件系统

SD NAND 也称之为贴片式TF卡&#xff0c;贴片式SD卡&#xff0c;采用标准的SDIO接口&#xff0c;兼容SPI接口。下图所示为CS 新一代CS SD NAND NP1GCR01-AOW 大小为128M&#xff0c;对比128M的SD卡&#xff0c;可以看到贴片SD卡尺寸更小&#xff0c;不要SD卡座&#xff0c;占…

STM32杂乱笔记

问题都比较的基础和低级&#xff0c;仅记录一下。 问题一&#xff1a;stm32的某个.c文件中无法调用另一个.c中的指定变量&#xff0c;怎么解决&#xff1f; 以g_ADC_sample_vaule.Iu 为例&#xff0c;它是 drive_adc_info.c 里面的变量&#xff0c;想要在system_time_sequenc…

5.4 二叉树的性质和存储结构

博主简介&#xff1a;一个爱打游戏的计算机专业学生博主主页&#xff1a; 夏驰和徐策所属专栏&#xff1a;算法设计与分析 5.4.1 二叉树的性质 二叉树是一种特殊的树结构&#xff0c;它具有一些重要的性质&#xff1a; 1. 每个节点最多有两个子节点&#xff1a;二叉树的每个…

JavaScript:setInterval() 用法详解

文章目录 1 基本语法2 参数说明3 使用示例4 停止 setInterval() 方法 1 基本语法 setInterval() 是 JavaScript 中的一个内置函数&#xff0c;它用于在指定的间隔时间内重复执行一段代码&#xff0c;实现周期性操作。该函数的语法如下&#xff1a; setInterval(function, mil…

线程(Linux系统实现)

目录 1. 线程概述 2.主线程和子线程 3.创建线程 线程函数 创建线程示例 4.线程退出 线程退出的原理主要包括以下两个方面&#xff1a; 5.线程回收 回收子线程数据 6.线程分离 7.线程取消 8.线程 ID 比较 1. 线程概述 线程是轻量级的进程&#xff08;LWP&#xff…

【Java多线程进阶】常见的锁策略

前言 众所周知&#xff0c;拳击运动员是要分等级&#xff08;轻量级、重量级等等&#xff09;来参加比赛的&#xff0c;在 Java 多线程中 锁&#xff08;synchronized&#xff09; 也会根据锁的竞争程度来升级为相关“高等级”锁&#xff0c;为了更好的理解 synchronized 加锁机…

微信小程序node+vue医院挂号预约系统fun17

从而实现管理员后端&#xff1b;首页、个人中心、用户管理、专家管理、科室类型管理、职称类型管理、医院挂号管理、挂号信息管理、留言板管理、系统管理&#xff0c;专家后端&#xff1b;首页、个人中心、医院挂号管理、挂号信息管理、系统管理&#xff0c;用户前端&#xff1…

【Linux】网络基础+UDP网络套接字编程

只做自己喜欢做的事情&#xff0c;不被社会和时代裹挟着前进&#xff0c;是一件很奢侈的事。 文章目录 一、 网络基础1.局域网和广域网2.协议初识和网络协议分层&#xff08;TCP/IP四层模型&#xff09;3.MAC地址和IP地址&#xff08;子网掩码&#xff0c;路由表&#xff0c;I…

美国金融科技公司SoFi的增长难以持久,股价也将下跌

来源&#xff1a;猛兽财经 作者&#xff1a;猛兽财经 公司介绍 SoFi Technologies(SoFi)是一家来自美国的知名金融科技公司&#xff0c;自2011年成立以来&#xff0c;已成为领先的个人理财在线平台。SoFi为年轻的高收入客户提供多样化的产品和服务&#xff0c;包括学生和汽车贷…

如何在 Python 中使用断点调试

入门教程、案例源码、学习资料、读者群 请访问&#xff1a; python666.cn 实际上没人能一次就写出完美的代码&#xff0c;除了我。但是世界上只有一个我。 林纳斯托瓦兹&#xff08;Linux 之父&#xff09; 大家好&#xff0c;欢迎来到 Crossin的编程教室 &#xff01; 上面这段…

【CSS3系列】第二章 · CSS3 新增盒模型和背景属性

写在前面 Hello大家好&#xff0c; 我是【麟-小白】&#xff0c;一位软件工程专业的学生&#xff0c;喜好计算机知识。希望大家能够一起学习进步呀&#xff01;本人是一名在读大学生&#xff0c;专业水平有限&#xff0c;如发现错误或不足之处&#xff0c;请多多指正&#xff0…