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。
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)
.
这是一个叉子炸弹,因为您永远不会终止子进程,所以它会继续 运行 通过在 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。