英語フランス語スペイン語

Ad


OnWorksファビコン

Explain_lca2010 - クラウド上のオンライン

Ubuntu Online、Fedora Online、Windows オンライン エミュレーター、または MAC OS オンライン エミュレーター上の OnWorks 無料ホスティング プロバイダーで Explain_lca2010 を実行します。

これは、Ubuntu Online、Fedora Online、Windows オンライン エミュレーター、MAC OS オンライン エミュレーターなどの複数の無料オンライン ワークステーションの 2010 つを使用して、OnWorks 無料ホスティング プロバイダーで実行できるコマンド Explain_lcaXNUMX です。

プログラム:

NAME


Explain_lca2010 - 媒体が見つかりません: 読むのをやめるべき時です ストエラー(3)の
マインド。

動機


libexplain のアイデアは 1980 年代初頭に思いつきました。 システムコールが発生するたびに
エラーを返すと、カーネルは何が問題だったのかを正確に認識し、これを次のように圧縮します。
8ビット未満 エラー。 ユーザー空間はカーネルと同じデータにアクセスできます。
ユーザー空間では何がエラーを引き起こしたのかを正確に把握できるはずです
に戻り、これを使用して適切なエラー メッセージを作成します。

そんなに簡単なことだろうか?

エラー メッセージ as フィネス
良いエラー メッセージは、多くの場合、スケジュールされたときにドロップされる「XNUMX パーセント」のタスクです。
プレッシャーがプロジェクトを圧迫します。 ただし、適切なエラー メッセージは大きな問題を引き起こす可能性があります。
ユーザーが恐怖に陥ったときのユーザーエクスペリエンスの不釣り合いな改善
普段は出会うことのない未知の領域。 これは簡単な作業ではありません。

幼虫プログラマとして、作者は(完全に正確な)エラーの問題に気づきませんでした。
次のようなメッセージ:
フローティング例外 (コアダンプ)
プログラマ以外の代替解釈が指摘されるまでは。 しかし、それはそうではありません
Unix のエラー メッセージに問題があるだけです。 次のようなエラー メッセージがどのくらいの頻度で表示されますか。
$ 。/バカ
ファイルを開けません
$
現時点では、開発者には XNUMX つのオプションがあります。

1.
次のようなデバッガを実行できます GDB(1)、または

2.
あなたは使うことができます ストラス(1)または トラス(1) 中を見てみる。

· ユーザーはこれらのツールにアクセスすることさえできない可能性があることに注意してください。
それらを使用するために。 (とても久しぶりです Unixの 初心者 意味は「書いただけだ」 XNUMXつ
デバイスドライバ"。)

ただし、この例では、 ストラス(1)明らかにする
$ ストラス -e トレース=オープン 。/バカ
open("some/file", O_RDONLY) = -1 ENOENT (そのようなファイルまたはディレクトリはありません)
ファイルを開けません
$
これは、エラー メッセージが提供する情報よりもはるかに多くの情報です。 通常、
愚かなソースコードは次のようになります
int fd = オープン("何か"、O_RDONLY);
if (fd < 0)
{
fprintf(stderr, "ファイルを開けません\n");
終了する(1);
}
ユーザーには通知されない which ファイルを作成し、ユーザーに通知することもできません。 which エラー。 ファイルだったのか
そこでも? 権限に問題がありましたか? それは、それを開こうとしていたことを示しています
ファイルに保存されましたが、これはおそらく偶然でした。

手がかりの棒をつかみ、それで幼虫プログラマーを倒しに行きましょう。 彼にそのことを話してください 恐怖とします。
次回このプログラムを使用すると、別のエラー メッセージが表示されます。
$ 。/バカ
open: そのようなファイルまたはディレクトリはありません
$
進歩はありましたが、期待したほどではありませんでした。 エラー メッセージが表示された場合、ユーザーは問題を解決するにはどうすればよいですか?
何が問題なのか彼に話さなかったのですか? ソースを見てみると、
int fd = オープン("何か"、O_RDONLY);
if (fd < 0)
{
perror("開く");
終了する(1);
}
手がかり棒を使ってもう一度実行する時間です。 今回は、エラー メッセージが XNUMX ステップ進みます
前進して一歩後退:
$ 。/バカ
何か: そのようなファイル、又はディレクトリはありません
$
これで、開こうとしていたファイルはわかりましたが、そのファイルが開こうとしていたことについては知らされなくなりました。 開いた(2)
それは失敗しました。 この場合、それはおそらく重要ではありませんが、次の場合には重要になる可能性があります。
他のシステムコール。 そうかもしれない クリート(2) 代わりに、次のことを意味する操作
さまざまな権限が必要です。
const char *ファイル名 = "何か";
int fd = open(ファイル名, O_RDONLY);
if (fd < 0)
{
エラー(ファイル名);
終了する(1);
}
残念なことに、上記のコード例は、larval 以外のプログラマにも典型的なものです。 時間
パダワンの学習者にそのことを伝えるために ストエラー(3)システムコール。
$ 。/バカ
開いた 何か: そのようなファイル、又はディレクトリはありません
$
これにより、ユーザーに表示できる情報が最大化されます。 コードは次のようになります
この:
const char *ファイル名 = "何か";
int fd = open(ファイル名, O_RDONLY);
if (fd < 0)
{
fprintf(stderr, "%s を開きます: %s\n", ファイル名, strerror(errno));
終了する(1);
}
これで、システム コール、ファイル名、エラー文字列が得られました。 これにはすべてのものが含まれています
という情報 ストラス(1)を印刷しました。 それはそれでいいことだ。

またはそれは?

制限事項 of 恐怖 & ストエラー
1980 年代に著者が目にした問題は、エラー メッセージが不完全であるということでした。
「そのようなファイルやディレクトリはありません」とは、「一部」ディレクトリ、または「もの」ファイル
"一部」ディレクトリ?

manページをざっと見てみると、 ストエラー(3) は次のように語っています。
strerror - エラー番号を説明する文字列を返す
よく注意してください: これはエラーを説明しています 、エラーではありません。

一方、カーネルは、 知っている エラーは何だったのか。 具体的なポイントがありました
カーネル コード。カーネル コードが分岐して「いいえ」となった特定の条件によって引き起こされます。
ユーザー空間プログラムは特定の条件を把握して、より適切なエラーを作成できるでしょうか
メッセージ?

しかし、問題はさらに深刻になります。 途中で問題が発生した場合はどうなりますか read(2) システム
ではなく、電話してください 開いた(2) 電話しますか? 関連するエラーメッセージは簡単です
開いた(2) ファイル名を含めるには、そこにあります。 ただし、ファイル名を含めることができるようにするには
関連するエラーで read(2) システムコールでは、ファイル名をすべて渡す必要があります
ファイル記述子だけでなく、コールスタックの途中まで。

そして、これが重要な点です。カーネルは、そのファイルの名前をすでに知っています。
記述子が関連付けられています。 なぜプログラマは冗長データをすべて渡さなければならないのか
決して発行されないかもしれないエラーメッセージを改善するためだけにコールスタックを下る途中でしょうか? で
実際には、多くのプログラマは気にせず、結果として生じるエラー メッセージは、プログラマにとってはさらに悪いものです。
ボーマンは

しかし、それは 1980 年代の PDP11 でのことであり、リソースは限られており、共有ライブラリはありませんでした。 戻る
その場合、Unix のフレーバーは含まれていません / proc たとえ初歩的な形であっても、 lsof(1)プログラム
XNUMX年以上離れていた。 そのため、このアイデアは実用的ではないとして棚上げされました。

レベル 無限大 サポート
あなたがレベル無限サポートであると想像してください。 あなたの職務内容には、決してそうはしないと書かれています
これまで ユーザーと話さなければなりません。 では、なぜ今でも欲しい人が後を絶たないのでしょうか。
地元の Unix の第一人者であるあなたが、さらに別のエラー メッセージを解読できるでしょうか?

奇妙なことに、25 年後、シンプルな権限システムにもかかわらず、完全な権限システムが実装されました。
一貫性があっても、ほとんどの Unix ユーザーはまだ「そのようなファイルまたはディレクトリはありません」を解読する方法を知りません。
または毎日目にするその他の不可解なエラー メッセージ。 または、少なくとも不可解です
それら。

第一レベルの技術サポートがエラー メッセージを解読する必要がなければ、素晴らしいと思いませんか?
ユーザーが電話をかけずに理解できるエラー メッセージがあればよいと思いませんか
技術サポート?

最近 / proc Linux では、デコードに必要な情報を十分に提供できます。
エラー メッセージの大部分を表示し、ユーザーにエラー メッセージの直接の原因を示します。
問題。 制限のあるシステムでは / proc 実装、 lsof(1)コマンドで記入可能
ギャップの多さ。

2008 年、著者のもとには翻訳の依頼が頻繁に起こりました。 そうだった
25 年前のアイデアを再検討する時期が来ており、その結果が libexplain です。

使用する としょうかん


ライブラリへのインターフェイスは、可能な限り一貫性を保つように努めます。 まずは、
使用例 ストエラー(3):
if (名前変更(古いパス, 新しいパス) < 0)
{
fprintf(stderr, "%s %s の名前変更: %s\n", old_path, new_path,
strerror(errno));
終了する(1);
}
libexplain の背後にある考え方は、 ストエラー(3) に相当 システムコール、
より詳細なエラーを提供できるように、そのシステム コールに合わせて特別に調整されています。
セクションの「エラー」見出しの下に表示される情報の多くが含まれるメッセージ
2と3 man 実際の状況、実際の議論に関する情報を補足したページ
値とシステム制限。

  簡単な拡張で シミュレーション例
  ストエラー(3) 交換:
if (名前変更(古いパス, 新しいパス) < 0)
{
fprintf(stderr, "%s\n", Explain_rename(old_path, new_path));
終了する(1);
}

  エルノ シミュレーション例
明示的に渡すことも可能です エラー(3) 値、最初に何かを行う必要がある場合
邪魔になるような処理 エラーエラー回復など:
if (名前変更(古いパス, 新しいパス < 0))
{
int old_errno = エラー番号;
...コード それ 邪魔する エラー...
fprintf(stderr, "%s\n", Explain_errno_rename(old_errno,
古いパス、新しいパス));
終了する(1);
}

  マルチスレッド ケース
一部のアプリケーションはマルチスレッドであるため、libexplain の内部コンポーネントを共有できません。
バッファ。 次を使用して独自のバッファを提供できます
if (リンク解除(パス名))
{
文字メッセージ[3000];
Explain_message_unlink(メッセージ, のサイズ(メッセージ)、パス名);
エラーダイアログ(メッセージ);
-1を返します。
}
完全を期すために、両方とも エラー(3) スレッドセーフ:
ssize_t nbytes = read(fd, data, sizeof(data));
if (nバイト < 0)
{
文字メッセージ[3000];
int old_errno = エラー番号;
...エラー 回復...
Explain_message_errno_read(メッセージ, のサイズ(メッセージ)、
old_errno、fd、データ、sizeof(data));
エラーダイアログ(メッセージ);
-1を返します。
}

これらは次の代替品です strerror_r(3)、それを備えたシステム上。

インタフェース シュガー
プログラマーに便利な関数として追加された一連の関数。
libexplain ライブラリは、作成者が最もよく使用する libexplain 関数であることが判明しました。
コマンドラインプログラム:
int fd = Explain_creat_or_die(ファイル名, 0666);
この関数は、新しいファイルの作成を試みます。 それができない場合は、エラー メッセージが出力され、
EXIT_FAILURE で終了します。 エラーがない場合は、新しいファイル記述子を返します。

関連する機能:
int fd = Explain_creat_on_error(ファイル名, 0666);
失敗するとエラー メッセージが出力されますが、元のエラー結果も返されます。
エラー(3)も同様に痴漢されていません。

すべて   他の   呼び出し
一般に、すべてのシステム コールには独自のインクルード ファイルがあります。
#含む.h>
これは、XNUMX つの関数の関数プロトタイプを定義します。

・ 説明する_,

· Explain_errno_,

· 説明_メッセージ_,

· Explain_message_errno_,

・ 説明する__or_die そして

・ 説明する__エラー中。

すべての関数プロトタイプには Doxygen ドキュメントがあり、このドキュメントは is 剥奪
インクルード ファイルがインストールされるとき。

  wait (2) システムコール (およびその仲間) には、失敗も解釈する追加のバリアントがいくつかあります。
EXIT_SUCCESS ではない終了ステータスになります。 これに該当するのは、  (3)と 閉じる(3)として
よく。

対象範囲には、221 件のシステム コールと 547 件の ioctl リクエストが含まれます。 他にもたくさんのシステムがあります
まだ実装されていない呼び出し。 決して戻らないシステムコール。 終了する(2)、存在しません
図書館にありますし、今後もありません。 の exec システムコールのファミリー   サポートされているので、
エラーが発生した場合に戻ります。

ネコ
これは、完全なエラー報告を備えた仮想の「cat」プログラムがどのように見えるかです。
libexplainを使用します。
#含む
#include
#含む
libexplain のインクルードが XNUMX つと、通常の容疑者が含まれています。 (削減したい場合は、
プリプロセッサの負荷に応じて、特定の.h> が含まれます。)
静的ボイド
プロセス(FILE *fp)
{
のために (;;)
{
文字バッファ[4096];
size_t n = Explain_fread_or_die(buffer, 1, sizeof(buffer), fp);
もし (!n)
破る;
Explain_fwrite_or_die(バッファー、1、n、標準出力);
}
}
  プロセス 関数はファイル ストリームを標準出力にコピーします。 エラーが発生した場合
読み取りまたは書き込みの場合、それが報告されます (パス名は
エラー)、コマンドは EXIT_FAILURE で終了します。 追跡することさえ心配しません
パス名を指定するか、コールスタックにパス名を渡します。
int型
main(int argc, char **argv)
{
のために (;;)
{
int c = getopt(argc, argv, "o:");
if (c == EOF)
破る;
スイッチ(c)
{
ケース「o」:
Explain_freopen_or_die(optarg, "w", stdout);
破る;
このコードの面白いところは、libexplain がエラーを報告できることです。 配合工業用化学製品の   パス名 さらに
あなたの場合 しない ここで行っているように、stdout を明示的に再度開きます。 私たちは心配さえしません
ファイル名を追跡します。
デフォルト:
fprintf(stderr, "使用法: %ss [ -o 】 ...\n"、
argv[0]);
EXIT_FAILURE を返します。
}
}
if (optind == argc)
プロセス(標準入力);
ほかに
{
while (optind < argc)
{
FILE *fp = Explain_fopen_or_die(argv[optind]++, "r");
プロセス(fp);
Explain_fclose_or_die(fp);
}
}
標準出力は暗黙的に閉じられますが、エラーレポートを作成するには遅すぎます。
バッファされた I/O がまだ何も書き込んでいない場合に備えて、ここでそれを実行します。
ENOSPC エラーか何かが発生しています。
Explain_fflush_or_die(標準出力);
EXIT_SUCCESS を返します。
}
それだけです。 完全なエラー報告、明確なコード。

ラスティさんの 規模 of インタフェース
馴染みのない人のために説明すると、Rusty Russel の「How Do I Make This Hard to Misuse?」
このページは API 設計者にとって必読です。
http://ozlabs.org/~rusty/index.cgi/tech/2008‐03‐30.html

10. 結論 不可能 〜へ 取得する 違う。

目標は高く、野心的に高く設定する必要があります。そうしないと、目標を達成したと思われないようにすることができます。
あなたがいないときに終了します。

libexplain ライブラリは、偽のポインタやその他の多くの偽のシステム コール パラメータを検出します。
そして一般に、最も困難な状況であってもセグメンテーション違反を回避しようとします。

libexplain ライブラリはスレッドセーフになるように設計されています。 現実世界での使用がさらに増える可能性がある
これを改善できる箇所を明らかにします。

最大の問題は、実際の関数名自体にあります。 Cには無いから
名前空間では、libexplain ライブラリは常に Explain_name プレフィックスを使用します。 これは
シンボルの競合を避けるために疑似名前空間を作成する従来の方法。
ただし、不自然な響きの名前がいくつか発生します。

9.   コンパイラ or リンクス なくなり 貴社 取得する it 違う。

よくある間違いは、explain_open_or_die が意図されていた場所で Explain_open を使用することです。
幸いなことに、コンパイラはこの時点で型エラーを発行することがよくあります (マシン情報の記入> という構文でなければなりません。例えば、 割り当てられません
const char * rvalue から int lvalue)。

8.   コンパイラ 意志 警告する if 貴社 取得する it 違う。

Explain_rename_or_die が意図されていたときに Explain_rename が使用された場合、他の問題が発生する可能性があります。
問題。 GCC には便利な warn_unused_result 関数属性と、libexplain があります。
ライブラリはそれをすべての Explain_ に添付します 関数呼び出しを実行すると、警告が生成されます。
この間違いを犯してください。 これと組み合わせる gccの -エラー これをレベル9の良さに昇格させます。

7.   明白 つかいます is (おそらく)   正しい 1。

関数名はその意味を伝えるために選択されていますが、常にそうであるとは限りません。
成功。 説明しながら__あるいは_死んで説明する__on_error はかなり説明的です。
あまり使用されていないスレッドセーフなバリアントはデコードが困難です。 関数プロトタイプは、
コンパイラは理解を促進し、ヘッダー ファイル内の Doxygen コメントはユーザーの助けになります。
理解に向けて。

6.   伝える 貴社 〜へ つかいます ボーマンは

Explain_ を読むことが特に重要です。_or_die として「説明 ( さもなければ死ぬ)」。
一貫した Explain_ 名前空間プレフィックスを使用すると、いくつかの残念な副作用が発生します。
自明性部門も同様です。

名前内の単語の順序は、引数の順序も示します。 議論
常にリストします end システムコールに渡されるのと同じ引数を使用します。 of それら。 場合
_errno_ が名前に含まれており、その引数は常にシステム コールの引数よりも前にあります。 もし
_message_ が名前に含まれており、その XNUMX つの引数は常に最初に来ます。

5. Do it or it 意志 破る at ランタイム。

libexplain ライブラリは、偽のポインタやその他の多くの偽のシステム コール パラメータを検出します。
そして一般に、最も困難な状況であってもセグメンテーション違反を回避しようとします。 それはすべきです
実行時に壊れることはありませんが、実際に使用する機会が増えると間違いなくこれが改善されます。

一部のエラー メッセージは、エンド ユーザーではなく開発者や保守者を対象としています。
バグ解決を支援できます。 「実行時に中断する」というよりも、「実行時に有益である」ということです。
runtime」(システムコール barfs 後)。

4. 続きます 一般的な 大会 & あなたは 取得する it 右。

C には名前空間がないため、libexplain ライブラリは常に Explain_ name を使用します。
接頭辞。 これは、疑似名前空間を作成する伝統的な方法です。
シンボルの競合。

すべての libexplain 呼び出しの末尾引数は、システム呼び出しと同じです。
を説明しています。 これは、
システムはそれ自体を呼び出します。

3. 読む   ドキュメント & あなたは 取得する it 右。

libexplain ライブラリは、あらゆるものについての完全な Doxygen ドキュメントを提供することを目的としています。
パブリック API 呼び出し (内部でも)。

MESSAGE コンテンツ


libexplain で作業するのは、車が起動しているときに車の下側を見るのと少し似ています。
整備士のところにあるホイスト。 その下には醜いものがあり、それに加えて泥やゴミがあります。
ユーザーがそれを目にすることはほとんどありません。 優れたエラー メッセージは、次のようなユーザーにとっても有益である必要があります。
幸運にも下側を見る必要がほとんどなくなりました。
電話でユーザーの説明を聞いている整備士にとって有益です。 これは
簡単な仕事ではありません。

最初の例をもう一度見てみると、libexplain を使用する場合、コードは次のようになります。
int fd = Explain_open_or_die("何か/もの", O_RDONLY, 0);
このようなエラーメッセージが表示されて失敗します
open(pathname = "some/file", flags = O_RDONLY) が失敗しました。そのようなファイルまたはディレクトリはありません
(2, ENOENT) 現在のディレクトリに「ある」ディレクトリが存在しないため
これはXNUMXつの部分に分解されます
システムコール 失敗した、 システムエラー なぜなら
説明

なぜなら
「なぜなら」より前のメッセージの部分は、過度に専門的で非専門的であると見ることもできます。
技術ユーザー、主にシステム コール自体を正確に出力した結果として
エラーメッセージの始まり。 そしてそれは次のようになります ストラス(1) 出力、ボーナス オタク向け
を指しています。
open(pathname = "some/file", flags = O_RDONLY) が失敗しました。そのようなファイルまたはディレクトリはありません
(2、エノエント)
エラー メッセージのこの部分は、開発者がコードを作成する際に不可欠です。
そして、バグレポートを読んでバグを修正しなければならないメンテナにとっても同様に重要です。
コード。 何が失敗したかを正確に述べています。

このテキストがユーザーに表示されない場合、ユーザーはそれをコピーして貼り付けることができません。
バグレポートに記載されていない場合、メンテナは実際に何が起きたのかを知ることができません。
違う。

技術スタッフが頻繁に使用する ストラス(1)または トラス(1) 正確な情報を取得するためですが、
バグレポートを読むとき、この道は開かれていません。 バグ報告者のシステムははるかに遠い
遠く離れて、今でははるかに異なる状態にあります。 したがって、この情報は
バグ レポート。つまり、エラー メッセージに含まれている必要があります。

システム コール表現は、メッセージの残りの部分にコンテキストも与えます。 必要なら
問題が発生した場合、問題のシステムコール引数が説明の中で名前で参照される場合があります。
「だから」の後。 さらに、すべての文字列は完全に引用符で囲まれ、エスケープされた C 文字列であるため、
埋め込まれた改行や非印刷文字によってユーザーの端末が動作しなくなることはありません。
ヘイワイヤー。

  システムエラー から出てくるものです ストエラー(2) にエラー記号を加えます。 せっかちで、
専門のシステム管理者はこの時点で読むのをやめるかもしれませんが、著者のこれまでの経験は次のとおりです。
さらに読むとやりがいがあります。 (やりがいがない場合は、おそらく次の領域です。
libexplain で改善できる可能性があります。 もちろん、コードの貢献も歓迎します。)

なぜなら
これは、技術者以外のユーザーを対象としたエラー メッセージの一部です。 それは超えて見えます
単純なシステムコール引数を使用して、より具体的なものを探します。
現在のディレクトリに「some」ディレクトリがありません
この部分では、エラーの根本的な原因を平易な言葉で説明しようとしています。
ここで国際化が不可欠です。

一般に、ポリシーは、ユーザーが
探す必要はありません (また、バグレポートから除外することもありません)。

国際化
libexplain ライブラリのエラー メッセージのほとんどは国際化されています。 そこには
まだローカライズされていないため、母国語での説明が必要な場合は、
貢献してください。

上記の「ほとんど」という修飾子は、概念実証が行われるという事実に関連しています。
実装には国際化サポートが含まれていませんでした。 コードベースは
通常はメッセージをリファクタリングした結果として、各エラーが
メッセージ文字列はコード内に XNUMX 回だけ出現します。

の部分をアセンブルする必要がある言語用に用意されています。
システムコール 失敗した、 システムエラー なぜなら 説明
ローカライズされたエラー メッセージの正しい文法を異なる順序で確認します。

検視
プログラムがまだ libexplain を使用しておらず、使用できない場合があります。 ストラス(1)
どちらか。 があります 説明します(1) libexplain に含まれるコマンドで、次の目的で使用できます。
基盤となるシステムの状態があまり変化していない場合は、エラー メッセージを解読します。
$ 説明します リネーム foo /tmp/バー/バズ -e エノエント
rename(oldpath = "foo", newpath = "/tmp/bar/baz") が失敗しました。そのようなファイルまたはディレクトリはありません
(2, ENOENT) newpath に「bar」ディレクトリがないため/ tmpに"ディレクトリ
$
システム コールの引数名を使用してパスのあいまいさがどのように解決されるかに注目してください。 の
もちろん、エラーとシステムコールを知っておく必要があります。 説明します(1) 役に立ちます。 として
余談ですが、これは libexplain 自動テスト スイートが次のことを検証するために使用する方法の XNUMX つです。
libexplainが機能しています。

哲学
「私が調べて知らなかったことも含めて、すべて教えてください。」

このライブラリは、静的にリンクされると、作成したコードのみがリンクされるような方法で実装されています。
実際に使ってみるとリンクされます。 これは、ソース ファイルごとに XNUMX つの関数を持たせることで実現されます。
可能な限り。

さらに詳しい情報を提供できる場合は、libexplain が提供します。 ユーザーが減れば減るほど
自分たちで追跡しなければならないほど良いのです。 これは、UID には
ユーザー名、GID にはグループ名が伴い、PID にはプロセスが伴います。
名前、ファイル記述子、およびストリームにはパス名が伴います。 .

パスを解決するときに、パス コンポーネントが存在しない場合、libexplain は類似のものを探します。
タイプミスの代替案を提案するために、名前を付けます。

libexplain ライブラリは、使用するヒープをできる限り少なくしようとし、通常は使用しません。 これは
プロセスの状態を乱すことを可能な限り避けるためですが、場合によっては乱雑になることもあります。
やむを得ない。

libexplain ライブラリは、グローバル変数を回避し、
可能な限りスタック上の状態にします。 単一の共通メッセージ バッファがあり、
これを使用する関数はスレッドセーフではないことが文書化されています。

libexplain ライブラリは、プロセスのシグナル ハンドラーを妨げません。 これにより、
ポインタがチャレンジをセグメンテーション違反にするかどうかを判断しますが、不可能ではありません。

情報がシステム コール経由で入手できる場合と、 / proc
エントリでは、システムコールが優先されます。 これは、プロセスの状態を乱さないようにするためです。
利用可能なファイル記述子がない場合もあります。

libexplain ライブラリは、大きなファイルをサポートしてコンパイルされています。 大/小はありません
統合失調症。 これが API の引数の型に影響し、エラーが発行される場合
必要な大きなファイル定義が存在しない場合。

修正: ファイル システム クォータがコード内で確実に処理されるようにするための作業が必要です。 これ
一部に当てはまる 取得制限(2) 境界線も同様です。

相対パスが有益でない場合があります。 例: システム デーモン、
サーバーとバックグラウンドプロセス。 このような場合、エラーでは絶対パスが使用されます。
説明。

パス 分解能


短いバージョン: を参照 パスの解像度とします。

ロングバージョン: ほとんどのユーザーは聞いたことがない パスの解像度(7)、多くの上級ユーザー
読んだことがない。 注釈付きのバージョンは次のとおりです。

手順 1: 開始 of   分解能 プロセス
パス名がスラッシュ (「/」) 文字で始まる場合、開始ルックアップ ディレクトリは次のとおりです。
呼び出しプロセスのルート ディレクトリ。

パス名がスラッシュ (「/」) 文字で始まらない場合、開始ルックアップは
解決プロセスのディレクトリは、プロセスの現在の作業ディレクトリです。

手順 2: 歩く 沿って   path
現在のルックアップ ディレクトリを開始ルックアップ ディレクトリに設定します。 さて、それぞれの非
パス名の最後のコンポーネント。コンポーネントはスラッシュ (「/」) で区切られた部分文字列です。
文字を含む場合、このコンポーネントは現在のルックアップ ディレクトリで検索されます。

プロセスに現在のルックアップ ディレクトリに対する検索権限がない場合、EACCES
エラー (「許可が拒否されました」) が返されます。
open(pathname = "/home/archives/.ssh/private_key"、flags = O_RDONLY) が失敗しました。
プロセスに検索権限がないため、権限が拒否されました (13、EACCES)
パス名「/home/archives/.ssh」ディレクトリ、プロセス有効 GID 1000
「pmiller」はディレクトリ所有者 1001 「archives」と一致しないため、所有者は
許可モード「rwx」は無視され、その他の許可モードは「---」です。
プロセスには特権がありません (DAC_READ_SEARCH 機能がありません)

コンポーネントが見つからない場合は、ENOENT エラー (「そのようなファイルまたはディレクトリはありません」) が返されます。
unlink(pathname = "/home/microsoft/rubbish") が失敗しました。そのようなファイルまたはディレクトリはありません (2,
ENOENT) パス名に "microsoft" ディレクトリがないため、"/ホーム"ディレクトリ

ユーザーがパス名を誤って入力した場合に、提案を行うサポートもあります。
ENOENT が返されます。
open(pathname = "/user/include/fcntl.h"、flags = O_RDONLY) が失敗しました。そのようなファイルが存在しないか、または
パス名「/」に「user」ディレクトリが存在しないため、ディレクトリ (2, ENOENT)
ディレクトリ、つまり "usr" ディレクトリのことですか?

コンポーネントが見つかったものの、ディレクトリでもシンボリック リンクでもない場合、ENOTDIR
エラーが返されます (「ディレクトリではありません」)。
open(pathname = "/home/pmiller/.netrc/lca", flags = O_RDONLY) が失敗しました。
パス名に「.netrc」通常ファイルがあるため、ディレクトリ (20, ENOTDIR)
「/home/pmiller」ディレクトリがディレクトリとして使用されていないとき

コンポーネントが見つかり、それがディレクトリである場合、現在のルックアップ ディレクトリをそのディレクトリに設定します。
ディレクトリに移動し、次のコンポーネントに移動します。

コンポーネントが見つかり、それがシンボリック リンク (シンボリック リンク) である場合、最初にこのシンボリック リンクを解決します。
リンク (現在のルックアップ ディレクトリを開始ルックアップ ディレクトリとして使用)。 エラーが発生すると、
エラーが返されます。 結果がディレクトリでない場合は、ENOTDIR エラーが返されます。
unlink(pathname = "/tmp/dangling/rubbish") が失敗しました。そのようなファイルまたはディレクトリはありません (2,
ENOENT) パス名に「ぶら下がっている」シンボリック リンクがあるため、/ tmpに"ディレクトリ
存在しない「どこにもない」ことを指します
シンボリックリンクの解決が成功し、ディレクトリが返された場合は、現在のディレクトリを設定します。
ディレクトリを検索してそのディレクトリを参照し、次のコンポーネントに移動します。 注意してください。
ここでの解決プロセスには再帰が含まれます。 カーネルをスタックから保護するには
オーバーフロー、およびサービス妨害から保護するために、最大数には制限があります。
再帰の深さ、およびたどるシンボリック リンクの最大数。 ELOOPエラーとは、
最大値を超えると返されます (「シンボリック リンクのレベルが多すぎます」)。
open(pathname = "/tmp/dangling", flags = O_RDONLY) が失敗しました。レベルが多すぎます。
シンボリック リンク (40、ELOOP)。シンボリック リンク ループが発生したためです。
「/tmp/dangling」で始まるパス名
シンボリックリンクが多すぎる場合、ELOOP または EMLINK エラーが発生する可能性もありますが、
ループが検出されました。
open(pathname = "/tmp/rabbit‐hole", flags = O_RDONLY) が失敗しました。レベルが多すぎます。
シンボリック リンク (40、ELOOP) は、シンボリック リンクが多すぎるためです。
パス名 (8)
実際の制限もどのように出力されるかに注目してください。

手順 3: もう完成させ、ワークスペースに掲示しましたか?   ファイナル エントリ
パス名の最後のコンポーネントの検索は、他のすべてのコンポーネントの検索とまったく同じように行われます。
前のステップで説明したコンポーネントと同じですが、次の XNUMX つの違いがあります。

(i) 最後のコンポーネントはディレクトリである必要はありません (少なくともパス解決に関する限り)
プロセスが関係します。 次の理由により、ディレクトリまたは非ディレクトリである必要がある場合があります。
特定のシステムコールの要件)。

(ⅱ)
最終コンポーネントが見つからない場合でも、必ずしもエラーになるわけではありません。 たぶん私たちはただ
それを作成しています。 最終エントリの処理の詳細については、
特定のシステムコールのマニュアルページ。

(iii)
最後のコンポーネントがシンボリック リンクの場合、問題が発生する可能性もあります。
そしてそれに従うべきではありません。 たとえば、 開いた(2) O_NOFOLLOW フラグ:
open(pathname = "a-symlink", flags = O_RDONLY | O_NOFOLLOW) が失敗しました。レベルが多すぎます。
シンボリック リンク (ELOOP) O_NOFOLLOW が指定されているが、パス名が
シンボリックリンク

(iv)
ユーザーがパス名を入力するときに間違いを犯すことはよくあります。 libexplain ライブラリ
ENOENT が返されると、次のように提案を試みます。
open(pathname = "/usr/include/filecontrol.h", flags = O_RDONLY) が失敗しました。そのようなファイルが存在しないか、または
パス名に通常のファイル「filecontrol.h」がないため、ディレクトリ (2, ENOENT)
"/ usr / include" ディレクトリではなく、"fcntl.h" という通常のファイルのことを指しますか?

(v) 最終コンポーネントが、
通常のファイル:
readlink(pathname = "just-a-file"、data = 0x7F930A50、data_size = 4097) が失敗しました。
パス名がシンボリック リンクではなく通常のファイルであるため、無効な引数 (22、EINVAL)

(VI)
FIXME: 「t」ビットの処理。

境界
パス名とファイル名に関しては、多くの制限があります。

パス名の長さの制限
パス名には最大長があります。 パス名 (または中間のパス名) の場合、
シンボリック リンクの解決中に取得されたパス名) が長すぎます。ENAMETOOLONG
エラーが返されます (「ファイル名が長すぎます」)。 システム制限がどのように含まれているかに注目してください
エラーメッセージに記載されています。
open(パス名 = "とても...長い"、フラグ = O_RDONLY) が失敗しました。ファイル名が長すぎます (36、
ENAMETOOLONG) パス名がシステムの最大パス長 (4096) を超えているため

ファイル名の長さの制限
一部の Unix バリアントでは、各パス コンポーネントのバイト数に制限があります。
黙ってこれに対処する人もいれば、ENAMETOOLONG を与える人もいます。 リブエクスプレイン
図書館の用途 パス設定(3) _PC_NO_TRUNC でどちらであるかを示します。 このエラーが発生した場合、
libexplain ライブラリはエラー メッセージに制限を示します。制限は次のとおりです。
から得られました パス設定(3) _PC_NAME_MAX。 システム制限がどのように含まれているかに注目してください
エラーメッセージに記載されています。
open(パス名 = "システム7/14文字のみ"、フラグ = O_RDONLY) 失敗しました、ファイル
名前が長すぎます (36、ENAMETOOLONG)。これは、「14 文字のみ」コンポーネントが含まれているためです。
システム制限より長い (14)

空のパス名
オリジナルの Unix では、空のパス名は現在のディレクトリを参照していました。
現在、POSIX では、空のパス名は正常に解決されてはならないと定められています。
open(pathname = "", flags = O_RDONLY) が失敗しました。そのようなファイルまたはディレクトリはありません (2,
ENOENT) POSIX では空のパス名を解決してはならないと定められているため、
首尾よく

権限
ファイルの許可ビットは、XNUMX つのビットからなる XNUMX つのグループで構成されます。 最初のグループは、
XNUMX は、呼び出しプロセスの実効ユーザー ID がプロセスの所有者 ID と等しい場合に使用されます。
ファイル。 XNUMX 番目の XNUMX つのグループは、ファイルのグループ ID が次のいずれかに等しい場合に使用されます。
呼び出しプロセスの実効グループ ID、またはプロセスの補助グループ ID の XNUMX つです。
呼び出しプロセス。 どちらも当てはまらない場合は、XNUMX 番目のグループが使用されます。
open(パス名 = "/etc/passwd"、フラグ = O_WRONLY) が失敗しました、アクセス許可が拒否されました (13、
EACCES) プロセスには通常の「passwd」への書き込み権限がないため、
パス名「」のファイルの/ etc" ディレクトリ、プロセスの実効 UID 1000 "pmiller"
通常のファイル所有者 0「root」と一致しないため、所有者権限モード「rw-」
は無視され、その他の許可モードは「r--」であり、プロセスには特権がありません。
(DAC_OVERRIDE 機能はありません)
ほとんどのユーザーはこのことを知らないため、この説明にはかなりのスペースが与えられています。
これが権限システムの仕組みです。 特に: オーナー、グループ、その他
権限は排他的であり、「OR」で結合されません。

ストランジ そして 面白い SYSTEM 呼び出し


システムコールごとに特定のエラーハンドラを作成するプロセスでは、多くの場合、次のことが明らかになります。
興味深い癖と境界条件、または不明瞭な エラー(3) 値。

エノメディアム、 いいえ ミディアム 発見
CD をコピーするという行為がこの論文のタイトルの由来となっています。
$ dd if=/dev/cdrom の=fubar.iso
dd: 「/dev/cdrom」をオープンしています: メディアが見つかりません
$
著者は、なぜコンピューターが超能力者など存在しないと言っているのか不思議に思った
中くらい。 膨大な数の英語をネイティブスピーカーが英語ではないという事実とはまったく異なります。
「メディア」が単数形であることは言うまでもなく、「メディア」が複数形であることさえ認識しています。
によって返される ストエラー(3) ENOMEDIUM の場合、非常に簡潔なので、ほとんど完全に含まれていません。
コンテンツ。

日時 開いた(2) ENOMEDIUM を返します。libexplain ライブラリが
ドライブの種類に応じて、これについてはほとんどわかりません。 例えば:
...フロッピー ドライブにディスクがないため
...CD-ROM ドライブにディスクが入っていないため
...テープドライブにテープがないため
...カードリーダーにメモリースティックがないため

そしてそれは実現しました...
open(pathname = "/dev/cdrom"、flags = O_RDONLY) が失敗しました。メディアが見つかりません (123、
ENOMEDIUM) は、CD-ROM ドライブにディスクが入っていないようです。
著者がこれまで気づいていなかったトリックは、
O_NONBLOCK フラグ。メディアが入っていないドライブを開くことができます。 それではあなたは
デバイス固有の問題を発行する ioctl(2) それが一体何なのか理解するまでリクエストしてください。 (ない
これが POSIX であれば確かにそうですが、BSD や Solaris でもそのように動作するようです。
  Wodim(1) 出典。)

文脈内での「ディスク」と「ディスク」の使用法が異なることにも注意してください。 CD規格の起源
フランスでは、その他のものにはすべて「k」が付きます。

フォールト、 悪い 住所
ポインター引数を取るシステムコールはすべて EFAULT を返す可能性があります。 libexplain ライブラリ
どの引数に問題があるかを判断でき、プロセスを妨げることなくそれを実行します。
(またはスレッド) シグナル処理。

利用可能な場合は、 ミンコア(2) システムコールを使用して、メモリ領域が有効かどうかを問い合わせます。
マップされているが物理メモリにない、マップされているが物理メモリにあるという XNUMX つの結果が返されます。
メモリにあり、マッピングされていません。 ポインタの有効性をテストする場合、最初の XNUMX つは「はい」です
そして最後は「いいえ」です。

C 文字列のチェックはさらに困難です。ポインタとサイズの代わりに、
ポインタを持っています。 サイズを決定するには、NUL を見つける必要があります。
セグメンテーション違反、キャッチ 22。

これを回避するために、libexplain ライブラリは lstat(2) システムコール (既知の
XNUMX 番目の引数が適切です) を使用して C 文字列の有効性をテストします。 失敗が返される && errno == EFAULT
は「いいえ」、それ以外は「はい」です。 もちろん、これにより文字列は PATH_MAX に制限されます
文字が含まれていますが、libexplain ライブラリにとっては通常は問題ではありません。
ほとんどの場合、対象となるのは最長の文字列です。

EMFILE、 あまりに 多くの 開いた ファイル
このエラーは、プロセスがすでに最大数のファイル記述子を開いている場合に発生します。
実際の制限を出力する場合、libexplain ライブラリが出力しようとしても、開くことはできません。
のファイル / proc それが何なのかを読んでください。
open_max = sysconf(_SC_OPEN_MAX);
これはそれほど難しくありません。 sysconf(3) 限度額の求め方。

エンファイル、 あまりに 多くの 開いた ファイル in  
このエラーは、開いているファイルの総数がシステム制限に達している場合に発生します。
に達しました。 この場合、便利なものはありません sysconf(3) 限界値の求め方。

さらに深く掘り下げると、Linux には次のような機能があることがわかるかもしれません。 / proc 読み取れるエントリ
この値を取得します。 キャッチ-22: ファイル記述子が不足しているため、ファイルを開くことができません
限界を読む。

Linux ではそれを取得するためのシステムコールがありますが、[e]glibc ラッパー関数がないため、
すべてを非常に慎重に行う必要があります。
長い
Explain_maxfile(void)
{
#ifdef __linux__
struct __sysctl_args 引数;
int32_t 最大ファイル;
サイズ_t 最大ファイルサイズ = のサイズ(最大ファイル);
int 名[] = { CTL_FS, FS_MAXFILE };
memset(&args, 0, sizeof(struct __sysctl_args));
args.name = 名前;
args.nlen = 2;
args.oldval = &maxfile;
args.oldlenp = &maxfile_size;
if (syscall(SYS__sysctl, &args) >= 0)
最大ファイルを返します。
#endif
-1を返します。
}
これにより、利用可能な場合、制限をエラー メッセージに含めることができます。

アインヴァル "無効 口論" vs エノシス "関数 実装されました」
サポートされていないアクション ( シンボリックリンク(2) FAT ファイル システム上) は報告されません
あるシステムコールから次のシステムコールまで一貫して実行されます。 EINVAL または
ENOSYSが戻ってきました。

結果として、これらのエラーのケースを正しく処理するには、特に注意を払う必要があります。
EINVAL は XNUMX つ以上のシステム コール引数の問題を参照している可能性もあるためです。

Note それ エラー(3) is 常に セッションに
場合によっては、[e]glibc ソースを読んで、その方法と方法を判断する必要がある場合があります。
一部のシステムコールでエラーが返されたとき。

フェオフ(3) ファイル番号(3)
多くの場合、これらの関数はエラーを返すことができないと考えられています。 これは次の場合にのみ当てはまります
  流れ 引数は有効ですが、無効な引数を検出できます。
ポインター。

fpathconf(3) パス設定(3)
の戻り値 fpathconf(2)と パス設定(2) は正当に -1 である可能性があるため、次のようになります。
かどうかを確認するために必要です エラー(3)は明示的に設定されています。

ioctl(2)
の戻り値 ioctl(2) は正当に -1 である可能性があるため、次のことがあるかどうかを確認する必要があります。
エラー(3)は明示的に設定されています。

読み取り(3)
の戻り値 読み取り(3) は、エラーとファイルの終わりの両方で NULL です。 それは
かどうかを確認するために必要です エラー(3)は明示的に設定されています。

セットバッファ(3) セットバッファ(3) セットラインブフ(3) setvbuf(3)
これらの関数は最後を除いてすべて void を返します。 そして setvbuf(3) は次のようにのみ文書化されています。
エラー時には「非ゼロ」を返します。 かどうかを確認する必要があります。 エラー(3) は明示的に行われています
設定します。

ストロッド(3) ストトル(3) 言われた(3) ストトール(3) ストラトゥール(3) ストトール(3)
これらの関数はエラー時に 0 を返しますが、これも正当な戻り値です。 それは
かどうかを確認するために必要です エラー(3)は明示的に設定されています。

取得しない(3)
ANSI C 標準ではバックアップの XNUMX 文字だけが義務付けられていますが、
[e]glibc ではさらに多くのことが許可されています...しかし、それは ENOMEM では失敗する可能性があることを意味します。 できる
次の場合は EBADF でも失敗します fp 偽物です。 最も難しいのは、EOF にエラーが発生した場合です。
return は発生しますが、errno は設定されていません。

libexplain ライブラリは、これらのエラーをすべて正しく検出します。
エラー値は、たとえあったとしても十分に文書化されていません。

ENOSPC、 いいえ スペース on デバイス
このエラーがファイル システム上のファイルを参照している場合、libexplain ライブラリはマウントを出力します。
問題のあるファイルシステムのポイント。 これにより、エラーの原因が大きくなる可能性があります
より明確に。
write(fildes = 1 "example"、data = 0xbfff2340、data_size = 5) が失敗しました、スペースが残っていません
ファイル システムに fildes (" が含まれているため、デバイス (28、ENOSPC) 上で/ホーム") ありません
データのためのより多くのスペース
さらに特殊なデバイスのサポートが追加されると、エラー メッセージにはデバイスに関する情報が含まれることが予想されます。
デバイスの名前と実際のサイズ。

エロフス 読み取り専用 file  
このエラーがファイル システム上のファイルを参照している場合、libexplain ライブラリはマウントを出力します。
問題のあるファイルシステムのポイント。 これにより、エラーの原因が大きくなる可能性があります
より明確に。

さらに特殊なデバイスのサポートが追加されると、エラー メッセージにはデバイスに関する情報が含まれることが予想されます。
名前と種類。
open(pathname = "/dev/fd0"、O_RDWR、0666) が失敗しました、読み取り専用ファイル システム (30、EROFS)
フロッピーディスクには書き込み禁止タブが設定されているため、

...CD-ROM が書き込みできないため
...メモリカードに書き込み禁止タブが設定されているため
...XNUMX/XNUMX インチ磁気テープには書き込みリングがないため

リネーム
  リネーム(2) システムコールは、ファイルの場所や名前を変更し、移動するために使用されます。
必要に応じてディレクトリ間を移動します。 宛先パス名がすでに存在する場合は、次のようになります。
アトミックに置き換えられるため、別のプロセスが置き換えを試みることはありません。
アクセスすると、見つからないことがわかります。

ただし、制限があります。ディレクトリの名前を変更できるのは、別のディレクトリの上にある場合のみです。
宛先ディレクトリが空でない場合はディレクトリ。
rename(oldpath = "foo", newpath = "bar") が失敗しました。ディレクトリが空ではありません (39,
ENOTEMPTY) newpath は空のディレクトリではないためです。 つまり、エントリが含まれています
「。」以外そして ".."
ディレクトリ以外のディレクトリの上にあるディレクトリの名前を変更することもできません。
rename(oldpath = "foo", newpath = "bar") が失敗しました。ディレクトリではありません (20、ENOTDIR)
oldpath はディレクトリですが、newpath はディレクトリではなく通常のファイルであるためです。
またその逆も許されない
rename(oldpath = "foo", newpath = "bar") が失敗しました。ディレクトリ (21、EISDIR) です。
newpath はディレクトリですが、oldpath はディレクトリではなく通常のファイルであるためです。

もちろん、これにより libexplain ライブラリの作業がより複雑になります。
リンク解除(2)または rmdir(2) システムコールは暗黙的に呼び出されます。 リネーム(2)、つまりすべて
リンク解除(2)または rmdir(2) エラーも検出して処理する必要があります。

dup2
  dup2(2) システムコールは、
最初のファイル記述子と同じオブジェクト。 通常、これはシェル入力を実装するために使用されます。
そして出力のリダイレクト。

面白いのは、それと同じように、 リネーム(2) ファイルの名前をアトミックに変更できます。
既存のファイルを削除し、古いファイルを削除します。 dup2(2) すでに開いているファイルに対してこれを行うことができます
ディスクリプタ。

もう一度言いますが、これにより libexplain ライブラリの作業がより複雑になります。 閉じる(2)
システムコールは暗黙的に呼び出されます dup2(2) など、すべて 閉じる(2) のエラーは次のとおりです。
検出および処理も行います。

冒険 IN IOCTL サポート


  ioctl(2) システム コールは、デバイス ドライバーの作成者に、デバイス ドライバーと通信する方法を提供します。
既存のカーネル API 内に収まらないユーザー空間。 見る ioctl_listとします。

デコード リクエスト 数字
ざっと見てみると、 ioctl(2) インターフェイス、大規模だが有限であるように見えます。
可能な数 ioctl(2) リクエスト。 それぞれ違う ioctl(2) リクエストは有効です
別のシステムコールですが、型安全性がまったくありません - コンパイラは、
プログラマーはこれらを正しく理解してください。 おそらくこれが背後にある動機でした tcflush(3)と
友人。

最初の印象は、デコードできるということです ioctl(2) 巨大なスイッチを使用したリクエスト
声明。 これは実行不可能であることが判明します。
さまざまなヘッダーを定義する必要なシステム ヘッダーをすべて含めることは不可能です。 ioctl(2)
なぜなら、彼らはお互いに仲良く遊ぶのが難しいからです。

さらに詳しく調べると、さまざまな「プライベート」リクエスト番号とデバイスがあることがわかります。
ドライバーの作成者はこれらを使用することをお勧めします。 これは、はるかに大きな可能性があることを意味します
曖昧なリクエスト番号を持つ一連のリクエストは、すぐにわかります。 また、
歴史的に曖昧な点もいくつかあります。

この切り替えが非現実的であることはすでにわかっていましたが、今では、
適切なリクエスト名と説明を考慮する必要があります。リクエスト番号だけでなく、
ファイル記述子も。

の実装 ioctl(2) libexplain ライブラリ内でのサポートには、次のテーブルがあります。
へのポインター ioctl(2) リクエスト記述子。 これらの各記述子には、オプションの
曖昧さ回避関数へのポインタ。

各リクエストは実際には個別のソース ファイルに実装されるため、必要な
インクルード ファイルは、他のファイルとうまく連携する義務から解放されます。

表現
libexplain ライブラリの背後にある哲学は、できるだけ多くの情報を提供することです。
システムコールの正確な表現を含めて、可能です。 の場合
ioctl(2) これは、正しいリクエスト番号 (名前による) と、正しい (または
少なくとも有用) XNUMX 番目の引数の表現。

  ioctl(2) プロトタイプは次のようになります。
int ioctl(int fildes, int request, ...);
これにより、タイプセーフティアラームが鳴るはずです。 [e]glibc の内部で、これは変更されます
さまざまな形に:
int __ioctl(int fildes、int request、long arg);
int __ioctl(int fildes, int request, void *arg);
Linux カーネルのシステムコール インターフェイスは次のことを期待しています。
asmlinkage long sys_ioctl(unsigned int fildes、unsigned int リクエスト、unsigned long
引数);
libexplain ライブラリを使用する場合、XNUMX 番目の引数の極端な変動性が課題になります。
XNUMX 番目の引数の表現を出力しようとします。 ただし、リクエスト番号が
曖昧さが解消され、libexplain ライブラリの ioctl テーブルの各エントリには
カスタム print_data 関数 (OO は手動で行われます)。

説明
使用する説明を決定する問題が少なくなります。 リクエスト番号を取得したら
曖昧さが解消され、libexplain ライブラリの ioctl テーブルの各エントリにはカスタムの
print_explanation 関数 (繰り返しになりますが、OO は手動で行われます)。

セクション 2 およびセクション 3 のシステム コールとは異なり、ほとんどのシステム コールは ioctl(2) リクエストにエラーがない
文書化されています。 つまり、エラーを適切に説明するには、カーネルを読み取る必要があります。
発見する情報源

・ 何 エラー(3) 値が返される場合があります。

· 各エラーの原因。

カーネル内での関数呼び出しディスパッチの OO の性質のため、以下を読む必要があります。
それを実装しているソース ioctl(2) 一般的な実装だけでなく、リクエストも必要です。 それ
カーネルが異なればエラー番号も異なり、微妙にエラー番号が異なることが予想されます。
エラーの原因が異なります。

アインヴァル vs イノッティ
状況はさらに悪化している ioctl(2) システムコールよりもリクエスト、EINVAL および
ENOTTY は両方とも、 ioctl(2) 以下の点で不適切な要求
コンテキスト、および場合によっては ENOSYS、ENOTSUP、および EOPNOTSUPP (ソケットに使用されることを意図した)
良い。 Linux カーネル ソースには、進歩的なものであると思われるコメントがあります。
クリーンアップが進行中です。 さらに混乱を招くために、BSD では ENOIOCTL が追加されています。

結果として、これらのエラーのケースを正しく処理するには、特に注意を払う必要があります。
EINVAL は XNUMX つ以上のシステム コール引数の問題を参照している可能性もあるためです。

intptr_t
C99 標準では、任意のポインターを保持できることが保証される整数型が定義されています。
代表権を失うことなく。

上記の関数 syscall プロトタイプをより適切に記述することができます。
long sys_ioctl(unsigned int fildes、unsigned int リクエスト、intptr_t arg);
問題は、デバイス固有またはファイル システム固有によって引き起こされる認知的不協和です
ioctl(2) 次のような実装:
long vfs_ioctl(構造体ファイル *filp、unsigned int cmd、unsigned long arg);
大多数の ioctl(2) リクエストには実際には int *arg の XNUMX 番目の引数があります。 しかし、それを持っている
long と宣言すると、これをlong *argとして扱うコードが生成されます。 これは 32 ビットでは無害です
(sizeof(long) == sizeof(int)) ですが、64 ビットでは厄介です (sizeof(long) != sizeof(int))。
エンディアンに応じて、期待した値が得られるか得られないかが決まりますが、 常に 取得する
メモリスクリブルまたはスタックスクリブルも同様です。

これらすべてを次のように書くと、
int ioctl(int fildes, int request, ...);
int __ioctl(int fildes, int request, intptr_t arg);
long sys_ioctl(unsigned int fildes、unsigned int リクエスト、intptr_t arg);
long vfs_ioctl(構造体ファイル *filp、unsigned int cmd、intptr_t arg);
整数は、ほぼ次の量を表す整数にすぎないことを強調します。
常に無関係なポインタ型です。

結論


libexplain を使用すると、ユーザーはそれを気に入るはずです。

COPYRIGHT


libexplain バージョン 1.4
Copyright (C) 2008, 2009, 2010, 2011, 2012, 2013, 2014 ピーター・ミラー

onworks.net サービスを使用してオンラインで Explain_lca2010 を使用する


無料のサーバーとワークステーション

Windows と Linux のアプリをダウンロード

Linuxコマンド

Ad