4

我的 C 类包括实现一个非常小的外壳。

我们的 shell 检查用户的输入行是否有内部命令(例如 cd 或 exit),如果没有找到,它会 forks() 和 execs() 输入行。

我希望实现一个万无一失的分叉(这超出了这门课的职责范围,请不要让我自己做作业,这更多的是个人研究,以了解更多 linux 的内部原理) . 我目前的做法是这样的:

t = fork();             
if (t < 0) {            
  if (errno == ENOSYS) {                                
    printf("fork() is not supported on this platform. Minishell will not work.");
    break;
  }
  else if (errno == ENOMEM) {   
    printf("No free kernel memory. Minishell will exit.");
    break;
  }
  else {        
    int try = 0;
    while (t < 0 && try < 10) {
      try++;
      t = fork();
    }
  }
  continue;
}

我的理解是 ENOSYS 不允许分叉,因此我们退出,ENOMEM 表示内核内存问题 - 远远超过我的工资等级(我没有付钱,我是学生;)),因此我们也退出。仍然是 EAGAIN,它有两种风格,这两种风格都可以通过等待并再次调用 fork() 来解决。

在之前的练习中,我们启动了一万个分叉,如果我没记错的话,大约有 1500 个失败,直到我们实现了一个像我上面的 while 一样的计数器。如果我想以一种不那么天真的方式实现这个东西,我该如何继续?我想,特别是硬编码的十次尝试有点愚蠢。

谢谢。

4

2 回答 2

5

我认为您当前的重试方法误解了叉子的 EAGAIN 错误的含义。从 Linux 手册页:

EAGAIN fork() 无法分配足够的内存来复制父级的页表并为子级分配任务结构。

EAGAIN 由于遇到调用者的 RLIMIT_NPROC 资源限制,因此无法创建新进程。要超过此限制,进程必须具有 CAP_SYS_ADMIN 或 CAP_SYS_RESOURCE 功能。

第一个几乎等同于 ENOMEM,第二个是达到工艺上限的问题。除非您希望您的 minishell 始终让子进程死亡,否则快速连续尝试 10 次可能无济于事。

我建议您也将 EAGAIN 设为您的 fork 代码的致命错误。

但是,如果您的程序可以采取一些措施来减轻内存压力(释放程序缓存等),您可以这样做并重试一次。

值得一提的是,在正常负载下的任何现代系统上几乎不存在导致分叉失败的条件。

于 2013-11-08T15:32:13.143 回答
2

EAGAIN表示您有太多的进程/子进程。

如果子命令的生命周期相当短,那么wait让其中一个退出的方法可能会奏效。但是,如果您由于其他进程而不是 shell 的子进程而导致资源(进程或内存)不足,那么您就不走运了。

于 2013-11-08T15:40:15.293 回答