今天在将项目从虚拟机上移植到真实机器上面的时候,发现问题,总是不成功,最后判断是userspace的程序没有向kernel发送消息成功,因为无法触发kernel的行为,但是userspace显示正常。
这个问题好像两个月之前,netlink模块测试的时候遇到过这个问题,当时加上sleep就好了,同样复制这个方法,发现使用usleep(1)就解决问题了。
接下来分析问题的原因,问题锁定在sendmsg上面。
查看sendmsg的返回值,发现是-1,也就是说sendmsg失败了,看errno: ENOBUFS。
ENOBUFS The output queue for a network interface was full. This generally indicates that the interface has stopped sending, but may be caused by transient conges- tion. (Normally, this does not occur in Linux. Packets are just silently dropped when a device queue overflows.) |
也就是说发送队列占满了,怎么会出现出现这样的问题呢?
追踪sendmsg函数:
sys_sendmsg()
...... err = -ENOBUFS; if (msg_sys.msg_controllen > INT_MAX) ...... |
发现在sys_sendmsg中会引用一些msg中的成员变量,一些变量很大导致返回ENOBUFS错误,最终导致no buffer space available。
看我们的userspace代码:
struct msghdr msg;
msg.msg_name = (void *)&dest_addr; |
OMG,竟然没有初始化,这样的话,msg.msg_controllen就有可能比较大,导致出现ENOBUFS错误。
解决方法就是初始化msg:
memset(&msg, 0 ,sizeof(msg)); |
注意:在调试内核相关程序的时候,一定要检查返回值,一定要进行初始化,不要想当然。有些编译器会帮你初始化,但是有些不会,程序员不能够依赖编译器来干活 :-)