是否通过加载到RAM的进程打开文件?


24

命令,例如sed,是程序,程序是文件内的编码逻辑,而这些文件位于硬盘上的某个位置。但是,在运行命令时,会将硬盘中文件的副本放到RAM中,它们在那里可以发挥作用并可以做些事情,因此称为进程

进程可以利用其他文件,对其进行读取或写入,如果这样做,这些文件称为打开文件。有一个命令可以列出所有正在运行的进程的所有打开文件:lsof

好的,所以我想知道的是,对于其他类型的文件,例如一个没有编程的逻辑,而仅仅是容器的命令,一个命令的双重寿命(一个在硬盘上,另一个在RAM中)是否也适用吗?数据。

我的假设是,进程打开的文件也将加载到RAM中。我不知道这是否是真的,这只是一种直觉。

拜托,有人能理解吗?


Answers:


27

但是,在运行命令时,会将硬盘中文件的副本放入RAM中,

这是错误的(通常)。当程序被执行时(通过execve(2) ...),进程(正在运行该程序)正在更改其虚拟地址空间,并且内核为此重新配置MMU。还请阅读有关虚拟内存的信息。请注意,应用程序可以使用mmap(2)munmapmprotect(2)来更改其虚拟地址空间,动态链接程序也可以使用它们(请参阅ld-linux(8))。另请参阅madvise(2)posix_fadvise(2)mlock(2)

内核将处理将来的页面错误,以从可执行文件中加载(延迟)页面。另请阅读有关打动的信息

内核维护着大页面缓存。另请参阅有关写时复制。另请参见readahead(2)

好的,所以我想知道的是,对于其他类型的文件,例如一个没有编程的逻辑,而仅仅是容器的命令,一个命令的双重寿命(一个在硬盘上,另一个在RAM中)是否也适用吗?数据。

对于系统调用就像读(2) write(2)之还使用页面缓存。如果要读取的数据位于其中,则不会执行任何磁盘IO。如果需要磁盘IO,则读取的数据很可能放在页缓存中。因此,实际上,如果您两次运行同一命令,则可能第二次没有对磁盘进行物理I / O(如果您有旧的旋转硬盘-而不是SSD-您可能会听到;或仔细观察硬盘上的LED)。

我建议读一本类似《操作系统:简单的三本书》的书(可免费下载,每章一个PDF文件)的书,其中介绍了所有这些内容。

另请参见Linux的吃了我的RAM等,并运行命令xosviewtophtopcat /proc/self/mapscat /proc/$$/maps(见PROC(5))。

PS。我专注于Linux,但是其他OS也具有虚拟内存和页面缓存。


35

不,打开文件不会自动将其读入内存。那将是非常低效的。sed例如,与许多其他Unix工具一样,它逐行读取其输入。它很少需要在内存中保留比当前行更多的内容。

随着awk它是相同的。它一次读取一条记录,默认情况下是一行。如果您将部分输入数据存储在变量中,那将是多余的,当然是1

有些人有做类似的事情的习惯

for line in $(cat file); do ...; done

由于外壳程序$(cat file)甚至在运行for循环的第一次迭代之前都必须完全扩展命令替换,因此这会将全部内容file入内存(读入执行for循环的外壳程序使用的内存)。这有点愚蠢,也不雅致。相反,应该做

while IFS= read -r line; do ...; done <file

这将file逐行处理(但请阅读理解“ IFS = read -r line”)。

不过,很少需要在shell中逐行处理文件,因为大多数实用程序都是面向行的(请参阅为什么使用shell循环处理文本被认为是不好的做法?)。

我正在从事生物信息学工作,当处理大量的基因组数据时,除非只保留在内存中绝对必要的数据位,否则我将无能为力。例如,当我需要剥离可用于识别VCF文件中包含DNA变体的1 TB数据集中的个体的数据位(因为无法公开该类型的数据)时,我会逐行进行用一个简单的awk程序进行处理(这是可能的,因为VCF格式是面向行的)。我没有将文件读入内存,在那儿进行处理,然后再写回去!如果文件被压缩,我将通过zcat或馈入文件gzip -d -c,由于gzip进行数据流处理,因此也不会将整个文件读入内存。

即使使用面向行的文件格式(例如JSON或XML),也可以使用流解析器处理大型文件,而无需将其全部存储在RAM中。

对于可执行文件,它稍微复杂一些,因为共享库可以按需加载和/或在进程之间共享(例如,请参阅加载共享库和RAM使用情况)。

缓存是我在这里没有提到的东西。这是使用RAM来保存经常访问的数据的动作。较小的文件(例如可执行文件)可以由OS缓存,以希望用户对它们进行多次引用。除了第一次读取文件外,随后的访问将访问RAM,而不是磁盘。诸如输入和输出缓冲之类的高速缓存通常对用户基本上是透明的,并且用于高速缓存事物的内存量可能会动态变化,具体取决于应用程序分配的RAM数量等。


1 从技术上讲,大多数程序可能一次使用显式缓冲或隐式地通过标准I / O库执行的缓冲读取输入数据的一部分,然后逐行向用户代码显示该块。与一次读取一个字符相比,读取磁盘块大小的倍数要高效得多。不过,该块的大小很少会超过几千字节。


您说过,可以将共享库加载到RAM中,也可以将仅包含数据的常规文件加载到RAM中,即使这样做没有意义吗?
sharkant

1
@sharkant当然。这只是将数据添加到变量(或数组,哈希或所讨论语言提供的任何数据结构),直到所有文件都存储完为止。使用awk{ a[i++] = $0 }将输入文件的所有行添加到数组a。您可能还想查找C函数mmap(),但此处的用法可能有点不合时宜。
库萨兰达

6
sed,,awk和其他面向行的程序不会一次将行读入内存,因为纯文本文件不包含行索引,并且文件系统API和低级存储硬件读取一个或多个“扇区”(通常为512)或1024字节)。如果在处理第一行之前操作系统将少于8KB的内容读入内存,我会感到惊讶。
罗素·波罗戈夫

5
尽管类似的实用工具sed一次只能将一行读入内存,但值得一提的是,操作系统将使用免费的ram来缓存文件,以便可以快速访问它们。如果运行sed的文件较小,则操作系统可以将整个文件缓存在内存中,而操作将完全在RAM中进行。参见:en.wikipedia.org/wiki/Page_cache
肖恩·道森

5
@sharkant在内存中具有完全可访问的文件是有用的(请参阅其他答案,mmap是此处的关键字系统调用)。例如,为了方便和快速地访问,数据库系统通常希望将整个数据库或至少某些索引映射到内存中。这并不一定意味着整个事情实际上都在内存中。操作系统可以“假装”文件在内存中。它告诉应用程序“这里是您的文件,在此内存范围内”,并且仅在完成读取后(就像交换了进程时一样),才实际读取数据。
乔纳斯·谢弗

5

否。虽然这些天有大量的RAM很棒,但是有一段时间RAM是非常有限的资源(我在带有2MB RAM的VAX 11/750上学习编程),而RAM中唯一的东西是活动的可执行文件和数据页活动进程以及缓冲区高速缓存中的文件数据。
刷新了缓冲区缓存,并换出了数据页。并且经常。只读的可执行页面已被覆盖,并标记了页面表,因此,如果程序再次触摸这些页面,则会从文件系统中对其进行分页。数据已从交换页面调入。如上所述,STDIO库以块为单位提取数据,并可以根据需要由程序获取:fgetc,fgets,fread等。使用mmap,可以将文件映射到进程的地址空间,例如共享库对象甚至常规文件。是的,无论它是否在RAM中(mlock),您都可以对其进行一定程度的控制,但是仅此而已(请参见mlock的错误代码部分)。


1
现在的说法是“您的RAM太小,无法容纳您的文件”,就像过去的VAX一样。
费德里科·波洛尼

1
@Federico_Poloni今天并非如此。在我的最后一位雇主那里,我们有一台工作站级的PC,它具有1Tb的RAM和仅0.5Tb的硬盘。(问题类别:小的输入,中等的输出,在计算过程中随机访问的大型数组)。
nigel222 '17
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.