使用Runtime.exec()
在java中创建子流程时,我知道我必须填充输入/排出输出流以防止阻塞子流程。
有趣的是,Process
的javadoc声明了一点:
...failure to promptly write the input stream or read the output stream of
the subprocess may cause the subprocess to block, and even deadlock.
我想知道在这种情况下,子进程可以也死锁!
问题:
1.它在哪些条件下陷入僵局?
2.为什么会陷入僵局?
你能提供一个显示这种僵局的简短示例程序吗?
4.这个死锁是操作系统中的错误吗?
答案 0 :(得分:2)
当父级在读取任何输出之前尝试向其子级的输入流发送过多数据时,由于缓冲区大小有限,可能会发生死锁。
考虑以下代码:
final int LINES = 10;
// "tr" is a Unix command that translates characters;
// Here, for every line it reads, it will output a line with
// every 'a' character converted to 'A'. But any command that outputs
// something for every line it reads (like 'cat') will work here
Process p = Runtime.getRuntime().exec("tr a A");
Writer out = new OutputStreamWriter(p.getOutputStream());
for (int i = 0; i < LINES; i++) {
out.write("abcdefghijklmnopqrstuvwxyz\n");
}
out.close();
// Read all the output from the process and write it to stdout
BufferedReader in = new BufferedReader(new InputStreamReader(p.getInputStream()));
String line;
while ((line = in.readLine()) != null) {
System.out.println(line);
}
对于lines
的小值,它会正常工作;在我们开始读取它之前,所有tr
的输出都可以放在OS缓冲区中。但是,对于较大的值(> 10000应该足够),OS缓冲区将填满;在tr
命令中,对write
的调用将阻塞,等待缓冲区被耗尽,反过来,Java代码写入的缓冲区将填满(因为tr
被阻止,阻止它从其输入读取),反过来阻止我们对out.write
的调用,导致死锁,两个进程都在等待写入不是的完整缓冲区正在积极地阅读。
此死锁不是操作系统中的错误,因为进程间通信的有限缓冲区大小是故意的设计决策。替代方案(无限缓冲区大小)有一些缺点:
另外,由于进程内缓冲区也可能发生死锁。假设为了尝试解决上面的死锁,我们改变了我们的Java代码来写一行,然后交替地读取一行。但是,it's common for Linux processes to not flush after every line when they're not writing directly to a terminal。所以tr
可能会读取一行,并将其写入其libc输出缓冲区,然后阻塞等待下一行写入 - 我们的Java代码将阻止等待tr
输出一行
答案 1 :(得分:1)
死锁总是涉及至少2名参与者。因此,不是单独的子进程可能会死锁,而是父进程和子进程的组合。
举个例子:
Subprocess想要读取一行,处理它并打印结果。 父进程认为它可以先读取子进程的输出,然后才为它提供输入。
结果:死锁,两个进程都在等待另一个进程的输入。
因此,不,它不是操作系统中的错误,而是(父)进程中的逻辑错误。