web-dev-qa-db-ja.com

ポインターを整数にキャストする動機は何ですか?

Linuxカーネルコードでいくつかの変更を行っていますが、ポインタが整数にキャストされていることに気づきました。

以下のbufをチェックしてください( 完全なコード ):

snd_pcm_sframes_t snd_pcm_lib_read(struct snd_pcm_substream *substream, void __user *buf, snd_pcm_uframes_t size)
{
    ....
    return snd_pcm_lib_read1(substream, (unsigned long)buf, size, nonblock, snd_pcm_lib_read_transfer);
}

void __user *bufから(unsigned long)buf。その後のコードでは、bufはポインタとしてではなくlongとして扱われます。

技術的なレベルではどちらもメモリ内の数値ですが、これまでのところ、概念的には別のものとして認識しています。これを使用するときに低レベルのパターンはありますか?

いくつかのポインターをintptr_t<stdint.h>標準C99またはC11ヘッダーから)にキャストする一般的な使用例(ホストされているユーザーコードアプリケーション)は、そのポインターのハッシュコードを計算することです。

uint32_t hash_of_foo_ptr(struct foo_st *foo) {
   return (uint32_t)(((intptr_t)foo) * 613) ^ ((intptr_t)foo % 51043));
}

歴史的な理由により(つまり、LinuxはC99より前に存在しました)、Linuxカーネルはunsigned long(ポインタと同じsizeofの符号なし整数型)またはuintptr_tではなくintptr_tを使用します。

また、ユーザー空間からカーネル空間への送信(たとえば、syscallsの引数)は、Linuxカーネルではlongまたはunsigned longの観点からあります(ここでも、intptr_tまたはuintptr_t)。ユーザー空間からカーネル空間にアドレスを送信する場合でも、アドレス空間と仮想メモリについて考える必要があります(ユーザー空間とカーネルコードが異なるアドレス空間に存在するため、複雑になってきています)。

LDD からのトピックの引用:

概念的にはアドレスはポインタですが、メモリ管理は多くの場合、符号なし整数型を使用することでより適切に実行されます。カーネルは物理メモリを巨大な配列のように扱い、メモリアドレスは配列への単なるインデックスです。さらに、ポインターは簡単に逆参照されます。メモリアドレスを直接扱う場合、この方法でそれらを逆参照することはほとんどありません。整数型を使用すると、この逆参照が防止され、バグが回避されます。したがって、カーネル内の汎用メモリアドレスは通常unsigned longであり、少なくともLinuxで現在サポートされているすべてのプラットフォームで、ポインタと長整数は常に同じサイズであるという事実を利用しています。

Linuxカーネルは、ホストされている学術的に標準のポータブルC99でコーディングされていないことに注意してください。これは、いくつかの特にCコンパイラを念頭に置いてコード化された独立したプログラムです( [〜#〜] gcc [〜#〜] そしておそらく将来的にもClang/LLVM ...).

Linuxカーネルは GCC拡張 をCに使用しています(__attribute__- s、組み込み、計算されたgoto-sなど...)。おそらくマクロでラップされています。これらの拡張機能のほとんどは Clang/LLVM でもサポートされています。

カーネル初心者lkml はおそらくそれについて尋ねるのに良い場所です。