AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • 主页
  • 系统&网络
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • 主页
  • 系统&网络
    • 最新
    • 热门
    • 标签
  • Ubuntu
    • 最新
    • 热门
    • 标签
  • Unix
    • 最新
    • 标签
  • DBA
    • 最新
    • 标签
  • Computer
    • 最新
    • 标签
  • Coding
    • 最新
    • 标签
主页 / unix / 问题 / 539502
Accepted
Gregg Leventhal
Gregg Leventhal
Asked: 2019-09-08 05:27:22 +0800 CST2019-09-08 05:27:22 +0800 CST 2019-09-08 05:27:22 +0800 CST

当 100% 分页到页面缓存的文件被另一个进程修改时会发生什么

  • 772

我知道当页面缓存页面被修改时,它被标记为脏并需要写回,但是在以下情况下会发生什么:

场景: 文件 /apps/EXE 是一个可执行文件,被完全分页到页面缓存中(它的所有页面都在缓存/内存中)并被进程 P 执行

然后持续发布将 /apps/EXE 替换为全新的可执行文件。

假设 1: 我假设进程 P(以及具有引用旧可执行文件的文件描述符的任何其他人)将继续使用旧的,在内存中 /apps/EXE 没有问题,并且任何尝试执行该路径的新进程都会得到新的可执行文件。

假设2: 我假设如果不是文件的所有页面都映射到内存中,那么一切都会好起来的,直到出现页面错误需要文件中的页面已被替换,并且可能会发生段错误?

问题 1: 如果你使用 vmtouch 之类的东西来锁定文件的所有页面,这会改变场景吗?

问题 2: 如果 /apps/EXE 位于远程 NFS 上,那会有什么不同吗?(我假设不是)

请更正或验证我的 2 个假设并回答我的 2 个问题。

假设这是一个带有某种 3.10.0-957.el7 内核的 CentOS 7.6 机器

更新:进一步考虑,我想知道这个场景是否与任何其他脏页场景没有什么不同..

我想写入新二进制文件的进程将执行读取并获取所有缓存页面,因为它全部被分页,然后所有这些页面都将被标记为脏。如果它们被锁定,它们将只是在 ref 计数变为零后占用核心内存的无用页面。

我怀疑当当前执行的程序结束时,其他任何东西都会使用新的二进制文件。假设这一切都是正确的,我想只有当只有部分文件被分页时才有趣。

linux
  • 3 3 个回答
  • 1694 Views

3 个回答

  • Voted
  1. Best Answer
    filbranden
    2019-09-08T07:17:29+08:002019-09-08T07:17:29+08:00

    然后持续发布将 /apps/EXE 替换为全新的可执行文件。

    这是重要的部分。

    发布新文件的方式是创建一个新文件(例如/apps/EXE.tmp.20190907080000),写入内容,设置权限和所有权,最后rename(2) 将其重命名为最终名称/apps/EXE,替换旧文件。

    结果是新文件有一个新的 inode 编号(这实际上意味着它是一个不同的文件。)

    旧文件有自己的 inode 编号,即使文件名不再指向它(或者不再有文件名指向该 inode) ,它实际上仍然存在。

    所以,这里的关键是,当我们在 Linux 中谈论“文件”时,我们最常真正谈论的是“inode”,因为一旦打开了文件,inode 就是我们保留对文件的引用。

    假设1:我假设进程P(以及具有引用旧可执行文件的文件描述符的任何其他人)将继续使用旧的,在内存中 /apps/EXE 没有问题,并且任何尝试执行该路径的新进程都会得到新的可执行文件。

    正确的。

    假设2:我假设如果不是文件的所有页面都映射到内存中,那么事情会很好,直到出现页面错误需要文件中的页面已被替换,并且可能会发生段错误?

    不正确。旧的 inode 仍然存在,因此使用旧二进制文件的进程中的页面错误仍然能够在磁盘上找到这些页面。

    通过查看运行旧二进制文件的进程的/proc/${pid}/exe符号链接(或等效的输出),您可以看到一些效果,这将表明名称不再存在,但 inode 仍然存在。lsof/app/EXE (deleted)

    您还可以看到二进制文件使用的磁盘空间只有在进程死亡后才会被释放(假设它是唯一打开该 inode 的进程。)检查df杀死进程之前和之后的输出,您会看到它的大小下降您认为不再存在的旧二进制文件。

    顺便说一句,这不仅适用于二进制文件,而且适用于任何打开的文件。如果您在进程中打开一个文件并删除该文件,该文件将保留在磁盘上,直到该进程关闭该文件(或死掉)。与硬链接如何保持计数器记录有多少名称指向磁盘中的 inode 类似,文件系统驱动程序(在 Linux 内核中)会记录内存中存在多少对该 inode 的引用,并且只有在来自正在运行的系统的所有引用都被释放后,才会从磁盘中释放该 inode。

    问题 1:如果您使用 vmtouch 之类的东西来锁定文件的所有页面,是否会改变场景

    这个问题基于错误的假设 2,即不锁定页面会导致段错误。它不会。

    问题 2:如果 /apps/EXE 在远程 NFS 上,会有什么不同吗?(我假设不是)

    它应该以相同的方式工作,而且大部分时间都是这样,但是 NFS 有一些“陷阱”。

    有时您会看到删除仍在 NFS 中打开的文件的工件(在该目录中显示为隐藏文件。)

    您还可以通过某种方式将设备编号分配给 NFS 导出,以确保在 NFS 服务器重新启动时这些设备不会被“重新洗牌”。

    但主要思想是一样的。NFS 客户端驱动程序仍然使用 inode,并且会在仍然引用 inode 时尝试保留文件(在服务器上)。

    • 13
  2. mosvy
    2019-09-08T09:13:36+08:002019-09-08T09:13:36+08:00

    假设2:我假设如果不是文件的所有页面都映射到内存中,那么一切都会好起来的,直到出现页面错误需要文件中的页面已被替换,并且可能会发生段错误?

    不,这不会发生,因为内核不会让您打开以写入替换当前正在执行的文件中的任何内容。这样的操作将因ETXTBSY[1]失败:

    cp /bin/sleep sleep; ./sleep 3600 & echo none > ./sleep
    [9] 5332
    bash: ./sleep: Text file busy
    

    当 dpkg 等更新二进制文件时,它不会覆盖它,而是使用rename(2)它简单地将目录条目指向一个完全不同的文件,并且任何仍然具有旧文件的映射或打开句柄的进程将继续使用它而不会出现问题.

    [1]保护ETXBUSY不扩展到其他也可以被视为“文本”(= 实时代码/可执行文件)的文件:共享库、java 类等;在被另一个进程映射时修改这样的文件将导致进程崩溃。在 linux 上,动态链接器尽职尽责地将MAP_DENYWRITE标志传递给mmap(2),但不要搞错——它没有任何作用。例子:

    $ cc -xc - <<<'void lib(){}' -shared -o lib.so
    $ cc -Wl,-rpath=. lib.so -include unistd.h -xc - <<<'
       extern void lib();
       int main(){ truncate("lib.so", 0); lib(); }
    '
    ./a.out
    Bus error
    
    • 8
  3. R.. GitHub STOP HELPING ICE
    2019-09-08T13:40:25+08:002019-09-08T13:40:25+08:00

    filbranden 的答案是正确的,假设连续发布过程通过rename. 如果不是,而是就地修改文件,情况就不同了。但是,您的心智模型仍然是错误的。

    不可能在磁盘上修改内容并与页面缓存不一致,因为页面缓存是规范版本并且已被修改。对文件的任何写入都是通过页面缓存进行的。如果它已经存在,则修改现有页面。如果它还不存在,尝试修改部分页面将导致整个页面被缓存,然后进行修改,就好像它已经被缓存了一样。跨越整个页面或更多页面的写入可以(并且几乎肯定会)优化分页它们的读取步骤。无论如何,只有一个文件的规范可修改版本(*)存在,即页面缓存中的那个.

    (*) 我有点撒谎。对于 NFS 和其他远程文件系统,可能不止一个,而且它们通常(取决于使用哪一个以及使用什么挂载和服务器端选项)没有正确实现写入的原子性和排序语义。这就是为什么我们中的许多人认为它们从根本上被破坏并拒绝将它们用于与使用并发写入的情况。

    • 4

相关问题

  • 有没有办法让 ls 只显示某些目录的隐藏文件?

  • 使用键盘快捷键启动/停止 systemd 服务 [关闭]

  • 需要一些系统调用

  • astyle 不会更改源文件格式

  • 通过标签将根文件系统传递给linux内核

Sidebar

Stats

  • 问题 205573
  • 回答 270741
  • 最佳答案 135370
  • 用户 68524
  • 热门
  • 回答
  • Marko Smith

    模块 i915 可能缺少固件 /lib/firmware/i915/*

    • 3 个回答
  • Marko Smith

    无法获取 jessie backports 存储库

    • 4 个回答
  • Marko Smith

    如何将 GPG 私钥和公钥导出到文件

    • 4 个回答
  • Marko Smith

    我们如何运行存储在变量中的命令?

    • 5 个回答
  • Marko Smith

    如何配置 systemd-resolved 和 systemd-networkd 以使用本地 DNS 服务器来解析本地域和远程 DNS 服务器来解析远程域?

    • 3 个回答
  • Marko Smith

    dist-upgrade 后 Kali Linux 中的 apt-get update 错误 [重复]

    • 2 个回答
  • Marko Smith

    如何从 systemctl 服务日志中查看最新的 x 行

    • 5 个回答
  • Marko Smith

    Nano - 跳转到文件末尾

    • 8 个回答
  • Marko Smith

    grub 错误:你需要先加载内核

    • 4 个回答
  • Marko Smith

    如何下载软件包而不是使用 apt-get 命令安装它?

    • 7 个回答
  • Martin Hope
    user12345 无法获取 jessie backports 存储库 2019-03-27 04:39:28 +0800 CST
  • Martin Hope
    Carl 为什么大多数 systemd 示例都包含 WantedBy=multi-user.target? 2019-03-15 11:49:25 +0800 CST
  • Martin Hope
    rocky 如何将 GPG 私钥和公钥导出到文件 2018-11-16 05:36:15 +0800 CST
  • Martin Hope
    Evan Carroll systemctl 状态显示:“状态:降级” 2018-06-03 18:48:17 +0800 CST
  • Martin Hope
    Tim 我们如何运行存储在变量中的命令? 2018-05-21 04:46:29 +0800 CST
  • Martin Hope
    Ankur S 为什么 /dev/null 是一个文件?为什么它的功能不作为一个简单的程序来实现? 2018-04-17 07:28:04 +0800 CST
  • Martin Hope
    user3191334 如何从 systemctl 服务日志中查看最新的 x 行 2018-02-07 00:14:16 +0800 CST
  • Martin Hope
    Marko Pacak Nano - 跳转到文件末尾 2018-02-01 01:53:03 +0800 CST
  • Martin Hope
    Kidburla 为什么真假这么大? 2018-01-26 12:14:47 +0800 CST
  • Martin Hope
    Christos Baziotis 在一个巨大的(70GB)、一行、文本文件中替换字符串 2017-12-30 06:58:33 +0800 CST

热门标签

linux bash debian shell-script text-processing ubuntu centos shell awk ssh

Explore

  • 主页
  • 问题
    • 最新
    • 热门
  • 标签
  • 帮助

Footer

AskOverflow.Dev

关于我们

  • 关于我们
  • 联系我们

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve