Почему мое приложение D2XX не работает при разветвлении?
Я пишу простое приложение на C, работающее на Raspberry Pi, которое использует драйверы D2XX для связи с устройством с последовательным портом. Я следовал нескольким онлайн-учебникам и справочным руководствам, чтобы заставить его работать, и предпринял шаги, такие как установка пользовательских правил udev для обеспечения правильной загрузки драйверов, я следовал инструкциям по сборке FTDI для установки разделяемой библиотеки, я использую -l
аргумент gcc для ссылки в библиотеке при компиляции, и я запускаю свою программу на C с sudo
чтобы обеспечить водителям надлежащий доступ. И это был успех! Программа работает как задумано.
Сейчас я пытаюсь преобразовать мою простую программу в процесс-демон, которым можно управлять с помощью скрипта 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
файл, и я ясно вижу, что он достигает моего первого сообщения в журнале (т.е. "Я могу добраться до этой строки"), но сразу после этого он мертв в воде. Я никогда не вижу второе сообщение журнала, и действительно, в этот момент программа перестает отвечать на запросы. Я должен найти его идентификатор процесса и убить его.
Другими словами, как только он пытается вызвать драйверы D2XX в разветвленном процессе, он терпит неудачу. Что я делаю неправильно? На первом примере я уже продемонстрировал, что код работает, так что же такое запуск демона, который приводит к его полной поломке? Насколько я могу судить, у него даже нет шанса выполнить рассматриваемый метод D2XX; как будто он просто не может найти метод во-первых, работая в разветвленном процессе.
1 ответ
Наверное, потому что он использует libusb
... и они, кажется, делают это плохо.
См. Мой вопрос здесь: события hotplug libusb-1.0 перестают работать в parent после fork(), когда child вызывает libusb_exit()
И обсуждение здесь: https://github.com/libusb/libusb/issues/268
Моя конкретная проблема была связана с событиями горячего подключения, но я ожидаю, что другие вещи пойдут не так, как надо.
Причина, по которой это менее очевидно в вашем сценарии, заключается в том, что они, вероятно, выполняют некоторую настройку / инициализацию при загрузке библиотеки (как это происходит), а не когда вы начинаете ее использовать.
Как указал @duskwuff, здесь есть еще один ответ: /questions/14493994/pochemu-ftread-terpit-neudachu-v-dochernem-protsesse-no-rabotaet-v-roditelskom-protsesse/14494009#14494009
Я только что поиграл, следуйте ниже:
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()
,