プロセス間通信
プロセス間のメモリは分離されているため、複数のプロセス間で情報を交換する必要がある場合、以下の一般的な方法があります:
- 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;
}