関数からエラーコードを返す1つのトリックについて

Linuxカーネルは、適用されたアルゴリズムと、メモリのサイズ(メモリフットプリント)を高速化および/または削減するために設計されたハッカーまたはハーフハッカーのトリックの両方の倉庫です。 これらのハーフハッカーのトリックの1つについてさらに説明したいと思います。





私たちの惨めな世界では、C言語自体には参照とクラスは存在しませんが、ポインターと構造があります。 呼び出し元に生きているオブジェクトへのポインタを返す必要がある関数の戻り値として何を選択するのかという疑問がしばしば発生します。 少なくとも2つのプロトタイプオプションがあります。



struct obj *get_obj(...);
      
      





そして

 void get_obj(..., struct obj**);
      
      







ここでは効果がないように見えますが、なぜ2番目の方法を強調したのですか? そして、ここにあります。 最初は、オブジェクトへのポインタ、または見つからない場合はNULLを返すことができます。 ただし、プログラマには疑問があります。メソッド内の例外はどうでしょうか。 はい、はい、C言語のWild Westでは、戻りコードを使用します。 そして、ここにそのような写真があります( 上記のプロトタイプを参照して比較してください):

 struct obj *get_obj(..., int *ret); int get_obj(..., struct obj **);
      
      







しかし、現在のタスクは、関数の引数の数を減らすことです。これは、実行速度とスタックで消費されるメモリ量の両方に大きく影響するためです(OSカーネルについてですよね?:-))。



このため、そのようなソリューションが発明されました。 void *は依然として数値なので、可能な値を3つのセグメントに分割します(32ビットの場合を考慮してください)。



最初はNULLポインター、2番目は通常のアドレススペース、3番目は無効なポインターまたはエラーコードです。 したがって、検証のためにいくつかのマクロが登場しました。

 ZERO_OR_NULL_PTR(); IS_ERR(); IS_ERR_OR_NULL();
      
      





そして人々はそれらを使い始めました!



結果のプロトタイプは次のようになりました。

 struct obj *get_obj(...);
      
      





そして使用例:

 struct obj *get_obj(...) { struct obj *obj = malloc(sizeof(*obj)); int ret; if (!obj) return ERR_PTR(-ENOMEM); ret = do_smth(obj, ...); if (ret) { free(obj); return ERR_PTR(ret); } return obj; } int whatever(...) { struct obj *obj = get_obj(...); if (IS_ERR(obj)) return PTR_ERR(obj); printf("Cool object %p\n", obj); return 0; }
      
      





ただし、たとえば、このカーネルアセンブリでオフになっているカーネルの一部の機能をオブジェクトが担当している場合など、NULLが有効なリターンであるコードの部分に対してIS_ERR_OR_NULLマクロを誤って使用するため、このアプローチには問題もあります。 次に、NULLポインターが返されますが、これは別の方法で処理する必要があります!



All Articles