我想解析/proc/net/tcp/
,但是安全吗?
我应该如何打开文件/proc/
并从中读取文件,不要担心其他进程(或操作系统本身)会在同一时间对其进行更改?
sysctl
帮助我解析/proc/net/tcp/
文件?
我想解析/proc/net/tcp/
,但是安全吗?
我应该如何打开文件/proc/
并从中读取文件,不要担心其他进程(或操作系统本身)会在同一时间对其进行更改?
sysctl
帮助我解析/proc/net/tcp/
文件?
Answers:
一般来说,没有。(因此,这里的大多数答案都是错误的。)根据您想要的属性,它可能是安全的。但是,如果您过多地假设中的文件一致性,则很容易在代码中导致错误/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.c
和established_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
,您可以阅读并解析每一行而不必担心。但是,如果您尝试一次从多行中得出任何结论,请注意,在阅读时,其他进程和内核会对其进行更改,并且很可能会产生错误。
clock_gettime(2)
与CLOCK_MONOTONIC
(尽管也许有一个技术性问题,我不知道在这里,但我个人只因为开机时间看到它)。对于Linux,您还可以选择sysinfo(2)
。
虽然在文件/proc
出现在用户空间普通文件,他们是不是真的从用户空间支持标准的文件操作文件,而是实体(open
,read
,close
)。注意,这与内核更改磁盘上的普通文件完全不同。
内核所做的全部工作就是使用sprintf
-like函数将其内部状态打印到其自己的内存中,并且每当您发出read(2)
系统调用时,该内存就会被复制到用户空间中。
内核以与常规文件完全不同的方式处理这些调用,这可能意味着您将在读取时准备好要读取的数据的整个快照。 open(2)
,同时内核确保并发调用是一致且原子的。我没有在任何地方阅读过,但是没有其他意义。
我的建议是看一下您的Unix风格的proc文件的实现。这实际上是不受标准约束的实现问题(输出的格式和内容也是如此)。
最简单的示例是uptime
在Linux中实现proc文件。请注意,提供给的回调函数是如何产生整个缓冲区的single_open
。
proc
文件是打开供内核写入的普通文件。
open()
是,对于许多文件,尤其/proc/net/tcp
是与OP有关的文件,所有快照都被快照的猜测是错误的。如果您考虑提供这些语义的成本,这是有道理的-您必须执行诸如锁定记录所有这些TCP连接的内部数据结构之类的操作,即使在很长一段时间内,即使在繁忙的系统上,它都将是一场灾难足以扫描并将数据格式化到缓冲区。有关实际情况的详细信息,请参见我的答案。
/ proc是一个虚拟文件系统:实际上,它只是提供了内核内部结构的便捷视图。阅读它绝对是安全的(这就是为什么它在这里),但从长远来看是有风险的,因为这些虚拟文件的内部可能会随着内核的更新版本而发展。
编辑
有关更多信息,请参见Linux内核doc的proc文档中的第1.4章网络。我找不到该信息是否随时间变化。我以为它是开放式的,但无法给出确切的答案。
编辑2
根据Sco doc(不是linux,但我可以肯定* nix的所有形式都具有这种功能)
尽管进程状态以及/ proc文件的内容可能会在瞬间之间发生变化,但是/ proc文件的单个read(2)可以保证返回状态的``合理''表示,即读操作为进程状态的原子快照。没有这样的保证适用于对正在运行的进程的/ proc文件应用的连续读取。另外,对于应用到as(地址空间)文件的任何I / O,特别不能保证原子性。任何进程的地址空间的内容都可以由该进程的LWP或系统中的任何其他进程同时修改。
proc
视为在不同内核之间具有相似的行为(甚至假设它存在-在Unix系统中不必如此) )会给您带来很多伤害。
/proc/net/tcp
是对于OS而言,它并不是正确的,这是OP的主要关注点。相反,仅输出中的每一行都是原子的。有关详细信息,请参见我的答案。
Linux内核中的procfs API提供了一个接口,以确保读取返回一致的数据。阅读中的评论__proc_file_read
。大注释块中的项目1)解释了此界面。
话虽如此,当然要具体实现proc文件才能正确使用此接口,以确保其返回的数据一致。因此,回答您的问题:不,内核不能保证读取过程中proc文件的一致性,但是它提供了实现这些文件的一致性的方法。
/proc
实际上并没有提供一致性。有关详细信息,请参见我的答案。
__proc_file_read()
不推荐使用seq_file
。请看长条注释正上方的听起来很气愤的评论(由Linus撰写)。
我现在有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));
哪个输出
[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
?你有什么建议?
/proc/net/tcp
格式的解释,并且完全独立于任何人的读取方式。
/proc/net/tcp
)不是来自同一快照。看到我的答案以获得一些解释。
缺少未知的错误,没有争用条件/proc
会导致读取损坏的数据或新旧数据混合。从这个意义上讲,它是安全的。但是,仍然存在一种竞争条件,即您从中读取的许多数据/proc
一旦生成就可能已过时,甚至在您开始读取/处理它们时也是如此。例如,进程可以随时死亡,并且可以为新进程分配相同的pid。您可以在没有竞争条件的情况下使用的唯一进程ID是您自己的子进程。网络信息(开放端口等)和大多数信息也是/proc
。我认为依靠任何数据都不是危险的做法/proc
除了有关您自己的流程及其潜在子流程的数据外,其他信息均应准确无误。当然,提供其他信息仍然可能有用/proc
给用户/管理员提供信息/记录/等。目的。
getpid()
)。因此,它必须安全。
/proc
界面而言,它们都具有相同的弱点和优点。无论如何,OP询问有关设备驱动程序的信息,而不是过程。
N
是您的子进程,则可以确保pid N
仍引用相同的进程(可能已终止),直到您对其调用wait
-family函数为止。这样可以确保没有种族。
当您从/ proc文件读取时,内核会调用一个已预先注册为该proc文件的“读取”功能的函数。请参阅__proc_file_read
fs / 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()实现。