Отладка программирования сокетов Linux? - PullRequest
1 голос
/ 05 мая 2009

У меня есть такая функция:

static int
rcv_kern(int sock, void *buf, int len, struct sockaddr *addr,
     socklen_t *addrlen)
{
    struct timeval timeout = {1, 0};
    fd_set set;
    int status;

    FD_SET(sock, &set);
    if ((status = select(sock + 1, &set, NULL, NULL, &timeout)) == 0) {
        FD_ZERO(&set);
        fprintf(stderr, 
            "timeout while receiving answer from kernel\n");
        exit(1);
    } else if (status == -1) {
        FD_ZERO(&set);
        perror("recvfrom failed");
        exit(1);
    }
    FD_ZERO(&set);
    return recvfrom(sock, buf, len, 0, addr, addrlen);
}

, который используется для получения сообщения из пространства ядра с использованием netlink. Но когда я запускаю его, результат всегда говорит о том, что «тайм-аут при получении ответа от ядра», из исходного кода, это вызвано тем, что метод «select» всегда возвращает «0». Я не знаю причину, кто может дать мне несколько предложений, спасибо.

Ответы [ 5 ]

4 голосов
/ 05 мая 2009

Чарли,
Пара вещей:

1) Вероятно, вам следует зациклить ваш вызов select () и ТОЛЬКО вызвать recvfrom, если FD_ISSET () возвращает true в вашем файловом дескрипторе.
2) Убедитесь, что ваш фактический драйвер или код ядра, который отправляет через сокет netlink, действительно записывает / отправляет данные в него. Если нет, то ваша функция будет отключена, если она не получит данные в течение 1 секунды. (это то, на что вы установили тайм-аут).

Пара общих комментариев ... В Linux при использовании системного вызова select (). структура данных тайм-аута сбрасывается после каждого вызова, поэтому, если вы измените свой код на цикл вокруг select, что вам, вероятно, следует ... вам придется сбрасывать значение тайм-аута для каждой итерации в цикле.

Кроме того, если время ожидания выбрано, это не обязательно означает, что это ошибка. Помните, что выбор является неблокирующим вызовом. Он просто будет ждать в сокете в течение заданного периода ожидания и вернется. Если вы хотите читать из файлового дескриптора, несмотря ни на что ... то есть вы хотите, чтобы ваша функция recv_kern () блокировалась до тех пор, пока не будут возвращены данные, тогда не беспокойтесь об использовании select (). Просто вызовите recvfrom () прямо в дескрипторе файла. Таким образом, ваша функция recv_kernel () будет блокироваться и возвращаться только после чтения данных, отправленных ядром.


Здесь довольно сложно дать более конкретную помощь, не зная больше о том, как используется этот код. Я предполагаю, что это пользовательский модуль ядра, который вы написали для отправки данных в пользовательское пространство, правильно?
Попробуйте изменить функцию recv_kern () на блокирование (уберите код выбора и просто вызовите recvfrom ()). Таким образом, вы сможете определить, правильно ли драйвер вашего ядра отправляет данные в пользовательское пространство. Если вы блокируете функцию recvfrom () и ничего не возвращается ... тогда у вас также может быть проблема в драйвере ядра.

Надеюсь, это поможет.

2 голосов
/ 17 июня 2009

Вы должны переписать функцию следующим образом:

static int
rcv_kern(int sock, void *buf, int len, struct sockaddr *addr,
     socklen_t *addrlen)
{
    struct timeval timeout = {1, 0};
    fd_set set;
    int status;

    FD_ZERO(&set);
    FD_SET(sock, &set);
    if ((status = select(sock + 1, &set, NULL, NULL, &timeout)) == 0) {
        fprintf(stderr, 
                "timeout while receiving answer from kernel\n");
        exit(1);
    } else if (status < 0) {
        perror("recvfrom failed");
        exit(1);
    }
    if ((status = recvfrom(sock, buf, len, 0, addr, addrlen)) < 0) {
        perror("recvfrom error");
        exit(1);
    }
    if (status == 0) {
        fprintf(stderr, "kernel closed socket\n");
        exit(1);
    }
    return status;
}

Как сказал кто-то еще, вам нужно вызвать FD_ZERO, прежде чем вызывать select. Другие вызовы FD_ZERO излишни. Также вам необходимо выполнить полную проверку ошибок.

1 голос
/ 05 мая 2009

Не связано с тайм-аутом, но вам нужно FD_ZERO (& set) перед FD_SET (sock, & set), иначе fd_set будет неинициализирован и, вероятно, будет содержать много установленных битов. Кроме того, FD_ZERO () перед выходом довольно бессмысленно.

0 голосов
/ 05 мая 2009

Для начала, вы можете узнать, какая была фактическая ошибка, распечатав strerror(errno) (печатать также ошибочно), когда истечет время ожидания.

Что касается того, чтобы угадать, в чем может быть проблема при отсутствии errno, обратите внимание, что нет гарантии, что есть что почитать; даже если вы получили сокет через accept (2), это может быть просто соединение, которое было настроено, но клиент не удосужился написать. Как правило, вы не делаете только один выбор (2); вы хотите иметь единственный основной цикл, который будет вызывать select (2) до тех пор, пока программа не захочет завершить работу, поскольку тайм-ауты могут происходить практически по любой причине и в любое время.

Другие возможные проблемы:

  • Клиент не может подключиться.
  • Вы не можете правильно связать сокет.
  • Вы забыли вызвать listen (2) на сокете сервера после вызова bind (2).

Если вы используете IP-сокеты, вы можете посмотреть на свой сетевой трафик с помощью Wireshark, чтобы убедиться, что клиент делает то, что вы ожидаете.

0 голосов
/ 05 мая 2009

Я исследовал свой код в пространстве ядра, я знаю, что ядро ​​не может получить сообщение от клиента, используя метод "skb_dequeue (& sk-> sk_receive_queue)". Я не знаю, как это происходит.

...