プロセス間通信の比較
デバイスドライバによるプロセス間通信 - ka2yanの日記のデバイスドライバは、既存のプロセス間通信と比べて本当に早いのか、を測定した。
調べること
100バイト程度のメッセージ(画像データ等の大容量データを対象にしているのではない)をやりとりするプロセス間通信で一番早いのは何か?
測定するプロセス間通信は、前回の日記に書いたデバイスドライバ(共有メモリ方式)とデバイスドライバ(メッセージRead/Write)、そして、メッセージキュー(POSIX)、UNIXドメインソケット、名前付パイプ(FIFO)の5種類。
メッセージキュー(System V)は、fd として扱えないので、測定しない。
測定プログラムの概要
−2プロセス間で128バイトのメッセージを送受信を繰り返すプログラム
−2プロセスは、NON_BLOCKING でメッセージをread/write
−read/writeする前に、select()する
測定内容
−メッセージの送受信回数 1000,000回
−timeコマンドでのコマンド実行時間(厳密な送受信時間ではない)
測定環境
−CentOS5.2 (2.6.18)
−CPU:Pentium4 2.8GHz Memory:2GB
結果
プロセス間通信 | real[s] | user[s] | sys[s] |
---|---|---|---|
デバイスドライバ(共有メモリ方式) | 7.322 | 0.716 | 2.922 |
デバイスドライバ(メッセージRead/Write方式) | 7.663 | 0.715 | 3.124 |
メッセージキュー(POSIX) | 6.935 | 0.792 | 2.755 |
UNIXドメインソケット | 9.679 | 1.716 | 7.956 |
名前付パイプ(FIFO) | 8.272 | 1.465 | 6.801 |
real :実時間(コマンドを起動してから終了するまでの時間)
user :ユーザCPU時間(ユーザープロセスが動作している時間)
sys :システムCPU時間(カーネルでの動作時間)
なんと、メッセージキュー(POSIX)が一番早い!
デバイスドライバが一番早いことを期待していたのだが、残念。
ただ、デバイスドライバ(メッセージRead/Write方式)は、メッセージキュー(POSIX)より
速度が1割程度遅くても、プロセス間通信のログが取れるというメリットがある。
そんなに遅くはないけど、なんとかもう少し早くならないかなぁ。
TODO
デバイスドライバ(メッセージRead/Write方式)をもう少し使えるドライバにする。
# もう少し2.6ドライバーらしくする
# 複数プロセス対応
今は、2プロセスでしか使えない。
# 複数メッセージ対応
今は、1メッセージしか保持できない
# シェルスクリプトからのメッセージ受信処理の実装
測定に使ったプログラム達
実行順序は、デバイスドライバと同様で、read 側を実行しておき、write 側の引数に1000000を渡す。
プログラムが多いので、Makefile と 実行例は省略する。
デバイスドライバ(共有メモリ方式)
デバイスドライバ(メッセージRead/Write方式)
メッセージキュー(POSIX)
mqread.c
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/stat.h> #include <sys/select.h> #include <mqueue.h> #include <fcntl.h> #include <errno.h> #define DPRINTF(...) //#define DPRINTF(...) printf(__VA_ARGS__) int main(int argc, char *argv[]) { mqd_t fd1, fd2; fd_set rfds, wfds; char buf[128]; int read_start; unsigned int prio=10; struct mq_attr attr; char *rbuf; memset(&attr, 0, sizeof(attr)); mq_unlink("/mq1"); mq_unlink("/mq2"); if ((fd1 = mq_open("/mq1", O_RDONLY|O_NONBLOCK|O_CREAT|O_EXCL, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH)) == -1) { perror("open /mq1"); exit(-1); } if ((fd2 = mq_open("/mq2", O_WRONLY|O_NONBLOCK|O_CREAT|O_EXCL, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH)) == -1) { perror("open /mq2"); exit(-1); } mq_getattr(fd1,&attr); if((rbuf = malloc(attr.mq_msgsize)) == NULL) { fprintf(stderr, "malloc error\n"); exit(-1); } read_start=1; while (1) { if (read_start) { buf[0] = '\0'; FD_ZERO(&rfds); FD_SET(fd1, &rfds); DPRINTF("selecting read...\n"); if (select(fd1+1, &rfds, NULL, NULL, NULL) == -1) { perror("select"); } if (FD_ISSET(fd1, &rfds)) { int n; n = mq_receive(fd1, rbuf, attr.mq_msgsize, &prio); if (n > 0 ) { DPRINTF("%d = read [%s] prio=%d\n", n, rbuf, prio); } else { perror("read"); exit(-1); } } read_start = 0; } else { memset(buf, 'r', sizeof(buf)); buf[sizeof(buf)-1] = '\0'; FD_ZERO(&wfds); FD_SET(fd2, &wfds); DPRINTF("selecting write...\n"); if (select(fd2+1, NULL, &wfds, NULL, NULL) == -1) { perror("select"); exit(-1); } if (FD_ISSET(fd2, &wfds)) { if (mq_send(fd2, buf, sizeof(buf), prio) != 0) { perror("write"); exit(-1); } } read_start = 1; // sleep(1); } } mq_close(fd1); mq_close(fd2); return 0; }
mqwrite.c
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/stat.h> #include <sys/select.h> #include <mqueue.h> #include <fcntl.h> #include <errno.h> #define DPRINTF(...) //#define DPRINTF(...) printf(__VA_ARGS__) int main(int argc, char *argv[]) { mqd_t fd1, fd2; fd_set rfds, wfds; char buf[128]; int read_start; unsigned int prio=10; int nloop, i; struct mq_attr attr; char *rbuf; if (argc != 2) { printf("usage:\n./mqwrite <NLOOP>\n"); exit(-1); } nloop = atoi(argv[1]); memset(&attr, 0, sizeof(attr)); if ((fd1 = mq_open("/mq2", O_RDONLY|O_NONBLOCK)) == -1) { perror("open /mq2"); exit(-1); } if ((fd2 = mq_open("/mq1", O_WRONLY|O_NONBLOCK)) == -1) { perror("open /mq1"); exit(-1); } mq_getattr(fd1,&attr); if((rbuf = malloc(attr.mq_msgsize)) == NULL) { fprintf(stderr, "malloc error\n"); exit(-1); } read_start=0; for(i=0; i<nloop; i++) { if (read_start) { buf[0] = '\0'; FD_ZERO(&rfds); FD_SET(fd1, &rfds); DPRINTF("selecting read...\n"); if (select(fd1+1, &rfds, NULL, NULL, NULL) == -1) { perror("select"); } if (FD_ISSET(fd1, &rfds)) { int n; n = mq_receive(fd1, rbuf, attr.mq_msgsize, &prio); if (n > 0 ) { DPRINTF("%d = read [%s] prio=%d\n", n, rbuf, prio); } else { perror("read"); exit(-1); } } read_start = 0; } else { memset(buf, 'w', sizeof(buf)); buf[sizeof(buf)-1] = '\0'; FD_ZERO(&wfds); FD_SET(fd2, &wfds); DPRINTF("selecting write...\n"); if (select(fd2+1, NULL, &wfds, NULL, NULL) == -1) { perror("select"); exit(-1); } if (FD_ISSET(fd2, &wfds)) { if (mq_send(fd2, buf, sizeof(buf), prio) != 0) { perror("write"); exit(-1); } } read_start = 1; // sleep(1); } } printf("write loop= %d\n", i/2); mq_close(fd1); mq_close(fd2); return 0; }
UNIXドメインソケット
sread.c
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/types.h> #include <sys/socket.h> #include <sys/un.h> #include <sys/select.h> #include <sys/ioctl.h> #include <errno.h> #define DPRINTF(...) //#define DPRINTF(...) printf(__VA_ARGS__) #define SOCK_NAME "/tmp/test_socket" int main(int argc, char *argv[]) { int fd1, fd2; struct sockaddr_un saddr; struct sockaddr_un caddr; int len, val; fd_set rfds, wfds; char buf[128]; int read_start; if ((fd1 = socket(PF_UNIX, SOCK_STREAM, 0)) < 0) { perror("socket"); exit(-1); } memset((char *)&saddr, 0, sizeof(saddr)); saddr.sun_family = AF_UNIX; strcpy(saddr.sun_path, SOCK_NAME); unlink(SOCK_NAME); if (bind(fd1, (struct sockaddr *)&saddr, sizeof(saddr.sun_family) + strlen(SOCK_NAME)) < 0){ perror("bind"); exit(-1); } if (listen(fd1, 1) < 0) { perror("listen"); exit(-1); } len = sizeof(caddr); if ((fd2 = accept(fd1, (struct sockaddr *)&caddr, (socklen_t *)&len)) < 0) { perror("accept"); exit(-1); } close(fd1); /* set non-blocking */ val = 1; ioctl(fd2, FIONBIO, &val); read_start=1; while (1) { if (read_start) { buf[0] = '\0'; FD_ZERO(&rfds); FD_SET(fd2, &rfds); DPRINTF("selecting read...\n"); if (select(fd2+1, &rfds, NULL, NULL, NULL) == -1) { perror("select"); } if (FD_ISSET(fd2, &rfds)) { int n; n = read(fd2, buf, sizeof(buf)); if (n > 0 ) { DPRINTF("%d = read [%s]\n", n, buf); } else { perror("read"); exit(-1); } } read_start = 0; } else { memset(buf, 'r', sizeof(buf)); buf[sizeof(buf)-1] = '\0'; FD_ZERO(&wfds); FD_SET(fd2, &wfds); DPRINTF("selecting write...\n"); if (select(fd2+1, NULL, &wfds, NULL, NULL) == -1) { perror("select"); exit(-1); } if (FD_ISSET(fd2, &wfds)) { if (write(fd2, buf, sizeof(buf)) != sizeof(buf)) { perror("write"); exit(-1); } } read_start = 1; // sleep(1); } } close(fd2); return 0; }
swrite.c
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/types.h> #include <sys/socket.h> #include <sys/un.h> #include <sys/select.h> #include <sys/ioctl.h> #include <errno.h> #define DPRINTF(...) //#define DPRINTF(...) printf(__VA_ARGS__) #define SOCK_NAME "/tmp/test_socket" int main(int argc, char *argv[]) { int fd; struct sockaddr_un addr; int len, val; fd_set rfds, wfds; char buf[128]; int i, nloop; int read_start; if (argc != 2) { printf("usage:\n./swrite <NLOOP>\n"); exit(-1); } nloop = atoi(argv[1]); if ((fd = socket(PF_UNIX, SOCK_STREAM, 0)) < 0) { perror("socket"); exit(-1); } memset((char *)&addr, 0, sizeof(addr)); addr.sun_family = AF_UNIX; strcpy(addr.sun_path, SOCK_NAME); /* set non-blocking */ val = 1; ioctl(fd, FIONBIO, &val); if (connect(fd, (struct sockaddr *)&addr, sizeof(addr.sun_family) + strlen(SOCK_NAME)) < 0){ perror("connect"); exit(1); } read_start=0; for (i=0; i<nloop; i++) { if (read_start) { buf[0] = '\0'; FD_ZERO(&rfds); FD_SET(fd, &rfds); DPRINTF("selecting read...\n"); if (select(fd+1, &rfds, NULL, NULL, NULL) == -1) { perror("select"); } if (FD_ISSET(fd, &rfds)) { int n; n = read(fd, buf, sizeof(buf)); if (n > 0 ) { DPRINTF("%d = read [%s]\n", n, buf); } else { perror("read"); exit(-1); } } read_start = 0; } else { memset(buf, 'w', sizeof(buf)); buf[sizeof(buf)-1] = '\0'; FD_ZERO(&wfds); FD_SET(fd, &wfds); DPRINTF("selecting write...\n"); if (select(fd+1, NULL, &wfds, NULL, NULL) == -1) { perror("select"); exit(-1); } if (FD_ISSET(fd, &wfds)) { if (write(fd, buf, sizeof(buf)) != sizeof(buf)) { perror("write"); exit(-1); } } read_start = 1; // sleep(1); } } printf("write loop= %d\n", i/2); close(fd); return 0; }
名前付きパイプ(FIFO)
fread.c
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/stat.h> #include <sys/select.h> #include <fcntl.h> #include <errno.h> #define DPRINTF(...) //#define DPRINTF(...) printf(__VA_ARGS__) int main(int argc, char *argv[]) { int fd1, fd2; fd_set rfds, wfds; char buf[128]; int read_start; unlink("/tmp/fifo1"); unlink("/tmp/fifo2"); if (mkfifo("/tmp/fifo1", 0666) == -1) { perror("mkfifo /tmp/fifo1"); exit(-1); } if (mkfifo("/tmp/fifo2", 0777) == -1) { perror("mkfifo /tmp/fifo2"); exit(-1); } if ((fd1 = open("/tmp/fifo1", O_RDONLY|O_NONBLOCK)) == -1) { perror("open /tmp/fifo1"); exit(-1); } if ((fd2 = open("/tmp/fifo2", O_RDWR|O_NONBLOCK)) == -1) { perror("open /tmp/fifo2"); exit(-1); } read_start=1; while (1) { if (read_start) { buf[0] = '\0'; FD_ZERO(&rfds); FD_SET(fd1, &rfds); DPRINTF("selecting read...\n"); if (select(fd1+1, &rfds, NULL, NULL, NULL) == -1) { perror("select"); } if (FD_ISSET(fd1, &rfds)) { int n; n = read(fd1, buf, sizeof(buf)); if (n > 0 ) { DPRINTF("%d = read [%s]\n", n, buf); } else { perror("read"); exit(-1); } } read_start = 0; } else { memset(buf, 'r', sizeof(buf)); buf[sizeof(buf)-1] = '\0'; FD_ZERO(&wfds); FD_SET(fd2, &wfds); DPRINTF("selecting write...\n"); if (select(fd2+1, NULL, &wfds, NULL, NULL) == -1) { perror("select"); exit(-1); } if (FD_ISSET(fd2, &wfds)) { if (write(fd2, buf, sizeof(buf)) != sizeof(buf)) { perror("write"); exit(-1); } } read_start = 1; // sleep(1); } } close(fd1); close(fd2); return 0; }
fwrite.c
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/stat.h> #include <sys/select.h> #include <fcntl.h> #include <errno.h> #define DPRINTF(...) //#define DPRINTF(...) printf(__VA_ARGS__) int main(int argc, char *argv[]) { int fd1, fd2; fd_set rfds, wfds; char buf[128]; int read_start; int nloop, i; if (argc != 2) { printf("usage:\n./fwrite <NLOOP>\n"); exit(-1); } nloop = atoi(argv[1]); if ((fd1 = open("/tmp/fifo2", O_RDONLY|O_NONBLOCK)) == -1) { perror("open /tmp/fifo2"); exit(-1); } if ((fd2 = open("/tmp/fifo1", O_WRONLY|O_NONBLOCK)) == -1) { perror("open /tmp/fifo1"); exit(-1); } read_start=0; for (i=0; i<nloop; i++) { if (read_start) { buf[0] = '\0'; FD_ZERO(&rfds); FD_SET(fd1, &rfds); DPRINTF("selecting read...\n"); if (select(fd1+1, &rfds, NULL, NULL, NULL) == -1) { perror("select"); } if (FD_ISSET(fd1, &rfds)) { int n; n = read(fd1, buf, sizeof(buf)); if (n > 0 ) { DPRINTF("%d = read [%s]\n", n, buf); } else { perror("read"); exit(-1); } } read_start = 0; } else { memset(buf, 'w', sizeof(buf)); buf[sizeof(buf)-1] = '\0'; FD_ZERO(&wfds); FD_SET(fd2, &wfds); DPRINTF("selecting write...\n"); if (select(fd2+1, NULL, &wfds, NULL, NULL) == -1) { perror("select"); exit(-1); } if (FD_ISSET(fd2, &wfds)) { if (write(fd2, buf, sizeof(buf)) != sizeof(buf)) { perror("write"); exit(-1); } } read_start = 1; // sleep(1); } } printf("write loop= %d\n", i/2); close(fd1); close(fd2); return 0; }