copy_from_user
Rev.1を表示中。最新版はこちら。
copy_from_user()の実装は、アーキテクチャに依存していて、ここではasm-genericのそれを見てみます。アーキテクチャに依存したcopy_from_user()が定義されてないと、ここでのcopy_from_user()が使われます。copy_from_user()はシステムコール時、ユーザ空間の引数をカーネル空間に複写するのに使われます。直接ユーザ空間のデータをカーネルが参照していて、タスクが切り替わり、再び先のパスに戻ってきても、その時のユーザ空間は先のユーザ空間と同じではありません。カーネルは任意のプロセスのメモリー空間で動作しているからです。ただしカーネル空間は共通で、そのためユーザ空間のデータをカーネル空間に複写したのち、そのデータを参照しなければなりません。
__copy_from_user()で、引数に応じた適切なコードを取捨選択するため、copy_from_user()はstatic inlineで、インライン展開の実装となります。
static inline long copy_from_user(void *to, const void __user * from, unsigned long n) { might_sleep(); if (access_ok(VERIFY_READ, from, n)) return __copy_from_user(to, from, n); else return n; }__builtin_constant_p()はgccの内部関数(コードとして展開されません。)で、引数が定数なら1を返します。従って、gccはnに応じた適切なコード取捨選択して展開することができます。
#define __must_check __attribute__((warn_unused_result)) static inline __must_check long __copy_from_user(void *to, const void __user * from, unsigned long n) { if (__builtin_constant_p(n)) { switch(n) { case 1: *(u8 *)to = *(u8 __force *)from; return 0; case 2: *(u16 *)to = *(u16 __force *)from; return 0; case 4: *(u32 *)to = *(u32 __force *)from; return 0; #ifdef CONFIG_64BIT case 8: *(u64 *)to = *(u64 __force *)from; return 0; #endif default: break; } } memcpy(to, (const void __force *)from, n); return 0; }copy_from_user(to, from, 1)でコールすると、以下のソースをコンパイルするのと同等で、変数間の代入となります。nが2/4/8についても同じです。
static inline __must_check long __copy_from_user(void *to, const void __user * from, unsigned long n) { *(u8 *)to = *(u8 __force *)from; return 0; }copy_from_user(to, from, n)でコールすると、以下のソースをコンパイルするのと同等で、memcpy()をコールします。
static inline __must_check long __copy_from_user(void *to, const void __user * from, unsigned long n) { memcpy(to, (const void __force *)from, n); return 0; }__must_checkは返り値をチェックしなければwarningを表示させる設定ですが、__copy_from_user()は0しか返してません。意味あるのかな?
p/s
カーネルはgccの拡張機能をフルに活用して実装されているようで、カーネル読むに当たって、カーネルの本質的な物でないのですが、GCCの拡張機能について慣れておいた方が、なにかと躓かないでいいかと思います。