繁体   English   中英

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

[英]Is it safe to parse a /proc/ file?

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

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

一般来说,没有。 (所以这里的大多数答案都是错误的。)它可能是安全的,取决于你想要的属性。 但是,如果你对/proc中的文件的一致性考虑太多,那么很容易在代码中出现bug。 例如,请参阅此假设/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()的原子,请查看fs/namespace.c中的m_start()并查看它获取一个保护挂载点列表的信号量,它保持到m_stop() ,这在read()时被调用read()完成了。 要查看可能出现的问题,请查看去年 (我在上面链接过的同一个)中的这个错误, 这些错误是高质量的软件,可以快速读取/proc/mounts

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

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

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

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

尽管/proc文件在用户空间中显示为常规文件,但它们实际上不是文件,而是支持来自用户空间( openreadclose )的标准文件操作的实体。 请注意,这与内核更改磁盘上的普通文件完全不同。

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

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

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

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

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

编辑

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

EDIT2

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

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

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

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

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

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

    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));

哪个输出

[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
...

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

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

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

缺少未知错误, /proc中没有竞争条件会导致读取损坏的数据或混合新旧数据。 从这个意义上说,它是安全的。 然而,仍有竞争条件,你从/proc读取的大部分数据一旦生成就可能过时,甚至更多的时候你会阅读/处理它。 例如,进程可以随时死亡,并且可以为新进程分配相同的pid; 您可以在没有竞争条件的情况下使用的唯一流程ID是您自己的子流程。 网络信息(开放端口等)和/proc大部分信息都是一样的。 我认为依赖/proc任何数据都是准确的,这是不好的和危险的做法,除了关于你自己的进程和潜在的子进程的数据。 当然,从/proc向用户/管理员提供其他信息以获取信息/日志/等可能仍然是有用的。 目的。

当你从/ proc文件中读取时,内核正在调用一个事先已经注册为该proc文件的“读取”函数的函数。 请参阅fs / proc / generic.c中的__proc_file_read函数。

因此,proc读取的安全性仅与内核调用以满足读取请求的函数一样安全。 如果该函数正确锁定它接触的所有数据并在缓冲区中返回给您,则使用该函数读取是完全安全的。 因为用于满足对/ proc / net / tcp的读取请求的proc文件已经存在了一段时间并且经过了严格审查,所以它们就像你要求的那样安全。 事实上,许多常见的Linux实用程序依赖于从proc文件系统读取并以不同的方式格式化输出。 (在我的头顶,我认为'ps'和'netstat'这样做)。

一如既往,你不必为此承担责任; 你可以看看源头来平息你的恐惧。 proc_net_tcp.txt中的以下文档告诉您/ proc / net / tcp的“读取”函数的位置,因此您可以查看从该proc文件读取时运行的实际代码,并验证自己没有锁定危险。

本文档描述了接口/ proc / net / tcp和/ proc / net / tcp6。
请注意,不推荐使用这些接口而使用tcp_diag。 这些/ proc接口提供有关当前活动的TCP连接的信息,分别由net / ipv4 / tcp_ipv4.c中的tcp4_seq_show()和net / ipv6 / tcp_ipv6.c中的tcp6_seq_show()实现。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM