我想用libev
创建一个示例应用程序。我想在键盘输入上有一个观察器,它将解析几个命令,如“start”“stop”“exit”。在“开始”我想创建一个管道并分叉应用程序以启动一个工作者(如ffmpeg
与exec()
或只是一些打印一些字符的循环)在一个孩子并在此管道上设置一个观察者在父级中将输出打印到控制台。在“停止”命令我想杀死一个孩子并删除一个观察者。
我已经实现了这个程序,但在fork之后,当孩子正在运行时,我总是在键盘输入上遇到段错误。
起初我认为是因为如果STDIN
可以在孩子和父母之间共享。我试图将孩子分开,关闭STDIN
。然后,我尝试dup
STDIN
并在开头关闭父级中的默认STDIN
,并在重复的STDIN
上设置观察者。我还尝试在分叉之前关闭STDOUT/ERR
描述符,并在fork之后在父级中恢复它们。
我停止并在user_input
回调中启动user_input
观察者,以防这有用。
然后我尝试在一个孩子中执行ev_default_fork()
和ev_loop_fork()
(这不是必需的,因为我想在叉子之后exec()
或者在任何情况下,子循环永远不会得到控制)没有成功。
我还尝试使用不同的后端(select
代替epoll
)。
我也试图忽略SIGHUP
SIGPIPE
SIGCHILD
等信号。
我还注意到我在fork()
之后的输入导致了segfault和bash将它作为一个命令,所以如果我做这样的事情(用“>”我表示自写手写输入和“< “程序和系统输出”:
> $ ./libev_example
> start
< Debug: fork data got:
< [Data got from child through pipe]
> asd
< Segmentation fault (core dumped)
< $ asd
< bash: asd: command not found...
然后我从源代码构建libev
并尝试调试。段错误发生在ev.c:1698
if (expect_false (w_->pending))
pendings [pri][w_->pending - 1].events |= revents;
else
pri
值为4,据我所知,这是一个优先事项。 pendings[4]
为0x0
,因此发生了段错误。当程序没有崩溃时,代码进入else
分支。
epoll
返回的fd为0,但在任何情况下我都没有使用0作为fd。此外,对于0,loop->anfds
中有一个观察者对我的user_input
回调进行回调。在上一次迭代中,当我输入任何字符串时,没有0的事件。我检查了管道fds,它们也有一个数字格式,然后是0。
我无法想象这里发生了什么以及我做错了什么。我可以在这里放一些代码,但那里没什么特别的。这篇文章相当大,所以如果有人要求代码我会稍后发布。
感谢。
答案 0 :(得分:0)
确定。有一段时间用gdb解决了这个问题。我在代码中发现了几个与libev或fork没有直接关系的错误。
导致这种奇怪行为的问题是我的错误,因为我的疏忽而没有注意到。我将标准ev_io
结构子类化,但出于某种原因我做了这个:
typedef struct lee_user_input_event_t{
struct ev_io *event;
struct lee_process_data_t *child_process_data;
...
};
而不是:
typedef struct lee_user_input_event_t{
struct ev_io event;
struct lee_process_data_t *child_process_data;
...
}
因此,将回调中的事件指针强制转换为我自己的结构是一个彻底而巨大的灾难。