命令例如sed
,程序是程序,程序是文件内的编码逻辑,而这些文件位于硬盘上的某个位置。但是,当运行命令时,会从硬盘被放入内存,在那里他们复活并可以做事并被称为流程。
进程可以使用其他文件,读取或写入它们,如果它们这样做,这些文件称为打开文件。有一个命令可以列出所有正在运行的进程打开的所有文件:lsof
。
好的,所以我想知道的是,命令的双重生命,一个在硬盘上,另一个在 RAM 中是否也适用于其他类型的文件,例如那些没有逻辑编程的文件,而只是容器数据。
我的假设是,进程打开的文件也会加载到 RAM 中。我不知道这是不是真的,这只是一种直觉。
请问有人能看懂吗?
答案1
不,文件不会通过打开而自动读入内存。那将是非常低效的。sed
例如,与许多其他 Unix 工具一样,逐行读取其输入。它很少需要在内存中保留比当前行更多的内容。
与awk
它是一样的。它读到一个记录一次,默认为一行。如果将部分输入数据存储在变量中,那当然是额外的1。
有些人有这样的习惯
for line in $(cat file); do ...; done
$(cat file)
由于 shell在运行循环的第一次迭代之前必须完全扩展命令替换for
,因此将要将整个 读file
入内存(读入执行循环的 shell 使用的内存for
)。这有点愚蠢,而且也不优雅。相反,人们应该做
while IFS= read -r line; do ...; done <file
这将逐行处理file
(但请阅读理解“IFS=读取-r行”)。
不过,很少需要在 shell 中逐行处理文件,因为大多数实用程序都是面向行的(请参阅为什么使用 shell 循环处理文本被认为是不好的做法?)。
我从事生物信息学工作,在处理大量基因组数据时,除非我只将绝对必要的数据位保留在内存中,否则我将无法做太多事情。例如,当我需要从 VCF 文件中包含 DNA 变体的 1 TB 数据集中剥离可用于识别个体的数据位时(因为该类型的数据无法公开),我会逐行进行操作使用简单的awk
程序进行处理(这是可能的,因为 VCF 格式是面向行的)。我不要将文件读入内存,在那里处理它,然后再次写回!如果文件被压缩,我将通过zcat
or提供它gzip -d -c
,因为gzip
它对数据进行流处理,所以也不会将整个文件读入内存。
即使文件格式是不是面向行,如 JSON 或 XML,有流解析器可以处理巨大的文件,而无需将其全部存储在 RAM 中。
对于可执行文件,情况会稍微复杂一些,因为共享库可以按需加载,和/或在进程之间共享(请参阅共享库的加载和 RAM 使用情况, 例如)。
缓存是我在这里没有提到的。这是使用 RAM 来保存经常访问的数据块的操作。较小的文件(例如可执行文件)可能会被操作系统缓存,希望用户能够多次引用它们。除了第一次读取文件之外,后续访问将针对 RAM 而不是磁盘。缓存(例如输入和输出的缓冲)通常对用户来说基本上是透明的,并且用于缓存内容的内存量可能会根据应用程序分配的 RAM 量等动态变化。
1 从技术上讲,大多数程序可能一次读取一块输入数据,或者使用显式缓冲,或者通过标准 I/O 库所做的缓冲隐式地读取,然后将该块逐行呈现给用户的代码。读取磁盘块大小的倍数比一次读取一个字符要高效得多。不过,这个块的大小很少会大于几千字节。
答案2
然而,当命令运行时,硬盘上的文件副本会被放入 RAM 中,
这是错误的(一般而言)。当程序执行时(通过执行(2)...)进程(运行该程序)正在改变它的虚拟地址空间并且内核正在重新配置内存管理单元为了这个目的。另请阅读有关虚拟内存。请注意,应用程序可以使用以下命令更改其虚拟地址空间映射(2)&&munmap
保护(2),也被使用动态链接器(看ld-linux(8))。也可以看看疯狂的维斯(2)&posix_fadvise(2)&时钟锁(2)。
未来页面错误将由内核处理以从可执行文件加载(延迟)页面。另请阅读有关殴打。
内核维护着一个大的页面缓存。另请阅读有关写时复制。也可以看看预读(2)。
好的,所以我想知道的是,命令的双重生命,一个在硬盘上,另一个在 RAM 中是否也适用于其他类型的文件,例如那些没有逻辑编程的文件,而只是容器数据。
为了系统调用喜欢阅读(2)&写(2)还使用页面缓存。如果要读取的数据位于其中,则不会执行磁盘 IO。如果需要磁盘IO,读取的数据很可能会放入页缓存中。因此,在实践中,如果您运行相同的命令两次,则可能会发生第二次没有对磁盘执行任何物理 I/O 的情况(如果您有一个旧的旋转硬盘 - 而不是 SSD - 您可能会听到这样的说法;或仔细观察硬盘 LED)。
我建议读一本书,比如操作系统:三个简单的部分(可免费下载,每章一个 PDF 文件)解释了这一切。
也可以看看Linux 吃掉了我的 RAM并运行诸如xosview
、top
、htop
或cat /proc/self/maps
之类的命令cat /proc/$$/maps
(请参阅过程(5))。
附言。我主要关注 Linux,但其他操作系统也有虚拟内存和页面缓存。
答案3
不。虽然如今拥有千兆 RAM 非常棒,但曾经有一段时间 RAM 是一种非常有限的资源(我在具有 2MB RAM 的 VAX 11/750 上学习编程),RAM 中唯一的东西就是活动的可执行文件和数据页活动进程的数量以及缓冲区高速缓存中的文件数据。
缓冲区高速缓存被刷新,数据页被换出。并且时常频繁。只读可执行页面被覆盖并标记页表,因此如果程序再次触及这些页面,它们将从文件系统调入。数据从交换区调入。如上所述,STDIO 库以块的形式提取数据,并由程序根据需要获取:fgetc、fgets、fread 等。使用 mmap,可以将文件映射到进程的地址空间,例如使用共享库对象甚至常规文件。是的,您可能有一定程度的控制权,无论它是否在 RAM 中(mlock),但也仅限于此(请参阅 mlock 的错误代码部分)。