Ruby
 Computer >> コンピューター >  >> プログラミング >> Ruby

Rubyの奇妙なErrno例外の正体を理解する ― OSエラーとRubyをつなぐ仕組み

Rubyの例外階層を覗いたことがあるなら、そこに少し奇妙なものが混ざっていることに気づいたかもしれません。RuntimeErrorNoMethodErrorといった馴染み深い例外に加えて、Errno::*という不思議な表記を見かけたことはないでしょうか。

Exception
  StandardError
    ...
    SystemCallError
      Errno::*
    ...

ディスクが満杯の状態で書き込みを行ったり、不安定なネットワーク越しにAPIコールを試みたりした経験があれば、この種のエラーに遭遇したことがあるはずです。実際、存在しないファイルを開こうとするだけで、今すぐにでも発生させられます。

irb> File.open("badfilename.txt")
Errno::ENOENT: No such file or directory @ rb_sysopen - badfilename.txt
    from (irb):9:in `initialize'
    from (irb):9:in `open'
    from (irb):9

では、Errno例外とは一体何者なのでしょうか?そして、なぜ他の例外とは違う扱いを受けているのでしょうか?

OSに合わせたRubyの適応 ― Errnoは「アダプター」

Errno例外の正体は、いわばアダプターです。オペレーティングシステム(OS)が返すエラーを、Rubyの例外システムへと変換してつなぐ役割を担っています。OSはRubyとはまったく異なる方式でエラーを扱うため、両者をつなぐ何らかの仕組みがどうしても必要になるのです。

Rubyでは、エラーは通常「例外」として報告されます。ところが、OSレベルのエラーは単なる整数値です。そこでRubyは、発生しうるOSエラーの種類ごとに個別の例外クラスを定義し、それらをすべてErrnoというモジュールの下にまとめているのです。

IRBでこのモジュールの中身を覗いてみましょう。その数、実に膨大です。

irb> Errno.constants
=> [:NOERROR, :EPERM, :ENOENT, :ESRCH, :EINTR, :EIO, :ENXIO, :E2BIG,
    :ENOEXEC, :EBADF, :ECHILD, :EAGAIN, :ENOMEM, :EACCES, :EFAULT,
    :ENOTBLK, :EBUSY, :EEXIST, :EXDEV, :ENODEV, :ENOTDIR, :EISDIR,
    :EINVAL, :ENFILE, :EMFILE, :ENOTTY, :ETXTBSY, :EFBIG, :ENOSPC,
    :ESPIPE, :EROFS, :EMLINK, :EPIPE, :EDOM, :ERANGE, :EDEADLK,
    :ENAMETOOLONG, :ENOLCK, :ENOSYS, :ENOTEMPTY, :ELOOP, ...
    # 実際には150個以上の定数が定義されています
]

しかし、なぜこんなに暗号的な名前ばかりなのでしょう?例えばENOENTが「ファイルが見つからない」という意味だと、どうやって推測すればいいのでしょうか?

…実は、この疑問にはとてもシンプルな答えがあります。

libcからの丸ごとの借用

Errnoモジュールを最初に設計した人は、エラー名をC標準ライブラリ(libc)からそのまま持ってきました。つまりENOENTとは、C言語において「ファイルが見つからなかったときにOSが返す整数のエラーコード」を格納するマクロの名前なのです。

したがって、各エラーコードの正確な意味を知りたければ、C標準ライブラリのドキュメントを参照するのが一番の近道です。以下に、Rubyプログラミングで特に関係の深いものを抜粋してまとめました。

エラーコード意味
EPERM許可されていない操作。適切な権限がないとファイルにアクセスできない。
ENOENTファイルまたはディレクトリが見つからない。
EIO入出力エラー。物理的な読み書きの失敗によく使われる。
EBADF不正なファイルディスクリプタ。例えば読み取り専用で開いたファイルに書き込もうとすると発生する。
ECHILD子プロセスを操作しようとしたが、子プロセスが存在しない。
ENOMEMメモリ不足。これ以上仮想メモリを割り当てられない。
EACCESアクセス権限なし。ファイルのパーミッションがその操作を許可していない。
ENOTBLK通常のファイルをHDDなどのブロックデバイスとしてマウントしようとした。
EBUSYリソースが使用中。共有できないシステムリソースがすでに使われている。例えば、マウント中のファイルシステムのルートにあるファイルを削除しようとすると発生する。
EEXISTファイルが既に存在する。新しいファイルを指定すべき場面で既存のファイルが指定された。
ENOTDIRディレクトリが必要な場面で、ディレクトリではないファイルが指定された。
EISDIR対象がディレクトリである。ディレクトリを書き込み用に開いたり、ハードリンクの作成・削除をしたりすることはできない。
EINVAL引数が不正。ライブラリ関数に誤った引数を渡したことによる様々な問題を示す。
EMFILE現在のプロセスが開けるファイル数の上限に達し、これ以上開けない。複製されたディスクリプタもこの上限にカウントされる。
EFBIGファイルが大きすぎる。ファイルサイズがシステムの許容上限を超えようとしている。
ENOSPCデバイスに空き容量がない。ディスクが満杯のためファイルへの書き込みが失敗した。
ESPIPE不正なシーク操作(パイプへのシークなど)。
EROFS読み取り専用のファイルシステム上の内容を変更しようとした。
EPIPEパイプ切断。パイプの反対側で読み出しているプロセスが存在しない。
ENOTSOCKソケットが必要な場面で、ソケットではないファイルが指定された。
ENETUNREACHリモートホストの属するサブネットに到達できないため、ソケット操作が失敗した。
ENETRESETリモートホストのクラッシュにより、ネットワーク接続がリセットされた。
ECONNABORTEDネットワーク接続がローカル側で中断された。
ECONNRESETローカルホストの制御外の理由(リモートマシンの再起動や回復不能なプロトコル違反など)により、ネットワーク接続が切断された。
ENOBUFSカーネルの入出力バッファがすべて使用中。GNU環境では常にENOMEMと同義であり、ネットワーク操作ではどちらかが返される。
EISCONN既に接続済みのソケットに対して、さらに接続を試みた。
ENOTCONNソケットがどこにも接続されていない。接続先を指定せずにデータを送信しようとすると発生する。
EDESTADDRREQソケットに宛先アドレスが設定されていない。connectで宛先を指定せずにコネクションレス型ソケットで送信しようとすると発生する。
ESHUTDOWNソケットはすでにシャットダウンされている。
ETIMEDOUTタイムアウト付きのソケット操作が、制限時間内に応答を受け取れなかった。
ECONNREFUSEDリモートホストがネットワーク接続を拒否した(通常は対象サービスが稼働していないため)。
EHOSTDOWN接続先のリモートホストがダウンしている。
EHOSTUNREACH接続先のリモートホストに到達できない。
ENOTEMPTY空のディレクトリが必要な場面で、ディレクトリが空ではなかった。通常、ディレクトリの削除時に発生する。
EPROCLIMforkを試みるとユーザーごとのプロセス数上限(RLIMIT_NPROC)を超えてしまうことを意味する。

実践:Errno例外の捕捉方法

Errno例外はSystemCallErrorのサブクラスなので、通常の例外と同じようにrescueできます。特定のエラーだけをピンポイントで捕捉したい場合は、次のように書きます。

begin
  File.open("badfilename.txt")
rescue Errno::ENOENT
  puts "ファイルが見つかりません"
rescue Errno::EACCES
  puts "アクセス権限がありません"
end

なお、Errnoに定義される定数の一覧はプラットフォームによって異なります。これは、各定数がOS固有のerrno値に対応しているためです。Unix系OSとWindowsでは利用できる定数に差がある点にも留意しておきましょう。


  1. Rubyで学ぶ挿入ソート:仕組みから計算量まで徹底解説

    ※本記事は、Rubyでさまざまなソートアルゴリズムを実装するシリーズの第4回です。第1回ではバブルソート、第2回では選択ソート、第3回ではマージソートを取り上げました。データのソート手法をさまざまな角度から探っていくシリーズもいよいよ折り返し地点。今回は挿入ソート(Insertion Sort)に焦点を当てます。挿入ソートには魅力的な特徴がたくさんあります。まず、挿入ソートは安定(stable)なアルゴリズムです。つまり、同じキーを持つ要素同士の相対的な順序が入れ替わることがありません。また、インプレース(in-place)アルゴリズムでもあるため、ソート結果を保存するための新しい配列を作成す

  2. WordPress のファイル構造とデータベースを理解するための初心者向けガイド

    通常、WordPress のファイル構造について気にする必要はありません。 . WordPress には優れた wp-admin インターフェースがあり、エンジンが内部でどのように機能するかを知らなくても、サイトの運営に集中できます。 ただし、サイトの要点を知っておくと役立つ場合があります。これは、予期しない問題が発生した場合に特に当てはまります。 、およびすべてのヘルプは、特定の WordPress ファイルの変更またはデータベース内の値のリセットについて説明しています。 問題が発生していなくても、バックアップやセキュリティについて調べている可能性があります。コア ファイルとデータベ