ka2yanの日記

2009-03-27 プロセス間通信の比較

[]プロセス間通信の比較 01:28

デバイスドライバによるプロセス間通信 - 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.3220.7162.922
デバイスドライバ(メッセージRead/Write方式)7.6630.7153.124
メッセージキュー(POSIX6.9350.7922.755
UNIXドメインソケット9.6791.7167.956
名前付パイプ(FIFO8.2721.4656.801

real :実時間(コマンドを起動してから終了するまでの時間)

user :ユーザCPU時間(ユーザープロセスが動作している時間)

sys :システムCPU時間(カーネルでの動作時間)


なんと、メッセージキュー(POSIX)が一番早い!


デバイスドライバが一番早いことを期待していたのだが、残念。

ただ、デバイスドライバ(メッセージRead/Write方式)は、メッセージキュー(POSIX)より

速度が1割程度遅くても、プロセス間通信のログが取れるというメリットがある。


そんなに遅くはないけど、なんとかもう少し早くならないかなぁ。


TODO

デバイスドライバ(メッセージRead/Write方式)をもう少し使えるドライバにする。

# もう少し2.6ドライバーらしくする

# 複数プロセス対応
今は、2プロセスでしか使えない。

# 複数メッセージ対応
今は、1メッセージしか保持できない

# シェルスクリプトからのメッセージ受信処理の実装


測定に使ったプログラム

実行順序は、デバイスドライバと同様で、read 側を実行しておき、write 側の引数に1000000を渡す。

プログラムが多いので、Makefile と 実行例は省略する。

デバイスドライバ(共有メモリ方式)

デバイスドライバによるプロセス間通信 - ka2yanの日記参照

デバイスドライバ(メッセージRead/Write方式)

デバイスドライバによるプロセス間通信 - ka2yanの日記参照

メッセージキュー(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;
}

ふぇじょーーーあwwwwふぇじょーーーあwwww 2009/06/16 09:01
これヤった後でパチ屋に行ったら勝率上がりすぎwwwwww

http://shiofuki.navi-y.net/tCQrO6W/

ただの軍資金稼ぎのつもりでヤってたんだけど、
パチも負けねーもんだから金が余りまくりっす・・(^^;
まー金は余っても困らないからまだ続けるけどねーヽ( ・∀・)ノ
とりあえずBMWでも買うわwwwwwww

ネトゲ廃人ぽにゃたの場合ネトゲ廃人ぽにゃたの場合 2009/07/28 01:46
働かざるものヤルべし!!!ほんと働いたら負けだわ(´Д`;)
オレ真面目に会社員やってたけど、今はその頃より月の稼ぎ3倍だよ?
初めてヤった時は4万だけだったけど、今じゃ平均一回7万だかんなwww
もうアフォらしくて会社員ヤメたしwwwww 毎日ネトゲ最高wwww

http://netoge.bolar.net/6cxUpG7/

これはいい使い捨てwwwwwこれはいい使い捨てwwwww 2009/08/07 09:51
ナニコレwwww アフォほど女溢れてるんだがwwwwwwww
毎日毎日セクゥス三昧でもうティムポ一本じゃ足りないっすwwwwwwww
良いマヌコはキープするけど、基本はヤリ捨てでおkwwwwwwwwwww

http://ene.creampie2.net/ZhjY5aY/

えふwwえふwwえふwwwえふwwえふwwえふwww 2009/08/11 07:59
ケイジの奴・・ネットやっててコレ知らないって何なのwwwwww
金に困ってるみたいだから教えてやったらソッコーでヤりやがったしww
てかあいつキモデブなのに何でいきなり8 万貰えてんの???
わけわかんねぇしwwwwwww

http://kachi.strowcrue.net/5Dvmx7y/

ケ ツ コ キ!!!!!!!!!ケ ツ コ キ!!!!!!!!! 2009/08/18 03:02
すんげえケ ツでかい女に当たった!!! コイツのケ ツ 技すぎすぎwwwww

ケ ツにロ -ショ ン塗りたくって、俺のティ ヌコ挟んですんげー前後すんの!!!
前後してる時にク リに当たったりマ ヌ コに入ったりして
女もアヒアヒしまくりで俺も女も絶 頂しまくりで最高ですたwwwwwww

こりゃハマるわぁ・・・・

http://yuzo.plusnote.net/uUkSqGm/

よーちよちよちよち!!!!よーちよちよちよち!!!! 2009/08/24 02:22
最近ここの女におしゃぶり咥えさせてガラガラ持たせて
パッコンパッコンしてやったんだが、反応がハンパネェっすwwwwwwww

「気持ちいいですぅーん!!はあっぁぁああ!!!」

こんな萌えボイスで叫ばれたら余計に興 奮するっての!!!!!!

仕方ないからずぶずぶ奥まで挿れてあげたら
ずっと潮ピュルーって飛ばして痙攣しまくりー(・∀・)ぐっふふ

http://okane.d-viking.com/FMHSN4u/

2009-03-12 デバイスドライバによるプロセス間通信

[]デバイスドライバを用いたプロセス間通信 03:07

だいぶ前から、こんなことに興味があった。

今から、2年近くも前だろうか、以下の記事を読ませて頂いた。

tutorialog » 仮想デバイスドライバを利用したプロセス間通信について


これを参考に、以下のようなプロセス間通信を実装した。

  • 共有メモリを使うのではなく、メッセージデータをデバイスドライバにread(2)/write(2)する
    上記したような用途(100バイト程度のメッセージ)ならデバイスドライバでメッセージをコピーしてもあまりスループットに影響しないのではないか?と予想。
    プロセス間通信のログを取るために、read/writeする、という意味もある。
  • /procfs を使って、プロセス間通信のログを簡単に保存できるようにする。

(実行に移すのにえらく時間がかかったなぁ・・・)。

以下、サンプルとして、

プロセス間で128バイトのメッセージを送受信を繰り返すプログラム書く。

まずは、上記した記事のプログラムを参考に共有メモリ方式でのプログラム

次に、共有メモリ方式ではなく、メッセージをデバイスドライバにread/writeするプログラム

を準備し、結果の比較をする。

  1. デバイスドライバ+共有メモリを用いたプログラム

tutorialog » 仮想デバイスドライバを利用したプロセス間通信について をベースに、連続でメッセージの送受信するように修正したプログラム。変更したのは、ワークをプロセス数分用意して、read(2)/write(2)時に相手のキューを検索して、wake_up_interruptible_sync()を呼ぶようにした点。

wake_up_interruptible_sync()を呼ぶようにした理由は、こっちの方が高速に動作したから。後々、他のプロセス間通信との速度比較もしてみたいため、wake_up_interruptible_sync()を使っていく。

まずは、改造したデバイスドライバ(ipc_shmem.c)

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/poll.h>

#define DEV_NAME "ipc_shmem" // デバイス名称
#define IPC_MAJOR 100        // メジャー番号

struct IPC_DEVICE {
        int use;
        int is_readable;
        wait_queue_head_t write_q;  // write用のキュー
        wait_queue_head_t read_q;   // read用のキュー
};

static struct IPC_DEVICE *ipc_devs; // デバイスのワーク
static int ipc_devs_nr = 2;         // 用意するデバイス数
static struct semaphore ipc_sem;    // ipc_devs 操作用のセマフォ

static int
ipc_open(struct inode* inode, struct file* filp)
{
        struct IPC_DEVICE *dev=NULL;
        int i;

        // 空いているデバイスを見つける
        for(i=0; i<ipc_devs_nr; i++) {
                dev = ipc_devs + i;
                if (!dev->use)
                        break;   // 空きデバイスが見つかった
        }
        if (i == ipc_devs_nr) {  // 全てのデバイスが使用中
                return -EBUSY;
        }

        if (down_interruptible(&ipc_sem)) {
                printk(KERN_INFO "down_interruptible for read failed\n");
                return -ERESTARTSYS;
        }
        dev->use = 1;
        dev->is_readable = 0;
        up(&ipc_sem);

        // ワークを保存しておく
        filp->private_data = (void *)dev;

        return 0;
}

static int
ipc_close(struct inode* inode, struct file* filp)
{
        struct IPC_DEVICE *dev = (struct IPC_DEVICE *)filp->private_data;

        if (down_interruptible(&ipc_sem)) {
                printk(KERN_INFO "down_interruptible for read failed\n");
                return -ERESTARTSYS;
        }
        dev->use = 0;
        up(&ipc_sem);

        return 0;
}

static ssize_t
ipc_read(struct file* filp, char* buf, size_t count, loff_t* pos)
{
        struct IPC_DEVICE *dev = (struct IPC_DEVICE *)filp->private_data;
        struct IPC_DEVICE *dst;
        int i;

        if (down_interruptible(&ipc_sem)) {
                printk(KERN_INFO "down_interruptible for read failed\n");
                return -ERESTARTSYS;
        }
        dev->is_readable = 0;
        for(i=0; i<ipc_devs_nr; i++) {
                dst = ipc_devs + i;

                // 自分じゃないデバイス=書き込みしてきたデバイスを探す
                if (dst != dev && dst->use) {
                        // wake_up 関数中で相手プロセスを起こさないよう、_sync を使う
                        wake_up_interruptible_sync(&dst->write_q);
                        break;
                }
        }
        up(&ipc_sem);

        return 0;
}

static ssize_t
ipc_write(struct file* filp, const char* buf, size_t count, loff_t* pos)
{
        struct IPC_DEVICE *dev = (struct IPC_DEVICE *)filp->private_data;
        struct IPC_DEVICE *dst;
        int i;

        if (down_interruptible(&ipc_sem)) {
                printk(KERN_INFO "down_interruptible for write failed\n");
                return -ERESTARTSYS;
        }
        for(i=0; i<ipc_devs_nr; i++) {
                dst = ipc_devs + i;

                // 自分じゃないデバイス=書き込み先デバイスを探す
                if (dst != dev && dst->use) {
                        dst->is_readable = 1;

                        // wake_up 関数中で相手プロセスを起こさないよう、_sync を使う
                        wake_up_interruptible_sync(&dst->read_q);
                        break;
                }
        }
        up(&ipc_sem);

        return 0;
}

static unsigned int
ipc_poll(struct file* filp, poll_table* wait)
{
        struct IPC_DEVICE *dev = (struct IPC_DEVICE *)filp->private_data;
        unsigned int retmask = 0;

        poll_wait(filp, &dev->read_q,  wait);
        poll_wait(filp, &dev->write_q, wait);

        if (dev->is_readable) {
                retmask |= (POLLIN | POLLRDNORM);
        }
        if (!dev->is_readable) {
                retmask |= (POLLOUT | POLLWRNORM);
        }

        return retmask;
}

static struct file_operations ipc_fops =
{
        .owner   = THIS_MODULE,
        .read    = ipc_read,
        .write   = ipc_write,
        .poll    = ipc_poll,
        .open    = ipc_open,
        .release = ipc_close,
};

int
init_module(void)
{
        struct IPC_DEVICE *dev;
        int i;

        if (register_chrdev(IPC_MAJOR, DEV_NAME, &ipc_fops)) {
                printk(KERN_INFO "register_chrdev failed\n");
                return -EBUSY;
        }

        // 全デバイス分のワークエリアを確保
        ipc_devs = (struct IPC_DEVICE *)kmalloc((sizeof(*dev) * ipc_devs_nr), GFP_KERNEL);
        if (!ipc_devs) {
                return -ENOMEM;
        }

        for(i=0; i<ipc_devs_nr; i++) {
                dev = ipc_devs + i;
                dev->use = 0;
                dev->is_readable = 0;
                init_waitqueue_head(&dev->write_q);
                init_waitqueue_head(&dev->read_q);
                printk(KERN_INFO "init %d\n", i);
        }
        sema_init(&ipc_sem, 1);

        return 0;
}

void
cleanup_module(void)
{
        if (unregister_chrdev(IPC_MAJOR, DEV_NAME) ) {
                printk(KERN_INFO "unregister_chrdev failed\n");
        }
        kfree(ipc_devs);
}

Makefile はこちら。

obj-m := ipc_shmem.o

all:
        make -C /lib/modules/$(shell uname -r)/build M=`pwd` V=1 modules

clean:
        make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean

まずは、デバイスファイルを作成する。

# /bin/mknod /dev/ipc_shmem c 100 0
# chmod 777 /dev/ipc_shmem

次に、コンパイルして、ipc_shmem.ko をカーネルに組み込む。

# make
... 出力結果は省略 ...
# /sbin/insmod ipc_shmem.ko

次は動作確認用のユーザープロセス(rd.c)。

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

#define DPRINTF(...)
//#define DPRINTF(...) printf(__VA_ARGS__)

#define BUFSIZE 1024

int main(int argc, char *argv[])
{
        int pagesize, mapsize;
        int fd, dev;
        char *map;
        fd_set rfds, wfds;
        char buf[128];
        int read_start;

        pagesize = sysconf(_SC_PAGE_SIZE);
        mapsize = ((BUFSIZE-1)/pagesize+1) * pagesize;

        if((fd = open("./map.shm", O_RDWR|O_CREAT, 0666)) == -1) {
                perror("open map");
                exit(-1);
        }
        if ((dev = open("/dev/ipc_shmem", O_RDWR)) == -1) {
                perror("open ipc_shmem");
                exit(-1);
        }
        if (ftruncate(fd, mapsize) == -1) {
                perror("ftruncate");
                exit(-1);
        }
        map = (char *) mmap(0, mapsize, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
       if (map == MAP_FAILED) {
                perror("mmap");
                return -1;
        }

        read_start=1;   // メッセージのreadから開始
        while (1) {
                if (read_start) {
                        FD_ZERO(&rfds);
                        FD_SET(dev, &rfds);

                        DPRINTF("selecting read...\n");
                        if (select(dev+1, &rfds, NULL, NULL, NULL) == -1) {
                                perror("select");
                        }

                        if (FD_ISSET(dev, &rfds)) {
                                DPRINTF("read [%s]\n", map);  // 受信メッセージの表示
                                read(dev, buf, sizeof(buf));
                        }
                        read_start = 0;
                } else {
                        memset(buf, 'r', sizeof(buf));   // 送信メッセージの作成
                        buf[sizeof(buf)-1] = '\0';

                        FD_ZERO(&wfds);
                        FD_SET(dev, &wfds);
                        DPRINTF("selecting write...\n");
                        if (select(dev+1, NULL, &wfds, NULL, NULL) == -1) {
                                perror("select");
                                exit(-1);
                        }

                        if (FD_ISSET(dev, &wfds)) {
                                strncpy(map, buf, sizeof(buf));
                                if (write(dev, NULL, 1) == -1) {
                                        perror("write");
                                        exit(-1);
                                }
                        }
                       read_start = 1;
//                      sleep(1);
                }
        }

        munmap(map, mapsize);
        close(fd);
        close(dev);

        return 0;
}

次は動作確認用のユーザープロセス(wr.c)。

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

#define DPRINTF(...)
//#define DPRINTF(...) printf(__VA_ARGS__)

#define BUFSIZE 1024

int main(int argc, char *argv[])
{
        int pagesize, mapsize;
        int fd, dev;
        char *map;
        fd_set rfds, wfds;
        char buf[128];
        int read_start, i, nloop;

        if (argc != 2) {
                printf("usage:\n./wr <NLOOP>\n");
                exit(-1);
        }
        nloop = atoi(argv[1]);

        pagesize = sysconf(_SC_PAGE_SIZE);
        mapsize = ((BUFSIZE-1)/pagesize+1) * pagesize;

        if((fd = open("./map.shm", O_RDWR|O_CREAT, 0666)) == -1) {
                perror("open map");
                exit(-1);
        }
        if ((dev = open("/dev/ipc_shmem", O_RDWR)) == -1) {
                perror("open ipc_shmem");
                exit(-1);
       }
        if (ftruncate(fd, mapsize) == -1) {
                perror("ftruncate");
                exit(-1);
        }
        map = (char *) mmap(0, mapsize, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
        if (map == MAP_FAILED) {
                perror("mmap");
                exit(-1);
        }

        read_start=0;   // メッセージのwriteから開始
        for (i=0; i<nloop; i++) {
                if (read_start) {
                        FD_ZERO(&rfds);
                        FD_SET(dev, &rfds);

                        DPRINTF("selecting read...\n");
                        if (select(dev+1, &rfds, NULL, NULL, NULL) == -1) {
                                perror("select");
                        }

                        if (FD_ISSET(dev, &rfds)) {
                                DPRINTF("read [%s]\n", map);  // 受信メッセージの表示
                                read(dev, buf, sizeof(buf));  // メッセージ領域を解放
                        }
                        read_start = 0;
                } else {
                        memset(buf, 'w', sizeof(buf));  // 送信メッセージの作成
                        buf[sizeof(buf)-1] = '\0';

                        FD_ZERO(&wfds);
                        FD_SET(dev, &wfds);
                        DPRINTF("selecting write...\n");
                        if (select(dev+1, NULL, &wfds, NULL, NULL) == -1) {
                                perror("select");
                                exit(-1);
                        }

                        if (FD_ISSET(dev, &wfds)) {
                               strncpy(map, buf, sizeof(buf));
                                if (write(dev, NULL, 1) == -1) {  // メッセージの送信
                                        perror("write");
                                        exit(-1);
                                }
                        }
                        read_start = 1;
//                      sleep(1);
                }
        }
        printf("write loop = %d\n", i/2);
        munmap(map, mapsize);
        close(fd);
        close(dev);

        return 0;
}
all:
        gcc -g -o wr wr.c
        gcc -g -o rd rd.c
clean:
        rm -f *.o wr rd

早速実行して、プロセス間通信を100000回ぐらいやった時の時間を測定する。

測定PCは、VMWarePlayer上のFedoraCore4(CPU:Pentium4 メモリ割当:256MB)、ホストOSは、WinXP HomeEdition メモリ1GB。

結果をみても、ちょっとした組込み機器ぐらいの能力しかないなぁ。そのうち、Linuxネイティブ機で測定しなおそう。

$ make
gcc -g -o wr wr.c
gcc -g -o rd rd.c
$ ./rd&
$ time ./wr 100000
write loop = 50000

real    0m5.535s
user    0m0.008s
sys     0m2.784s





  1. デバイスドライバにRead/Writeするプログラム

次に、共有メモリではなく、デバイスドライバに対してメッセージをread(2)、write(2)することでプロセス間通信するプログラムを書く。

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/proc_fs.h>
#include <linux/poll.h>

#define DPRINTK(...)
//#define DPRINTK(...) printk(__VA_ARGS__)

#define DEV_NAME "ipc"              // デバイス名称
#define PROC_NAME "driver/ipc"      // procfs 上の名称。/proc/driver/ipc となる。
#define IPC_MAJOR 101               // メジャー番号、ipc_memより1つずらす

struct IPC_DEVICE {
        int use;                    // open 中は、use=1
        int is_readable;
        wait_queue_head_t write_q;  // write用のキュー
        wait_queue_head_t read_q;   // read用のキュー
        char msg[128];              // read待ちのメッセージ
};

static struct IPC_DEVICE *ipc_devs; // デバイスのワーク
static int ipc_devs_nr = 2;         // 用意するデバイス数
static struct semaphore ipc_sem;    // ipc_devs 操作用のセマフォ
static char proc_data[32];          // procfs で書き込まれたデータ
static int log = 0;                 // ログ取得するかどうかのフラグ

static int
ipc_open(struct inode* inode, struct file* filp)
{
        struct IPC_DEVICE *dev=NULL;
        int i;

        // 空いているデバイスを探す
        for(i=0; i<ipc_devs_nr; i++) {
                dev = ipc_devs + i;
                if (!dev->use)
                        break;   // 空きデバイスあり
        }
        if (i == ipc_devs_nr) {  // 空きデバイスなし
                return -EBUSY;
        }

        if (down_interruptible(&ipc_sem)) {
                printk(KERN_INFO "down_interruptible for read failed\n");
                return -ERESTARTSYS;
        }
        dev->use = 1;
        dev->is_readable = 0;
        up(&ipc_sem);

        // ワークを保存
        filp->private_data = (void *)dev;

        DPRINTK(KERN_INFO "%s no=%d\n", __func__, i);

        return 0;
}

static int
ipc_close(struct inode* inode, struct file* filp)
{
        struct IPC_DEVICE *dev = (struct IPC_DEVICE *)filp->private_data;

        if (down_interruptible(&ipc_sem)) {
                printk(KERN_INFO "down_interruptible for read failed\n");
                return -ERESTARTSYS;
        }
        dev->use = 0;
        up(&ipc_sem);

        DPRINTK(KERN_INFO "%s \n", __func__);

        return 0;
}

static ssize_t
ipc_read(struct file* filp, char* buf, size_t count, loff_t* pos)
{
        struct IPC_DEVICE *dev = (struct IPC_DEVICE *)filp->private_data;
        struct IPC_DEVICE *dst;
        int i;

        if (down_interruptible(&ipc_sem)) {
                printk(KERN_INFO "down_interruptible for read failed\n");
                return -ERESTARTSYS;
        }

        if (count < sizeof(dev->msg) ||
            copy_to_user(buf, dev->msg, sizeof(dev->msg))) {
                up(&ipc_sem);
                return -EFAULT;
        }
        dev->is_readable = 0;
        for(i=0; i<ipc_devs_nr; i++) {
                dst = ipc_devs + i;
                // 自分じゃないデバイス=書き込みしてきたデバイスを探す
                if (dst != dev && dst->use) {
                        // wake_up 関数中で相手プロセスを起こさないよう、_sync を使う
                        wake_up_interruptible_sync(&dst->write_q);
                        DPRINTK(KERN_INFO "%s wake_up %d\n", __func__, i);
                        break;
                }
        }

        up(&ipc_sem);

        if(log) {
                // ログ取得処理。ここでは、ひとまずsyslogに出力する。
                // 本来なら、他のログ取得専用のプロセスでメッセージを取得した方がいい。
                printk(KERN_INFO "%s %s\n", __func__, dev->msg);
        }

        return sizeof(dev->msg);
}

static ssize_t
ipc_write(struct file* filp, const char* buf, size_t count, loff_t* pos)
{
        struct IPC_DEVICE *dev = (struct IPC_DEVICE *)filp->private_data;
        struct IPC_DEVICE *dst = NULL;
        int i;

        if (down_interruptible(&ipc_sem)) {
                printk(KERN_INFO "down_interruptible for write failed\n");
                return -ERESTARTSYS;
        }

        for(i=0; i<ipc_devs_nr; i++) {
                dst = ipc_devs + i;
                // 自分じゃないデバイス=書き込み先バイスを探す
                if (dst != dev && dst->use) {
                        if (!dst->is_readable) {
                                if(count > sizeof(dst->msg))
                                        count = sizeof(dst->msg);
                                if(copy_from_user(dst->msg, buf, count)) {
                                        up(&ipc_sem);
                                        return -EFAULT;
                                }
                                dst->is_readable = 1;
                                // wake_up 関数中で相手プロセスを起こさないよう、_sync を使う
                                wake_up_interruptible_sync(&dst->read_q);
                                DPRINTK(KERN_INFO "%s wake_up %d\n", __func__, i);
                        }
                        break;
                }
        }

        up(&ipc_sem);

        if(log) {
                // ログ取得処理。ここでは、ひとまずsyslogに出力する。
                // 本来なら、他のログ取得専用のプロセスでメッセージを取得した方がいい。
                printk(KERN_INFO "%s %s\n", __func__, dst->msg);
        }

        return count;
}

static unsigned int
ipc_poll(struct file* filp, poll_table* wait)
{
        struct IPC_DEVICE *dev = (struct IPC_DEVICE *)filp->private_data;
        unsigned int retmask = 0;

        poll_wait(filp, &dev->read_q,  wait);
        poll_wait(filp, &dev->write_q, wait);

        if (dev->is_readable) {
                retmask |= (POLLIN | POLLRDNORM);
        }
        if (!dev->is_readable) {
                retmask |= (POLLOUT | POLLWRNORM);
        }
        DPRINTK(KERN_INFO "%s %x\n", __func__, retmask);

        return retmask;
}

static struct file_operations ipc_fops =
{
        .owner   = THIS_MODULE,
        .read    = ipc_read,
        .write   = ipc_write,
        .poll    = ipc_poll,
        .open    = ipc_open,
        .release = ipc_close,
};

// procfs の書き込み処理。 echo -n "XXX" > /proc/driver/ipc というのを想定。
static int
proc_write( struct file *filp, const char *buf, unsigned long len, void *data )
{
        if (len >= (sizeof(proc_data)-1)) {
                printk(KERN_INFO "%s length is over\n", __func__);
                return -ENOSPC;
        }

        // 書き込みデータの保存
        if (copy_from_user(proc_data, buf, len)) {
                return -EFAULT;
        }
        proc_data[len] = '\0';

        if (!strcmp(proc_data, "on")) {
                log = 1;   // ログ取得開始
        } else {
                log = 0;   // ログ取得停止
        }

        DPRINTK(KERN_INFO "%s =%s(%d) log=%d\n", __func__, proc_data, (int)len, log);
        return len;
}

// procfs の読み出し処理。 cat /proc/driver/ipc というのを想定。
// 今回のプログラムでは必須ではないが、書き込んだ文字列を読み出す。
static int
proc_read( char *page, char **start, off_t offset, int count, int *eof, void *data )
{
        int ret;
        ret = snprintf(page, count, "%s", proc_data);
        *eof = 1;
        DPRINTK(KERN_INFO "%s =%s(%d)\n", __func__, proc_data, ret);

        return ret;
}

int
init_module(void)
{
        struct IPC_DEVICE *dev;
        struct proc_dir_entry* entry;
        int i;

        if (register_chrdev(IPC_MAJOR, DEV_NAME, &ipc_fops)) {
                printk(KERN_INFO "register_chrdev failed\n");
                return -EBUSY;
        }
        // 全デバイス分の領域を取得
        ipc_devs = (struct IPC_DEVICE *)kmalloc((sizeof(*dev) * ipc_devs_nr), GFP_KERNEL);
        if (!ipc_devs) {
                return -ENOMEM;
        }

        for(i=0; i<ipc_devs_nr; i++) {
                dev = ipc_devs + i;
                dev->use = 0;
                dev->is_readable = 0;
                init_waitqueue_head(&dev->write_q);
                init_waitqueue_head(&dev->read_q);
                memset(dev->msg, 0, sizeof(dev->msg));
                printk(KERN_INFO "init %d\n", i);
        }
        sema_init(&ipc_sem, 1);

        // procfs用のエントリを作成
        entry = create_proc_entry(PROC_NAME, 0666, NULL);
        if (entry) {
                // write, read 用の関数を登録
                entry->write_proc  = proc_write;
                entry->read_proc  = proc_read;
                entry->owner = THIS_MODULE;
        } else {
                printk(KERN_ERR "create_proc_entry failed\n");
                return -EBUSY;
        }
        DPRINTK(KERN_INFO "ipc driver is loaded\n");

        return 0;
}

void
cleanup_module(void)
{
        if (unregister_chrdev(IPC_MAJOR, DEV_NAME) ) {
                printk(KERN_INFO "unregister_chrdev failed\n");
        }
        kfree(ipc_devs);

        remove_proc_entry(PROC_NAME, NULL);

        DPRINTK(KERN_INFO "ipc driver is unloaded\n");
}

オブジェクト名しか変わらないが、Makefile はこちら。

obj-m := ipc.o

all:
        make -C /lib/modules/$(shell uname -r)/build M=`pwd` V=1 modules

clean:
        make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean

同様に、デバイスファイルを作成する。

# /bin/mknod /dev/ipc c 101 0
# chmod 777 /dev/ipc

同様に、コンパイルして、ipc.ko をカーネルに組み込む。

# make
... 出力結果は省略 ...
# /sbin/insmod ipc.ko

次は動作確認用のユーザープロセス(rd.c)。

共有メモリの操作がなくなり、メッセージ送信はwrite(2)、メッセージ受信はread(2) と、より直感的なコードになった。POSIXのメッセージキューとよく似たプログラムだなぁ。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.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 dev;
        fd_set rfds, wfds;
        char buf[128];
        int read_start;

        if ((dev = open("/dev/ipc", O_RDWR)) == -1) {
                perror("open ipc");
                exit(-1);
        }

        read_start=1;   // メッセージ受信から開始
        while (1) {
                if (read_start) {
                        buf[0] = '\0';

                        FD_ZERO(&rfds);
                        FD_SET(dev, &rfds);

                        DPRINTF("selecting read...\n");
                        if (select(dev+1, &rfds, NULL, NULL, NULL) == -1) {
                                perror("select");
                        }

                        if (FD_ISSET(dev, &rfds)) {
                               int n;
                                n = read(dev, 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(dev, &wfds);
                        DPRINTF("selecting write...\n");
                        // 今回、writeのselectはなくてもよいが、共有メモリ方式と
                        // 処理内容を合わせるために使用
                        if (select(dev+1, NULL, &wfds, NULL, NULL) == -1) {
                                perror("select");
                                exit(-1);
                        }

                        if (FD_ISSET(dev, &wfds)) {
                                // メッセージの送信
                                if (write(dev, buf, sizeof(buf)) != sizeof(buf)) {
                                        perror("write");
                                        exit(-1);
                                }
                        }
                        read_start = 1;
//                      sleep(1);
                }
        }
        close(dev);

        return 0;
}

次は動作確認用のユーザープロセス(wr.c)。

include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.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 dev;
        fd_set rfds, wfds;
        char buf[128];
        int read_start, i, nloop;

        if (argc != 2) {
                printf("usage:\n./wr <NLOOP>\n");
                exit(-1);
        }
        nloop = atoi(argv[1]);

        if ((dev = open("/dev/ipc", O_RDWR)) == -1) {
                perror("open ipc");
                exit(-1);
        }

        read_start=0;  // メッセージの送信から開始
        for (i=0; i<nloop; i++) {
                if (read_start) {
                        buf[0] = '\0';

                        FD_ZERO(&rfds);
                        FD_SET(dev, &rfds);

                        DPRINTF("selecting read...\n");
                        if (select(dev+1, &rfds, NULL, NULL, NULL) == -1) {
                                perror("select");
                        }

                        if (FD_ISSET(dev, &rfds)) {
                                int n;
                                n = read(dev, 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(dev, &wfds);
                        DPRINTF("selecting write...\n");
                        // 今回、writeのselectはなくてもよいが、共有メモリ方式と
                        // 処理内容を合わせるために使用
                         if (select(dev+1, NULL, &wfds, NULL, NULL) == -1) {
                                perror("select");
                                exit(-1);
                        }

                        if (FD_ISSET(dev, &wfds)) {
                                // メッセージの送信
                                if (write(dev, buf, sizeof(buf)) != sizeof(buf)) {
                                        perror("write");
                                        exit(-1);
                                }
                        }
                        read_start = 1;
//                      sleep(1);
                }
        }
        printf("write loop= %d\n", i/2);
        close(dev);

        return 0;
}
all:
        gcc -g -o wr wr.c
        gcc -g -o rd rd.c
clean:
        rm -f *.o wr rd

早速実行して、プロセス間通信を100000回ぐらいやった時の時間を測定する。

$ make
gcc -g -o wr wr.c
gcc -g -o rd rd.c
$ ./rd&
$ time ./wr 100000
write loop= 50000

real    0m5.624s
user    0m0.004s
sys     0m2.792s

共有メモリ方式とメッセージread/write方式で、実行時間は 5.535 秒から 5.624 秒へと101.6%長くなった。この1.6%がメッセージのread/writeにかかるコストということになる。

128バイトのコピーがプロセス間通信の1.6%を占める?

長すぎるような気がしないわけでもないが、そういうことらしい。

近いうちに、Linuxネイティブ環境でも測定してみよう。



また、echoコマンド1つで、syslogプロセス間通信のログ取得をON/OFFすることができるようになりました。他の文字をechoするまで、ログを取得し続けます。

$ echo -n "on" > /proc/driver/ipc   # ログ取得開始
$ echo -n "off" > /proc/driver/ipc   # ログ取得停止

  1. まとめ

デバイスドライバを用いて比較的高速なプロセス間通信ができました。

また、このデバイスドライバシェルスクリプトをから、プロセス間通信のログ取得を簡単にON/OFFできるので、アプリケーションデバッグ/評価/市場トラブル(?)等で役に立つのではないか、と思います。

  1. TODO

近日中に以下をやっていきたいと思っています。

2008-08-22 カーネル読書会

[]カーネル読書会 02:44

YLUG カーネル読書会行ってきました、実は今日で3回目、、

今回は Ottawa Linux Symposium の発表者や参加者のお話を聞くことができました。

今回の発表で特に印象的だったのは、

中村さんのselinux組み込み対応させていくよ(いるよ)、もう世の中そういう方向だよ、と言う話。

自分も組み込みlinux技術者の端くれだが、selinuxとかを載せて行こう、とまで考えたことはなかった。

TOMOYO Linuxの原田さんもおっしゃってたが、組み込み業界ではセキュリティは大切という認識はあるが

対応は二の次になっている(あれ?ちょっとニュアンスちがう?)

まさにその通りで、実際、

自分達のハードウェアlinuxポーティングする、

ドライバアプリケーションを開発する(ドライバは scratchから書く場合が多い)、

という所から始まり、

求められる性能を出す(性能が出るような設計をする、がもちろん前提)、

長時間安定して動作させる、

というところで一杯一杯になる(私の場合)

要求仕様面からも、アクセス制限ぐらいあれば上等、という程度だったり、

開発者も本格的なサーバー管理を経験したことがある人が少ない、というのも理由にあるかもしれない。


というところに、組み込み機器のセキュリティ動向の話が聞けたので、私の中で多少なりとも意識改革ができた。

まずはSELinuxやTOMOYOLinuxなど、自分の環境で使ってみて、製品実装の可能性を探ってみよう。

能面への影響も心配だしね!


それにしても、まっちゃだいふくさん、パワーあったなー。

私はセキュリティに関して素人ですが、同じ世代だし、親近感が沸きましたw

2007-09-29 samba memo

[][] samba memo 01:53

samba で他の人が作成したファイルを変更したい。削除したい。

ということで、ユーザグループとsambaメモ。


debian では、ユーザ名と所属グループは同じ。

なんで?

Googleさまのお告げだと、以下のような感じらしい。

[debian-users:03313] Re: why groupname = username?


了解デス、

でも、自分今までこんな風にちゃんとグループ設定ってしたことない。

大人数で使うlinuxを設定したことないし。

どうせ今回も玄箱を使うのは家族の数人だし、

その間でこのファイルだけは他の人に見せたくない、なんてことは言わない(言わせない)

ということで、


debian な方々には申し訳ないが、今回(だけ、多分)は

全員同じユーザグループで、

ファイル属性は 664

となるように設定してみた。だってもう眠いし。


まず、グループを追加(わかりやすいようにGIDは1000)

# groupadd -g 1000 my_group

useradd 時にグループ指定するときは、

# useradd -g my_group newuser


次に各ユーザ(ここでは、user1, user2)のグループ変更

# usermod -g my_group user1

# id -a user1

で確認

# usermod -g my_group user2

# id -a user2

で確認


次に各ユーザの生成ファイルのデフォルトの属性を 664 に。

# echo "umask 002" >> ~/.bashrc


次に samba で作成するファイルのデフォルトの属性を 664 に。

# cat /etc/samba/smb.conf      <共有設定部分だけ、抜粋>

[share]

comment = KURO-BOX Share Folder

path = /mnt/share

valid users = user1, user2

read only = No

create mask = 0664



次回は、debian の流儀に従ってやってみよう。

2007-09-22 玄箱 debian 化

[][] 00:31

2年ほど前に玄箱debian化していたが、HDDアクセスが遅いのが気になってた。

もうほとほと嫌になったので、再度新しいパッケージでdebian化した

# もっと早く手を打て、というのはごもっとも。


Linux 2.4系 が気に入らなかった、ということもある。


で、まずはインストールメモ。


お世話になったページは、

玄箱/Debian/インストール - Revulo’s Laboratory

Genbako kernel collection

debian-sarge-2.6.17.3-kuroBOX-20060702.tgz を使わせて頂いた。感謝感謝。


debian化の大きな流れは、

である。


今回、玄人志向のupdateツールは使わなかった(IPアドレスの確認には使ったが、、、)。

というのも、KuroBoxUpdate.exe でupdate かけると、なぜかエラー終了。

debian化パッケージのファイルサイズが大きいのが原因かは不明。

この過程で my 玄箱は一度、kuro_102 のUpdateツールで初期化してしまった・・・


なので、このメモはkuro_102 で初期化してしまったところから始まる。


1.EMモードで立ち上げる

DHCPサーバとつながらない状態で起動させて、

telnet で 192.168.11.150 に接続(user:root, pass:kuro)


# echo -n "NGNGNG" > /dev/fl3

# reboot


以下は、最低限バックアップしておいた方がいいらしい。

/etc/samba

/etc/atalk

余裕があるなら、/etc 以下全部。

(今回忘れちったよ・・・)


2.HDDパーティション初期化

KuroBoxUpdate.exe で、玄箱IPアドレスを確認して、telnet

# mfdisk -e /dev/hda

# sh /sbin/mkfilesystem.sh



3.パッケージを展開する

ftp で接続

> cd /mnt2

> put debian-sarge-2.6.17.3-kuroBOX-20060702.tgz

> quit

なぜ/mnt2 に置くかというと、debian化されたあと、ここが/mntになる領域だから、だそう。


実は今回、debian-sarge-2.6.17.3-kuroBOX-20060702.tgz をそのまま使わなかった。

IPアドレスだけ予め変更しておいた。

Linux 上で一度展開して、以下のファイルを変更して、固めなおした。

/etc/resolv.conf

/etc/hosts

/etc/hosts.allow

/etc/network/interfaces


次に、telnet で接続

# cd /mnt

# tar xvzpf /mnt2/debian-sarge-2.6.17.3-kuroBOX-20060702.tgz

...

# cd /usr/bin

# ./write_ok

argv: ./write_ok

# reboot

なぜ/mnt に展開するかというと、ここはHDDの第1パーティションに該当し、debian化した後の/になるから、だそう。


これだけ。なんて簡単なんだ。

肝心のHDDアクセス速度はこれから確認である。