在學習Linux驅動的過程當中,遇到一個宏叫作container_of。該宏定義在include/linux/kernel.h中,首先來貼出它的代碼:
node
/** * 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) );})它的做用顯而易見,那就是 根據一個結構體變量中的一個域成員變量的指針來獲取指向整個結構體變量的指針 。好比,有一個結構體變量,其定義以下:
struct demo_struct { type1 member1; type2 member2; type3 member3; type4 member4; }; struct demo_struct demo;同時,在另外一個地方,得到了變量demo中的某一個域成員變量的指針,好比:
type3 *memp = get_member_pointer_from_somewhere();此時,若是須要獲取指向整個結構體變量的指針,而不單單只是其某一個域成員變量的指針,咱們就能夠這麼作:
struct demo_struct *demop = container_of(memp, struct demo_struct, member3);這樣,咱們就經過一個結構體變量的一個域成員變量的指針得到了整個結構體變量的指針。
下面說一說我對於這個container_of的實現的理解:
首先,咱們將container_of(memp, struct demo_struct, type3)根據宏的定義進行展開以下:
linux
struct demo_struct *demop = ({ \ const typeof( ((struct demo_struct *)0)->member3 ) *__mptr = (memp); \ (struct demo_struct *)( (char *)__mptr - offsetof(struct demo_struct, member3) );})其 中,typeof是GNU C對標準C的擴展, 它的做用是根據變量獲取變量的類型。所以,上述代碼中的第2行的做用是首先使用typeof獲取結構體域變量member3的類型爲 type3,而後定義了一個type3指針類型的臨時變量__mptr,並將實際結構體變量中的域變量的指針memp的值賦給臨時變量__mptr。
則,在執行了上述代碼的第2行以後__mptr的值即爲0xA010。 網絡
再看上述代碼的第3行,其中須要說明的是offsetof,它定義在include/linux/stddef.h中,其定義以下:
函數
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)先分析一下這個宏的運行機理( 一共4步):
一樣,咱們將上述的offsetof調用展開,即爲:
學習
(struct demo_struct *)( (char *)__mptr - ((size_t) &((struct demo_struct *)0)->member3) );可見,offsetof的實現原理如上所述, 就是取結構體中的域成員相對於地址0的偏移地址,也就是域成員變量相對於結構體變量首地址的偏移。
因 此,offsetof(struct demo_struct, member3)調用返回的值就是member3相對於demo變量的偏移。結合上述給出的變量地址分佈圖可知,offsetof(struct demo_struct, member3)將返回0x10。 this
因而,由上述分析可知,此時,__mptr==0xA010,offsetof(struct demo_struct, member3)==0x10。
所以, (char *)__mptr - ((size_t) &((struct demo_struct *)0)->member3) == 0xA010 - 0x10 == 0xA000,也就是結構體變量demo的首地址(如上圖所示)。 spa
這就是從結構體某成員變量指針來求出該結構體的首指針。指針類型從結構體某成員變量類型轉換爲該結構體類型。 指針
由此,container_of實現了根據一個結構體變量中的一個域成員變量的指針來獲取指向整個結構體變量的指針的功能。
以上內容載自網絡,這篇文章分析的很透徹,順便說一下,宋寶華的《linux設備驅動開發詳解》P132 最後一行當中對該宏的參數解釋是錯誤的!固然了,暇不掩瑜! code
如下是我本身的一些理解: 對象
首先,我定義了一個字符設備結構體
struct globalmem_dev { struct cdev my_cdev; //字符設備之基礎結構體 unsigned char mem[GLOBALMEM_SIZE]; struct semaphore sem;/ };接下來我實例化了一個該設備的指針對象
struct globalmem_dev *pdev;後來在open函數中我是這麼來用的
//關於filp的產生和消亡參見《驅動詳解》P92 int globalmem_open(struct inode *inode, struct file *filp) { struct globalmem_dev *pdev; printk("\nFunction globalmem_open Invoked\n"); pdev = container_of(inode->i_cdev, struct globalmem_dev, my_cdev); filp->private_data = pdev; if(down_trylock(&pdev->sem))//得到信號量 return -EBUSY; return 0; }