java.io.IOException: error=11

10

我在使用Java ProcessBuilder时遇到了一个奇怪的问题。以下是代码(稍微简化了一下):

public class Whatever implements Runnable
{

public void run(){
        //someIdentifier is a randomly generated string
        String in = someIdentifier + "input.txt";
        String out = someIdentifier + "output.txt";
        ProcessBuilder builder = new ProcessBuilder("./whateveer.sh", in, out);
        try {
            Process process = builder.start();
            process.waitFor();
        } catch (IOException e) {
            log.error("Could not launch process. Command: " + builder.command(), e);
        } catch (InterruptedException ex) {
            log.error(ex);
        }
}

}

whatever.sh 的内容如下:

R --slave --args $1 $2 <whatever1.R >> r.log    

大量的Whatever实例被提交到一个固定大小(35)的ExecutorService。应用程序的其余部分等待它们全部完成-使用CountdownLatch实现。在几个小时后(Scientific Linux 5.0,java版本“1.6.0_24”),一切都运行良好,然后抛出以下异常:

java.io.IOException: Cannot run program "./whatever.sh": java.io.IOException: error=11, Resource temporarily unavailable
    at java.lang.ProcessBuilder.start(Unknown Source)
... rest of stack trace omitted...

有人知道这是什么意思吗?根据对java.io.IOException: error=11的google / bing搜索结果,这不是最常见的异常之一,我完全困惑了。

我猜测可能是有太多线程尝试同时启动同一个文件。 但是,为了重现问题,需要数小时的CPU时间,因此我尚未尝试使用较少的线程。

非常感谢任何建议。


你是否使用 lsof 命令检查了 Java 进程的打开文件数? - lidaobing
1
是 errno 11 还是 12 还是两者都有? - Peter Lawrey
2个回答

9

error=11几乎肯定是EAGAIN错误代码:

$ grep EAGAIN asm-generic/errno-base.h 
#define EAGAIN      11  /* Try again */
< p > clone(2)系统调用记录了一个EAGAIN错误返回:< /p>
   EAGAIN Too many processes are already running.

fork(2)系统调用记录了两个EAGAIN错误返回:

   EAGAIN fork() cannot allocate sufficient memory to copy the
          parent's page tables and allocate a task structure for
          the child.

   EAGAIN It was not possible to create a new process because
          the caller's RLIMIT_NPROC resource limit was
          encountered.  To exceed this limit, the process must
          have either the CAP_SYS_ADMIN or the CAP_SYS_RESOURCE
          capability.

如果您的内存确实非常低,系统日志几乎肯定会显示。请检查dmesg(1)输出或/var/log/syslog是否有关于系统内存不足的潜在消息。(其他事情也会出问题,这似乎不太可能。)
更有可能遇到的是超过用户进程限制或系统范围内进程最大数量的限制。也许你的某个进程没有正确地清理僵尸?通过随时间检查ps(1)输出,这将非常容易发现:
while true ; do ps auxw >> ~/processes ; sleep 10 ; done

(也许可以每分钟或每十分钟检查一次,看看真正需要多长时间才会出现问题。)
如果你没有消灭僵尸进程,那就了解一下如何使用ProcessBuilder来使用waitpid(2)消灭已死的子进程。
如果你真的运行了比rlimits允许的进程还要多的进程,你需要在bash(1)脚本中使用ulimit(如果是以root身份运行),或者在/etc/security/limits.confnproc属性设置更高的限制。
如果你遇到了系统范围内的进程限制,则可能需要将更大的值写入/proc/sys/kernel/pid_max。有关详细信息,请参阅proc(5)

谢谢,这让我朝着正确的方向前进了。由于代码中存在一个错误,导致在循环中创建了一个新线程而不是重用现有线程,最终导致了所描述的问题。 - mbatchkarov
那么问题不是 builder.start() 调用本身有错,而只是报告了问题的代码片段?好的调试技巧。 :) - sarnold
好的,实际上是 builder.start() 失败了,但它失败是因为应用程序的另一部分已经耗尽了可用资源。正如您在帖子中提到的,分叉进程需要一定量的内存等资源。感谢您的帮助。 - mbatchkarov

2

errno 11表示“资源暂时不可用”,通常是由于内存问题导致线程或套接字无法创建。

errno 12表示“无法分配内存”。这是直接调用内存而非需要内存的资源时,未能获取内存的失败。

我建议尝试增加系统的交换空间,这应该可以避免这个问题。


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