【问题标题】:Simple Linux IPC Question简单的 Linux IPC 问题
【发布时间】:2010-12-22 19:34:05
【问题描述】:

目前我有一个客户端进程和一个服务器进程。客户端进程需要经常联系服务器进程以交换数据,但需要知道服务器的 pid 才能这样做。客户应该如何知道如何做到这一点?我想避免重复的硬盘访问。此应用程序仅在 linux 下运行。当前,服务器使用其 pid 或 RAM 磁盘设置一个锁定文件。客户端检查文件。我还能如何有效地完成此事务,以便服务器可以向客户端发送信号? (注:客户端是PHP,服务器是c)

【问题讨论】:

  • 正如其他人所建议的那样,我错误地处理了整个问题,但是:确实存在一种方便的方法来精确地完成我当时想做的事情(优化重复的文件系统访问)!它是经常挂载在 /dev/shm 的 tmpfs 文件系统。它可能不是 100% 跨发行版的可移植性,但它似乎是相当标准的。

标签: c linux ipc


【解决方案1】:

一些想法:

  1. 什么都不做;如果您反复读取它,读取磁盘文件(在正确的永久磁盘上)不会导致任何 IO,因为该文件已经在缓存中。
  2. 重构您的系统,这样您就不需要知道 pid 文件了
  3. 你确定你真的在乎吗?过早的优化等等。您每秒执行多少次,1000 次或更多?

【讨论】:

    【解决方案2】:

    我假设,当您说“进程”时,这两个在同一台机器上?如果是这样,您可以使用/tmp 目录中的命名FIFO。这是两个进程使用 IPC 的示例,命名为 FIFO /tmp/test.fifofork()

    #include <stdio.h>
    #include <string.h>
    #include <errno.h>
    #include <unistd.h>
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <linux/stat.h>
    
    int errno;
    
    int main(int argc, char** argv)
    {
        char fifo_path[] = "/tmp/test.fifo";
        char buffer[128];
    
        int result = mkfifo(fifo_path, 0600);
    
        printf("mkfifo result = %d\n", result);
    
        if (errno == EEXIST)
            printf("errno == EEXIST\n");
    
        pid_t child = fork();
    
        if (child == 0)
        {
            printf("%d> child; opening fifo \"%s\" for writing\n", getpid(),
                     fifo_path);
    
            FILE* fifo = fopen(fifo_path, "w");
            char in_buffer[128];
    
            fgets(in_buffer, 128, stdin);
    
            fputs(in_buffer, fifo);
    
            fclose(fifo);
        }
        else
        {
            printf("%d> parent; opening fifo \"%s\" for reading\n", getpid(),
                     fifo_path);
    
            FILE* fifo = fopen(fifo_path, "r");
    
            fgets(buffer, 128, fifo);
    
            if (buffer[0] == EOF)
                printf("%d> got EOF\n", getpid());
            else
            {
                buffer[strlen(buffer) - 1] = 0;
                printf("%d> read string \"%s\"\n", getpid(), buffer);
            }
    
            fclose(fifo);
        }
    
        return 0;
    }
    

    所以只要两个进程都知道 FIFO 的完整路径,就可以对其进行读写。

    【讨论】:

    • 这是我已经做的,但问题是同步。目前我的设置是让客户端信号服务器准备好进行事务处理,然后事务发生在服务器的信号处理函数中。它如何知道将信号发送到哪个 pid?
    • 你不需要知道PID;您只需要知道客户端是否正在读取 FIFO。有一种方法可以做到这一点;不过我不确定。我相信服务器在写入 FIFO 时会阻塞,直到它被打开以供读取。您可以使用某些参数创建 FIFO,这意味着服务器不会阻塞,但如果没有被读取,服务器将收到 SIGPIPE 信号。我希望这会有所帮助。
    【解决方案3】:

    通常您不使用 pid,而是使用某种地址 - IP 地址(包括端口)、Unix 域套接字地址、文件系统中的路径或构建在一个之上的一些更高级别的 IPC 系统其中(D-Bus、X 等)到达服务器并与之通信。 pid 唯一有用的是发送信号,这可能是一种非常糟糕的通信方式,如果您将客户端和服务器分成不同的权限域,它将无法工作。

    【讨论】:

    • 好的,好的,很好,我喜欢这样的声音,而且我已经不得不做一些不愉快的事情来获得正确的权限,但是如果我实现这个我的服务器怎么知道当客户要求某事时? 它需要在大部分时间做一件事(为客户收集数据),然后当客户要求时它需要传输。现在,信号处理程序允许我在信号进入时异步执行代码。但是使用套接字......我可以为套接字设置某种事件处理程序吗?我应该分叉并让一个分支循环监听套接字吗?
    • 对此有两种通用方法。一种是使用select(或poll)将您的服务器构建为一个事件循环,以等待来自多个文件描述符中的任何一个的输入,并且可能还等待从缓冲区写入输出的能力。另一种是使用线程或单独的进程,每个进程都阻塞在 IO 中。但是,如果您使用进程,您将遇到如何使它们通信并最终返回select 的问题。所以我要么只使用select 循环开始,要么使用线程(与条件变量和障碍等pthread原语通信)。
    【解决方案4】:

    其他一些选项包括:

    1. 将服务器设置为侦听特定端口。
    2. 服务器可以设置命名管道,客户端可以通过它与之通信。

    【讨论】:

    • 所以...没有人回答我的问题。我的问题不完全是关于 IPC。它是关于 IPC 的同步。我已经让客户端和服务器通过命名管道进行通信。问题是服务器应该如何知道何时监听管道。现在这是通过信号完成的(因此知道服务器的 pid 并将其保存在重复访问信息不会垃圾磁盘读取的地方的问题)。您是说服务器不做任何其他事情时应该只在管道上侦听客户端通信,而不是尝试同步?
    【解决方案5】:
    • Unix 域套接字
    • D-Bus

    【讨论】:

      【解决方案6】:

      我认为您可以在非阻塞模式下使用名称 IPC 消息队列。我不知道命名管道是否支持非阻塞模式,但如果是,那么你也可以使用它。在 non_block 模式下,您只需注册队列描述符以发出信号并进入一般等待。如果队列中有任何活动,您的进程将被唤醒。请在互联网上进行一些搜索,您可以找到许多相同的示例。

      要给出正确/准确的答案,我应该知道您的服务器进程设计。

      【讨论】:

        猜你喜欢
        • 2012-05-26
        • 2012-09-28
        • 2017-02-18
        • 1970-01-01
        • 1970-01-01
        • 2012-09-30
        • 2010-10-13
        • 2010-11-28
        • 2011-02-22
        相关资源
        最近更新 更多