使用 pipe()、fork() 和信号处理在程序中进行内存和 pipe/socket 管理

memory and pipe/socket management in a program with pipe(), fork() and signal handling

使用 forkpipesignal.h 信号处理。 STDIN用于快速模拟与网络服务器的socket连接。伪代码:

struct message_t {
    uint32_t length;
    uint8_t *data;
};

int is_shutdown = 0;

void signal_handler(int) {
    //set is_shutdown if SIGINT
}

int main() {
    //initialize
    pipe();
    fork();

    if (parent) {
        while(!is_shutdown) {
            //0. initialize variables, buffer, message_t
            //1. read string of unknown length from STDIN (or socket) to a buffer
            //2. malloc() message_t and fill it's fields
            //3. write() message_t to pipe
            //4. free() message_t
        }
    }

    if (child) {
        while(!is_shutdown) {
            //0. initialize variables, message_t
            //1. read() message_t: read length, malloc() buffer, read data
            //2. execute command
            //4. free() message_t
        }   
    }

    //close pipe
}

让我困惑的几件事:

  1. 我应该 close() 信号处理程序中的 pipe 吗?
  2. 我不完全理解消息(它是内存)在我发送后发生了什么 (parent)。我应该 free() write() 之后的缓冲区吗?
  3. 如何处理信号处理程序的资源释放。我是否应该将缓冲区和 fd 实现为全局变量以从信号处理程序访问它们
  4. is_shutdown 标志和进程间通信。我是否理解 parent 和 child 不共享 is_shutdown 作为同一个变量(它们是副本)并且改变一个不会改变另一个?
  5. 信号处理程序和 mainis_shutdown 的并发访问怎么样?任何隐藏的细节,比如通过多线程并发?
  6. 当一个进程关闭时,另一个进程仍在运行。如果管道发生问题,我将无法通知另一个人。我应该希望得到 SIGPIPE 吗?
  7. 我怀疑从性能的角度来看,在 parent 的 while 循环的每次迭代中调用 malloc() 并不明智。但是定义 "big enough" 的缓冲区感觉就像一个黑客(如果有一天它不够大怎么办)。我错了吗?

我对 C 及其内存和资源管理还很陌生:从基于强大英特尔 server/desktops 的 C++14 开发切换到 ~180MHz ARM 嵌入式系统的 C 开发,所以我可能非常担心并忘记一些显而易见的事情。

  1. 不,你不应该。您应该在信号处理程序中做的 设置为 is_shutdown.
  2. write 复制一份。在 write returns 之后,可以安全地重用或释放缓冲区。 (不过,请确保处理短写入。)
  3. 你不知道。同样,您应该在信号处理程序中做的 设置为 is_shutdown。在 while 循环终止后清理 parentchild 块。
  4. 是的,每个进程都有一个独立的副本。您还应该知道,对于这种设计,如果您从终端 运行 程序,然后在其中键入 ^C,内核将在两个进程中触发 SIGINT,因为它们都在同一个进程组中.
  5. is_shutdown 需要用类型 volatile sig_atomic_t 来声明,并且对于像这样的简单程序,除了信号处理程序之外,它不应该被任何东西写入。此外,信号处理程序不应该读取它的值,它应该只设置它。根据您未向我们展示的代码的详细信息,安装信号处理程序而不使用 SA_RESTART 可能是合适的,这样它会中断阻塞的系统调用。
  6. 您唯一需要做的就是确保通信通道的每一端都能妥善处理另一端的突然断开连接。
    • 如果这是最终将成为网络服务器和客户端的模型,您应该在开始测试突然断开连接之前从使用 pipe 更改为使用 socketpair,因为 socketpair fds 的行为更像真正的网络套接字。
  7. I/O 开销可能比 malloc 开销大得多。在这个阶段担心 malloc 开销是过早的优化。