当 child 调用 libusb_exit() 后,fork() 之后,libusb-1.0 热插拔事件在 parent 中停止工作
libusb-1.0 hotplug events stop working in parent after fork(), when child calls libusb_exit()
我一直在开发一个使用 libusb_hotplug_register_callback()
监视 USB 设备树的应用程序。当连接符合某些条件的设备时,它将 fork()
和 exec()
一个应用程序来处理该设备。
该应用程序已经运行良好一段时间了,但我回来尝试 'tidy it up'...
libusb 将打开许多文件描述符(见下文),它监视事件等...问题是在我调用 fork()
之后和调用 exec()
之前,我我想关闭 libusb,关闭文件描述符并让 children 处于干净状态。
Parent:
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]
Child:
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]
我遇到的问题是,通过在 child 中调用 libusb_exit()
,parent 不再看到 any 热插拔事件。
我在 fork()
(在 parent)之后尝试了 re-registering 我的回调,但没有成功(也没有错误)。
我在 libusb 和 libudev 源代码中翻找了一下,我想知道这是否是 libusb 的 linux_udev_stop_event_monitor()
调用 udev_monitor_unref()
的 side-effect。 . 但是可惜那里没有 'communication' ,只是在引用计数达到零时调用 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);
}
}
将 'toggle this' #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
将 'toggle this' #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
- 内核:3.14.38
如果有人以前看过这个,或者可以复制它(或不能复制它!),我将不胜感激你的意见。
提前致谢!
一般来说,我不建议从可移植的 libusb 程序中调用 fork,除非 fork 紧跟在 exec 之后。这是由于 OS X 上的 fork 的已知问题。这个问题在一段时间前(2002 年)在 libusb-devel 邮件列表中讨论过,看起来我忘记将此警告添加到 libusb 的文档中-1.0。我建议改用线程。
在 Linux 上,我们继承了 libudev 的任何分叉注意事项。我在他们的文档中没有看到任何关于 fork 的信息,所以我不知道它是否应该工作。您始终可以通过添加 --disable-udev 来尝试使用 netlink。不建议将此选项用于生产,但它可能有助于隔离问题。
我一直在开发一个使用 libusb_hotplug_register_callback()
监视 USB 设备树的应用程序。当连接符合某些条件的设备时,它将 fork()
和 exec()
一个应用程序来处理该设备。
该应用程序已经运行良好一段时间了,但我回来尝试 'tidy it up'...
libusb 将打开许多文件描述符(见下文),它监视事件等...问题是在我调用 fork()
之后和调用 exec()
之前,我我想关闭 libusb,关闭文件描述符并让 children 处于干净状态。
Parent:
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]
Child:
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]
我遇到的问题是,通过在 child 中调用 libusb_exit()
,parent 不再看到 any 热插拔事件。
我在 fork()
(在 parent)之后尝试了 re-registering 我的回调,但没有成功(也没有错误)。
我在 libusb 和 libudev 源代码中翻找了一下,我想知道这是否是 libusb 的 linux_udev_stop_event_monitor()
调用 udev_monitor_unref()
的 side-effect。 . 但是可惜那里没有 'communication' ,只是在引用计数达到零时调用 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);
}
}
将 'toggle this' #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
将 'toggle this' #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
- 内核:3.14.38
如果有人以前看过这个,或者可以复制它(或不能复制它!),我将不胜感激你的意见。 提前致谢!
一般来说,我不建议从可移植的 libusb 程序中调用 fork,除非 fork 紧跟在 exec 之后。这是由于 OS X 上的 fork 的已知问题。这个问题在一段时间前(2002 年)在 libusb-devel 邮件列表中讨论过,看起来我忘记将此警告添加到 libusb 的文档中-1.0。我建议改用线程。
在 Linux 上,我们继承了 libudev 的任何分叉注意事项。我在他们的文档中没有看到任何关于 fork 的信息,所以我不知道它是否应该工作。您始终可以通过添加 --disable-udev 来尝试使用 netlink。不建议将此选项用于生产,但它可能有助于隔离问题。