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


152

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

我应该如何打开文件/proc/并从中读取文件,不要担心其他进程(或操作系统本身)会在同一时间对其进行更改?


29
+1。那是一个该死的好问题。我只希望我能得到答案,但是由于我之前已经做了很多这样的事情,所以我希望能找到答案。
paxdiablo 2011年

1
我敢肯定,只要阅读一下,它就会为您提供连接列表,以及拥有每个连接的UID,就像您打开它时一样。但是,我找不到记录在案的文件,因此暂时将其作为注释。
Tim Post

3
简单的答案显然是肯定的,因为它不是文件-读取文件应始终安全。下次阅读后答案可能不一致,但这是安全的。
罗里·阿尔索普

这就是为什么您应该改用sysctl的原因。(它的系统调用也更少)
好人2012年

@GoodPerson- 例如,这如何sysctl帮助我解析/proc/net/tcp/文件?
基里尔·基洛夫

Answers:


111

一般来说,没有。(因此,这里的大多数答案都是错误的。)根据您想要的属性,它可能是安全的。但是,如果您过多地假设中的文件一致性,则很容易在代码中导致错误/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_file2009年仍在迁移使用。我敢打赌,仍有一些使用较旧的机制,甚至没有这种原子性。这些警告很少记录。对于给定的文件,您唯一的保证就是阅读源代码。

对于/proc/net/tcp,您可以阅读并解析每一行而不必担心。但是,如果您尝试一次从多行中得出任何结论,请注意,在阅读时,其他进程和内核对其进行更改,并且很可能会产生错误。


1
readdir原子性如何?喜欢阅读/ proc / self / fd吗?安全吗?
socketpair

不,它回答了这个问题,但增加有关如何检查的正常运行时间,你可以用clock_gettime(2)CLOCK_MONOTONIC(尽管也许有一个技术性问题,我不知道在这里,但我个人只因为开机时间看到它)。对于Linux,您还可以选择sysinfo(2)
Pryftan

44

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

内核所做的全部工作就是使用sprintf-like函数将其内部状态打印到其自己的内存中,并且每当您发出read(2)系统调用时,该内存就会被复制到用户空间中。

内核以与常规文件完全不同的方式处理这些调用,这可能意味着您将在读取时准备好要读取的数据的整个快照。 open(2),同时内核确保并发调用是一致且原子的。我没有在任何地方阅读过,但是没有其他意义。

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

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


3
@Ignacio:我只是将OP指向这个方向,因为给我留下的印象是他认为proc文件是打开供内核写入的普通文件。
Blagovest Buyukliev 2011年

4
您的建议来看特定文件的实现是好的。不幸的open()是,对于许多文件,尤其/proc/net/tcp是与OP有关的文件,所有快照都被快照的猜测是错误的。如果您考虑提供这些语义的成本,这是有道理的-您必须执行诸如锁定记录所有这些TCP连接的内部数据结构之类的操作,即使在很长一段时间内,即使在繁忙的系统上,它都将是一场灾难足以扫描并将数据格式化到缓冲区。有关实际情况的详细信息,请参见我的答案。
格雷格·

16

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

编辑

有关更多信息,请参见Linux内核doc的proc文档中的第1.4章网络。我找不到该信息是否随时间变化。我以为它是开放式的,但无法给出确切的答案。

编辑2

根据Sco doc(不是linux,但我可以肯定* nix的所有形式都具有这种功能)

尽管进程状态以及/ proc文件的内容可能会在瞬间之间发生变化,但是/ proc文件的单个read(2)可以保证返回状态的``合理''表示,即读操作为进程状态的原子快照。没有这样的保证适用于对正在运行的进程的/ proc文件应用的连续读取。另外,对于应用到as(地址空间)文件的任何I / O,特别不能保证原子性。任何进程的地址空间的内容都可以由该进程的LWP或系统中的任何其他进程同时修改。


3
“我认为” ?有一个明确的答案会很不错的:)
static_rtti 2011年

给定内核中/ proc的实现,对于Linux也是如此。如果您在单个read调用中读取了一个procfs文件,那么它是一致的-当然,假设您读取的proc文件已在内核端正确实现。
Erik

8
我认为您不可能提供比SCO更糟糕的信息来源,并试图将其proc视为在不同内核之间具有相似的行为(甚至假设它存在-在Unix系统中不必如此) )会给您带来很多伤害。
尼古拉斯·奈特

1
@Nicholas:好吧,在内核文档中找不到一些明确的答案,如果知道的话,请随时指出。
布鲁斯

2
有趣的是,SCO文档这么说。不幸的是,这在Linux中并不总是正确的,尤其/proc/net/tcp是对于OS而言,它并不是正确的,这是OP的主要关注点。相反,仅输出中的每一行都是原子的。有关详细信息,请参见我的答案。
格雷格·

14

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

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


4
不幸的是,许多文件/proc实际上并没有提供一致性。有关详细信息,请参见我的答案。
格雷格·

3
此外,__proc_file_read()不推荐使用seq_file。请看长条注释正上方的听起来很气愤的评论(由Linus撰写)。
格雷格·

6

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

linux-2.6.27.8-lpc32xx/net/ipv4/raw.c934行的文件... 例如包含

    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读取肯定比更新信息少得多。

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

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


真的很抱歉,我没有得到太多。因此,您的意思是说,如果我使用ifstream,那将是不安全的,但是如果我使用read它将会是安全的?还是ifstream内部使用read?你有什么建议?
Kiril Kirov

@Kiril:很抱歉给您带来混乱。这是对数据/proc/net/tcp格式的解释,并且完全独立于任何人的读取方式。
wallyk 2011年

1
是的 您的猜测是正确的,因为不同的行(中的/proc/net/tcp)不是来自同一快照。看到我的答案以获得一些解释。
格雷格·

3

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


我这样做是为了获取和使用一些有关自己过程的信息(对于我的PID,使用getpid())。因此,它必须安全。
Kiril Kirov

1
是的,我认为这是完全安全的。
R .. GitHub停止帮助ICE,

我不同意子进程的行为比其他任何进程都要好。就/proc界面而言,它们都具有相同的弱点和优点。无论如何,OP询问有关设备驱动程序的信息,而不是过程。
wallyk 2011年

1
如果pid N是您的子进程,则可以确保pid N仍引用相同的进程(可能已终止),直到您对其调用wait-family函数为止。这样可以确保没有种族。
R .. GitHub停止帮助ICE,

-1的泛滥是什么,没有任何解释?
R .. GitHub停止帮助ICE,

2

当您从/ proc文件读取时,内核会调用一个已预先注册为该proc文件的“读取”功能的函数。请参阅__proc_file_readfs / proc / generic.c中的函数。

因此,proc读取的安全性仅与内核为满足读取请求而调用的函数一样安全。如果该函数正确锁定了它所触摸的所有数据并在缓冲区中返回给您,那么使用该函数进行读取就完全安全了。由于proc文件(例如用于满足对/ proc / net / tcp的读取请求的文件)已经存在了一段时间,并且经过了严格的审查,因此它们的安全性与您所要求的一样安全。实际上,许多常见的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()实现。

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.