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

时间:2011-04-19 08:12:45

标签: c++ c linux unix procfs

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

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

7 个答案:

答案 0 :(得分:105)

一般情况下,没有。(所以这里的大部分答案都是错误的。)可能是安全的,具体取决于您想要的属性。但是,如果你对/proc中文件的一致性有太多考虑,那么很容易在代码中出现错误。例如,请参阅this bug which came from assuming that /proc/mounts was a consistent snapshot

例如:

  • /proc/uptime 完全原子,就像另一个答案中提到的那样 - 但是仅限Linux 2.6.30 ,不到两年。因此,即使这个微小的,微不足道的文件在那之前也会受到竞争条件的影响,并且仍然存在于大多数企业内核中。有关当前来源,请参阅fs/proc/uptime.c,或the commit that made it atomic。在2.6.30之前的内核中,你可以open文件,read一点,然后如果你以后又回来read,那么你获得的文件就会不一致与第一件。 (我刚刚演示了这一点 - 亲自尝试一下。)

  • /proc/mounts 在单个read系统调用中原子。因此,如果您read整个文件全部您可以立即获得系统上挂载点的单一一致快照。但是,如果您使用多个read系统调用 - 如果文件很大,这正是如果您使用普通I / O库并且不特别注意此问题将会发生的情况 - 您将受到竞争条件的影响。您不仅不会获得一致的快照,而且在您开始之前存在并且永不停止存在的装载点可能会在您看到的内容中丢失。要查看它是一个read()的原子,请查看m_start() in fs/namespace.c并查看它获取一个保护挂载点列表的信号量,它保持到m_stop(),在read()时调用1}}完成了。要查看可能出现的问题,请参阅/proc/mounts的其他高质量软件中的this bug from last year(我在上面链接的相同内容)。

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

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

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

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

答案 1 :(得分:43)

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

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

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

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

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

答案 2 :(得分:16)

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

修改

proc documentation in Linux kernel doc,第1.4章网络中提供了更多信息 我无法找到信息如何随着时间的推移而演变。我以为它在开放时被冻结了,但无法得到明确的答案。

<强> EDIT2

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

  

虽然过程状态和   因此/ proc的内容   文件可以从即时更改为   即时,单个读取(2)/ proc   文件保证返回一个   ``理智''代表国家,那   是的,读取将是一个原子   进程状态的快照。   没有这样的保证适用于   连续读取应用于/ proc   正在运行的进程的文件。在   另外,原子性是具体的   不保证适用于任何I / O.   as(地址空间)文件;该   任何进程地址的内容   空间可能会同时修改   通过该流程或任何其他流程的LWP   系统中的过程。

答案 3 :(得分:14)

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

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

答案 4 :(得分:6)

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

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

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

答案 5 :(得分:3)

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

答案 6 :(得分:2)

当你从/ 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()   和tcp6_seq_show()   net / ipv6 / tcp_ipv6.c分别。