大家都知道典型的进程模型,即在套接字上监听连接并为每个新连接派生一个新进程来处理。通常的做法是父进程立即调用 close
关闭新创建的套接字,减少句柄计数器,以便只有子进程有新套接字的句柄。
我读到过在Linux中进程和线程之间唯一的区别是线程共享同一块内存。在这种情况下,我假设为了处理新连接而生成一个新线程也会复制文件描述符,并且还需要"父"线程关闭它自己的套接字副本吗?
大家都知道典型的进程模型,即在套接字上监听连接并为每个新连接派生一个新进程来处理。通常的做法是父进程立即调用 close
关闭新创建的套接字,减少句柄计数器,以便只有子进程有新套接字的句柄。
我读到过在Linux中进程和线程之间唯一的区别是线程共享同一块内存。在这种情况下,我假设为了处理新连接而生成一个新线程也会复制文件描述符,并且还需要"父"线程关闭它自己的套接字副本吗?
不会。线程共享同一块内存,因此它们共享相同的变量。如果您在父线程中关闭套接字,则子线程中也将关闭该套接字。
编辑:
man fork:子进程继承父进程的打开文件描述符的副本。
man pthreads:线程共享其他范围属性(即这些属性是进程范围而不是每个线程):[...]打开的文件描述符
还有一些代码:
#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
从原理上讲,Linux的clone()函数不仅可以实现新进程(与fork()类似),也可以实现介于进程和线程之间的任何操作。
但实际上,它只被用来实现其中一种。使用pthread_create创建的线程会与进程中所有其他线程共享文件描述符(而不仅仅是父线程)。这是不可谈判的。
共享文件描述符和拥有副本是不同的。如果你拥有一个副本(例如fork()),则在文件句柄消失之前必须关闭所有副本。如果你在线程中共享FD,则一旦有一个线程关闭它,它就会消失。
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);