Lambdaカクテル

京都在住Webエンジニアの日記です

Invite link for Scalaわいわいランド

読むたびに内容が変化するファイルは作成できるか

こういう話題を見て、そもそも読み出すたびに内容が変化するようなファイル(としてふるまう何か)を作ることはできるだろうか?ということを考えた。

リプライなどでは、symlinkを切り替える案や、デバイスファイルを作るという案が登場していて面白い。やろうと思えばなんとかなるのがUNIX wayの面白さだと思う。

名前付きパイプで行けるんちゃう?作戦

そもそも、ファイルとして振る舞うがプログラムが内容を注入できるものとして、名前付きパイプというものがある。

% mkfifo testpipe
% echo "foo" > testpipe
[block until read]
% cat testpipe
[block until writing to foo]
foo

これをうまく使って、複数回読み出せて、なおかつ内容も変化するような名前付きパイプは可能だろうか?

\d する作戦

よく、入力の終端文字として^Dが用いられる。じゃあ、パイプに入力する文字列に\dを埋め込んでおけば、勝手に読み込んだ側はそこがファイルの終端だと勘違いしてくれるんじゃないの?と直感的に思ったので試してみた:

% echo "foo\dbar\dbuzz" > testpipe
% cat testpipe
foo\dbar\dbuzz

普通に出力されてしまった。そもそも、Ctrl+DでEOFが送信されるというのは端末側で定義されている動作。

www.softel.co.jp

Stack Overflowで似たような事を質問している人がいて、直接EOFを送信することはできないらしい:

unix.stackexchange.com

  • EOF文字やそれに類似したイベントがpipeを通過して届くことはない
  • pipeをreadして発生しているブロックが解除されるためには、そのパイプの開いているハンドルが全て閉じる必要がある

そもそもパイプって何?

そもそもパイプはファイルとは異なり、非直感的な振舞いをするようだ。パイプを経由した読み書きは完全に同期的に行われ、途中でどこかに蓄積されていくということはない。

パイプについて書かれたドキュメントpipe(7)をおさらいしてみよう。

man7.org

  • パイプはファイルシステムのファイルのように読み書きできる
  • プロセスが空のパイプから読み出そうとした場合、データが到着するまでブロックする
  • プロセスがfull状態のパイプに書き込もうとした場合もブロックする
  • パイプはバイトストリームである: メッセージ境界は存在しない
  • パイプの書き込み側( write end )を向いた全てのファイルディスクリプタが閉じたとき、パイプの読み出し(read)は0を返す(いわゆる「EOFを見た」状態になる)
  • パイプの読み込み側( read end )を向いた全てのファイルディスクリプタが閉じたとき、パイプへの書き込みはSIGPIPEを引き起こす
    • fork/pipeを使うプロセスが、未使用のファイルディスクリプタを即座に閉じなければならないのはこのためである。適切なEOF/SIGPIPEを発生させるためである。

このため、先程の「単一のパイプをうまく読み込ませる」作戦を行うならば、「パイプに書き込む」「パイプを読ませる」「パイプを読んだことを検知して書き込み側のファイルディスクリプタを閉じてEOFを発生させる」「readがreturnしたことを確認して再度パイプを書き込みモードで開く」という器用な行いをする必要があることがわかる。

そして、この作戦は「同時に読み書きされる」という状況が発生すると容易に崩壊してしまう・・・。

たぶんこれが一番早いとおもいます・・・

★記事をRTしてもらえると喜びます
Webアプリケーション開発関連の記事を投稿しています.読者になってみませんか?