Создает ли новый дубликат дескрипторов файлов и дескрипторы Socket в Linux?
Всем известна классическая модель процесса, который прослушивает соединения в сокете и разворачивает новый процесс для обработки каждого нового соединения. Обычная практика заключается в том, чтобы родительский процесс немедленно вызывал close
во вновь созданный сокет, уменьшая количество обработок, чтобы только дочерний элемент имел дескриптор нового сокета.
Я читал, что разница между процессом и потоком в Linux заключается в том, что потоки разделяют одну и ту же память. В этом случае я предполагаю, что появление нового потока для обработки нового соединения также дублирует файловые дескрипторы, а также потребует, чтобы "родительский" поток закрыл его копию сокета?
Ответы
Ответ 1
Нет. Темы имеют одинаковую память, поэтому они используют одни и те же переменные. Если вы закрываете сокет в родительском потоке, он также будет закрыт в дочернем потоке.
EDIT:
-
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
Ответ 2
В потоках Linux реализовано с помощью clone syscall с использованием флага 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);
Ответ 3
В принципе, Linux clone() может реализовать не только новый процесс (например, fork()), либо новый поток (например, pthread_create), но и все, что между ними.
На практике он используется только для одного или другого. Темы, созданные с помощью pthread_create, совместно используют файловые дескрипторы со всеми остальными потоками процесса (а не только для родителя). Это не подлежит обсуждению.
Совместное использование дескриптора файла и наличие копии. Если у вас есть копия (например, fork()), все копии должны быть закрыты до удаления файла. Если вы разделите FD в потоке, как только он его закроет, он исчез.