【发布时间】: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