libusb-1.0 hotplug事件在fork()之后停止在parent中工作,当child调用libusb_exit()时

时间:2017-02-09 19:13:46

标签: c linux udev netlink libusb-1.0

我一直在开发一个使用libusb_hotplug_register_callback()监控USB设备树的应用程序。当附加符合某些条件的设备时,它将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()。 无论如何,上面缺少的套接字很可能是udev打开的netlink套接字(礼貌地,使用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后,我会看到以下会话(3次连接):

# ./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后,我会看到以下会话(3个连接,只有第一个被操作):

# ./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
  • kernel:3.14.38

如果有人以前看过这个,或者可以重现它(或者不能重现它!)我会很感激你的意见。 提前谢谢!

1 个答案:

答案 0 :(得分:1)

一般情况下,我不建议从便携式libusb程序中调用fork,除非fork后面紧跟exec。这是由于OS X上的fork的已知问题。这个问题在前一段时间(2002年)在libusb-devel邮件列表中讨论过,看起来我忘了在libusb-1.0的文档中添加这个警告。我建议改用线程。

在Linux上,我们继承了libudev的任何fork警告。我在他们的文档中没有看到任何关于fork的内容,所以我不知道它是否应该工作。您可以通过添加--disable-udev来尝试使用netlink。建议不要将此选项用于生产用途,但它可能有助于隔离问题。