在Linux中创建新线程是否会复制文件描述符和套接字描述符?

在Linux中创建新线程是否会复制文件描述符和套接字描述符?,linux,multithreading,sockets,Linux,Multithreading,Sockets,每个人都知道一个进程侦听套接字上的连接,然后分叉一个新进程来处理每个新连接的经典模型。通常的做法是,父进程立即调用新创建的套接字上的close,减少句柄计数,以便只有子进程具有新套接字的句柄 我已经读到,Linux中进程和线程的唯一区别是线程共享相同的内存。在这种情况下,我假设生成一个新线程来处理一个新连接也会复制文件描述符,并且还需要“父”线程关闭它的套接字副本?否。线程共享相同的内存,因此它们共享相同的变量。若在父线程中关闭套接字,它也将在子线程中关闭 编辑: man fork:子对象继承

每个人都知道一个进程侦听套接字上的连接,然后分叉一个新进程来处理每个新连接的经典模型。通常的做法是,父进程立即调用新创建的套接字上的
close
,减少句柄计数,以便只有子进程具有新套接字的句柄


我已经读到,Linux中进程和线程的唯一区别是线程共享相同的内存。在这种情况下,我假设生成一个新线程来处理一个新连接也会复制文件描述符,并且还需要“父”线程关闭它的套接字副本?

否。线程共享相同的内存,因此它们共享相同的变量。若在父线程中关闭套接字,它也将在子线程中关闭

编辑:

  • man fork:子对象继承父对象打开的文件描述符集的副本

  • manpthreads:线程共享一系列其他属性(即,这些属性是进程范围的,而不是每个线程):[…]打开文件描述符

还有一些代码:

#include <cstring>
#include <iostream>
using namespace std;

#include <errno.h>
#include <fcntl.h>
#include <pthread.h>
#include <unistd.h>

// global variable
int fd = -1;

void * threadProc(void * param) {
    cout << "thread: begin" << endl;
    sleep(2);
    int rc = close(fd);
    if (rc == -1) {
        int errsv = errno;
        cout << "thread: close() failed: " << strerror(errsv) << endl;
    }
    else {
        cout << "thread: file is closed" << endl;
    }
    cout << "thread: end" << endl;
}

int main() {
    int rc = open("/etc/passwd", O_RDONLY);
    fd = rc;

    pthread_t threadId;
    rc = pthread_create(&threadId, NULL, &threadProc, NULL);

    sleep(1);

    rc = close(fd);
    if (rc == -1) {
        int errsv = errno;
        cout << "main: close() failed: " << strerror(errsv) << endl;
        return 0;
    }
    else {
        cout << "main: file is closed" << endl;
    }

    sleep(2);
}

Linux上的线程是通过使用CLONE_FILES标志的系统调用实现的:

如果设置了克隆_文件,则调用 进程和子进程共享 相同的文件描述符表。任何 调用程序创建的文件描述符 进程或由子进程执行 在其他流程中也有效。 类似地,如果其中一个进程 关闭文件描述符,或更改 其关联的标志(使用 fcntl(2)F_SETFD操作),另一个 过程也受到影响

还可以查看glibc源代码,了解其在以下领域中的使用细节:

原则上,LinuxClone()不仅可以实现一个新进程(比如fork())或一个新线程(比如pthread_create),还可以实现这两者之间的任何东西

实际上,它只用于一个或另一个。使用pthread_create创建的线程与进程中的所有其他线程(不仅仅是父线程)共享文件描述符。这是不可谈判的


共享文件描述符和拥有副本是不同的。如果您有一个副本(如fork()),则必须在文件句柄消失之前关闭所有副本。如果你在一个线程中共享FD,一旦有人关闭它,它就会消失。

我在工作时没有它,但我可以在回家后查看史蒂文斯的UNPv2副本。@Shelby-谢谢,我有一份UNP副本,但只能阅读其中的三分之一。“我读到Linux中进程和线程的唯一区别是线程共享相同的内存。”进程和线程之间还有很多其他区别。例如,进程可以包含多个线程。
thread: begin
main: file is closed
thread: close() failed: Bad file descriptor
thread: end
  int clone_flags = (CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGNAL
             | CLONE_SETTLS | CLONE_PARENT_SETTID
             | CLONE_CHILD_CLEARTID | CLONE_SYSVSEM
#if __ASSUME_NO_CLONE_DETACHED == 0
             | CLONE_DETACHED
#endif
             | 0);