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

时间:2011-08-29 03:49:31

标签: linux networking network-programming linux-kernel linux-device-driver

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

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

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

那么,是否有任何方法除了编写内核模块以使用块模式捕获C程序中的NIC中断(向上/向下)?

3 个答案:

答案 0 :(得分:15)

是的,打开一个netlink套接字并收听RTMGRP_LINK(网络接口创建/删除/上/下事件)组播组。

netlink手册页here有一个具体的示例来执行此操作。

答案 1 :(得分:8)

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

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

答案 2 :(得分:-1)

您是否尝试使用/sys/class/net/eth0/operstateselect功能监控poll文件?据我所知, sysfs 文件在轮询方面的行为应与常规文件相同:每当发生更改时,您应该在文件句柄上收到通知已发生变化且您应该能够做出相应的回应。