【问题标题】:Executed C binary from java and reading from output stream of the process从 java 执行 C 二进制文件并从进程的输出流中读取
【发布时间】:2015-02-10 17:31:35
【问题描述】:

好的,所以我试图从 java 代码中读取 c 二进制文件的输出,但我无法确定通信通道是阻塞还是非阻塞。

设置是这样的:

  1. 一个 java 类 (A.java) 正在运行

  2. A.java 使用 Runtime.getRuntime().exec("B.o") 运行 c 二进制文件 (B.o)。此时我有了 Process 对象(由 Runtime.exec 返回)

  3. A.java 使用缓冲读取器从 Process 对象的输入流中读取数据

  4. A.java 将从输入流中读取的数据输出到文件 (output.txt)

B.o 二进制文件使用 printf 函数调用简单地打印随机行。

现在,如果我运行上述设置,我将完美接收B.o 发送的所有数据。然后为了测试(阻塞/非阻塞的东西),我将A.java 更改为在每次读取B.o 的 Process 对象的输入流后休眠 5 毫秒。事实证明,现在我没有收到由B.o 发送的A.java 中的完整数据。这表明正在使用的通信通道是非阻塞的(根据我的薄弱理解)。

然后为了确定,我开始查看java的源代码,看看我是否正确。到目前为止,我发现了以下内容:

Runtime.getRuntime().exec(...) 的每次调用都以ProcessImpl_md.c 中的forkAndExec() 方法结束。在 ProcessImpl_md.c 中执行命令,创建进程,并设置 PIPES 进行通信(使用 c 中的管道函数调用)。我在源代码中找不到将 PIPES 设置为非阻塞模式的任何地方(如我的代码所示)。我假设 PIPES 默认是阻塞的。

我知道这是检查我要检查的内容的一种非常糟糕的方法。我在这里超出了我的深度,我想我只是在无用地敲打头。

谁能指出我正确的方向或告诉我:

  1. 通过 java 运行时 API 创建的进程的 PIPES 是阻塞的还是非阻塞的?

  2. 当我从输入流读取后使A.java睡眠时,为什么没有收到所有数据? (假设管道阻塞)

  3. 任何非编程方式(即我不必更改 java 等的源代码!)来确定进程的 PIPES 是阻塞还是非阻塞?

谢谢。

编辑:(添加代码)

以下不是实际的(甚至是可编译的)代码,但它显示了我正在尝试做的事情。

“B.o”的来源:

#include <stdio.h>
void main(int argc, char*argv[]){
    int a = 0;
    for(; a<9000000; a++){
        printf("%s", argv[1]);
    }
}

“A.java”的来源:

<java imports>
public class A{
    public static void main(String[] args) throws Exception{
        Process p = Runtime.getRuntime().exec("./B.o");
        BufferedReader br = new 
            BufferedReader(new InputStreamReader(p.getInputStream()));
        int a = 0;
        while(br.readLine() != null){
            a++;
            Thread.sleep(5);//data missed if this line not commented out
        }
        br.close();
        System.out.println(a);
    }
}

请检查我的答案。我的无用问题。

【问题讨论】:

  • 我会避免对可执行文件使用 *.o 扩展名(您的 B.o)。该扩展名通常用于对象文件。
  • 睡眠不应改变您从包装在 BufferedReader 中的 InputStream 获得的输入。 “非阻塞”读取并不意味着您跳过输入以获取管道中最近的写入。也就是说,尚不清楚您为什么要观察跳过的数据。能贴出相关代码吗?
  • 真的是“随机”行吗?你永远不会知道是否有一些失踪。
  • @Tim Cas。谢谢。会记住这一点。
  • @Andy Thomas。我认为问题实际上出在 B.o 二进制文件正在写入的管道的写入端。你怎么看?感谢您的回复。

标签: java c linux


【解决方案1】:

Java与外部程序的通信通道(共有三种,一种从Java到native,两种返回)是在阻塞还是非阻塞模式下运行,与是否能成功传输所有数据没有直接关系横跨每一个。同样,读取请求之间的延迟与是否将成功传输所有数据没有直接关系,无论您在java.lang.Process 的特定实现中是阻塞还是非阻塞 I/O。

确实,您探索阻塞与非阻塞进程间 I/O 的努力是徒劳的,因为提供给 Java 程序的 I/O 接口基于 InputStreamOutputStream,它们仅提供阻塞 I/O。即使在实现的某个低级别涉及非阻塞 I/O,我也想不出任何方法让您的程序检测到这一点。

但是,关于您的具体问题:

通过 java 运行时 API 创建的进程的 PIPES 是阻塞的还是非阻塞的?

它们可能是两者之一,但它们更有可能被阻止,因为这更好地匹配呈现给Process 用户的界面。

当我从输入流中读取后让 A.java 休眠时,为什么没有收到所有数据? (假设管道阻塞)

我只能推测,但问题可能出在外部程序中。当它的输出缓冲区填满时,它可能会进入睡眠状态,并且没有任何事情可以唤醒它。如果您的 Java 程序没有向外部程序发送数据,调用 myProcess.getOutputStream().close() 可能会有所帮助。无论如何,一旦您将要写入的所有内容都写入该流,关闭该流是一个好主意。

任何非编程方式(即我不必更改 java 等的源代码!)来确定进程的 PIPES 是阻塞还是非阻塞?

您可以在strace 下运行虚拟机或将本机调试器连接到它,并以这种方式分析虚拟机的行为。如果您打算从 Java 内部执行此操作,那么答案是响亮的“不”。由于InputStreamOutputStream 的合约要求,您的Java 程序在任何情况下都会看到阻塞行为。

【讨论】:

  • 感谢您非常详细的回复。如果 B.o(c 二进制)的管道的写入端是阻塞的还是非阻塞的,您能否进一步告诉我?因为我能想到的所有数据未通过的唯一原因是管道写入缓冲区(在 B.o 端)变满了(因为我让我的 java 代码进入睡眠状态,从而导致延迟清空缓冲区)。这会导致溢出的数据被丢弃。但要做到这一点,管道是否必须是非阻塞的?
  • 阻塞与非阻塞关系到read()write() 调用在不能立即传输任何数据时是否会阻塞。在任何情况下,您都不应该期望管道丢失数据,无论是阻塞模式还是非阻塞模式。如果您没有收到来自B.o 的所有数据,那么几乎可以肯定是因为B.o 有问题。您的数据首先不会进入管道。
  • 还要注意,成功的 read()write() 系统调用不能保证传输请求的全部字节数,除非指定了一个字节并且文件描述符在阻塞模式下打开.除了一字节的特殊情况,它不依赖于阻塞模式。假设这些调用将传输请求的全部字节数是一种常见错误,但只有在直接调用这些函数时才需要关注这一错误。
  • 谢谢。你指出了很多我认为理所当然的微妙事情。
【解决方案2】:

我犯了一个大错误,完全脱离了基地。发布此答案以清除问题(尽管我想完全删除该问题)。我想知道从 Java 代码运行的 C 二进制文件之间的通信通道是阻塞的还是非阻塞的。我提到当我从创建的进程(C 代码)的输入流中读取后让我的 java 代码休眠时,数据丢失了。数据并没有因此而丢失。实际上,我在 Java 代码中放置了一个计时器,然后终止 C 二进制文件的进程。而且由于 PIPES 被阻塞,它无法在计时器到期之前接收所有数据。我将这种数据丢失误解为 PIPES 是非阻塞的。通过在创建的 C 二进制进程上运行 STRACE 来确认这一点。写入系统调用没有 EAGAIN 错误。我的错。但是非常感谢大家抽出时间来回复。

【讨论】:

    猜你喜欢
    • 2015-07-27
    • 1970-01-01
    • 2021-09-07
    • 1970-01-01
    • 1970-01-01
    • 2018-01-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多