pipe と fd のフラグ

さいきんブログを書いていないと思ったら今年に入ってから更新をしていなかった。
覚えている部分からとりあえず気がついたことをメモしておこうと思う。

今回は pipe と fd のフラグについて。

Linux ほか *nix のシステムではpipe(2)というシステムコールでプロセス間通信用の fd を作成できる。

このパイプの fd にはO_NONBLOCKというフラグを立てたり消したりすることが可能である。
つまり,パイプはノンブロッキングに読める。
手元の ArchLinux で Linux 4.12,glibc 2.25 を使ってるとデフォルトでこのフラグが立ってたっぽくて予想してない挙動に気がつかなくてハマった。

フラグの確認方法は,
#inlude <fcntl.h>
...
fcntl( fd, F_GETFL );
この返却値を調べれば良い。
逆にフラグをセットするならば,
#include <fcntl.h>
...
fcntl( fd, F_SETFL, fcntl(fd, F_GETFL) | O_NONBLOCK );
のように取得したフラグに立てたいフラグを追加すれば良い。
フラグの消去もまた同様。




O_NONBLOCKが立てられていた場合について。
この場合,fd をread(2)で読むと,まだ何もデータが到着していない場合,read(2)は -1 を返却する。
なので,
while( read( fd, buf, sizeof(buf) ) > 0 ) {
...
などという手抜きコードを書いているとread(2)はブロックしないので何も読まずに終わることがある。
この場合,
for(;;) {
    ssize_t read_size = read( fd, buf, sizeof(buf) );
    if ( read_size < 0 ) {
        if ( errno == EAGAIN ) { continue; }
        else { break; }
    } else {
...
という風にread(2)の返却値が正の値じゃない場合かつerrnoEAGAINが入っていた場合もきちんとフォローしましょう。
ただ,この例のように continue するだけならそもそも読み出しでブロックさせとけば良いので,元よりフラグを消しておけば済む話である。

これに気がつかずにわりとハマりました。