Обработка неизящных отключений при использовании форка и сокетов

У меня есть сервер, который прослушивает соединения сокетов и выполняет различные действия в зависимости от запроса. Один из них — долгоживущие запросы к базе данных, для которых сервер разветвляется.

Сервер ведет журнал всех активных дочерних элементов, и всякий раз, когда его просят завершить работу, он убивает всех своих дочерних элементов перед выходом. Пару раз я сталкивался с ситуацией, когда сервер падал или был некрасиво убит, что приводило к тому, что дочерний процесс становился сиротой. Если я попытаюсь снова вернуть сервер, он откажется сказать, что прослушивающий сокет не может выполнить привязку, потому что этот адрес/порт уже привязан.

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

Сервер написан на Python, но приветствуются любые объяснения или предложения на любом языке.


person elventear    schedule 14.05.2009    source источник
comment
Вы знаете, как остановился сервер? Если вы можете обнаружить проблему в обработчике сигналов, вы можете выйти из дочерних процессов.   -  person Kekoa    schedule 15.05.2009
comment
Вы уверены, что дочерние процессы обнаруживают исчезновение своего родителя и вызывают выход? Если родитель исчезает, init (pid 1) должен наследовать дочерние процессы, вызывать wait() для завершенных дочерних процессов, и зомби никогда не должны происходить.   -  person sigjuice    schedule 15.05.2009


Ответы (3)


Сделайте свой сервер лидером группы процессов. В этом случае дети завершаются, когда лидер группы выходит.

Там, где в Unix-подобной системе используется текстовый пользовательский интерфейс, сеансы используются для реализации сеансов входа в систему. Один процесс, лидер сеанса, взаимодействует с контролирующим терминалом, чтобы гарантировать, что все программы будут завершены, когда пользователь «повесит» соединение с терминалом. (Если лидер сеанса отсутствует, ожидается, что процессы в группе процессов переднего плана терминала будут обрабатывать зависания.)

person lothar    schedule 15.05.2009

Используйте это в своем сокете перед вызовом listen():

int on = 1;
setsockopt (sockfd_wan, SOL_SOCKET, SO_REUSEADDR, &on, sizeof (on));

Это позволяет вашей программе использовать этот сокет, даже если он был случайно выбран другим исходящим TCP-соединением (не может произойти для портов ‹1024). Но это также должно помочь непосредственно с вашей проблемой!

Не связанные:

Есть еще одна плохая вещь, которая может случиться: если ваши потомки разветвлены, они наследуют КАЖДЫЙ открытый файловый дескриптор. Если они просто разветвятся и запустят другую долго работающую программу, у них также будет открытый дескриптор вашего прослушивающего сокета, поэтому он останется в использовании (узнайте с помощью команд lsof и netstat!)

Итак, нужно назвать это:

int close_on_exec_on(int fd)
{
  return fcntl(fd, F_SETFD, FD_CLOEXEC);
}

close_on_exec_on(sockfd);

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

Но имейте это в виду и в любом случае вызовите его на сокет прослушивания в основной программе! На всякий случай, если вы запускаете внешнюю программу

person Christian    schedule 14.05.2009

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

Вы можете отслеживать дочерние процессы, но по-другому. Вы больше не будете получать события SIGCHLD.

person Kekoa    schedule 14.05.2009