container_of という大変便利なマクロ関数の存在を知った
今更ながら、include/linux/kernel.h container_of が相当使えるマクロ関数だと気づきました.
一見わかりにくいのですが、なんと「変数が入っている構造体へのポインタ」をコンパイル時に計算してくれるのです.
container_of を用いることで、API に縛られず、データのひも付けを行うことができます. 以下のような感じです*1:
struct callback_timer{ struct hrtimer timer; void (*callback)(unsigned long data); unsigned long data; int index; } struct callback_timer cb_timer[128]; // タイマが点火したときに呼ばれる関数. enum hrtimer_restart handle_hrtimer (struct hrtimer* hrt) { struct callback_timer *ct; // hrtimer へのポインタから、callback_timer の構造体へのポインタを逆算 ct = container_of(hrt, struct callback_timer, timer); if( ct->ballback ){ ct->callback(ct->data); } return HRTIMER_RESTART; } // コールバック関数 void test_fn(unsigned long data) { printk("handled! data %lu\n",data); } void start_timer(int i, unsigned int n) { hrtimer_init( &cb_timer[i]->timer, CLOCK_REALTIME, HRTIMER_MOD_ABS ); // コールバック関数の登録 cb_timer[i].callback = handle_hrtimer; // せっかくなので何番目か覚えておく cb_timer[i].data = i; // n sec 後にタイマを点火 hrtimer_start( &cb_timer[i]->timer, ktime_add( ktime_get_real(), sec), HRTIMER_MOD_ABS); ... }
この例だと、handle_hrtimer の中で hrt をメンバとして持つ callback_timer のアドレスを、container_ofを求めています. hrtimer は、add_timerのようにデータを用いてコールバック関数を登録することができないので、container_of を用いてデータを引っ張ってくることが多いみたいです*2.
これはとても便利ですね:D
定義
container_ofの定義は以下のようになっています. コンパイル時計算の威力はすさまじいです...!
include/linux/kernel.h 436 /** 437 * container_of - cast a member of a structure out to the containing structure 438 * @ptr: the pointer to the member. 439 * @type: the type of the container struct this is embedded in. 440 * @member: the name of the member within the struct. 441 * 442 */ 443 #define container_of(ptr, type, member) ({ \ 444 const typeof( ((type *)0)->member ) *__mptr = (ptr); \ 445 (type *)( (char *)__mptr - offsetof(type,member) );})
include/linux/stddef.h 21 #ifdef __compiler_offsetof 22 #define offsetof(TYPE,MEMBER) __compiler_offsetof(TYPE,MEMBER) 23 #else 24 #define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER) 25 #endif
上記の例の場合だと、以下のように展開されます.:
({const typeof ( ((struct hrtimer*)0)->timer ) *__mptr = (hrt); \ (type *)( (char *) __mptr - offsetof(struct callback_timer,hrt) );})
手順としては、
- hrt のアドレスを求めて
- hrt のまでのオフセット分だけ引く.
という感じですね.