4

我正在用 C 语言编写一个简单的应用程序,在 Raspberry Pi 上运行,它利用D2XX 驱动程序与串行端口设备进行通信。我遵循了许多在线教程和参考指南来使其正常工作,并采取了一些步骤,例如设置自定义 udev 规则以确保驱动程序可以正确加载,我遵循 FTDI 的构建说明来安装共享库,我使用-lgcc 的参数在编译时链接到库中,我运行我的 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,但不幸的是它不起作用。在一个单独的控制台窗口中,我正在跟踪/var/log/messages文件,我可以清楚地看到它到达了我的第一条日志消息(即“我可以到达这条线”),但在那之后它就死在了水中。我从来没有看到第二条日志消息,实际上,此时程序完全没有响应。我必须找到它的进程 ID 并杀死它。

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

4

1 回答 1

0

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

在这里查看我的问题:当子调用 libusb_exit() 时,libusb-1.0 hotplug events 在 fork() 之后停止在父进程中工作

以及这里的讨论: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)

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

于 2017-03-29T16:04:23.793 回答