在某些情况下写入管道块

时间:2019-01-24 01:33:59

标签: pipe ocaml fork ipc

我正在尝试使用管道建立双向父子通讯,尤其是在我的进程和smt求解器(Z3)之间。我的代码(在OCaml中)似乎在许多情况下都可以工作,但有时从我的过程写入到求解器的代码会阻塞。

如果您在阅读OCaml代码时需要帮助,可以在以下位置找到我使用的OCaml Unix函数的文档: http://ocaml-batteries-team.github.io/batteries-included/hdoc2/BatUnix.html

let (solver_in, main_out) = BatUnix.pipe ~cloexec:false () in
(* pipe that solver writes to and parent reads from *)
let (main_in, solver_out) = BatUnix.pipe ~cloexec:false () in

(* Solver should not get the descriptors used by parent to read and write *)
BatUnix.set_close_on_exec main_in;
BatUnix.set_close_on_exec main_out;

let pid = BatUnix.create_process solver (Array.of_list (solver :: params))
          solver_in solver_out solver_out in

(* Parent should close the descriptors used by the solver *)
BatUnix.close solver_in;
BatUnix.close solver_out;
let cin = Unix.in_channel_of_descr main_in in
set_binary_mode_in cin false;
let cout = Unix.out_channel_of_descr main_out in
set_binary_mode_out cout false

这是我用来写入求解器管道的代码:    output_string cout问题;    同花顺

所涉及的工作流程是我向查询程序发送查询,获取答案,然后根据答案,我是否可以向它发送另一个查询(不幸的是,很难包含该查询的代码)。在很多情况下,这种方法效果很好,我设法对求解器进行了一些来回的操作。 我正在尝试一个非常大的示例,虽然我可以向求解器发送一个(巨大的)查询,然后阅读答复,但是当我尝试发送第二个(其大小小于第一个的)时,写块。如果我尝试发送一个小的字符串,它将起作用,或者如果我将新查询分为两个,则前一半不会被阻止,而后一半会被阻止。

由于某种原因,求解器似乎已停止阅读。我还将所有内容分散在一个单独的文本文件中,该文件Z3可以很好地处理而不会崩溃或发生任何事情。我该如何调试呢?

编辑:基于Goswin von Brederlow的回答,我想我可以大致了解为什么会这样。我正在提供一个庞大的查询,但我不是在要求求解器做任何事情,而是在发送约束。然后我再发送一个约束,要求求解器求解它,并阻塞等待不是立即的答案。这一切都很好,因为父级和求解器不会尝试在同一时间通话。 问题是当我请求模型时,我发送一堆查询,求解器立即为之回答(我不必在末尾明确要求一个答案),而我仍在发送查询时,求解器为发回答案。我当时在想,因为求解器正在从管道读取,所以应该清除它,但是我写的速度可能比求解器可以读取和处理管道数据的速度快。我可以使用非阻塞IO,但这可能需要我弄乱程序的逻辑。我将尝试生成另一个线程来进行写操作(因此主过程会继续进行,直到到达从求解器开始读取的部分为止),或者在生成另一个线程之前就开始进行读取。

2 个答案:

答案 0 :(得分:1)

与管道通信通常存在两个问题:

1)正如Jeffrey Scofield在他的回答中提到的那样,不会冲洗管道的输出,因此实际上不会将其写入管道。这只发生在缓冲的IO上,但这就是ocamls通道。

2)双方都试图写入管道并进行阻止,因此没有人可以阅读。这是经典的僵局。

  

似乎求解器由于某种原因停止了阅读。

在冲洗管道末端时,这似乎是问题的第二种情况。您正在为求解器提供问题,并已填充管道缓冲区。因此,您的过程将阻塞,等待求解器从管道读取。另一方面,求解器正在写回信息,进度或解决方案,并且还填充了其他管道缓冲区。现在它被阻止,等待您回读输出。经典的僵局。

使用高级渠道,恐怕可能只有一种解决方案。创建第二个线程以回读输出。替代方法是将低级API用于文件描述符,并将main_out设置为非阻塞,或者使用LIO提供的异步IO。

答案 1 :(得分:0)

使用管道与其他人编写的进程进行通信的常见问题是它们的代码没有在适当的时候刷新输出。您正在刷新输出,但是很可能求解器不会做同样的事情。 Unix stdio的通常行为是不按行刷新输出,除非输出将输出到终端(不是管道)。

查看正在发出的启动求解器的命令可能会有所帮助。如果这不是您自己编写的过程,那可能就是问题所在。