C、TCP服务器上的'fork: resource temporarily unavailable'

C, 'fork: resource temporarily unavailable' on TCP server

int
run ()
{
  char str[100];
  int listen_fd, comm_fd;

  struct sockaddr_in servaddr;
  listen_fd = socket (AF_INET, SOCK_STREAM, 0);

  bzero (&servaddr, sizeof (servaddr));
  servaddr.sin_family = AF_INET;
  servaddr.sin_addr.s_addr = htons (INADDR_ANY);
  servaddr.sin_port = htons (8080);
  int n;
  bind (listen_fd, (struct sockaddr *) &servaddr, sizeof (servaddr));
  listen (listen_fd, 10);

  int pid;
  while (1)
    {
      comm_fd = accept (listen_fd, (struct sockaddr *) NULL, NULL);
      if (comm_fd < 0)
        printf ("ERROR on accept");
      //fork new process
      pid = fork ();
      if (pid < 0)
        {
          printf ("ERROR in new process creation");
        }
      if (pid == 0)
        {
          close (listen_fd);
          bzero (str, 256);
          n = read (comm_fd, str, 255);
          if (n < 0)
            printf ("ERROR reading from socket");
          printf ("Here is the message: %s\n", str);
          n = write (comm_fd, "I got your message", 18);
          if (n < 0)
            printf ("ERROR writing to socket");
          close (comm_fd);
        }
      else
        {

          close (comm_fd);
        }
    }

  printf ("readIn Exit!");
  exit (1);

}

为什么这会造成分叉炸弹,我怎样才能让它作为可以处理客户端丢失的多客户端服务器工作?

一些帖子建议每个用户的最大进程太低,但我的是 709。对我来说,这对于这篇 运行 来说似乎足够了(我正在使用 mac 书)

我也试过下面的方法,结果一样:

while((comm_fd = accept(listen_fd, (struct sockaddr *) NULL, NULL))){
         pid = fork();
         if(pid == 0){
            close(listen_fd);
            while(1){
              bzero( str, 100);
              read(comm_fd, str, 100);
              printf("S: %s", str);
              write(comm_fd, str, strlen(str)+1);
              sleep(1);
            }
           exit(1);
         } else {
           close(listen_fd);
         }
    }

由于这部分,这是一个叉子炸弹:

while (1) {
    pid = fork();
    if (pid == 0) { /* child */
       ...read()...write()...
    } else {        /* parent */
       close(comm_fd);
    }
}

这是一个无限循环,其中 parent 不断分叉和关闭 comm_fd。每一个child,在一次读写成功后,会在下一次迭代中依次fork。重复直到所有进程槽都泄漏。

您的第二个代码段用完了所有进程槽,因为 parent 需要 wait()wait4() 来收集 children 的退出状态。在完成此操作之前,进程将保持僵尸状态。每个僵尸占用一个进程槽。

就像 malloc 需要相应的空闲,fork 需要等待。

PS:C 中的规范无限循环写成 for(;;) 以避免 while(1).

中的 constant-value 布尔值

这是一个叉子炸弹,因为您永远不会终止子进程,所以它会继续 运行 通过在 accept() 给出错误但不会终止进程的 while 中循环。所以它会继续 fork() 并永远这样做。

修改代码如下:

if (pid == 0)
  {
    close (listen_fd);
    bzero (str, 256);
    n = read (comm_fd, str, 255);
    if (n < 0)
      printf ("ERROR reading from socket");
    printf ("Here is the message: %s\n", str);
    n = write (comm_fd, "I got your message", 18);
    if (n < 0)
      printf ("ERROR writing to socket");
    close (comm_fd);
    exit(some_value); // terminates the child
  }

要清理系统 tables,您还需要等待子进程终止:

else
  {
    close (comm_fd);
    while (waitpid(0,NULL,WNOHANG)!=-1); // clean system tables non blocking
  }

这不是执行 table 的最佳方法,但它相对有效,至少足够了。最好的方法是捕获信号 SIGCHLD 并清除处理程序中的 table。