每个人都知道一个进程的经典模型,它监听套接字上的连接并分支新进程来处理每个新连接。正常做法是父进程立即在新创建的套接字上调用close
,减少句柄计数,以便只有子进程有新套接字的句柄。
我已经读过Linux中进程和线程之间的唯一区别是线程共享相同的内存。在这种情况下,我假设产生一个新线程来处理新连接也复制文件描述符,还需要'父'线程来关闭它的套接字副本?
答案 0 :(得分:10)
没有。线程共享相同的内存,因此它们共享相同的变量。如果在父线程中关闭套接字,它也将在子线程中关闭。
编辑:
man fork:孩子继承父母的一组打开文件描述符的副本。
man pthreads:threads share 一系列其他属性(即,这些属性是进程范围而不是每个线程):[...]打开文件描述符
还有一些代码:
#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
答案 1 :(得分:9)
在Linux上,线程是通过clone系统调用使用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);
答案 2 :(得分:9)
原则上,Linux clone()不仅可以实现新进程(如fork()),还可以实现新线程(如pthread_create),也可以实现其间的任何内容。
在实践中,它只用于其中一种。使用pthread_create创建的线程与进程中的所有其他线程(不仅仅是父线程)共享文件描述符。这是不容谈判的。
共享文件描述符并拥有副本是不同的。如果您有副本(如fork()),则必须在文件句柄消失之前关闭所有副本。如果你在一个线程中共享FD,一旦关闭它,它就会消失。