События горячего подключения libusb-1.0 перестают работать в parent после fork(), когда child вызывает libusb_exit()

Я разрабатывал приложение, которое отслеживает дерево USB-устройств, используя libusb_hotplug_register_callback(), Когда устройство, соответствующее некоторым критериям, подключено, оно будет fork() а также exec() приложение для обработки этого устройства.

Приложение работает нормально в течение некоторого времени, но я вернулся, чтобы попытаться "привести в порядок"...

libusb откроет несколько файловых дескрипторов (см. ниже), которые он отслеживает для событий и т. д. Проблема в том, что после того, как я вызываю fork() и прежде чем я позвоню exec()Я хотел бы закрыть libusb, закрыть дескрипторы файлов и оставить детей в чистом состоянии.

родитель:

root@imx6q:~# ls -l /proc/14245/fd
total 0
lrwx------ 1 root root 64 Feb  9 18:15 0 -> /dev/pts/2
lrwx------ 1 root root 64 Feb  9 18:15 1 -> /dev/pts/2
lrwx------ 1 root root 64 Feb  9 18:15 2 -> /dev/pts/2
lrwx------ 1 root root 64 Feb  9 18:15 3 -> socket:[1681018]
lr-x------ 1 root root 64 Feb  9 18:15 4 -> pipe:[1681019]
l-wx------ 1 root root 64 Feb  9 18:15 5 -> pipe:[1681019]
lr-x------ 1 root root 64 Feb  9 18:15 6 -> pipe:[1681020]
l-wx------ 1 root root 64 Feb  9 18:15 7 -> pipe:[1681020]
lrwx------ 1 root root 64 Feb  9 18:15 8 -> anon_inode:[timerfd]

Ребенок:

root@imx6q:~# ls -l /proc/14248/fd
total 0
lr-x------ 1 root root 64 Feb  9 18:15 0 -> /dev/null
l-wx------ 1 root root 64 Feb  9 18:15 1 -> /dev/null
lrwx------ 1 root root 64 Feb  9 18:15 2 -> /dev/pts/2
lr-x------ 1 root root 64 Feb  9 18:15 4 -> pipe:[1681019]
l-wx------ 1 root root 64 Feb  9 18:15 5 -> pipe:[1681019]
lr-x------ 1 root root 64 Feb  9 18:15 6 -> pipe:[1681020]
l-wx------ 1 root root 64 Feb  9 18:15 7 -> pipe:[1681020]
lrwx------ 1 root root 64 Feb  9 18:15 8 -> anon_inode:[timerfd]

Проблема, с которой я столкнулся, заключается в том, что libusb_exit() у ребенка родитель больше не видит никаких событий горячей замены.

Я попытался перерегистрировать мой обратный вызов после fork() (в родителе) без удачи (и без ошибок).

Я немного порылся в источниках libusb и libudev, и мне интересно, является ли это побочным эффектом libusb linux_udev_stop_event_monitor() призвание udev_monitor_unref()... Но, увы, там нет "общения", просто звонок close() когда счетчик ссылок достигает нуля. И вообще, отсутствующий сокет сверху - это, скорее всего, сокет netlink, который открывает udev (вежливо, SOCK_CLOEXEC).

Ниже приведен пример приложения, которое демонстрирует проблему:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <assert.h>
#include <libusb-1.0/libusb.h>

libusb_context *libusb_ctx;
libusb_hotplug_callback_handle cb_handle;

int hotplug_callback(struct libusb_context *ctx, struct libusb_device *dev, libusb_hotplug_event event, void *user_data) {
    pid_t pid;
    char *cmd[] = {
        "sleep", "600", NULL
    };

    fprintf(stderr, "event! %d\n", event);

    /* fork, and return if in parent */
    pid = fork();
    assert(pid != -1);
    if (pid != 0) {
        fprintf(stderr, "intermediate child's PID is: %d\n", pid);
        return 0;
    }

    /* setsid() and re-fork() to complete daemonization */
    assert(setsid() != -1);
    pid = fork();
    assert(pid != -1);
    if (pid != 0) {
        fprintf(stderr, "child's PID is: %d\n", pid);
        exit(0);
    }

#if 1 /* toggle this */
    fprintf(stderr, "libusb is NOT shutdown in child...\n");
#else
    /* shutdown libusb */
    libusb_hotplug_deregister_callback(libusb_ctx, cb_handle);
    libusb_exit(libusb_ctx);
    fprintf(stderr, "libusb is shutdown in child...\n");
#endif

    /* now that the child has reached this point, you'll never see a hotplug event again! */

    /* exec() */
    assert(execvp(cmd[0], cmd) == 0);
    abort();
}

void main(void) {
    pid_t pid;

    /* setup libusb & hotplug callback */
    assert(libusb_init(&libusb_ctx) == 0);
    assert(libusb_hotplug_register_callback(libusb_ctx, LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED, LIBUSB_HOTPLUG_NO_FLAGS, LIBUSB_HOTPLUG_MATCH_ANY,
                                            LIBUSB_HOTPLUG_MATCH_ANY, LIBUSB_HOTPLUG_MATCH_ANY, hotplug_callback, NULL, &cb_handle) == LIBUSB_SUCCESS);

    pid = getpid();
    fprintf(stderr, "running... parent's PID is: %d\n", pid);

    /* allow libusb to handle events */
    while (1) {
        assert(libusb_handle_events_completed(NULL, NULL) == 0);
    }
}

С "переключить это" #if установлен в 1Я вижу следующую сессию (3x соединения):

# ./main
running... parent's PID is: 14370
event! 1
intermediate child's PID is: 14372
child's PID is: 14373
libusb is NOT shutdown in child...
event! 1
intermediate child's PID is: 14375
child's PID is: 14376
libusb is NOT shutdown in child...
event! 1
intermediate child's PID is: 14379
child's PID is: 14380
libusb is NOT shutdown in child...
^C

С "переключить это" #if установлен в 0Я вижу следующую сессию (3x соединения, действует только первая):

# ./main
running... parent's PID is: 14388
event! 1
intermediate child's PID is: 14390
child's PID is: 14391
libusb is shutdown in child...
^C

Версии программного обеспечения:

  • libusb: libusb-1.0.so.0.1.0 / 1.0.20-r1
  • libudev: libudev.so.0.13.1 / 182-r7
  • ядро: 3.14.38

Если кто-то видел это раньше, или может воспроизвести это (или не может воспроизвести это!), Я был бы признателен за ваш вклад. Заранее спасибо!

1 ответ

Решение

В общем, я не рекомендую вызывать fork из переносимой программы libusb, если сразу за fork не последует exec. Это связано с известными проблемами с fork на OS X. Эта проблема обсуждалась в списке рассылки libusb-devel некоторое время назад (2002), и похоже, что я забыл добавить это предупреждение в документацию для libusb-1.0. Я рекомендую использовать темы вместо.

В Linux мы наследуем любые предупреждения о вилках от libudev. Я не вижу ничего в их документации о форке, поэтому я не знаю, должен ли он работать или нет. Вы всегда можете попробовать использовать netlink, добавив --disable-udev. Эта опция не рекомендуется для производственного использования, но она может помочь изолировать проблему.

Другие вопросы по тегам