Создает ли создание нового потока дубликаты файловых дескрипторов и дескрипторов сокетов в Linux? - PullRequest
11 голосов
/ 05 октября 2009

Всем известна классическая модель процесса, прослушивающего соединения в сокете и разветвляющего новый процесс для обработки каждого нового соединения. Обычная практика заключается в том, что родительский процесс немедленно вызывает close во вновь созданном сокете, уменьшая количество дескрипторов, так что только дочерний элемент имеет дескриптор нового сокета.

Я читал, что единственное отличие между процессом и потоком в Linux состоит в том, что потоки совместно используют одну и ту же память. В этом случае я предполагаю, что создание нового потока для обработки нового соединения также дублирует файловые дескрипторы и также потребует, чтобы родительский поток закрыл свою копию сокета?

Ответы [ 3 ]

10 голосов
/ 06 октября 2009

Нет. Потоки совместно используют одну и ту же память, поэтому они используют одни и те же переменные. Если вы закроете сокет в родительском потоке, он также будет закрыт в дочернем потоке.

EDIT:

  • 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
9 голосов
/ 06 октября 2009

В принципе, Linux clone () может реализовывать не только новый процесс (например, fork ()) или новый поток (например, pthread_create), но и все, что между ними.

На практике он используется только для одного или другого. Потоки, созданные с помощью pthread_create, совместно используют файловые дескрипторы со всеми другими потоками в процессе (не только с родительским). Это не подлежит обсуждению.

Совместное использование файлового дескриптора и наличие копии отличается. Если у вас есть копия (например, fork ()), все копии должны быть закрыты до того, как дескриптор файла исчезнет. Если вы разделяете FD в потоке, как только вы его закроете, он исчезнет.

9 голосов
/ 06 октября 2009

В 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);
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...