Linuxシステムコールの勉強(その15)

Linuxシステムコール

Linuxシステムコール

前回はこちら


名前付きパイプの使用方法の続きです。今回は名前付きパイプを使用してプロセス間でデータを送受信するプログラムを作成します。


[受信用プログラム(receiver.c)]

      1 /*********************************************************
      2  * データ受信側プログラム(receiver.c)
      3  *********************************************************/
      4
      5 #include <unistd.h>
      6 #include <sys/stat.h>
      7 #include <fcntl.h>
      8 #include <stdio.h>
      9
     10 #include "named_pipe.h"
     11
     12 int main()
     13 {
     14     int  fifo, nbyte ;
     15     char buff[BUFSIZ];
     16
     17     /* パイプ用スペシャルファイルが無ければ作成する */
     18     if (access(MYNAMEDPIPE, F_OK) == -1)
     19     {
     20         if (mknod(MYNAMEDPIPE, 0666 | S_IFIFO, 0) == -1)
     21         {
     22             perror("mknod()");
     23             return 1;
     24         }
     25         else
     26         {
     27             printf("1. パイプ(%s)を作成しました\n", MYNAMEDPIPE);
     28         }
     29     }
     30     else
     31     {
     32         printf("1. パイプ(%s)は既に存在します\n", MYNAMEDPIPE);
     33     }
     34
     35     printf("receiver : データ待ち受け中...\n");
     36
     37     /* 読取専用でパイプを開く */
     38     if ((fifo = open(MYNAMEDPIPE, O_RDONLY)) == -1)
     39     {
     40         perror("open()");
     41         unlink(MYNAMEDPIPE);
     42         return 1;
     43     }
     44
     45     /* パイプ経由で入ってきたデータを読取 */
     46     while ( (nbyte = read(fifo, buff, BUFSIZ)) > 0)
     47     {
     48         write(fileno(stdout), buff, nbyte);
     49     }
     50
     51     close(fifo);
     52     unlink(MYNAMEDPIPE);
     53
     54     if (nbyte == -1)
     55     {
     56         perror("write()");
     57         return 2;
     58     }
     59
     60     return 0;
     61 }


[送信用プログラム(sender.c)]

      1 /**********************************************************
      2  * データ送信側プログラム(sender.c)
      3  **********************************************************/
      4
      5 #include <unistd.h>
      6 #include <fcntl.h>
      7 #include <stdio.h>
      8
      9 #include "named_pipe.h"
     10
     11 int main()
     12 {
     13     int  fifo, nbyte ;
     14     char buff[BUFSIZ];
     15
     16     /* 書込専用でパイプを開く */
     17     if ((fifo = open(MYNAMEDPIPE, O_WRONLY)) == -1)
     18     {
     19         perror("open()");
     20         return 1;
     21     }
     22
     23     /* 標準入力からデータを読み込んでパイプへ書き込み */
     24     while((nbyte = read(fileno(stdin), buff, BUFSIZ)) > 0)
     25     {
     26         if (write(fifo, buff, nbyte) != nbyte)
     27         {
     28             perror("write()");
     29             close(fifo);
     30             return 2;
     31         }
     32     }
     33
     34     if (close(fifo) == -1)
     35     {
     36         perror("close()");
     37         return 3;
     38     }
     39
     40     return 0;
     41 }


[ヘッダーファイル(named_pipe.h)]

      1 #define MYNAMEDPIPE "/home/itotto/pg/c/named_pipe"


まずは受信用プログラム(receiver.c)を[端末1]で実行します。

[端末1]

[itotto@itotto ]$ gcc -o receiver receiver.c
[itotto@itotto ]$ ./receiver
1. パイプ(/home/itotto/pg/c/named_pipe)を作成しました
receiver : データ待ち受け中...

↑この状態でデータが来るまで待機します


続いて送信用プログラム(sender.c)を[端末2]で実行します。標準入力からデータを読み取って送信するように作っているので、シェルから実行したときにパイプ(シェルのパイプ機能)経由でls -alの結果を渡して見ます。


[端末2]

[itotto@itotto ]$ gcc -o sender sender.c

[itotto@itotto ]$ ls -al | ./sender

[itotto@itotto ]$


さて[端末1]に戻ってみると、ls -alの結果が表示されています。


[端末1]

[itotto@itotto ]$ ./receiver
1. パイプ(/home/itotto/pg/c/named_pipe)を作成しました
receiver : データ待ち受け中...

合計 48
drwxr-xr-x 4 itotto itotto 4096 112122:15 ./
drwxr-xr-x 4 itotto itotto 4096  62721:57 ../
-rw-r--r-- 1 itotto itotto   14 111500:30 .vimrc
drwxr-xr-x 2 itotto itotto 4096 112113:21 executable/
prw-r--r-- 1 itotto itotto    0 112122:15 named_pipe|
-rw-r--r-- 1 itotto itotto   52 112114:20 named_pipe.h
-rwxr-xr-x 1 itotto itotto 6260 112122:15 receiver*
-rw-r--r-- 1 itotto itotto 1084 112120:51 receiver.c
-rwxr-xr-x 1 itotto itotto 5505 112122:15 sender*
-rw-r--r-- 1 itotto itotto  708 112120:50 sender.c
drwxr-xr-x 3 itotto itotto 4096 112114:27 source/

[itotto@itotto ]$


これは[端末2]からパイプ経由で書き込まれたデータがあり、それが正しく表示されている事が分かります。これは前回コマンドレベルで確認したことと同じ事をプログラムで実装出来たといえます。


さて。ここまで名前付きパイプを使ってデータをやり取りする方法についてまとめてきました。その際に出てきた問題点として

    1. データを書き込んだ場合、それを読み取ってもらわない限りブロックされてしまう
    2. 読取側(receiver)を先に起動しておかないとエラーになる


1.についてはパイプを開く際(open())の第二引数 int flagsで O_NONBLOCKフラグかO_NDELAYフラグをorで指定するとノンブロッキングモード(non-blocking mode)でパイプを開く事が出来ます。ノンブロッキングモードって?という人には後で説明します。


2.についてはsender側の処理にパイプを作成する手順が抜けてるのが原因です。ちなみにエラーはこんな感じで出ます。

[itotto@itotto ]$ls -al | ./sender 
open(): No such file or directory

[itotto@itotto ]$


これについてはソースを直します。


話を戻して。
ノンブロッキングモードについてですが、このモードは名前のとおり、処理がブロックされないモードです。つまり、前回receiverを実行した時のようにデータが送信されてくるのを待つような事はしません。つまりこのような動きになります。

>|sh|
[itotto@itotto ]$ ./receiver
1. パイプ(/home/itotto/pg/c/named_pipe)を作成しました
receiver : データ待ち受け中...

[itotto@itotto ]$ 
↑すぐにプロンプトに戻ります。


データが来るまでウェイトさせるのか、それとも受信側ではなく送信側を待たせるべきなのかどうかを判断し、受信側O_NONBLOCKフラグを立てるのかどうかを考えることが大事です。


匿名パイプや名前付きパイプについては今回で終了です。次回からはソケット通信についてまとめます。


次へ進む