【问题标题】:How to be sure if a file descriptor has already been closed?如何确定文件描述符是否已经关闭?
【发布时间】:2011-04-18 17:45:13
【问题描述】:

投票结束前,请阅读,我知道有类似的问题(:

这是我的情况 - 我有一个应用程序,它是多线程的。所以,假设我有 10 个线程。它们都从同一个文件描述符中读取(它实际上是一个套接字)。并且在极少数情况下,当发生严重错误时,其中一个线程应该是shutdown。问题是,这些线程中的任何一个都可以做到这一点。如果套接字关闭失败,则执行 _Exit( FAILURE )(我知道,这听起来像是代码中的糟糕设计或问题,但实际上并非如此,因为这是由非开源 3rd 方库引起的,这有一个错误)。

这就是问题所在——他们都可能同时尝试shutdown 套接字。一个人关闭它,但其他人无法关闭它(shutdown 返回 -1,因为套接字已经关闭)并且错误的 _Exit( FAILURE ) 被执行,这会破坏一切。

显然,我需要进行额外检查 - 套接字是否已关闭(可能所有线程由于某种原因未能关闭套接字,然后至少有一个必须执行 _Exit,这就是检查返回码的原因shutdown 的数量还不够)。

嗯,我找到了this 问题,看起来这正是我想要做的。 但我知道,任何类型的系统调用都需要时间(当然),并且它取决于操作系统何时关闭套接字。

这是一个问题 - 如果套接字已经关闭或由于某种原因无法关闭,我该如何改变? fcntl 是否会向我保证,如果一个线程已关闭套接字,同时如果另一个线程尝试 shutdown 套接字,它将失败,然后,如果我进行此检查(使用 fcntl ),这对我有用吗?

我还看到了其他答案,例如:“您可以使用selectpoll”,但它们仍然是系统调用,我不知道它们是否会是更好的选择。我也不知道具体如何使用它们,但我想这没什么大不了的。

谢谢!


我也可以查看shutdown设置的errno,但是“已连接”是什么意思呢? “已连接”和“不是有效描述符”有什么区别

ENOTCONN
    The specified socket is not connected.

另外,困扰我的是,我试图关闭的 FD 可能无效,因为我从映射到 proc/PID/fd/proc/net/tcp 中获取它,我不知道是否所有文件看起来像顺便说一句,他们看我的操作系统(操作系统肯定是 RHEL4 或 RHEL5,如果重要的话)

哇!这该死的长,但我无法解释得更短。

【问题讨论】:

    标签: c++ c unix system-calls file-descriptor


    【解决方案1】:

    我假设您是说在关闭套接字后您的应用程序可以合理地继续?

    似乎更好的方法是让一个中介线程接收来自任何工作线程的套接字关闭请求的通知,通知其他线程套接字现在已死,并负责关闭套接字本身。这消除了对失败原因的担忧,因为它都是在一个线程中处理的。

    【讨论】:

    • 是的,应用程序可以在关闭套接字后继续工作(解释太长了)。嗯,中介线程的想法对我来说听起来不错。但是在应用程序的当前架构中实现和支持将相当困难。无论如何,我会考虑的,谢谢!欢迎其他想法,顺便说一句(:
    • 事实证明这是最好的解决方案,真的。 10x
    【解决方案2】:

    当您有一个资源被多个线程使用并且可能被其中一个线程释放时,您必须用锁保护所有访问。否则你会有危险的比赛条件。我会在包含文件描述符的int 上使用读写锁。任何想要使用 fd 的线程都应该在它使用它的持续时间内持有一个读锁,并且任何想要更改 fd 变量的线程(例如close 它并用 -1 替换它以防止进一步使用)应该持有一个写锁.

    基本上这与使用动态分配的内存和free 相同。

    【讨论】:

    • 是的,我知道这一点,但这是线程同步的基本答案。假设我做了这个锁定,但是其他可以更改这些文件的进程呢? /proc/ 是系统文件夹,我无法锁定其中的任何内容,可以吗?这使得解析tcp 文件(或其他)很危险,并可能导致竞争条件。对?所以,我无法避免竞争条件,我只能降低它们的概率,对吧?
    • 解析tcp(在/proc下)用于任何目的但通知用户听起来真的很可疑......
    • 这是正确答案。您有一个共享资源,由多个线程共享。当另一个线程正在使用、可能正在使用或将来可能尝试使用该资源时,您不能允许一个线程释放该资源。你如何解决这个问题取决于你。您可以使用受互斥体保护的计数器。线程必须碰撞计数器才能使用资源。线程只能在计数器为零时释放资源。您可以在另一个线程正在或可能正在使用句柄时调用“关闭”,因为这不会释放它。但是“关闭”确实如此。所以你必须保护它。
    【解决方案3】:

    查看errno 是迄今为止您的最佳选择。从shutdown(2)我可以看到:

    • EBADF s 不是有效的描述符
    • ENOTCONN指定的socket没有连接
    • ENOTSOCK s 是文件,而不是套接字。

    我的看法:EBADF 表示它已经关闭,ENOCONN 表示此描述符后面没有连接(不是三次握手和所有爵士乐)。

    找出答案的最佳方法:在调用shutdown 失败后执行perror(3) 并查看它的内容。

    干杯

    【讨论】:

    • 我认为这是我最好的选择,但你认为我可以依赖 /proc/net/tcpproc/PID/fd 的格式吗?好消息是,唯一可能的操作系统是 RHEL{4, 5}。
    • @Kiril Kirov 这不是一个好主意。假设您有每个线程检查/proc/PID/fd。所以一个线程检查,看到它在那里,然后调度程序停止它。然后另一个线程看到fd 在那里并关闭它。现在第一个线程被恢复并试图关闭它。瞧,比赛条件
    • @nc3b - 啊,我没想到。谢谢,我会想办法解决的。
    • 如果有可能打开其他文件描述符,这种方法不可靠;你最终可能会探测到错误的 fd。
    • @nc3b - 如果您有兴趣,可以查看stackoverflow.com/questions/5713451/…
    【解决方案4】:

    你有没有想过用互斥锁来保护套接字的关闭?通过使用其中之一,您可以确保只有一个线程试图关闭套接字。您必须进行一些系统调用,即初始化时的pthread_mutex_init 和实际关闭套接字之前的pthread_mutex_trylock。但是,这些调用应该针对快速返回进行优化。

    另一种避免系统调用的方法是自己实现互斥锁,这样实际上只有一个线程可以关闭套接字。当然,您必须调整existing algorithms for mutual exclusion 之一,以便后面的线程不会等待,而只是继续执行。

    【讨论】:

    • 当然,但会成为解决方案的一部分,@Mark B 建议 - 中介线程。反正10倍
    【解决方案5】:

    正确的答案是 R..,它处理线程问题。回复问题'如何查看fd的状态?':fstat()

    根据 POSIX: 如果文件描述符或套接字出现任何问题,它将返回 -1。 errno 将在已关闭的文件描述符或套接字上设置为 EBADF。 stat 系列调用专门用于测试:文件(stat)、链接(lstat)和文件描述符(fstat)。

    closeshutdown 等其他调用也会在已关闭的 fd 上返回错误。但它们的目的是别的,测试连接的套接字是一个副作用。

    发布是因为提到的其他系统调用并不是为了测试文件描述符。我读到的原始问题的一部分。

    【讨论】:

      猜你喜欢
      • 2013-11-08
      • 2017-09-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-09-21
      • 1970-01-01
      • 2020-10-23
      • 1970-01-01
      相关资源
      最近更新 更多