Linuxでのプロセス間通信

プロセス間通信

プロセス間のメモリは分離されているため、複数のプロセス間で情報を交換する必要がある場合、以下の一般的な方法があります:

  • UnixドメインソケットIPC
  • パイプ(名前付きパイプ、無名パイプ)
  • 共有メモリ
  • メッセージキュー
  • セマフォ

SystemV IPC と POSIX IPC

System V

System VはUNIXベースのオペレーティングシステムバージョンであり、1983年に初めて発表されました。多くのUNIXおよび類似のUNIXシステム(例:Linux)に採用された機能や標準を導入しました。

System V IPC

System V IPCはSystem Vオペレーティングシステムが導入した一連のプロセス間通信機構です。メッセージキュー、セマフォ、共有メモリが含まれます。

  • メッセージキュー:プロセス間でメッセージ形式でデータを交換します。
  • セマフォ:主にプロセス間同期に使用され、複数のプロセスが同時に同じリソースにアクセスすることを防ぎます。
  • 共有メモリ:複数のプロセスが同じメモリ領域にアクセスできるようにし、効率的なデータ共有を提供します。

POSIX IPC

POSIX IPCはPOSIX標準の一部であり、現代的かつ標準化されたプロセス間通信方式を提供します。メッセージキュー、セマフォ、共有メモリも含まれますが、よりシンプルなAPIと優れたエラーハンドリングが特徴です。

  • メッセージキュー:System V IPCと似ていますが、APIが簡潔でエラーハンドリングが強化されています。
  • セマフォ:より多くの機能と高いパフォーマンスを提供し、幅広い操作をサポートします。
  • 共有メモリ:より多くの制御と構成オプションがあり、複雑なユースケースに対応します。

POSIX IPCでは、リソースを一意に識別するために名前(name)を使用します。

比較

System V IPCとPOSIX IPCは機能的に重複していますが、実装とAPI設計には明確な違いがあります。POSIX IPCは通常、より現代的で標準化された解決策として認識されており、プラットフォーム間の互換性や使いやすいAPIを提供します。

無名パイプ

perror関数

#include <stdio.h>

int main() {
    FILE* file = fopen("存在しないファイル.txt", "r");
    if (!file) {
        perror("エラー: ファイルを開けませんでした");
    }
    return 0;
}

pipeシステムコール

無名パイプはカーネル内のバッファ領域で動作し、父子プロセス間でデータを転送できます。

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>

int main() {
    int pipefd[2];
    pid_t cpid;

    if (pipe(pipefd) == -1) {
        perror("パイプの作成に失敗しました");
        exit(EXIT_FAILURE);
    }

    cpid = fork();
    if (cpid == -1) {
        perror("フォークに失敗しました");
        exit(EXIT_FAILURE);
    }

    if (cpid == 0) {
        close(pipefd[1]);
        char buf;
        while (read(pipefd[0], &buf, 1) > 0) {
            write(STDOUT_FILENO, &buf, 1);
        }
        close(pipefd[0]);
        _exit(EXIT_SUCCESS);
    } else {
        close(pipefd[0]);
        const char* message = "こんにちは";
        write(pipefd[1], message, strlen(message));
        close(pipefd[1]);
        wait(NULL);
        exit(EXIT_SUCCESS);
    }
}

名前付きパイプ(FIFO)

名前付きパイプは、任意のプロセス間での通信を可能にします。

mkfifo関数

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>

int main() {
    const char* fifo_path = "/tmp/myfifo";

    if (mkfifo(fifo_path, 0664) != 0 && errno != EEXIST) {
        perror("mkfifo failed");
        exit(EXIT_FAILURE);
    }

    int fd = open(fifo_path, O_WRONLY);
    if (fd == -1) {
        perror("open failed");
        exit(EXIT_FAILURE);
    }

    const char* message = "こんにちは!";
    write(fd, message, strlen(message));
    close(fd);

    unlink(fifo_path);
    return 0;
}

共有メモリ

mmap関数

#include <sys/mman.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

int main() {
    const char* shm_name = "/shared_memory";
    int fd = shm_open(shm_name, O_CREAT | O_RDWR, 0644);
    if (fd == -1) {
        perror("shm_open failed");
        exit(EXIT_FAILURE);
    }

    ftruncate(fd, 100);

    void* ptr = mmap(NULL, 100, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
    if (ptr == MAP_FAILED) {
        perror("mmap failed");
        close(fd);
        exit(EXIT_FAILURE);
    }

    strcpy((char*)ptr, "こんにちは!");
    munmap(ptr, 100);
    close(fd);
    shm_unlink(shm_name);

    return 0;
}

メッセージキュー

mq_open関数

#include <mqueue.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>

int main() {
    struct mq_attr attr = {0};
    attr.mq_maxmsg = 10;
    attr.mq_msgsize = 100;

    mqd_t mqdes = mq_open("/test_queue", O_CREAT | O_RDWR, 0644, &attr);
    if (mqdes == -1) {
        perror("mq_open failed");
        exit(EXIT_FAILURE);
    }

    const char* message = "テストメッセージ";
    mq_send(mqdes, message, strlen(message), 0);

    char buffer[100];
    mq_receive(mqdes, buffer, sizeof(buffer), NULL);
    printf("受信: %s\n", buffer);

    mq_close(mqdes);
    mq_unlink("/test_queue");

    return 0;
}

タグ: linux IPC C

7月24日 19:01 投稿