У меня есть сервер, написанный на C, который заблокирован в функции accept()
и ожидает новых входящих подключений. Когда новое соединение принято, он создает новый процесс, вызывая fork()
. Я не использую epoll
, поскольку каждый клиентский сокет обрабатывается независимым процессом, а одна из используемых им библиотек дает сбой в многопоточной среде.
Вот код сервера:
srv_sock = init_unix_socket();
listen(srv_sock, 5);
/* Other code which handles SIGCLD. */
while (1) {
log_info("Awaiting new incoming connection.");
clt_sock = accept(srv_sock, NULL, NULL);
if (clt_sock < 0) {
log_err("Error ...");
continue;
}
log_info("Connection %d accepted.", clt_sock);
cld_pid = fork();
if (cld_pid < 0) {
log_err("Failed to create new process.");
close(clt_sock);
continue;
}
if (clt_pid == 0) {
/* Initialize libraries. */
/* Handle client connection ... */
shutdown(clt_sock, SHUT_RDWR);
close(clt_sock);
_exit(0);
}
else {
log_info("Child process created for socket %d.", clt_sock);
close(clt_sock);
}
}
Клиент написан на Java, он подключается к серверу с помощью библиотеки junixsocket
, так как Java не поддерживает сокет домена Unix. Когда он подключается к серверу, он отправляет запрос (заголовок + XML-документ) и ожидает ответа от сервера.
Вот код клиента:
File socketFile = new File(UNIX_SOCKET_PATH);
AFUNIXSocket socket = AFUNIXSocket.newInstance();
socket.connect(new AFUNIXSocketAddress(socketFile));
InputStream sis = socket.getInputStream();
OutputStream sos = socket.getOutputStream();
logger.info("Connected with server.");
byte[] requestHeader;
byte[] requestBuffer;
sos.write(requestHeader, 0, requestHeader.length);
logger.info("Header sent.");
sos.write(requestBuffer, 0, requestBuffer.length);
logger.info("Request XML sent.");
sos.flush();
Теперь проблема в том, что у меня есть 3 клиентских потока, которые одновременно подключаются к серверу. У меня всегда запущена одна задача, а две другие ждут завершения первой.
Я проверил журналы. Все 3 клиентских потока подключились и отправили запрос на сервер (почти) в одно и то же время, но сервер принял только первый поток и задержал 2 других. Согласно журналам, существует задержка в 3 минуты между connect
на стороне клиента и accept
на стороне сервера.
Сначала я подумал, что задержка может быть вызвана каким-то буфером, поэтому я вызываю OutputStream.flush()
после каждого вызова OutputStream.write
, но проблема остается.
Я не могу понять, что может вызвать эту задержку, есть идеи, пожалуйста?
Спасибо.
Обновление от 15 марта 2016 г.
pstack
показывает, что родительский процесс был заблокирован в waitpid
в моем обработчике SIGCHLD
. Вероятно, поэтому accept
не возвращался, когда поступало новое входящее соединение, поскольку процедура выполнения была прервана обработчиком сигнала.
Вот код моего обработчика сигналов:
static void _zombie_reaper (int signum) {
int status;
pid_t child;
if (signum != SIGCHLD) {
return;
}
while ((child = waitpid(-1, &status, WNOHANG)) != -1) {
continue;
}
}
/* In main function */
struct sigaction sig_act;
memset(&sig_act, 0, sizeof(struct sigaction));
sigemptyset(&sig_act.sa_mask);
sig_act.sa_flags = SA_NOCLDSTOP;
sig_act.sa_handler = _zombie_reaper;
if (sigaction(SIGCHLD, &sig_act, NULL) < 0) {
log_err("Failed to register signal handler.");
}
Awaiting new incoming connection.
иConnection accepted
? Возможно это на стороне клиента то - person Antti Haapala   schedule 15.03.2016fork
есть зловещее предупреждение: Существуют ограничения на то, что вы можете делать в дочернем процессе. Чтобы быть в полной безопасности, вы должны ограничить себя выполнением только безопасных операций с асинхронным сигналом до тех пор, пока не будет вызвана одна из функций exec. Все API, включая глобальные символы данных, в любой структуре или библиотеке следует считать небезопасными после fork(), если только явно не задокументировано, что они безопасны или безопасны для асинхронных сигналов. - person user3386109   schedule 15.03.2016fork()
ing с многопоточностью; если вы используете чужие фреймворки, у вас могут быть потоки, не зная об этом - person Antti Haapala   schedule 15.03.2016fork
? Если вы заботитесь о производительности, запуск отдельного потока (не говоря уже о процессе) для каждого сокета заставит вас плакать, когда вы масштабируете до 10000 соединений (и, следовательно, 10000 потоков/процессов/что угодно). Рассмотрите возможность использования неблокирующих или асинхронных вызовов сокетов или установки неблокирующих параметров сокета (что отличается от использования неблокирующих или асинхронных вызовов сокетов). Вы должны быть в состоянии создать достаточно производительное серверное приложение всего за один поток. Если это не работает, вы можете масштабировать это, используяpthread_create
, неfork
. - person autistic   schedule 15.03.2016fork
не может решить. - person autistic   schedule 15.03.2016Connection accepted
иConnected with server
есть задержка в 3 минуты. Обе программы работают на одном компьютере, поэтому они используют одно и то же системное время. - person vesontio   schedule 15.03.2016backlog
размера 5, я буду тестировать с большим значением. И... я никогда не закрываюsrv_sock
в дочернем процессе. У меня есть обработчик сигналов, которыйwait
для завершенного дочернего процесса, и я использовал только функции, безопасные для асинхронных сигналов, в обработчике сигналов. - person vesontio   schedule 15.03.2016async-signal-safe
предназначен для обработчика сигналов. - person vesontio   schedule 15.03.2016epoll
+pthread
и т. д., и каждая задача обрабатывается независимым потоком. Однако библиотека, которую я должен использовать, время от времени страдает от сбоя, когда одна задача вызывает сбой библиотеки, вся программа не работает, как и все другие параллельные задачи. Вот почему я должен создать эту самую многопроцессорную версию. - person vesontio   schedule 15.03.2016close(srv_sock)
, так как у дочернего процесса нет причин использовать этот сокет - это может помочь выявить другие ошибки, которые могут возникнуть, если, например. ошибка приводит к тому, что дочерний процесс начинает использовать неправильный файловый дескриптор. То же самое с другими файловыми дескрипторами, которые дочерний процесс не должен использовать, закройте их. - person nos   schedule 15.03.2016