为什么我的 D2XX 应用程序在分叉时不起作用?

Why does my D2XX application not work when forked?

我正在用 C 编写一个简单的应用程序,运行在 Raspberry Pi 上使用 D2XX drivers 与串行端口设备进行通信。我遵循了许多在线教程和参考指南来使其正常工作,并采取了设置自定义 udev 规则等步骤以确保驱动程序可以正确加载,我按照 FTDI 的构建说明安装共享库,我使用编译时 gcc 的 -l 参数为库中的 link,我 运行 我的 C 程序带有 sudo 以确保驱动程序具有正确的访问权限。那是成功的!该程序按预期工作。

现在我正在尝试将我的简单程序转换为可以使用 init.d 脚本(a la service start)控制的守护进程,并且 运行 遇到了麻烦。

为简单起见,这里是我的 C 程序的简化版本,确实可以工作

myprog.c:

#include <stdlib.h>
#include "ftd2xx.h"

int main(int argc, char *argv[])
{
    DWORD i, iNumDevs = 0;
    char *serialNumber = malloc(64);
    FT_STATUS ftStatus = FT_CreateDeviceInfoList(&iNumDevs);
    for (i = 0; i < iNumDevs; i++) {
        ftStatus = FT_ListDevices((PVOID)i, serialNumber, FT_LIST_BY_INDEX|FT_OPEN_BY_SERIAL_NUMBER);
        if (FT_OK == ftStatus) {
            break;
        }
    }

    // more code here...

    return EXIT_SUCCESS;
}

我用 gcc -lftd2xx -o myprog myprog.c 编译它,然后 运行 用 sudo ./myprog 编译它,相信我的话,它做了它应该做的一切。但现在我正在尝试将同样的代码重新编写成一个守护进程,我一直在关注其他一些在线教程,并且上面的代码已经被转换成看起来更像这样的东西。目前,此 不起作用:

mydaemon.c:

#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <syslog.h>
#include <unistd.h>
#include "ftd2xx.h"

int main(int argc, char *argv[])
{
    pid_t pid, sid;
    pid = fork();
    if (pid < 0) {
        return EXIT_FAILURE;
    }

    if (pid > 0) {
        return EXIT_SUCCESS;
    }

    umask(0);
    openlog("mydaemon", LOG_PID|LOG_CONS, LOG_USER);

    sid = setsid();
    if (sid < 0) {
        syslog(LOG_ERR, "Failed to set session ID on child process");
        return EXIT_FAILURE;
    }

    if ((chdir("/")) < 0) {
        syslog(LOG_ERR, "Failed to change working directory");
        return EXIT_FAILURE;
    }

    close(STDIN_FILENO);
    close(STDOUT_FILENO);
    close(STDERR_FILENO);

    while (1) {

        DWORD i, iNumDevs = 0;
        char *serialNumber = malloc(64);

        syslog(LOG_INFO, "I get to this line");
        FT_STATUS ftStatus = FT_CreateDeviceInfoList(&iNumDevs);
        syslog(LOG_INFO, "I do not get to this line :( ");

        // more code here...

        sleep(10);
    }

    return EXIT_SUCCESS;
}

我以完全相同的方式编译该程序:gcc -lftd2xx -o mydaemon mydaemon.c;我运行也是这样:sudo ./mydaemon,但不幸的是它不起作用。在一个单独的控制台 window 中,我正在跟踪 /var/log/messages 文件,我可以清楚地看到它到达了我的第一条日志消息(即 "I can get to this line"),但紧接着它就死在了水。我从来没有看到第二条日志消息,事实上,在那个时候程序变得完全没有响应。我必须找到它的进程 ID 并杀死它。

换句话说,一旦它试图调用派生进程中的 D2XX 驱动程序,它就会失败。我究竟做错了什么?我已经在第一个示例中演示了代码 确实有效 ,那么 运行ning 作为守护进程会导致它完全崩溃的原因是什么?据我所知,它甚至没有机会执行相关的 D2XX 方法;就好像它一开始就找不到方法,而 运行 在分叉的过程中。

可能是因为它使用 libusb...而且他们似乎做得不好。

在这里查看我的问题:

以及此处的讨论:https://github.com/libusb/libusb/issues/268

我的具体问题与热插拔事件有关,但我预计您也会遇到其他问题。

这在您的场景中不太明显的原因是因为它们可能在加载库时(它们的种类)而不是在您开始使用它时进行一些设置/初始化。

正如@duskwuff 所指出的,这里还有另一个答案:


刚刚玩完了,请看下面:

cd $(mktemp -d)
curl http://www.ftdichip.com/Drivers/D2XX/Linux/libftd2xx-x86_64-1.3.6.tgz | tar -xvz

将其放入 test.c:

#include <stdio.h>
#include "ftd2xx.h"

int main(void) {
    int num_devs;

    fprintf(stderr, "in to main()\n");

    FT_STATUS ft_status = FT_CreateDeviceInfoList(&num_devs);
    fprintf(stderr, "FT_CreateDeviceInfoList() returned: %d\n", ft_status);

    fprintf(stderr, "out of main()\n");

    return 0;
}

编译:

gcc test.c -o test -g -I release -L release/build -lftd2xx -ldl -lpthread

现在 gdb:

[...]
Reading symbols from test...done.
(gdb) b libusb_init
Breakpoint 1 at 0x40cd20
(gdb) start
Temporary breakpoint 2 at 0x401d75: file test.c, line 7.
Starting program: /tmp/tmp.jJpBNywVzB/test 
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".

Breakpoint 1, 0x000000000040cd20 in libusb_init ()
(gdb) bt
#0  0x000000000040cd20 in libusb_init ()
#1  0x0000000000401f36 in my_init ()
#2  0x000000000041a05d in __libc_csu_init ()
#3  0x00007ffff7614ed5 in __libc_start_main (main=0x401d6d <main>, argc=1, argv=0x7fffffffe228, init=0x41a010 <__libc_csu_init>, fini=<optimised out>, rtld_fini=<optimised out>, stack_end=0x7fffffffe218)
    at libc-start.c:246
#4  0x0000000000401ca9 in _start ()
(gdb)

它甚至在到达 main() 之前就在 libusb_init() 上遇到了断点,从他们的 my_init() 调用。