You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Linux守护进程使用VM167 USB GPIO用户空间驱动无法识别设备问题

问题概述

我正在编写Linux守护进程,用于监控Velleman VM167型号USB GPIO设备的状态,状态变更时执行对应操作。我已完成该设备的用户空间驱动部署、/etc/udev规则配置、ldconfig路径设置,编写的简化测试程序编译后运行正常,可成功识别设备并获取驱动版本信息,迁移到标准守护进程模板后,仅能正常获取驱动版本号,无法识别设备。

正常运行的测试代码

#include <iterator>
#include <cstdlib>
#include <cstring>
#include <sstream>
#include <iostream>
#include <stdlib.h>
#include <string>
#include <sys/stat.h>
#include <syslog.h>
#include <unistd.h>
#include <vector>
#include "VM167.h"

int test_openDevices();
void test_readWriteDigital(int card);
void test_readAnalog(int card);
void test_PWM(int card);

int main(void)
{
   syslog(LOG_INFO, "Loop");
   int devices;
        devices = OpenDevices();
        if (devices == 0)
        {
                syslog(LOG_ERR, "Driver error condition, exiting");
                //fflush(stdout);
                //return -1;
        }
        else if (devices == -1)
        {
                syslog(LOG_ERR, "No devices found on system, exiting\n");
                //fflush(stdout);
                //return -1;
        }
        int ver = VersionDLL();
        syslog(LOG_INFO, "DLL Version: %d.%d.%d.%d\n", (ver>>24), (ver>>16)&0xFF, (ver>>8)&0xFF, ver&0xFF);
}

测试程序syslog输出

Nov  4 01:12:12 testserver console: Loop
Nov  4 01:12:12 testserver console: DLL Version: 0.1.0.19

故障守护进程代码

#include <dirent.h>
#include <iterator>
#include <cstdlib>
#include <cstring>
#include <sstream>
#include <iostream>
#include <stdlib.h>
#include <string>
#include <sys/stat.h>
#include <syslog.h>
#include <unistd.h>
#include <vector>
#include "VM167.h"

int test_openDevices();
void test_readWriteDigital(int card);
void test_readAnalog(int card);
void test_PWM(int card);

void do_heartbeat()
{
   // TODO: implement processing code to be performed on each heartbeat
   syslog(LOG_INFO, "Loop");
   int devices;
        devices = OpenDevices();
        if (devices == 0)
        {
                syslog(LOG_ERR, "Driver error condition, exiting");
                //fflush(stdout);
                //return -1;
        }
        else if (devices == -1)
        {
                syslog(LOG_ERR, "No devices found on system, exiting\n");
                //fflush(stdout);
                //return -1;
        }
        int ver = VersionDLL();
        syslog(LOG_INFO, "DLL Version: %d.%d.%d.%d\n", (ver>>24), (ver>>16)&0xFF, (ver>>8)&0xFF, ver&0xFF);
}

// For security purposes, we don't allow any arguments to be passed into the daemon
int main(void)
{
   // Define variables
   pid_t pid, sid;

   // Fork the current process
   pid = fork();
   // The parent process continues with a process ID greater than 0
   if(pid > 0)
   {
      exit(EXIT_SUCCESS);
   }
   // A process ID lower than 0 indicates a failure in either process
   else if(pid < 0)
   {
      exit(EXIT_FAILURE);
   }
   // The parent process has now terminated, and the forked child process will continue
   // (the pid of the child process was 0)

   // Since the child process is a daemon, the umask needs to be set so files and logs can be written
   umask(0);

   // Open system logs for the child process
   openlog("Daemon-GPIO", LOG_NOWAIT | LOG_PID, LOG_USER);
   syslog(LOG_NOTICE, "Successfully started Daemon-GPIO");

   // Generate a session ID for the child process
   sid = setsid();
   // Ensure a valid SID for the child process
   if(sid < 0)
   {
      // Log failure and exit
      syslog(LOG_ERR, "Could not generate session ID for child process");

      // If a new session ID could not be generated, we must terminate the child process
      // or it will be orphaned
      exit(EXIT_FAILURE);
   }

   // Change the current working directory to a directory guaranteed to exist
   if((chdir("/")) < 0)
   {
      // Log failure and exit
      syslog(LOG_ERR, "Could not change working directory to /");

      // If our guaranteed directory does not exist, terminate the child process to ensure
      // the daemon has not been hijacked
      exit(EXIT_FAILURE);
   }

   // A daemon cannot use the terminal, so close standard file descriptors for security reasons
   close(STDIN_FILENO);
   close(STDOUT_FILENO);
   close(STDERR_FILENO);

   // Daemon-specific intialization should go here
   const int SLEEP_INTERVAL = 5;

   // Enter daemon loop
   while(1)
   {
      syslog(LOG_INFO, "Starting helper loop");
      // Execute daemon heartbeat, where your recurring activity occurs
      do_heartbeat();

      // Sleep for a period of time
      sleep(SLEEP_INTERVAL);
   }

   // Close system logs for the child process
   syslog(LOG_NOTICE, "Stopping Daemon-GPIO");
   closelog();

   // Terminate the child process when the daemon completes
   exit(EXIT_SUCCESS);
}

守护进程syslog输出

Nov  4 00:18:10 testserver Daemon-GPIO[29638]: Loop
Nov  4 00:18:10 testserver Daemon-GPIO[29638]: No devices found on system, exiting
Nov  4 00:18:10 testserver Daemon-GPIO[29638]: DLL Version: 0.1.0.19
修复方案

按照以下优先级排查修复:

  • 调整设备打开逻辑:不要在心跳循环中反复调用OpenDevices(),将设备打开操作移到守护进程初始化阶段(关闭标准文件描述符之后、进入循环之前)仅执行一次,心跳循环只保留设备状态读取逻辑,反复开关设备会导致内核USB驱动资源占用异常,引发识别失败。
  • 排查权限问题:确认守护进程的运行用户归属的用户组,与udev规则中分配VM167设备权限的用户组一致,测试时可以临时将udev规则中设备的MODE改为0666验证是否为权限问题。
  • 处理标准描述符关闭的影响:VM167驱动依赖的libusb在标准输入输出描述符被直接关闭的场景下可能出现异常,将关闭描述符的逻辑替换为重定向到/dev/null:
    int fd = open("/dev/null", O_RDWR);
    dup2(fd, STDIN_FILENO);
    dup2(fd, STDOUT_FILENO);
    dup2(fd, STDERR_FILENO);
    if (fd > 2) close(fd);
    
  • 调整初始化顺序:libusb的上下文无法跨fork继承,可将OpenDevices()的初始化逻辑移到fork操作之前的父进程中执行,再将设备句柄传递到子进程使用。

内容的提问来源于stack exchange,提问作者Dangerarea

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.28 02:15:06