简单的 Linux IPC 问题

Posted

技术标签:

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

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

【问题讨论】:

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

一些想法:

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

其他一些选项包括:

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

【讨论】:

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

【讨论】:

【参考方案6】:

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

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

【讨论】:

以上是关于简单的 Linux IPC 问题的主要内容,如果未能解决你的问题,请参考以下文章

使用IPC_PRIVATE信号量简单的例子

Windows IPC 模拟 Linux Unix 域和 Netlink 套接字

Linux IPC之共享内存C 事例

简单的Linux管道程序,收不到数据

IPC(进程间的通讯方式)

Python 和 PHP 之间的简单、强大的 IPC