Я изучаю сетевое программирование и пытаюсь реализовать HTTP-сервер. Я следую руководству по программированию сокетов на C (https://beej.us/guide/bgnet/html/split/client-server-background.html) и наткнулся на этот пример:
while(1) { // main accept() loop
sin_size = sizeof their_addr;
new_fd = accept(sockfd, (struct sockaddr *)&their_addr, &sin_size);
if (new_fd == -1) {
perror("accept");
continue;
}
inet_ntop(their_addr.ss_family,
get_in_addr((struct sockaddr *)&their_addr),
s, sizeof s);
printf("server: got connection from %s\n", s);
if (!fork()) { // this is the child process
close(sockfd); // child doesn't need the listener
if (send(new_fd, "Hello, world!", 13, 0) == -1)
perror("send");
close(new_fd);
exit(0);
}
close(new_fd); // parent doesn't need this
}
Меня смущает разветвление процессов.
Почему мы закрываем исходный дескриптор файла сокета (sockfd), если он был создан только в исходном родительском процессе? Или, в более общем смысле, все ли ресурсы, созданные родительским процессом, дублируются в дочернем - например, файл сокета?
Потому что sockfd — это серверный сокет, который используется только для приема соединений. Дочернему процессу это не нужно для связи с клиентом.
Дескрипторы файлов дублируются таким же образом, как dup() дублирует дескриптор файла, за исключением того, что fork() не меняет значения дескрипторов файлов. Дескриптор файла идентифицируется комбинацией его номера и процесса, которому он принадлежит. Дескрипторы файлов относятся к базовому файлу, который был первоначально открыт, или к сокету или каналу, который был первоначально создан. Базовое открытое описание файла, сокет или канал не освобождается и не уничтожается до тех пор, пока все ссылающиеся на него файловые дескрипторы не будут закрыты.
Итак, несколько файловых дескрипторов могут быть созданы параллельно «не влияя друг на друга» — по крайней мере, в этом случае, когда принимается одно ожидающее соединение. Спасибо всем за комментарии.
@VanjaStojanović Думайте о файловом дескрипторе как об индексе в таблице ссылок на объекты ядра для каждого процесса. Разные процессы могут ссылаться на разные объекты по одному и тому же индексу в своих таблицах. Разные записи в одной таблице могут ссылаться на один и тот же объект ядра (например, после dup()). После fork() таблица «копируется» так же, как и остальная память процесса.
Логично, я об этом не подумал, спасибо за идею :)





Потому что согласно спецификации POSIX fork().
Дочерний процесс должен иметь собственную копию файловых дескрипторов родительского [...].
Суть fork() заключается в том, что ресурсы процессов дублируются, хотя это не на 100% верно, поскольку потоки не дублируются.
Понятно, так что это не просто дублированный указатель, указывающий на один и тот же ресурс, а совершенно новый ресурс. Спасибо.
Если дескрипторы сокетов не были дублированы в дочерний процесс, то создание дочернего процесса для обработки нового соединения вообще не могло бы работать. Верно?