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

Linuxシステムコール

Linuxシステムコール

前回はこちら


今回からプロセス間通信の方法として利用されるパイプについてまとめます。


まずパイプとは何かというと、「メモリ内に設けられたバッファ領域をプロセス間通信の経路として利用出来るようにしたもの」となります。つまり、プロセスとプロセスの間でデータをやり取りする場合に使用出来るのがパイプなのです。パイプと言えばシェルの機能としても実装されているあのパイプを思い出す人も多いでしょうが、実行したコマンドの結果をlessとかmoreとかgrepに渡すために使っているあのパイプはまさしくこのパイプのことです。


パイプの動作イメージをちょっと図解してみました。



ここでパイプについて注目したいのは、データをやり取りするプロセスの組み合わせが豊富であるという事です。

    1. 一つのプロセス内での通信処理
    2. 同一サーバ or 端末内での別プロセス間の通信処理
    3. 互いに異なる端末間でのネットワーク経由での通信処理


パイプはこのようなさまざまな通信処理に利用することが出来ます。
ここまでパイプ、パイプと一言で書いてきましたが、パイプには大きく二つの種類があります。

名前 システムコール 同一プロセス間通信 別プロセス間通信 ネットワーク経由通信
名前なしパイプ
(匿名パイプ)
pipe()
(親子プロセス間でのみ可)
×
名前付きパイプ mdnod()


まずは匿名パイプから説明していきます。
上述のとおり、匿名パイプはpipe()というシステムコールを使用することで使うことが出来ます。

/*** パイプを開く ***/
// #include <unistd.h>
// 
// 第一引数 パイプを読み書きするためのファイルディスクリプタを取得
//
// 返却値
//  処理成功 :  0
//  処理失敗 : -1
int pipe(int fd[2]);


pipe()で開いたパイプに対してデータの読み書きをする場合は、ファイル同様read/writeを使用する。その際に使用するファイルディスクリプタは↑の引数を使用する。
また、読み書きに限らず後始末もファイルと同様に必須処理です。後始末にはclose()を使用します。


まずは標準入力から入力されたデータをパイプへ書き込み、そのパイプからデータを読み取って標準出力へ表示するプログラムを作成してみます。

      1 #include <unistd.h>
      2 #include <stdio.h>
      3 #include <string.h>
      4
      5 int main()
      6 {
      7     char buff[BUFSIZ];
      8     int  pipe_ds[2]  ;
      9
     10     /*** pipeを開く ***/
     11     if (pipe(pipe_ds) == -1)
     12     {
     13         perror("pipe");
     14         return 1;
     15     }
     16
     17     while (fgets(buff, BUFSIZ, stdin))
     18     {
     19         /*** パイプにデータを書き込み ***/
     20         if (write(pipe_ds[1], buff, strlen(buff) + 1) == -1)
     21         {
     22             perror("write");
     23
     24             /*** 後片付け ***/
     25             close(pipe_ds[0]);
     26             close(pipe_ds[1]);
     27             return 2;
     28         }
     29
     30         /*** パイプからデータを読み込む ***/
     31         if (read(pipe_ds[0], buff, BUFSIZ) == -1)
     32         {
     33             perror("read");
     34
     35             /*** 後片付け ***/
     36             close(pipe_ds[0]);
     37             close(pipe_ds[1]);
     38             return 3;
     39         }
     40
     41         printf("message from  pipe : %s\n", buff);
     42     }
     43
     44     /*** 後片付け ***/
     45     close(pipe_ds[0]);
     46     close(pipe_ds[1]);
     47 }


注意点は、一度オープンしたパイプはファイル同様必ずクローズするというところです。さて、さっそく実行してみます。

[itotto@itotto ]$ gcc -o anony_pipe1 anony_pipe1.c
[itotto@itotto ]$ ./anony_pipe1
vine linux
message from  pipe : vine linux

hogehoge
message from  pipe : hogehoge

[itotto@itotto ]$ ls -l | ./anony_pipe1
message from  pipe : 合計 24

message from  pipe : -rwxr-xr-x 1 itotto itotto 5809 111500:51 anony_pipe1*

message from  pipe : -rw-r--r-- 1 itotto itotto  743 111500:51 anony_pipe1.c

message from  pipe : drwxr-xr-x 2 itotto itotto 4096 111422:17 executable/

message from  pipe : drwxr-xr-x 2 itotto itotto 4096 111422:17 source/

[itotto@itotto ]$


手始めにプログラムを単独で起動してみました。fgets()が呼び出されているので標準入力からの入力待ち受け状態でウェイトします。そこで"vine linux"と入力したところ、"message from pipe : vine linux"と表示されました。これは標準入力から入力されたデータがパイプに書き込まれ、そのパイプからデータを読み取った後に41行目にprintf()で標準出力に出力をしている部分がこの処理を担当しています。


続いて、ls -lというありきたりのコマンドをパイプ経由(標準入力)でプログラムに渡したところ、手で入力する代わりにls -lの結果がプログラムに引き渡されました。うまく動いているようです。


次回はfork()を使用して親子間でプロセス通信をする方法、そしてexec()を使用した場合の問題点についてまとめます。


次へ進む