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の拡張機能について慣れておいた方が、なにかと躓かないでいいかと思います。






