linux网卡端口断开事件

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了linux网卡端口断开事件相关的知识,希望对你有一定的参考价值。

当Linux网卡端口断开时,系统会发出一个“netlink”消息,这个消息会被内核的网络堆栈捕获,然后会调用网络设备驱动程序的net_device_notifier函数。这个函数会检测网络设备的状态,如果网络设备的状态发生变化,它会向系统发出一个事件,用来通知系统网络设备的状态发生了变化。在这种情况下,当网卡端口断开时,系统会发出一个“NETDEV_DOWN”事件,用来通知系统网络设备的状态发生了变化。 参考技术A 当网卡接收到数据包后,会触发硬中断,通知CPU来收 包。硬中断是一个CPU和网卡交互的过程。这其实会消耗CPU资源。特别是在使用速度极快的万兆网卡之后,大量的网络交互使得CPU很大一部分资源消耗在 网卡中断处理上。此时,瓶颈并不在网卡,而是在CPU上。因此,现在的网卡都采用多队列的技术,用于充分利用多核心CPU。

使用 Windows USB 虚拟 Com 端口识别断开连接事件

【中文标题】使用 Windows USB 虚拟 Com 端口识别断开连接事件【英文标题】:Identify Disconnect Event with a Windows USB Virtual Com Port 【发布时间】:2013-12-20 22:52:52 【问题描述】:

我正在开发一个使用虚拟串行端口设备驱动程序与 USB 设备通信的应用程序。我们遇到过这样一种情况,如果在串口句柄打开时设备被拔出(或崩溃),在串口句柄关闭后重新连接的唯一方法是拔下设备然后重新插入。

如果我能够足够快地检测到故障,则有潜在的解决方法。问题是,在这些情况下,下面的函数调用不会报错:

ClearCommError GetCommModemStatus ReadFile

根据我的经验,拔出设备时返回错误的唯一函数是 WriteFile。可以理解,我真的不想写无意义的数据只是为了测试端口连接是否仍然有效。

我的问题是是否有一些方法可以用来确定端口连接是否仍然有效。

如果对我在做什么有任何疑问,以下代码片段显示了我的端口轮询线程在做什么:

// set up the communications timeouts
COMMTIMEOUTS timeouts;
if (!GetCommTimeouts(port_handle, &timeouts))
   throw OsException(my_strings[strid_get_comm_timeouts_failed].c_str());

timeouts.ReadIntervalTimeout = MAXDWORD;
timeouts.ReadTotalTimeoutMultiplier = MAXDWORD;
timeouts.ReadTotalTimeoutConstant = 10;
timeouts.WriteTotalTimeoutMultiplier = 0;
timeouts.WriteTotalTimeoutConstant = 10000;
if(!SetCommTimeouts(port_handle, &timeouts))
   throw OsException(my_strings[strid_set_comm_timeouts_failed].c_str());

on_open();

// we need to set a base for the carrier detect signal.  This will be used to determine
// when the signal "changes" while the loop executes
bool carrier_detect_set = false;
uint4 modem_status = 0;

if(!GetCommModemStatus(port_handle, &modem_status))
   throw OsException(my_strings[strid_get_modem_status_failed].c_str());
if(modem_status & MS_RLSD_ON)
   carrier_detect_set = true;

 // we are now ready to enter the main service loop for this thread.
 OVERLAPPED io_control;
 memset(&io_control, 0, sizeof(io_control));
 while(!should_close)
 
    // we need to check to see if any comm errors have occurred
    uint4 comm_errors = 0;
    if(!ClearCommError(port_handle, &comm_errors, 0))
       throw OsException(my_strings[strid_clear_comm_errors_failed].c_str());
    if(comm_errors != 0)
       on_comm_errors(comm_errors);

    // we also need to determine if the carrier detect line has changed
    modem_status = 0;
    if(!GetCommModemStatus(port_handle, &modem_status))
       throw OsException(my_strings[strid_get_modem_status_failed].c_str());
    if(carrier_detect_set && (modem_status & MS_RLSD_ON) == 0)
       on_carrier_detect_change(false);
    else if(!carrier_detect_set && (modem_status & MS_RLSD_ON) != 0)
       on_carrier_detect_change(true);

    // we will now execute any command that might be waiting
    command_handle command;
    commands_protector.lock();
    while(!commands.empty())
    
       command = commands.front();
       commands.pop_front();
       commands_protector.unlock();
       command->execute(this, port_handle, false);
       commands_protector.lock();
    
    commands_protector.unlock();

    // now we will try to write anything that is pending in the write queue
    fill_write_buffer(tx_queue);
    while(!tx_queue.empty() && !should_close)
    
       uint4 bytes_avail = tx_queue.copy(tx_buff, sizeof(tx_buff));
       uint4 bytes_written = 0;

       rcd = WriteFile(port_handle, tx_buff, bytes_avail, &bytes_written, &io_control);
       if(!rcd || bytes_written == 0)
          throw Csi::OsException(my_strings[strid_write_failed].c_str());
       if(rcd)
       
          SetLastError(0);
          if(bytes_written)
          
             tx_queue.pop(bytes_written);
             on_low_level_write(tx_buff, bytes_written);
          
          if(bytes_written < bytes_avail)
             throw OsException(my_strings[strid_write_timed_out].c_str());
       
    

    // we will now poll to see if there is any data available to be written
    uint4 bytes_read = 0;

    rcd = ReadFile(port_handle, rx_buff, sizeof(rx_buff), &bytes_read, &io_control);
    if(rcd && bytes_read)
       on_low_level_read(rx_buff, bytes_read);
    else if(!rcd)
       throw OsException(my_strings[strid_read_failed].c_str());
 

我在使用重叠 I/O 时也遇到了同样的问题。

【问题讨论】:

我可能会设置一个不可见的窗口并查找WM_DEVICECHANGE 消息,然后从那里处理句柄的有效性。我很确定这适用于任何类型的端口,包括 USB。 同意,另见RegisterDeviceNotification。查找DEV_BROADCAST_DEVICEINTERFACE 事件。 顺便说一句,显然是微软broke this in Windows 8 【参考方案1】:

我可以通过添加以下代码来检测端口故障:

while(!should_close)

  // we will attempt to get and then set the comm state each time the loop repeats.
  // This will hopefully allow use to verify that the port is still valid.
  if(tx_queue.empty())
  
      DCB dcb;
      init_dcb(&dcb);
      if(!GetCommState(port_handle, &dcb))
         throw OsException(my_strings[strid_get_modem_status_failed].c_str());
      if(!SetCommState(port_handle, &dcb))
         throw OsException(my_strings[strid_get_modem_status_failed].c_str());
 

我担心在每个轮询循环上执行此测试的开销。按照 MSalters 的建议,我可能会尝试将其与 RegisterDeviceNotification 结合使用。

阅读奖励

MSDN:Registering for Device Notification?

【讨论】:

以上是关于linux网卡端口断开事件的主要内容,如果未能解决你的问题,请参考以下文章

2018-5-8

linux 双网卡绑定~

“Linux”中如何判断哪个网卡连接网线?

Linux 端口 为什么要有端口

Linux 如何判断哪个网卡是不是连接网线

Linux中网卡相关命令以及SSH连接远程主机