Linux 如何在不轮询内核的情况下监视C程序中的NIC状态(向上/向下)?

Linux 如何在不轮询内核的情况下监视C程序中的NIC状态(向上/向下)?,linux,networking,network-programming,linux-kernel,linux-device-driver,Linux,Networking,Network Programming,Linux Kernel,Linux Device Driver,现在我需要实时获取NIC的状态(向上或向下)。这意味着当NIC在阻塞循环中启动或关闭时,我必须捕获内核中断 我的第一个愚蠢的方法是检查/sys/class/net/eth0/operate或使用ioctl在一个循环中每隔100ms获得一次ifflag。但100毫秒太长,应用程序无法重新路由流量,每100毫秒轮询一次内核也不是个好主意 一旦我注意到inotify功能,它可以在块模式下监视文件。但不幸的是,它无法监视/sys/class/net/eth0/operstate文件,因为/sys位于RA

现在我需要实时获取NIC的状态(向上或向下)。这意味着当NIC在阻塞循环中启动或关闭时,我必须捕获内核中断

我的第一个愚蠢的方法是检查/sys/class/net/eth0/operate或使用ioctl在一个循环中每隔100ms获得一次ifflag。但100毫秒太长,应用程序无法重新路由流量,每100毫秒轮询一次内核也不是个好主意

一旦我注意到inotify功能,它可以在块模式下监视文件。但不幸的是,它无法监视/sys/class/net/eth0/operstate文件,因为/sys位于RAM中,而不是磁盘中


那么,除了编写内核模块以块模式捕获C程序中的NIC中断(向上/向下)之外,还有其他方法吗

您是否尝试过使用
select
poll
功能监视
/sys/class/net/eth0/operstate
文件?据我所知,sysfs文件在轮询方面的行为应该与常规文件相同:每当发生更改时,您应该在文件句柄上收到一个通知,说明某些内容已更改,并且您应该能够相应地做出响应。

是的,打开一个netlink套接字并侦听RTMGRP\u链接(网络接口创建/删除/向上/向下事件)多播组


netlink手册页有一个具体的例子可以做到这一点。

在网上做了一些研究/阅读后,我设法编写了一个工作代码来监控NIC状态

#include <asm/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <net/if.h>
#include <netinet/in.h>
#include <linux/netlink.h>
#include <linux/rtnetlink.h>
#include <stdlib.h>
#include <sys/time.h>
#include <sys/types.h>

int
read_event (int sockint)
{
  int status;
  int ret = 0;
  char buf[4096];
  struct iovec iov = { buf, sizeof buf };
  struct sockaddr_nl snl;
  struct msghdr msg = { (void *) &snl, sizeof snl, &iov, 1, NULL, 0, 0 };
  struct nlmsghdr *h;
  struct ifinfomsg *ifi;

  status = recvmsg (sockint, &msg, 0);

  if (status < 0)
  {
      /* Socket non-blocking so bail out once we have read everything */
      if (errno == EWOULDBLOCK || errno == EAGAIN)
      return ret;

      /* Anything else is an error */
      printf ("read_netlink: Error recvmsg: %d\n", status);
      perror ("read_netlink: Error: ");
      return status;
  }

  if (status == 0)
   {
      printf ("read_netlink: EOF\n");
   }

  // We need to handle more than one message per 'recvmsg'
  for (h = (struct nlmsghdr *) buf; NLMSG_OK (h, (unsigned int) status);
       h = NLMSG_NEXT (h, status))
    {
      //Finish reading 
      if (h->nlmsg_type == NLMSG_DONE)
        return ret;

      // Message is some kind of error 
      if (h->nlmsg_type == NLMSG_ERROR)
    {
          printf ("read_netlink: Message is an error - decode TBD\n");
          return -1;        // Error
        }

      if (h->nlmsg_type == RTM_NEWLINK)
        {
        ifi = NLMSG_DATA (h);
            printf ("NETLINK::%s\n", (ifi->ifi_flags & IFF_RUNNING) ? "Up" : "Down");
    }
    }

  return ret;
}

int
main (int argc, char *argv[])
{
  fd_set rfds, wfds;
  struct timeval tv;
  int retval;
  struct sockaddr_nl addr;

  int nl_socket = socket (AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
  if (nl_socket < 0)
    {
      printf ("Socket Open Error!");
      exit (1);
    }

  memset ((void *) &addr, 0, sizeof (addr));

  addr.nl_family = AF_NETLINK;
  addr.nl_pid = getpid ();
  addr.nl_groups = RTMGRP_LINK | RTMGRP_IPV4_IFADDR | RTMGRP_IPV6_IFADDR;
//  addr.nl_groups = RTMGRP_LINK;

  if (bind (nl_socket, (struct sockaddr *) &addr, sizeof (addr)) < 0)
    {
      printf ("Socket bind failed!");
      exit (1);
    }

  while (1)
    {
      FD_ZERO (&rfds);
      FD_CLR (nl_socket, &rfds);
      FD_SET (nl_socket, &rfds);

      tv.tv_sec = 10;
      tv.tv_usec = 0;

      retval = select (FD_SETSIZE, &rfds, NULL, NULL, &tv);
      if (retval == -1)
        printf ("Error select() \n");
      else if (retval)
        {
          printf ("Event recieved >> ");
          read_event (nl_socket);
        }
      else
        printf ("## Select TimedOut ## \n");
    }
  return 0;
}
#包括
#包括
#包括
#包括
#包括
#包括
#包括
#包括
#包括
#包括
#包括
#包括
#包括
int
读取事件(int sockint)
{
智力状态;
int-ret=0;
char-buf[4096];
结构iovec iov={buf,sizeof buf};
结构sockaddr_nl snl;
结构msghdr msg={(void*)&snl,snl的大小,&iov,1,NULL,0,0};
结构nlmsghdr*h;
结构IFINFOMG*ifi;
状态=recvmsg(sockint和msg,0);
如果(状态<0)
{
/*插座不阻塞,所以一旦我们阅读了所有内容,就退出*/
if(errno==ewoldblock | | errno==EAGAIN)
返回ret;
/*其他任何东西都是错误的*/
printf(“read_netlink:Error recvmsg:%d\n”,状态);
perror(“读取网络链接:错误:”);
返回状态;
}
如果(状态==0)
{
printf(“read_netlink:EOF\n”);
}
//我们需要为每个“recvmsg”处理多条消息
对于(h=(结构nlmsghdr*)buf;NLMSG_OK(h,(无符号整数)状态);
h=NLMSG_下一步(h,状态))
{
//读完
如果(h->nlmsg\U类型==nlmsg\U完成)
返回ret;
//消息是某种错误
如果(h->nlmsg\U类型==nlmsg\U错误)
{
printf(“read_netlink:Message是一个错误-解码待定”);
返回-1;//错误
}
如果(h->nlmsg\U类型==RTM\U新链接)
{
ifi=NLMSG_数据(h);
printf(“网络链接::%s\n”,(ifi->ifi_标志和IFF_运行)?“向上”:“向下”);
}
}
返回ret;
}
int
main(int argc,char*argv[])
{
fd_设置RFD、WFD;
结构时间值电视;
内部检索;
结构sockaddr_nl addr;
int nl_套接字=套接字(AF_网络链接、SOCK_原始、网络链接路由);
如果(nl_插座<0)
{
printf(“套接字打开错误!”);
出口(1);
}
memset((void*)和addr,0,sizeof(addr));
addr.nl_family=AF_NETLINK;
addr.nl_pid=getpid();
addr.nl_groups=RTMGRP_LINK | RTMGRP_IPV4_IFADDR | RTMGRP_IPV6_IFADDR;
//addr.nl_groups=RTMGRP_链接;
if(bind(nl_socket,(struct sockaddr*)&addr,sizeof(addr))<0
{
printf(“套接字绑定失败!”);
出口(1);
}
而(1)
{
FD_ZERO(和RFD);
FD_CLR(nl_插槽和RFD);
FD_集(nl_插座和RFD);
tv.tv_sec=10;
tv.tv_usec=0;
retval=select(FD_SETSIZE,&rfds,NULL,NULL,&tv);
如果(retval==-1)
printf(“错误选择()\n”);
否则如果(返回)
{
printf(“收到的事件>>”;
读取事件(nl_套接字);
}
其他的
printf(“###Select TimedOut###\n”);
}
返回0;
}

相关的可能重复:我不知道当文件内容被修改时,
select
poll
是否会通知用户空间。/sys/class/net/eth0/operstate是内核的结果,指示NIC向上或向下。这是一个文件句柄:应该可以正常工作。是一个显示使用
select
在一个sysfs文件上:期望它工作似乎是合理的。并不是所有的sysfs文件都实现了
poll
支持。不幸的是
operate
是其中一个不支持的。不客气!如果您认为我的答案解决了您的问题,通常会将我的答案标记为正确答案(单击问题左侧的V)我尝试使用RTMGET_链接作为消息类型,并从内核获取信息,设备信息在
ifinfo msg结构中构建。该过程类似于user->kernel和kernel->user。但我想要的是在用户空间中运行一个循环,当NIC状态改变时,内核将自动与用户空间通信lly没有用户空间发送请求。你能给出一个简短的例子吗,例如使用RTMGRP_链接?好的!我明白了!再次感谢使用“V”!!你能分享你的解决方案吗..只是检测界面上一些事件的基本代码。你可能错过了问题中要求“无轮询”的部分,这就是您的解决方案似乎要做的。这与@victor接受的解决方案相同,并且当打开接口时,此代码会通过套接字收到通知。