container_ofマクロ
Rev.1を表示中。最新版はこちら。
container_ofマクロは、オブジェクトが汎用的な属性と、個々オブジェクトに特化して管理される属性を有する場合で、汎用的属性を特化属性のメンバーとし、それぞれの別の構造体で管理させるも、汎用オブジェクト属性から個別オブジェクト属性を参照可能とするマクロです。代表的な運用としてオブジェクトをリスト管理するリストノードとして使われます。下記サンプルは、procファイルのinodeからファイルを有するプロセスID等を取得する、struct proc_inodeのカーネル実装イメージのサンプルです。
container.c
#include <stdio.h> #include <stddef.h> #define container_of(ptr, type, member) ({ \ const typeof( ((type *)0)->member ) *__mptr = (ptr); \ (type *)( (char *)__mptr - offsetof(type,member) );}) struct inode { int fd; }; struct proc_inode { int pid; struct inode vfs_inode; }; void main() { struct proc_inode proc_inode; struct inode *inode; inode = &proc_inode.vfs_inode; inode->fd=123; proc_inode.pid=456; get_pid(inode); } int get_pid(struct inode *inode) { printf(" file ID:%d\n", inode->fd); printf("process ID:%d\n", container_of(inode, struct proc_inode, vfs_inode)->pid); return 0; }実効結果
[root@localhost test]# ./a.out file ID:123 process ID:456
補足
サンプルのcontainer_ofマクロは以下の様に展開されます。typeof()は引数のstructを定義し、従って、*__mptrは(struct proc_inode *)0)->vfs_inodeの型であるstruct inodeのポインタ変数という事です。そのアドレスからstruct proc_inodeのメンバーとしてのvfs_inodeのstruct proc_inode内の位置を差し引く事で、proc_inodeのアドレスを取得します。const typeof( ((struct proc_inode *)0)->vfs_inode ) *__mptr = (inode); (struct proc_inode *)( (char *)__mptr - offsetof (struct proc_inode, vfs_inode)
inodeからproc_inodeを取得は、PROC_I()でcontainer_ofマクロを展開するだけです。
struct proc_inode { struct pid *pid; int fd; union proc_op op; struct proc_dir_entry *pde; struct ctl_table_header *sysctl; struct ctl_table *sysctl_entry; void *ns; const struct proc_ns_operations *ns_ops; struct inode vfs_inode; }; static inline struct proc_inode *PROC_I(const struct inode *inode) { return container_of(inode, struct proc_inode, vfs_inode); }