解析/ proc /文件是否安全?

Kir*_*rov 148 c c++ unix linux procfs

我想解析/proc/net/tcp/,但它安全吗?

我应该如何打开和读取文件,/proc/而不是害怕,某些其他进程(或操作系统本身)将在同一时间更改它?

Gre*_*ice 105

一般来说,没有.(所以这里的大多数答案都是错误的.)它可能是安全的,取决于你想要的属性.但是,如果您对文件的一致性有太多假设,那么很容易在代码中出现错误/proc.例如,请参阅此错误,该错误来自假设/proc/mounts是一致的快照.

例如:

  • /proc/uptime完全原子的,正如有人在另一个答案中提到的 - 但仅限于Linux 2.6.30,这还不到两年.因此,即使这个微小的,微不足道的文件在那之前也会受到竞争条件的影响,并且仍然存在于大多数企业内核中.请参阅fs/proc/uptime.c当前源或使其成为原子的提交.在2.6.30之前的内核中,您可以使用open该文件,read如果您稍后read再次回来,您获得的文件将与第一部分不一致.(我刚刚演示了这一点 - 亲自试试吧.)

  • /proc/mounts在单个read系统调用中原子的.因此,如果您同时read拥有整个文件,则可以获得系统上安装点的单个一致快照.但是,如果您使用多个read系统调用 - 如果文件很大,这正是如果您使用普通I/O库并且不特别注意此问题将会发生的情况 - 您将受到竞争的影响条件.您不仅不会获得一致的快照,而且在您开始之前存在并且永不停止存在的装载点可能会在您看到的内容中丢失.要看到它的原子的一个read(),看m_start()fs/namespace.c,看它抢守卫挂载点的列表,它保持,直到信号m_stop()时,当被称为read()完成.要查看可能出现的问题,请查看去年(我在上面链接过的同一个)中的这个错误,这些错误是高质量的软件/proc/mounts.

  • /proc/net/tcp,这是你实际要问的那个,甚至不那么一致.它仅在表的每一行内都是原子的.看到这一点,看看listening_get_next()net/ipv4/tcp_ipv4.cestablished_get_next()正下方的同一个文件,看他们拿出依次对每个条目的锁.我没有方便的repro代码来证明行与行之间缺乏一致性,但是没有锁(或其他任何东西)可以使它保持一致.如果你考虑一下这是有道理的 - 网络通常是系统中非常繁忙的一部分,因此在这个诊断工具中提供一致的视图并不值得花费.

/proc/net/tcp在每行中保持原子的另一块是缓冲seq_read(),你可以读fs/seq_file.c.这确保了一旦您read()分开一行,整行的文本就会保存在缓冲区中,以便下一read()行将在开始新行之前获得该行的其余部分./proc/mounts即使您执行多次read()调用,也使用相同的机制来保持每一行的原子性,并且它也是/proc/uptime新内核用于保持原子性的机制.这种机制也没有缓冲整个文件,因为内核是谨慎的内存使用.

大多数文件/proc都至少/proc/net/tcp与每个文件一致,每条文件在一个条目的一致条件下,无论它们提供什么信息,因为大多数文件都使用相同的seq_file抽象./proc/uptime但是,正如该示例所示,一些文件仍在迁移,seq_file最近才在2009年使用; 我敢打赌仍有一些使用较旧的机制,甚至没有那种原子性水平.这些警告很少记录在案.对于给定的文件,您唯一的保证是阅读源.

在这种情况下/proc/net/tcp,您可以阅读它并解析每一行而不用担心.但是如果你试图一次从多行得出任何结论 - 小心,其他进程和内核在你阅读时改变它,你可能正在创建一个bug.

  • readdir原子性怎么样?喜欢阅读 /proc/self/fd 吗?安全吗? (2认同)

Bla*_*iev 43

虽然在文件/proc出现在用户空间普通文件,他们是不是真的从用户空间(支持标准的文件操作文件,而是实体open,read,close).请注意,这与内核更改磁盘上的普通文件完全不同.

所有内核都使用类似sprintf函数将其内部状态打印到自己的内存中,并且每当您发出read(2)系统调用时,该内存都会被复制到用户空间中.

内核以与常规文件完全不同的方式处理这些调用,这可能意味着您将读取的数据的整个快照可以在您使用时准备就绪open(2),而内核确保并发调用是一致的和原子的.我没有在任何地方阅读,但除此之外没有任何意义.

我的建议是看看你的特定Unix风格的proc文件的实现.这实际上是一个不受标准管理的实现问题(输出的格式和内容).

最简单的例子是uptime在Linux中实现proc文件.注意如何在提供的回调函数中生成整个缓冲区single_open.

  • 您建议查看特定文件的实现是好的.不幸的是,对于许多文件而言,所有在`open()`上快照的猜测是错误的,特别是对于OP所关注的`/ proc/net/tcp`.如果你考虑提供这些语义的成本,这是有道理的 - 你必须做一些事情,比如锁定记录所有那些TCP连接的内部数据结构,即使你只持有它,它在繁忙的系统上也是一场灾难足以扫描并格式化数据到缓冲区.有关实际情况的详细信息,请参阅我的答案. (4认同)
  • @Ignacio:我只是把OP指向这个方向,因为我留下的印象是他认为`proc`文件是由内核打开的普通文件. (3认同)

Bru*_*uce 16

/ proc是一个虚拟文件系统:事实上,它只是提供了一个方便的内核内部视图.读它肯定是安全的(这就是为什么它在这里)但从长远来看它有风险,因为这些虚拟文件的内部可能会随着更新版本的内核而发展.

编辑

有关Linux内核文档中的proc文档中可用的更多信息,第1.4章网络我无法找到信息如何随时间演变的信息.我以为它在开放时被冻结了,但无法得到明确的答案.

EDIT2

根据Sco doc(不是linux,但我很确定*nix的所有风格都像那样)

虽然进程状态以及因此/ proc文件的内容可以瞬间改变,但是/ proc文件的单个read(2)保证返回一个"理智"的状态表示,即读取将是进程状态的原子快照.对于正在运行的进程,应用于/ proc文件的连续读取不适用此类保证.此外,对于应用于as(地址空间)文件的任何I/O,特别不保证原子性; 任何进程的地址空间的内容可能会被该进程的LWP或系统中的任何其他进程同时修改.

  • 我不认为你能提出比SCO更糟糕的信息来源,并试图将`proc`视为在不同内核之间有相似的行为(或者甚至假设它存在 - 它不必在一个Unix系统)会给你一个伤害的世界. (8认同)
  • "我认为" ?有一个明确的答案会很高兴:) (3认同)
  • 有趣的是,SCO文件说.不幸的是,在Linux中并不总是如此,特别是对于`/ proc/net/tcp`来说并非如此,这是OP的主要关注点.相反,输出中只有每一行都是原子的.详情请见我的回答. (2认同)

Job*_*Job 14

Linux内核中的procfs API提供了一个接口,以确保读取返回一致的数据.阅读评论__proc_file_read.大评论块中的项目1)解释了这个界面.

话虽这么说,当然要特定的proc文件的实现才能正确使用这个接口,以确保其返回的数据是一致的.所以,回答你的问题:不,内核不保证在读取过程中proc文件的一致性,但它提供了实现这些文件的方法来提供一致性.

  • 不幸的是,`/ proc`中的许多文件实际上并不提供一致性.详情请见我的回答. (4认同)
  • 另外,不推荐使用`__proc_file_read()`而使用`seq_file`.在长块评论之上看到相当恼人的评论(由Linus发表). (3认同)

wal*_*lyk 6

我有Linux 2.6.27.8的源代码,因为我现在正在嵌入式ARM目标上进行驱动程序开发.

linux-2.6.27.8-lpc32xx/net/ipv4/raw.c例如,第934行的文件....

    seq_printf(seq, "%4d: %08X:%04X %08X:%04X"
            " %02X %08X:%08X %02X:%08lX %08X %5d %8d %lu %d %p %d\n",
            i, src, srcp, dest, destp, sp->sk_state,
            atomic_read(&sp->sk_wmem_alloc),
            atomic_read(&sp->sk_rmem_alloc),
            0, 0L, 0, sock_i_uid(sp), 0, sock_i_ino(sp),
            atomic_read(&sp->sk_refcnt), sp, atomic_read(&sp->sk_drops));
Run Code Online (Sandbox Code Playgroud)

哪个输出

[wally@zenetfedora ~]$ cat /proc/net/tcp
  sl  local_address rem_address   st tx_queue rx_queue tr tm->when retrnsmt   uid  timeout inode                                                     
   0: 017AA8C0:0035 00000000:0000 0A 00000000:00000000 00:00000000 00000000     0        0 15160 1 f552de00 299
   1: 00000000:C775 00000000:0000 0A 00000000:00000000 00:00000000 00000000     0        0 13237 1 f552ca00 299
...
Run Code Online (Sandbox Code Playgroud)

在函数中raw_sock_seq_show(),它是procfs处理函数层次结构的一部分.在read()/proc/net/tcp文件发出请求之前不会生成文本,这是一种合理的机制,因为procfs读取肯定比更新信息少得多.

一些驱动程序(例如我的)用单个实现proc_read函数sprintf().核心驱动程序实现中的额外复杂性是处理可能非常长的输出,这可能在单次读取期间不适合中间内核空间缓冲区.

我用一个使用64K读缓冲区的程序对它进行了测试,但是在我的系统中导致了一个3072字节的内核空间缓冲区,以便proc_read返回数据.需要使用前进指针进行多次调用才能获得比返回的更多文本更多的内容.当需要多个i/o时,我不知道使返回数据一致的正确方法.当然,每个条目/proc/net/tcp都是自洽的.并行线有可能在不同时间快照.