在C中使用管道进行父子IPC会使程序阻塞

发布于 2024-07-14 12:22:54 字数 1966 浏览 6 评论 0原文

我正在编写一个服务器,当它接受套接字连接时,它会关闭子进程。

当子级与客户端通信时,它必须将一些信息发送回父级。 我正在使用管道来完成此任务。

问题是,当我尝试执行父子 IPC 时,父级在读取子级输入时会阻塞。 这意味着,即使子进程同时运行,它们也只能一次被处理一个,因为它们都在等待父进程。

我的代码看起来像这样(为简洁起见,删除了错误检查):

/* loop: wait for clients to connect */
for(;;) {

  pipe(write_to_parent); /* create pipe between parent and child */
  pipe(write_to_child);  /* create pipe between parent and child */

  newsockfd = accept(...);

  pid = fork();

  if (pid == 0) { /* child process */
      close(write_to_parent[0]);
      close(write_to_child[1]);

     printf("Connected!\n");

     while ((rc = recv(newsockfd, ...)) > 0) {
         /* process socket request */

         /* write stuff to parent that is related to what we recv'd from client */
         rc = write(write_to_parent[1], &buf, 1024);
     }

     printf("Disconnected!\n");

     exit(0);
  } else { /* parent */

      close(write_to_parent[1]);
      close(write_to_child[0]);

       while (read(write_to_parent[0], &buf, 1024) > 0) {
           /* accept data from child process, and do some processing */
       }
  }
}

所以我的问题是,我该如何解决这个问题? 如何让父级使用管道与子级进行通信而不阻塞?

这对于管道来说是否可能,或者我应该使用共享内存(我猜是带有信号量)或消息队列? (我读过这篇文章:Comparing Unix/Linux IPC,但是很难找到如何使用的示例这些任务实际上已经完成。)

更多详细信息:

我有一个测试程序,它执行以下操作: 1.连接服务器 2.睡眠(5) 3. 与服务器断开连接

当我运行该程序的 2 个实例时,服务器输出以下内容:

connected!
  // pause for 5 seconds
disconnected!
connected!
  // pause for 5 seconds
disconnected!

显然,一次处理每个客户端一个。

当我删除 IPC 时 - 当我从父级和子级中删除管道 read() 和 write() 时,我得到:

connected!
connected!
  // pause for 5ish seconds
disconnected!
disconnected!

这就是我想要的!

关于我如何实现这一目标有什么想法吗? (或者我应该在解决这个问题的方式上做出改变?)

(编辑:这是网络课程作业的一部分。我们正在实现一个 P2P 协议,它使用集中式服务器来管理对等点。我们可以使用任何语言,我想我应该尝试一下 C 。)

I am writing a server which fork()'s off a child process when it accepts a socket connection.

As the child communicates with a client, it must send some of that information back to the parent. I am using a pipe to accomplish this.

The problem is that when I try to do the parent-child IPC, the parent blocks when reading input from the child. Which means that, even though the children are running concurrently, they can only be processed one-at-a-time because they are all waiting for the parent.

My code looks something like this (error-checking removed for brevity):

/* loop: wait for clients to connect */
for(;;) {

  pipe(write_to_parent); /* create pipe between parent and child */
  pipe(write_to_child);  /* create pipe between parent and child */

  newsockfd = accept(...);

  pid = fork();

  if (pid == 0) { /* child process */
      close(write_to_parent[0]);
      close(write_to_child[1]);

     printf("Connected!\n");

     while ((rc = recv(newsockfd, ...)) > 0) {
         /* process socket request */

         /* write stuff to parent that is related to what we recv'd from client */
         rc = write(write_to_parent[1], &buf, 1024);
     }

     printf("Disconnected!\n");

     exit(0);
  } else { /* parent */

      close(write_to_parent[1]);
      close(write_to_child[0]);

       while (read(write_to_parent[0], &buf, 1024) > 0) {
           /* accept data from child process, and do some processing */
       }
  }
}

So my question is, how do I remedy this? How can I have the parent communicating with a children using pipes in such a way that it is not blocking?

Is this even possible with pipes, or should I used shared memory (with a semaphore, i guess) or message queue instead? (I read this post: Comparing Unix/Linux IPC but it is really difficult to find examples how these tasks are actually accomplished.)

More details:

I have a test program that does the following:
1. Connect to the server
2. Sleep(5)
3. Disconnect from the server

When I run 2 instances of this program, the server outputs this:

connected!
  // pause for 5 seconds
disconnected!
connected!
  // pause for 5 seconds
disconnected!

Obviously processing each client one at a time.

When I remove the IPC - when I get rid of the pipe read() and write()'s from both the parent and child, I get this:

connected!
connected!
  // pause for 5ish seconds
disconnected!
disconnected!

Which is what I want!

Any thoughts on how I can accomplish this? (or changes I should make in the way I am going about solving this problem?)

(edit: This is part of an assignment for a networking class. We are implementing a P2P protocol that uses a centralized server to manage the peers. We could use any language, and I figured I'd give it a whirl in C .)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

你是暖光i 2024-07-21 12:22:54

我在下面写了答案,在重读您的问题后,发现我完全错过了您的实际问题。 然而,无论如何,它可能都是有用的信息。

要回答并发父级问题,您需要让父级设置一个 select() 循环来随时处理来自其任何子级的可能响应。 您将需要:

  • 跟踪打开了多少个子进程
  • 为每个子进程保留一组管道
  • 使用带有 select() 的循环来接受传入连接(因为它们可能随时发生),并且来自任何子进程的传入数据都
  • 使用 waitpid() 来获取已终止的子进程。

这种技术很强大,但确实需要大量簿记才能正确设置。


默认情况下,在父进程中打开的文件句柄在子进程中继承。 问题可能是父级和子级的管道的写入端仍然打开,因此当父级从管道读取时,它永远不会看到管道的末尾。 在开始从 write_to_parent[0] 读取之前,尝试在父级中使用 close(write_to_parent[1])。 所以:

} else { /* parent */
     close(write_to_parent[1]);
     while (read(write_to_parent[0], &buf, 1024) > 0) {
         /* accept data from child process, and do some processing */
     }
}

I wrote the answer below and upon rereading your question, noticed that I had missed your actual question completely. However, it may be useful info in any case.

To answer your concurrent parent question, you will need to have the parent set up a select() loop to handle possible responses from any of its children at any time. You will need to:

  • keep track of how many children are open
  • keep a set of pipes for each child
  • use a loop with select() to both accept incoming connections (since they may happen at any time) and also incoming data from any child
  • use waitpid() to reap children who have terminated

This technique is powerful but does require a lot of bookkeeping to set up properly.


File handles opened in the parent are by default inherited in child processes. The problem may be that both the parent and the child have the write end of the pipe still open, so the when the parent reads from the pipe, it will never see the end of it. Try using a close(write_to_parent[1]) in the parent immediately before you start reading from write_to_parent[0]. So:

} else { /* parent */
     close(write_to_parent[1]);
     while (read(write_to_parent[0], &buf, 1024) > 0) {
         /* accept data from child process, and do some processing */
     }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文