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 でもサポートされています。