在linux4.19的内核使用NETTLINK 时发现总是绑定失败,但是在linux2.6的内核使用时不会出现这种情况。
static void * usb_dev_plug_check(void *arg)
{
int ret = 0;
fd_set rset;
int nread = 0;
struct timeval select_timeout;
pthread_set_name("usb_plug_chk");
// 本函数返回值
int iResult = 0;
// 接收内核发来的消息字符串
char caKernelMsgBuff[1024];
// 接收内核发来的消息缓冲区大小
const unsigned int uiRecvBuffSize = sizeof( caKernelMsgBuff );
// 套接字地址
struct sockaddr_nl snl;
// 套接字文件描述符
int sfd = -1;
// 1.添写套接字地址
snl.nl_family = AF_NETLINK;
snl.nl_pad = 0;
// 如果希望内核处理消息或多播消息,就把该字段设置为 0,
// 否则设置为处理消息的进程ID。
// snl.nl_pid = getpid();
snl.nl_pid = 0; // 这里需要注意!!!!!!!
snl.nl_groups = 1;
// 2.创建套接字
// NETLINK_KOBJECT_UEVENT - 内核消息到用户空间,出现在 Linux 2.6.10
sfd = socket( PF_NETLINK, // 使用 netlink
SOCK_DGRAM, // 使用不连续不可信赖的数据包连接
NETLINK_KOBJECT_UEVENT );
// 如果创建套接字失败
if (-1 == sfd)
{
iResult = -1;
return NULL;
}
// 3.设置套接字接收缓冲区大小
setsockopt(sfd,
SOL_SOCKET, // 存取 socket 层
SO_RCVBUF, // 设置接收缓冲区大小
&uiRecvBuffSize,
sizeof(uiRecvBuffSize));
// 4.将套接字加入指定的多播组
iResult = bind(sfd, (struct sockaddr*)&snl, sizeof(snl));
// 如果将套接字加入指定的多播组失败
if (-1 == iResult)
{
log_error("bind error!");
goto f_exit;
}
while (1)
{
// 每次调用select前都要重新设置文件描述符和时间,因为事件发生后,文件描述符和时间都被内核修改了
select_timeout.tv_sec = 5;
select_timeout.tv_usec = 0;
FD_ZERO(&rset);
FD_SET((unsigned int)sfd, &rset);
ret = select(sfd + 1, &rset, NULL, NULL, &select_timeout);
if (ret < 0)
{
log_error("select fail: %s", strerror(errno));
continue;
}
else if (0 == ret)
{
// 超时
continue;
}
else
{
nread = recv(sfd, caKernelMsgBuff, sizeof(caKernelMsgBuff), 0);
if (nread < 0)
{
if(EINTR == errno)
{
nread = 0;
}
else
{
log_error("recv fail, %s", strerror(errno));
continue;
}
}
else if(0 == nread)
{
continue;
}
else
{
log_info("\n%s\n", caKernelMsgBuff);
// 暂时先不区分具体是哪个外设被拔掉,直接退出
if (NULL != strstr(caKernelMsgBuff, "remove"))
{
// 程序退出
log_error("error! usbxxx has been removed!!!");
system("sync");
// exit(-55);
}
}
注意程序中的如下两句,使用getpid();就会绑定失败,修改为0后就可以绑定上了。
// snl.nl_pid = getpid();
snl.nl_pid = 0; // 这里需要注意!!!!!!!