从Java进程生成tar文件时,tar命令卡住了。

4
使用Runtime的exec()方法运行以下命令,使用Java生成目录的tar文件。
tar -zcvf dest.tar.gz -C dirTotar .

创建tar文件的Java代码

    String command = "tar -zcvf dest.tar.gz -C dirTotar .";
    Process process = Runtime.getRuntime().exec(command);
    process.waitFor()
    return process.exitValue();

Java进程是一个服务器,它在每个输入处理后创建tar文件。在处理了一些输入后,该过程在tar步骤处卡住了。

运行strace -p查找进程是否被卡住,并显示以下输出:

strace -p 6782 
Process 6782 attached - interrupt to quit 
write(1, "./file"..., 66

我手动运行了tar命令以处理输入目录,它完美地工作了。有什么指点呢?
更新: 对于以下目录结构:
/tmp/dir1
/tmp/dir2
/tmp/dir3
.
.

正在生成tar文件的目录是dir1。还有其他进程在为dir2、dir3等目录生成tar文件。

当生成dir1的tar文件的进程被卡住时,试图对其他目录(如dir2、dir3)进行tar的其中一个进程也会被卡住。这可能是问题的原因吗?


2
请在执行tar的地方添加您的Java代码。 - Yeti
1
@HRgiger -x 是指“提取”。 - Gyro Gearless
哦,抱歉我以为你想要提取。 - HRgiger
没有任何进程或命令会打开源目录中的文件(我们正在尝试压缩)。 - noob
请提供要翻译的英文内容。 - noob
显示剩余2条评论
1个回答

4
事实1:每个文件被tar归档后,tar zcv...会输出包含该文件名的行。GNU tar(显然您正在使用)在指定输出文件时将列表输出到stdout,但当指定或默认为-(使用stdout存档)时,它使用stderr。列表中每行的长度取决于每个文件名,而此列表的总长度取决于文件数量及其文件名的总长度。
事实2:由Java Runtime.exec运行的进程的stdout被定向到提供给Java程序的另一个管道端,因此Java代码可以读取来自该进程的输出。stderr也是如此,stdin则相反,但在这种情况下不相关。您的代码不读取此管道(或利用任何管道)的任何内容。
事实3:当进程(例如tar)写入管道并且没有任何内容从该管道的(另一端)读取时,当管道缓冲区填满时,写入进程会挂起。您的strace显示tar(明显挂起)在向fd 1写入,即stdout。管道缓冲区的大小可以根据您的系统和有时其他因素而变化,但通常为几K字节,具体取决于要归档的文件名的数量和名称。
解决方案:要么更改您的代码以从tar读取输出--请参见Process.getInputStream()--要么更改命令(通过删除v)使其不产生(非错误)输出。
PS:正如javadoc中所述,自Java 5以来推荐使用ProcessBuilder。默认情况下,它还使用来自新进程的管道,但自Java 7以来有(多个)方法可以更改此设置。

事实证明,tar v 输出到 stderr,而不是 stdout,这使得 tar 命令有点不同,因为它在正常运行时非常快地填满 stderr 缓冲区,但很少向 stdout 写入数据。 - Mzzl
@Mzzl:不是带有f filename的GNU tar(除了特殊情况f -)。Linux上的tar几乎总是GNU。但我可能也需要明确一下,已编辑。 - dave_thompson_085
我刚刚验证了你所说的,你是完全正确的。看起来bsdtar 2.8.3(苹果笔记本电脑)和GNU tar 1.29(Linux虚拟机)在这里的行为非常不同。这是意外的行为,浪费了我整个上午的时间。 - Mzzl

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接