Linux套接字编程调试?

时间:2009-05-05 01:53:25

标签: linux linux-kernel sockets linux-device-driver

我有一个像这样的功能:

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 个答案:

答案 0 :(得分:4)

查理,
有几件事:

1)如果FD_ISSET()在文件描述符上返回true,你应该循环你的select()调用并且只调用recvfrom。
2)确保在netlink套接字上发送的实际驱动程序或内核代码实际上正在向其写入/发送数据。如果没有,那么如果1秒内没有收到数据,你的功能将会超时。 (这就是你设置超时的原因)。

一些一般性评论...... 在Linux中,使用select()系统调用时。每次调用后都会重置超时数据结构,所以如果你改变你的代码来循环选择,你可能应该......你将不得不为循环中的每次迭代重置你的超时值。

此外,如果选择超时,这并不一定意味着它是一个错误。请记住,select是非阻塞调用。它只是在套接字上等待给定的“超时”时间并返回。如果您想要从文件描述符读取,无论什么...意味着您希望您的recv_kern()函数在要返回数据之前阻塞,那么请不要使用select()。只需直接在文件描述符上调用recvfrom()即可。这样你的recv_kernel()函数就会阻塞,只有在读取内核发送的数据后才会返回。


如果不了解更多关于如何使用此代码的上下文,在此处提供更具体的帮助是很困难的。我假设这是一个你编写的自定义内核模块,它正在向用户空间发送数据,对吗? 尝试将recv_kern()函数更改为block(取出选择代码并调用recvfrom())。这种方式应该能够判断您的内核驱动程序是否实际上正确地将数据发送到用户空间。如果你在recvfrom()上阻塞,并且每个都没有回来..那么你的内核驱动程序中也可能存在问题。

希望有所帮助。

答案 1 :(得分:2)

你应该重写这个函数:

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;
}

就像别人说的那样,你需要在调用select之前调用FD_ZERO。对FD_ZERO的其他调用是多余的。此外,您需要进行完整的错误检查。

答案 2 :(得分:1)

与超时无关,但在FD_SET(sock,&amp; set)之前需要FD_ZERO(&amp; set),否则fd_set将是未初始化的并且可能包含许多设置位。此外,FD_ZERO()在退出之前是毫无意义的。

答案 3 :(得分:0)

我在内核空间中研究了我的代码,我知道内核无法使用方法“skb_dequeue(&amp; sk-&gt; sk_receive_queue)”从客户端接收消息。 我不知道它是怎么发生的。

答案 4 :(得分:0)

对于初学者,你可以通过在超时发生时打印strerror(errno)(打印errno也是明智的)来找出实际错误是什么。

至于猜测在缺少errno时问题可能是什么,请注意,不能保证有任何东西可读;即使你通过accept(2)获得套接字,它可能只是一个已建立的连接,但是客户端没有开始写入。通常你不只做一个select(2);你希望有一个主循环一直调用select(2)直到程序想要退出,因为几乎任何时候都可能出现任何原因的超时。

其他可能的问题:

  • 客户端无法连接。
  • 您无法正确绑定套接字。
  • 在调用bind(2)后,您忘记在服务器的套接字上调用listen(2)。

如果您正在使用IP套接字,您可以使用Wireshark查看您的网络流量,看看客户端是否正在按照您的预期进行操作。