在 Linux 守护进程中使用用户空间驱动程序

Using a userspace driver in Linux daemon

我正在尝试编写一个守护进程来监视 USB GPIO 设备 (Velleman VM167) 的状态,然后根据更改采取行动。

我找到了一个用户空间驱动程序 (https://github.com/rahlskog/VM167) 并设置了一些 /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);
}

系统日志:

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

然而,当我将它添加到守护程序模板时,我再也看不到 USB 卡了...

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

系统日志:

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

哎呀,我好像忘了每次循环都关闭设备。就这样解决了。

CloseDevices();

您在每个循环开始时调用 OpenDevices()。当然,您需要在循环结束时关闭它们。由于您的真实(注释掉的)代码早 returns,处理此问题的正确 C++ 方法是在 OpenDevices()CloseDevices() 周围添加一个 RAII 包装器,以确保打开的设备也是已关闭。

示例:

// A wrapper that open devices on creation and closes them on destruction, 
// like when it goes out of scope.
struct DeviceCtx {
    DeviceCtx() : devices(OpenDevices()) {}
    ~DeviceCtx() { CloseDevices(); }

    operator int() const { return devices; }  // implicit conversion to `int`

    int devices;
};

void do_heartbeat() { // 
    syslog(LOG_INFO, "Loop");

    DeviceCtx devices;                 // calls OpenDevices

    if (devices == 0) {
        syslog(LOG_ERR, "Driver error condition, exiting");
        return;                        // CloseDevices() will be called

    } else if (devices == -1) {
        syslog(LOG_ERR, "No devices found on system, exiting\n");
        return;                        // CloseDevices() will be called
    }

    int ver = VersionDLL();
    syslog(LOG_INFO, "DLL Version: %d.%d.%d.%d\n", (ver >> 24),
           (ver >> 16) & 0xFF, (ver >> 8) & 0xFF, ver & 0xFF);

}   // `devices` goes out of scope and calls CloseDevices()