Linux字符驅動中container_of宏的作用
首先看看這個宏的原型:
/** * container_of - cast a member of a structure out to the containing structure * @ptr: the pointer to the member. * @type: the type of the container struct this is embedded in. * @member: the name of the member within the struct. * */ #define container_of(ptr, type, member) ({ \ const typeof( ((type *)0)->member ) *__mptr = (ptr); \ (type *)( (char *)__mptr - offsetof(type,member) );})功能:根據一個結構體變量中的一個成員變量的指針來獲取指向整個結構體變量的指針。
參數:
ptr:已知的結構體成員的首地址(指針);
type:要獲取的結構體變量的類型
member:要獲取的結構體變量中成員的名字,而不是類型
返回值:要獲取的結構體變量的首地址
驅動中常用的用法,將每一個設備對應的cdev結構體和與之對應的私有成員用一個結構體封裝起來:
struct hello_device { struct cdev cdev; char data [128]; }hello_device[NUM_OF_DEV];其中NUM_OF_DEV表示次設備的數量,假設NUM_OF_DEV為2,那麼:hello_device[0]對應 設備0
hello_device[1]對應 設備1
int hello_open(struct inode * inode,struct file * file) { struct hello_device * dev = container_of(inode->i_cdev,struct hello_device,cdev); file->private_data = dev;
... }在驅動的open函數中用container_of通過inode結構體的成員i_cdev(指向hello_device中的cdev)獲取設備結構體(hello_device)的首地址,然後將獲取到的hello_device結構體地址放入file結構體中在read、write函數之間傳遞。當我們成功insmod一個設備驅動的時候,我們會通過mknod創建一個設備節點和一個具體的設備(驅動)關聯,這個節點就對應這個inode結構體的一個實例,這個實例有一個struct cdev類型的字段i_cdev,它會指向hello_device中的cdev結構體(初始化cdev時候完成的)。最終獲取到hello_device結構體的首地址,其cdev已經被實例化,對應一個具體的設備。
所以我們可以根據dev->cdev.dev就可以獲得當前這個設備的設備號。然後包含具體設備信息的dev結構體就可以通過file中的private_data成員在open,read,write,ioctl之間傳遞。
ssize_t hello_read(struct file * file,char __user * buff,size_t count,loff_t * off) { struct hello_device * dev = file->private_data; //dev指向打開的設備結構體 copy_to_user(buff,dev->data,count);
... }
在read函數中,首先根據傳進來的file結構體,接收open函數傳遞過來的打開的具體設備的hello_device結構體,然後就可以根據這個hello_device結構體操作這個具體的設備了。