【问题标题】:Why does my D2XX application not work when forked?为什么我的 D2XX 应用程序在分叉时无法工作?
【发布时间】:2017-03-24 03:43:57
【问题描述】:

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

现在我正在尝试将我的简单程序转换为一个可以用 init.d 脚本(例如 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,但不幸的是它不起作用。在一个单独的控制台窗口中,我正在跟踪/var/log/messages 文件,我可以清楚地看到它到达了我的第一条日志消息(即“我可以到达这条线”),但在那之后它就死在了水中。我从来没有看到第二条日志消息,实际上,此时程序完全没有响应。我必须找到它的进程 ID 并杀死它。

换句话说,一旦它尝试调用分叉进程中的 D2XX 驱动程序,它就会失败。我究竟做错了什么?我已经用第一个示例演示了代码确实可以工作,那么作为守护进程运行会导致它完全崩溃的原因是什么?据我所知,它甚至没有机会执行有问题的 D2XX 方法;就好像它在分叉进程中运行时根本找不到方法一样。

【问题讨论】:

  • 我一直在对这个问题进行一些研究,并决定尝试链接静态库而不是动态库。为此,我必须将编译命令更改为gcc -o mydaemon mydaemon.c /usr/local/lib/libftd2xx.a -lpthread -Wl,--no-as-needed -ldl 不幸的是,这也不起作用。它编译了,但最终还是遇到了完全相同的问题。
  • 更新:显然如果我使用vfork 而不是fork 那么它可以工作。唯一的问题是当您使用vfork 时父进程不会自动终止。我可以继续手动杀死它,子进程继续运行,并继续成功访问 D2XX 功能。但是,我真的更愿意使用常规的fork... 我不想每次都编写脚本来杀死父母,因为这似乎是一种便宜的解决方法。我宁愿弄清楚根本问题是什么,并在代码中解决它。
  • 如果你 strace 程序(使用 -f 选项跟随分叉)看看它卡在哪里可能会有所帮助。
  • 另外,虽然这显然不是问题所在,但关闭std{in,out,err} 而不用其他东西替换它们并不总是一个好主意。即使您的程序对它们什么都不做,一些库可能会假设它们存在于各种目的(即使它们不应该)。当然,如果这个 libftd2xx 是那些在 stderr 上吐出虚假消息的糟糕库之一(我自己没有这方面的经验),那么这可能是很多麻烦的根源。

标签: c linux raspberry-pi daemon d2xx


【解决方案1】:

可能是因为它使用了libusb... 而他们似乎做得很糟糕。

在这里查看我的问题:libusb-1.0 hotplug events stop working in parent after fork(), when child calls libusb_exit()

这里的讨论:https://github.com/libusb/libusb/issues/268

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

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

正如@duskwuff 所指出的,这里还有另一个答案:https://stackoverflow.com/a/35186414/149341


我刚刚玩了一些,请按照以下方式进行操作:

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() 调用。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-10-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-03-15
    相关资源
    最近更新 更多