在Linux中创建新线程重复文件描述符和套接字描述符?

Rob*_*nes 11 sockets linux multithreading

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

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

dan*_*dam 10

不会.线程共享相同的内存,因此它们共享相同的变量.如果在父线程中关闭套接字,它也将在子线程中关闭.

编辑:

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

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

还有一些代码:

#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);
}
Run Code Online (Sandbox Code Playgroud)

输出是:

thread: begin
main: file is closed
thread: close() failed: Bad file descriptor
thread: end
Run Code Online (Sandbox Code Playgroud)


cme*_*erw 9

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

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

另请参阅glibc源代码,了解如何在createthread.c中使用它的详细信息:

  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);
Run Code Online (Sandbox Code Playgroud)


Mar*_*rkR 9

原则上,Linux clone()不仅可以实现新进程(如fork()),也可以实现新线程(如pthread_create),也可以实现其间的任何内容.

在实践中,它只用于其中一种.使用pthread_create创建的线程与进程中的所有其他线程(不仅仅是父线程)共享文件描述符.这是不容谈判的.

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