2009-10-05 180 views
10

每个人都知道进程监听套接字上的连接的经典模型,并分派一个新进程来处理每个新连接。通常的做法是让父进程立即在新创建的套接字上调用close,递减句柄计数以便只有子对象具有新套接字的句柄。在Linux中创建新线程是否重复文件描述符和套接字描述符?

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

+0

“我读过Linux中进程和线程之间唯一的区别是线程共享相同的内存。”进程和线程之间还有很多其他差​​异。例如,进程可以包含多个线程。 – 2017-01-03 15:52:59

回答

8

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

编辑:

  • 人叉:孩子继承复制打开文件描述符的父母的一套

  • 人并行线程:线程份额一系列其他的属性(即,这些属性是进程范围,而不是每个线程):[...]打开文件描述符

有些代码:

#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); 
} 

输出是:

thread: begin 
main: file is closed 
thread: close() failed: Bad file descriptor 
thread: end 
+0

你有参考吗? – 2009-10-05 20:05:17

+0

我在这里工作时没有和我在一起,但是一旦我回家,我可以查看Stevens'UNPv2的副本。 – 2009-10-05 20:09:33

+0

@Shelby - 谢谢,我有一份UNP,但只读了三分之一。 – 2009-10-05 20:18:49

9

在Linux线程通过使用CLONE_FILES标志clone系统调用来实现:

如果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); 
8

原则上,Linux的克隆()不仅可以实现一个新的进程(如叉()),或者一个新的线程(也许像pthread_create),但也包括其中的任何东西。

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

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