【问题标题】:1 file descriptor for multiple threads, shows multiple open files on losf1 个多线程的文件描述符,显示 losf 上的多个打开文件
【发布时间】:2017-04-19 20:50:41
【问题描述】:

我有一个程序,一次有大约 200 个线程处于活动状态。当我打开一个 fd 时,我知道它是在线程之间共享的。

/proc/[pid]/fd 中,我实际上只能看到 1 个 fd,但是在查看所有打开的文件时,使用 lsof 我可以看到每个线程都打开了该文件。 (例如,同一个文件显示 200 次,相同的 pid,不同的 tid)

这是什么原因?

另外,我需要不同的线程写入同一个文件(不同的位置)。使用这个 1 fd 线程安全吗? (对我来说没有意义,但如果文件已经每个线程打开一次,如lsof 所示,它可能是安全的)。

【问题讨论】:

  • 与您的问题大多无关,但除非您拥有的大多数线程大部分时间都处于空闲状态,或者您的工作站的 CPU 内核数超过 32 个左右,否则可能有大约 200 个线程矫枉过正,实际上可能会对性能产生负面影响。线程之间的同步(如果它们写入同一个文件,需要的)肯定会成为主要的性能抑制因素。
  • 并非所有 200 个线程都在写入文件,一次只有 3 个。是的,同步是一个很大的挑战,但它是一个可扩展的系统,并且硬件可以支持它。
  • 那么问题就小多了。虽然写入本身是原子的(请参阅the write manual page),但不能保证线程之间的顺序。我真的建议您尝试重新设计或重构,以便不超过一个线程写入一个文件。
  • 哦,谢谢,错过了关于偏移设置与写入原子性有关的部分,猜猜这解决了问题。
  • @OrDinari 您可以使用pwrite() 直接写入您喜欢的任何文件偏移量而无需查找,因此您无需在每个lseek()/write() 周围使用锁定方案pair - 因为如果你使用lseek() 然后write(),你DO 必须锁定它。请参阅pubs.opengroup.org/onlinepubs/9699919799/functions/pwrite.htmlman7.org/linux/man-pages/man2/pread.2.html 请注意,如果您以附加模式打开文件,Linux 上的pwrite() 会损坏。

标签: c++ linux file file-descriptor


【解决方案1】:

lsof 列出每个“线程”的文件,因为 Linux 线程由于底层操作系统设计而不是真正的线程。

Linux 上的第一个线程是"LinuxThreads"

在 Linux 操作系统中,LinuxThreads 是部分 POSIX 线程的实现。此后已被 Native POSIX Thread Library (NPTL).1 主要开发者 LinuxThreads 是 Xavier Leroy。

LinuxThreads 有很多问题,主要是由于 实现,它使用克隆系统调用来创建一个新的 进程共享父地址空间。例如,线程有 不同的进程标识符,导致信号处理问题; LinuxThreads 使用信号 SIGUSR1 和 SIGUSR2 进行线程间 协调,这意味着这些信号不能被程序使用。

为了改善这种情况,启动了两个相互竞争的项目 开发替代品; NGPT(下一代 POSIX 线程)和 NPTL。 NPTL 胜出,今天与绝大多数 Linux 一起提供 系统。

LinuxThreads 被 NPTL - Native POSIX Thread Library 取代。但是仍然缺乏实际的、完整的内核级线程:

设计

NPTL 使用与 LinuxThreads 类似的方法,因为 内核已知的主要抽象仍然是一个进程,而新的 线程是使用 clone() 系统调用创建的(从 NPTL 调用 图书馆)。

大多数时候,Linux 缺乏完整的内核级线程这一事实并不明显。

操作系统如何处理并发处理并不重要。

但这就是为什么lsof 将文件列为由多个“进程”打开的原因。因为它是。只是这些“进程”与许多其他资源共享相同的地址空间。

请注意,“共享资源”之一是打开文件描述符的当前偏移量 - 如果您在一个线程中更改偏移量,那么您会为进程中的所有线程更改它。

如果需要从多个线程通过一个文件描述符打开的文件写入,您可以使用the pwrite() function 原子地写入文件中的任意位置,而不考虑描述符的当前偏移量:

#include <unistd.h>

ssize_t pwrite(int fildes, const void *buf, size_t nbyte,
       off_t offset);

...

pwrite() 函数应等效于 write(),除了 它写入给定位置并且不更改文件偏移量 (不管是否设置了O_APPEND)。前三个参数 pwrite()write() 相同,但增加了第四个 参数offset 用于文件内的所需位置。一次尝试 对无法搜索的文件执行pwrite() 导致错误。

注意在Linux上,如果你用O_APPEND打开文件,pwrite() is broken

BUG

POSIX 要求打开带有O_APPEND 标志的文件应该 对pwrite() 写入数据的位置没有影响。 但是,在 Linux 上,如果使用 O_APPEND 打开文件,pwrite() 将数据附加到文件的末尾,而不管 偏移量

【讨论】:

    猜你喜欢
    • 2012-08-03
    • 2018-08-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-01-31
    • 1970-01-01
    • 2015-03-22
    相关资源
    最近更新 更多