我想解析/proc/net/tcp/
,但是这样做安全吗?
如何打开和读取/proc/
中的文件,而不用担心其他进程(或操作系统本身)会在同一时间内更改它?
一般而言,不行。 (所以这里的大多数答案都是错的。) 它可能是安全的,这取决于您想要的属性。但是如果您过于假定 /proc
中的文件一致性,很容易在代码中出现 bug。例如,参见这个因为假定 /proc/mounts
是一致快照而导致的 bug。
例如:
/proc/uptime
是完全原子的,正如其他回答中提到的那样——但仅自从 Linux 2.6.30 以来,也就是不到两年前。因此即使是这个微小的、琐碎的文件,在此之前都存在竞争条件,而且在大多数企业内核中仍然存在。请参见当前源码的fs/proc/uptime.c
,或使其原子化的提交。在 2.6.30 之前的内核上,您可以 open
文件,read
一点内容,然后如果稍后再次 read
,则获取的片段将与第一个片段不一致。(我刚刚演示了这一点——您也可以自行尝试。)
/proc/mounts
是在单个 read
系统调用内是原子性的。因此,如果您一次读取整个文件,您将获得系统上挂载点的单个一致快照。但是,如果您使用多个 read
系统调用--如果文件很大,则使用普通I/O库并且不特别注意此问题,这正是会发生的--您将受到竞争条件的影响。您不仅不会获得一致的快照,而且在您开始之前存在且从未停止存在的挂载点可能会在您看到的内容中消失。要查看它对于一个 read()
是原子的,请查看fs/namespace.c
中的m_start()
并查看它获取了保护挂载点列表的信号量,直到m_stop()
被调用为止,后者在完成read()
时被调用。要了解可能出现的问题,请参见去年的这个错误(与我上面链接的相同),这是一种高质量软件,它轻率地读取了/proc/mounts
。
/proc/net/tcp
是你实际询问的文件,它与其他文件相比甚至更不一致。 它仅在每个表格行内是原子的。 要了解这一点,请查看net / ipv4 / tcp_ipv4.c
中的 listening_get_next()
和下面的established_get_next()
,并查看它们在逐个条目上获取的锁。 我没有方便的复制代码来演示从一行到另一行的不一致性,但是那里没有锁定(或其他任何东西)可以使其一致。 如果您考虑一下,这是有道理的-网络通常是系统中繁忙的部分,因此在此诊断工具中呈现一致的视图并不值得。
使/proc/net/tcp
在每个行内保持原子的另一个要素是seq_read()
中的缓冲区,您可以通过在fs / seq_file.c
中了解其中内容。 这确保了一旦读取一个行的一部分,整个行的文本都会保存在缓冲区中,以便下一个read()
将在开始新行之前获取该行的其余部分。 同样的机制也在/proc/mounts
中用于使每行原子,即使您进行多个read()
调用,这也是较新内核中/proc/uptime
使用的机制。 该机制不会缓存整个文件,因为内核对内存使用非常谨慎。
/proc
目录下的大多数文件至少与/proc/net/tcp
一样一致,每行都是提供信息条目的一张图片,因为它们大多使用相同的seq_file
抽象。然而,正如/proc/uptime
例子所示,一些文件直到2009年仍在迁移使用seq_file
,我敢打赌仍有一些使用较旧机制且连那种程度的原子性都没有。这些注意事项很少有文档记录。对于给定的文件,你唯一的保证就是阅读源代码。
在/proc/net/tcp
的情况下,您可以阅读并解析每个行而不担心。但是,如果您试图从多个行中得出任何结论,请注意,其他进程和内核正在您阅读时更改它,您可能会创建一个bug。
clock_gettime(2)
和 *CLOCK_MONOTONIC
*(尽管我可能不知道这里是否有技术细节,但我个人只看到它自启动以来的时间)。对于Linux,您还可以选择使用 *sysinfo(2)
*。 - Pryftan/proc
目录下的文件在用户空间中看起来像普通文件,但它们实际上不是文件,而是支持用户空间标准文件操作(open
、read
、close
)的实体。请注意,这与内核更改磁盘上的普通文件非常不同。sprintf
函数将其内部状态打印到自己的内存中,每当您发出read(2)
系统调用时,该内存就会被复制到用户空间。open(2)
它的时候准备好了,而内核确保并发调用是一致和原子的。我没有在任何地方读到过这一点,但否则就没有意义。uptime
proc文件的实现。请注意,整个缓冲区是在提供给single_open
的回调函数中生成的。proc
文件是内核可写的普通文件。” - Blagovest Buyuklievopen()
处快照是错误的,尤其是对于 OP 关心的 /proc/net/tcp
文件。这是有道理的,如果你考虑提供这些语义的成本--你必须做一些像锁定记录所有这些 TCP 连接的内部数据结构的事情,即使你只持有它足够长的时间来扫描并将数据格式化到缓冲区,对于一个繁忙的系统来说也是灾难性的。请参阅我的回答,了解实际发生的情况的详细信息。 - Greg Price/proc是一个虚拟文件系统:实际上,它只是提供了一个方便查看内核内部的视图。它肯定是安全的(这就是为什么它在这里),但从长远来看可能存在风险,因为这些虚拟文件的内部可能随着新版本的内核而发生变化。
编辑
有关更多信息,请参见Linux内核文档中的proc文档,第1.4章网络。我无法找到信息如何随时间演变。我认为它在开放时被冻结,但不能得到明确的答案。
编辑2
根据Sco文档(不是linux,但我很确定所有*nix都是如此行为)
尽管进程状态和因此/ proc 文件的内容可以从瞬间到 瞬间更改,但单个read(2)/ proc 文件保证返回 进程的``健全''表示, 也就是说,阅读将是原子性的 该进程的状态快照。 不能保证对连续读取的/ proc 文件适用任何这样的保证,对于正在运行的进程。 此外,特别不保证原子性 对as(地址空间)文件使用的任何I / O;该 任何进程的地址内容 空间可能会被同时修改 由该进程或任何其他进程 在系统中。
proc
视为在不同内核之间有类似行为的东西(甚至认为它存在 - 在Unix系统中可能没有)会给你带来痛苦。 - Nicholas KnightLinux内核中的procfs API提供了一个接口,用于确保读取返回一致的数据。请参阅__proc_file_read
中的注释。大块注释中的第1项解释了该接口。
话虽如此,当然具体的proc文件的实现需要正确使用这个接口来确保返回的数据是一致的。因此,回答你的问题:内核不保证在读取期间proc文件的一致性,但它提供了实现这些文件以提供一致性的手段。
__proc_file_read()
已被 seq_file
取代,详见长注释上方林纳斯声音有些恼怒的评论。 - Greg Price我目前在嵌入式ARM目标上进行驱动程序开发,因此手头有Linux 2.6.27.8的源代码。
例如,文件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));
输出
[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()
函数是其中之一。直到对/proc/net/tcp
文件发出read()
请求时才生成文本,这是一个合理的机制,因为procfs读取肯定比更新信息要少得多。sprintf()
实现proc_read函数。核心驱动程序实现中的额外复杂性在于处理可能非常长的输出,在单个读取期间可能无法适应中间内核空间缓冲区。/proc/net/tcp
中的每个条目都是自洽的。有一些可能性是并排的行在不同的时间快照。ifstream
,它会不安全,但如果我使用 read
,它就会安全?还是说 ifstream
内部使用了 read
?您有什么建议呢? - Kiril Kirov/proc/net/tcp
数据的解释,与任何人如何读取它完全无关。 - wallyk/proc
中没有竞态条件会导致读取损坏的数据或新旧数据混合。从这个意义上说,它是安全的。然而,仍存在竞态条件,即你从/proc
读取的大部分数据在生成后就已经过时,并且在你读取/处理它之前更加过时。例如,进程可以随时死亡,新进程可以被分配相同的pid;你只能使用自己的子进程的进程ID而没有竞争条件。对于网络信息(开放端口等)和实际上/proc
中的大多数信息也是如此。我认为依赖/proc
中的任何数据准确无误都是不好的和危险的做法,除了关于你自己的进程和潜在的子进程的数据。当然,将/proc
中的其他信息呈现给用户/管理员以进行信息记录/日志记录等目的可能仍然很有用。getpid()
获取我的PID),所以必须保证安全。 - Kiril KirovN
是您的子进程,则可以确保pid N
仍然指向同一(可能已终止)进程,直到您对其调用wait
系列函数。这可确保不存在竞争。 - R.. GitHub STOP HELPING ICE本文档描述了接口/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()分别实现。
sysctl
如何帮助我解析/proc/net/tcp/
文件呢? - Kiril Kirov