【问题标题】:Why does select return when USB cable is unplugged?为什么拔掉 USB 线后 select 会返回?
【发布时间】:2012-06-05 14:15:14
【问题描述】:

我遇到了一些使用 select、FD_ISSET、read 等从 ttyUSB 端口读取的 linux c 代码的问题。我的调制解调器使用 FTDI 串行到 USB 电缆进行输入。问题是拔下 USB 电缆时选择解除阻塞。有没有办法阻止它这样做?

count = 0;
while ( g_running ) {
   FD_ZERO(&readFdSet);
   maxfd = 0;
   numTransPorts = 0;
   logger( DEBUG, "Begin g_running loop - %d", count );
   for ( i = 0; i < MAX_CONFIG_PORTS; i++ ) {
      if ( configPorts[i].commType == 1 && configPorts[i].pttyHost != NULL ) {
         FD_SET( configPorts[i].pttyHost->fd, &readFdSet );
         logger( DEBUG, "FD_SET - fd=%d, index=%d", configPorts[i].pttyHost->fd, i );
         if ( configPorts[i].pttyHost->fd >= maxfd ) {
            maxfd = configPorts[i].pttyHost->fd;
         }
         numTransPorts++;
      }
   }
   maxfd++; // add one because select check a range to n-1 file descriptors
   if ( maxfd != 0 ) { // indicates no ports are available
      logger( DEBUG, "Calling select() with %d ports and maxfd of %d", numTransPorts, maxfd );
      logger( INFO, "Waiting for input ..." );
      select( maxfd, &readFdSet, NULL, NULL, NULL ); // blocking until one available
      if( result == -1 ){
         logger( INFO, "select() error. errno: %d", errno );
      } else if ( result > 0 ){
         for ( i = 0; i < MAX_CONFIG_PORTS; i++ ) {
            if ( FD_ISSET( configPorts[i].pttyHost->fd, &readFdSet ) ) { // input is available
               logger( INFO, "Input on port %s", configPorts[i].pttyHost->serialPath );
               result = serialPortRead( buffer, configPorts[i].pttyHost->fd );
               if ( result <= 0 ) {
                  // there was an error due to the file descriptor. It
                  // probably indicates that the tty ports are no longer available
               }
            }
         }
      } else {
         logger ( INFO, "select() returns 0" );
      }
   }
   count++;
}

串行端口读取:

int serialPortRead( char *buf, int serialHandle ) {
   //char ch;
   char *ptr;
   int res = 0;
   int bytesRead = 0;
   int i;

   logger( TRACE, "TRACE: serialPortRead() with fd = %d", serialHandle );

   ptr = buf;

   // try 3 times
   for ( i = 0; i < 3; i++ ) {
      while ( (res = read( serialHandle, ptr, 1 )) > 0 ) { // read 1 byte at a time
         if ( *ptr == 0x0d ) { //there is 0x0d as a terminate byte from ECR
            break;
         }
         ptr += res;
      }
      if ( res < 0 && errno == EAGAIN ) {
         continue;
      } else {
         break;
      }
   }

   *ptr = 0x00; // set 0x00 as a terminate byte
   //   pthread_mutex_unlock(&g_serial_trans_mutex);
   if ( res < 0 ) {
      // if res is -1, there is an error
      bytesRead = -1;
      logger( DEBUG, "serialPortRead error. errno = %d", errno );
   } else {
      bytesRead = (int) (ptr - buf);
      logger( DEBUG, "serialPortRead %d bytes", bytesRead );
   }

   return bytesRead;
}

拔掉USB线后,select()解除阻塞,表示输入可用,FD_ISSET返回true。在 serialPortRead 中,read() 将返回已读取的零字节。然后它循环回到 select() 再次解除阻塞,说输入可用,依此类推。因此,你会得到一个 select() 的无限循环,FD_ISSET 返回 true,fd 永远不会被清除,read 返回 0 等等。我怎样才能解决这个问题?我期望的行为是,当没有真正要阅读的内容时,select 不会错误地解除阻塞?

注意:选择取消阻止时,它返回正数

【问题讨论】:

  • 您确定选择返回仅表示“输入可用”吗? afaik 它也可能意味着“有一个错误”,这正是这里的情况。
  • 当 read 返回 0 时,没有什么要读的了,所以你必须从你选择的集合中取出 fd。发生错误时,您有一次机会将错误交付给您;一旦您被告知错误,您必须关闭 fd 并停止选择它,而不是尝试继续选择和阅读。
  • 我猜我希望的行为不是它的工作方式。当应用程序启动时,它会查看插入的 USB 设备并打开它们以供输入。除非意外拔出,否则配置不应更改。我希望它不做任何事情。然后,当重新插入电缆时,可以在不中断的情况下接收输入。
  • @user1100151 - 无论如何都行不通 - 您需要重新打开 USB 串行重新插入时创建的(新)tty 设备。确实,如果您尝试保留旧的打开,重新插入的转换器很可能会得到不同的名称!您似乎期望的那种行为是如果从转换器上拔下 rs232 电缆会发生什么,但是拔下转换器的 USB 端几乎相当于打开计算机机箱并将 UART 卡从总线中拉出(尽管在USB 外壳,它的设计使硬件和系统软件能够在这种情况下幸存)

标签: c usb embedded-linux


【解决方案1】:

select() 正在返回,因为有信息要读取 - 在这种情况下,文件描述符已到达“文件结尾”这一事实。这由read() 返回0 表示。

read() 返回0 时,您应该关闭相应的文件描述符。

【讨论】:

  • 我认为你是对的,应该关闭 fd,我这样做了。但是,我希望有一些方法可以防止选择首先解除阻塞。然后,当重新插入电缆时,应用程序仍将处于接收输入的状态。例如,可以拔下 RS232 电缆然后重新插入,而不会触发事件。
  • @user1100151:这不是 USB tty 的工作方式 - 当您拔下并重新插入 USB tty 时,整个串行端口设备会消失和重新出现。内核无法知道重新出现的是同一个 USB 设备还是不同的设备,因此它会在每次插入事件时创建一个新的设备节点。使用有线 RS232 串行端口,串行设备本身永远不会消失。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-01-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多