问题描述
我正在编写用户空间应用程序,该应用程序除其他功能外还使用netlink
套接字与内核进行通信。我使用开源库libmnl
提供的简单API。
我的应用程序通过netlink设置了某些选项,并且订阅了netlink事件(通知),对其进行解析等。因此,第二个功能(事件通知)是异步的,目前,我实现了一个基于select()
的简单循环:
...
fd_set rfd;
struct timeval tv;
int ret;
while (1) {
tv.tv_sec = 1;
tv.tv_usec = 0;
FD_ZERO(&rfd);
/* fd - is a netlink socket */
FD_SET(fd,&rfd);
ret = select(fd + 1,&rfd,NULL,&tv);
if (ret < 0) {
perror("select()");
continue;
} else if (ret == 0) {
printf("Timeout on fd %d",fd);
} else if (FD_ISSET(fd,&rfd)) {
/*
count = recv(fd,buf ...)
while (count > 0) {
parse 'buf' for netlink message,validate etc.
count = recv(fd,buf)
}
*/
}
}
所以我现在正在观察else if (FD_ISSET(fd,&rfd)) {
分支中的代码在第二个recv()
调用时的阻塞。
现在,我试图了解是否需要将netlink套接字设置为非阻塞(例如,SOCK_NOBLOCK
),但是那时我可能根本不需要select(),我只需拥有recv() -> message parse -> recv()
循环不会阻塞。
解决方法
...如果我需要将netlink套接字设置为非阻塞...,但是我可能根本不需要
select()
...
这恰好是非阻塞套接字的目的:不用执行if(FD_ISSET(...))
而是调用recv()
并计算返回值。
如果使用阻塞套接字,则在调用recv()
之后不得多次调用select()
;那么该程序将“有效地”无阻塞。
如何
...由于用户“ kaylum”已在其注释中建议,无论如何您都会遇到另一个问题:
不能保证同时有一个完整的“消息”。套接字的另一端可能会发送消息的第一部分,等待几秒钟,然后发送消息的第二部分。
但是,select()
会告诉您至少有一个字节可用;它不会告诉您是否有完整的消息。
如果您要等待内部循环(while(count > 0)
)中的完整消息,则将始终必须等待(这意味着即使套接字不是非托管程序,您的程序也具有“有效”的阻塞行为)屏蔽)。
如果您只想处理内部循环中所有可用的 字节,则条件count > 0
是错误的。相反,如果使用阻塞套接字,则应该执行以下操作:
else if(FD_ISSET(...))
{
while(FD_ISSET(...))
{
count = recv(...);
if(count > 0)
{
...
select(...);
}
else FD_ZERO(...);
}
}
但是,在大多数情况下,这不是必需的,您只需在下一个“外部”循环中处理“剩余”数据字节即可。