Разветвленный процесс продолжает прослушивать порт сервера

Это облегченная версия моего кода для выполнения команд:

void close_all_nonestandard_fds()
{
    struct rlimit fds_limit;
    int max_fd = 1024;
    if (getrlimit(RLIMIT_NOFILE, &fds_limit) == 0) max_fd = fds_limit.rlim_cur;
    for(int i = 0; i <= max_fd; ++i) {
        if(i != STDERR_FILENO && i != STDOUT_FILENO && i != STDIN_FILENO) close(i);
    }
}

void exec_command(char* command, char*const* args)
{
    pid_t pid = fork();
    if(pid != 0)
    {
        if(pid == -1) throw_error("Failed to fork: %s", strerror(errno));
        // Parent
    }
    else
    {
        // Child
        close_all_nonestandard_fds();
        if(execv(command, args) == -1) throw_error("Failed to execv: %s", trerror(errno));
    }
}

Метод exec_command используется в моем серверном приложении для запуска различных процессов, включая процессы демона. Но тут заметил проблему:

Когда сервер запускает дочерний процесс-демон, тогда он (сервер), будучи убитым или аварийным, начинает прослушивать порт, который прослушивал сервер.

Итак, как я могу выполнить команду и быть уверенным, что она не будет загружать порт сервера после завершения работы серверов (сбой, смерть)?


person Mihran Hovsepyan    schedule 29.10.2012    source источник
comment
Как узнать, что дочерний процесс прослушивает порт?   -  person Dietrich Epp    schedule 29.10.2012
comment
Кстати, вы не можете закрыть несуществующий fd. Скорее всего поэтому ваш сервер падает...   -  person Sdra    schedule 29.10.2012
comment
@Sdra, конечно можно. Вы получите EBADF с ошибкой.   -  person Duck    schedule 29.10.2012
comment
@DietrichEpp, используя [netstat -ap | grep ‹порт›]   -  person Mihran Hovsepyan    schedule 29.10.2012
comment
Вы уверены, что просто не видите pid родителя до истечения времени ожидания сокета?   -  person Duck    schedule 29.10.2012
comment
Не могли бы вы предоставить вывод netstat -ap | grep ‹порт›? В списке может быть закрытое TCP-соединение в состоянии TIME_WAIT.   -  person SKi    schedule 29.10.2012


Ответы (1)


Просто отслеживайте все серверные сокеты, сохраняя их, а затем, после fork()ing, добавляйте эти close()d в дочерний элемент.

Это сокеты listen()ing и сокеты accept()ed.


Обновление:

Также вы можете использовать setsockopt(), чтобы установить параметр SO_REUSEADDR для сокета, переданного bind().

SO_REUSEADDR

Указывает, что правила, используемые при проверке адресов, предоставленных в вызове bind(2), должны разрешать повторное использование локальных адресов. Для сокетов AF_INET это означает, что сокет может связываться, за исключением случаев, когда к адресу привязан активный прослушивающий сокет. Когда прослушивающий сокет привязан к INADDR_ANY с определенным портом, невозможно выполнить привязку к этому порту для любого локального адреса. Аргумент — целочисленный логический флаг.

person alk    schedule 29.10.2012
comment
Вам не кажется, что close_all_nonestandard_fds(); так делает? - person Mihran Hovsepyan; 29.10.2012
comment
@Mihran Hovsepyan: да, но эта функция должна просто закрыть существующие fds. - person Sdra; 29.10.2012
comment
@MihranHovsepyan может быть, а может и нет. Что, если сокет для прослушивания на самом деле установлен как стандартный ввод (что, помимо прочего, имеет место для серверов, запускаемых из inetd). Или, возможно, вызов getrlimit не работает, и закрываются не все дескрипторы — только первые 1024. - person Nik Bougalis; 29.10.2012
comment
@НикБ. Сокет прослушивания не установлен как стандартный ввод, а также я проверил, что getrlimit работает нормально. - person Mihran Hovsepyan; 29.10.2012