日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > linux >内容正文

linux

linux 内核宏container_of剖析

發布時間:2023/12/20 linux 19 豆豆
生活随笔 收集整理的這篇文章主要介紹了 linux 内核宏container_of剖析 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.


1、前面說的

我在好幾年前讀linux 驅動代碼的時候看到這個宏,百度了好久,知道怎么用了,但是對實現過程和原理還是一知半解。

container_of宏 在linux內核代碼里面使用次數非常非常多,對于喜歡linux編程的同學來說,了解其實現方法,對以后看內核代碼,寫內核驅動的幫助都非常大,當然,我不是說了解這個就可以為所欲為了,內核博大精深,先宏觀再微觀去學習,不積跬步何以致千里,不要想著一口就能吃成一個胖子,我這篇文章主要剖析一下這個函數的實現原理,希望對大家學習過程中有所幫助。

android7.1/kernel/drivers/input
kernel/drivers/input$?grep?-rn?container_of?./|wc?-l
710
android7.1/kernel/drivers/input$?

使用grep -rn container_of ./|wc -l統計了下kernel/drivers/input/目錄下的container_of出現的次數,一共有710次使用。

2、container_of的作用

container_of的作用的通過結構體成員變量地址獲取這個結構體的地址,假設你的名字叫李光明,你還有一個弟弟叫做XXX,警察叔叔發現你弟弟XXX干了一件壞事,但是警察叔叔不知道你弟弟的名字,抓你來審問,你嘴巴很硬就是不說,警察叔叔就拿到你的名字,查到了你家的戶口本,這下你弟弟就被查出來了,原來你弟弟XXX的名字叫做李小明。這種破案手法叫做順藤摸瓜

內核函數調用常常給函數傳入的是結構體成員地址,然后在函數里面又想使用這個結構體里面的其他成員變量,所以就引發了這樣的問題,我認為這個也是用C實現面向對象編程的一種方法。
比如這段代碼

static?void?sensor_suspend(struct?early_suspend?*h)???????????????????
{?????????????????????????????????????????????????????????????????????
????struct?sensor_private_data?*sensor?=??????????????????????????????
????????????container_of(h,?struct?sensor_private_data,?early_suspend);???
????if?(sensor->ops->suspend)?????????????????????????????????????????
????????sensor->ops->suspend(sensor->client);?????????????????????????
}?

early_suspend是sensor_private_data 里面的一個成員,通過這個成員的地址獲取sensor_private_data結構體變量的地址,從而調用里面的成員變量client。這個方法非常優雅。在這里我用到了一個比較叼的詞,叫“優雅”
這里簡單說下,傳進來的h一定在其他地方定義并且操作系統分配了內存空間,h分配了空間,說明他的老爸也有內存了,要不然你順藤摸瓜摸到一個NULL就傻逼了。

3、如何使用container_of

container_of需要傳入三個參數,第一個參數是一個指針,第二個參數是結構體類型,第三個是對應第二個參數里面的結構體里面的成員。

container_of(ptr, type, member)

  • ptr:表示結構體中member的地址 h

  • type:表示結構體類型 struct sensor_private_data

  • member:表示結構體中的成員 early_suspend type里面一定要有這個成員,不能瞎搞啊

  • 返回結構體的首地址

4、container_of 用到的知識點 剖析

4.1、({})的作用

({})、第一個先說這個表達式,很多人可能懂,可能在很多地方見到這個表達式,但是自己卻沒有注意,這個表達式返回最后一個表達式的值。比如x=({a;b;c;d;}),最終x的值應該是d。

代碼例子:

#include<stdio.h>

void?main(void)
{
????int?a=({1;2;4;})+10;
????printf("%d\n",a);//a=14
}

4.2、typeof獲取變量的類型

這個我們很少看到,這個關鍵字是C語言關鍵字的拓展,返回變量的類型,具體可以看GCC里面的介紹
https://gcc.gnu.org/onlinedocs/gcc/Typeof.html

++Another way to refer to the type of an expression is with typeof. The syntax of using of this keyword looks like sizeof, but the construct acts semantically like a type name defined with typedef.++

代碼例子:

void?main(void)
{
????int?a?=?6;
????typeof(a)?b?=9;
????printf("%d?%d\n",a,b);
}

4.3、(struct st*)0的作用

尺子大家應該都用過吧,比如我想用尺子量一本書本的長度,我們第一時間就需要找到尺子的0刻度的位置,然后用這個0刻度的位置去對準書本的邊,然后再貼合對齊,在書本的另一邊查看尺子刻度就可以知道書本的長度了。
現在我們需要量一個結構體的長度,我們也可以用尺子來量,我們只要找到這個0刻度的位置就可以了。同理,即使我們不知道0刻度位置,我們首尾刻度相減一樣可以計算出結構體的長度。
但是在C語言里什么事尺子呢?你想到的可能是sizeof,不幸的是,這個并不能滿足我們的需要,所以才有了(struct st *),這個當作尺子真的再好不過了。

struct?st{
????int?a;
????int?b;
}*p_st,n_st;

void?main(void)
{
????printf("%p\n",&((struct?st*)0)->b);
}

上面的代碼

(struct?st*)0

這個的意思就是把這個結構體放到0刻度上面開始量了,然后量到哪里呢?

&((struct?st*)0)->b)

這個就體現出來了,量到b的位置。所以上面的輸出應該是4

看完上面的解釋,應該知道下面這兩個代碼的功能是一樣的。

typeof?((struct?st*)0)->b)?c;?//?取b的類型來聲明c
int?c;

其實不只是對于0,用其他數字一樣是有效的,比如下面的代碼,編譯器關心的是類型,而不在乎這個數字。

printf("%p\n",&((struct?st*)4)->b??-4?);

這文章寫了有幾天了,但是一直不想直接發出去,因為我覺得這個核心點總是沒有找到一個特別好的論證方法,看完上面后,大概對這種測量應該有點感覺了吧,如果現在需要你把一個數組的首地址設置為0,要怎么做呢?

先思考一下,假設這里延遲了幾分鐘。

代碼如下:

struct?A?{
????short?array[100];
};

int?main(int?argc,?char?*argv[])
{
????int?i?=?10;

????A*?a?=?(A*)0;
????printf("%p?%d?%d\n",a,sizeof(short),?&a->array[20]);
????getchar();
????return?1;
}
//輸出?00000000?2?40

有什么辦法不使用==struct A *== 直接把數組的地址放到0位置呢?目前我還沒有找到其他更好的辦法,如果有好的建議的,請留言給我。

4.4、offsetof(TYPE, MEMBER)

#define?offsetof(TYPE,?MEMBER)?((size_t)?&((TYPE*)0)->MEMBER)

size_t 這個有不懂的可以百度下,就是unsigned 的整數,在32位和64位下長度不同,所以這個offsetof就是獲取結構體的偏移長度

4.5、const int* p的作用

上面的宏定義里面還有一個小知識點

const?typeof(?((type?*)0)->member?)?*__mptr

上面的代碼可以簡寫成

const?int?*?__mptr

這個說明什么問題呢?這個說明__mptr指向的整型數據是一個const(常數)。
這就涉及到兩外兩個知識

int?*?const?__mptr;//表示__mptr的值不能改變
//和
const?int?*?const?__mptr;?//表示__mptr不能改變而且指向的內容也不能改變

5、 container_of 剖析

看完上面的幾個知識點,再來看container_of這個宏就顯得非常清晰了。我把解析部分寫在下面的代碼注釋里面。

#define?offsetof(TYPE,?MEMBER)?((size_t)?&((TYPE*)0)->MEMBER)
#define?container_of(ptr,?type,?member)?({??????????\
????????const?typeof(?((type?*)0)->member?)?*__mptr?=?(const?typeof(?((type?*)0)->member?)?*)(ptr);?\
????????(type?*)(?(char?*)__mptr?-?offsetof(type,member)?);})
//-----分割線
struct?st{
????int?a;
????int?b;
}*pt;
//用這個來舉例
container_of(&pt->a,struct?st,a)
?const?typeof(?((struct?st?*)0)->a?)?*__mptr?=?(const?typeof(?((struct?st?*)0)->a?)?*)(&pt->a);
const?int?*__mptr?=?(int?*)(&pt->a);//第一句解析完,實際上就是獲取a的地址。
(type?*)(?(char?*)__mptr?-?offsetof(type,member)?);
//這個變成
(struct?st?*)(?(char?*)__mptr?-?((unsigned?int)?&((struct?st*)0)->a));
//這句的意思,把a的地址減去a對結構體的偏移地址長度,那就是結構體的地址位置了。

6、實例代碼

經過上面的解釋,至少對這個宏有感覺了吧,寫個代碼來測試一下,讓自己與代碼融合為一體,這樣才能做到人碼合一的境界。
代碼如下:

#include<stdio.h>
#include<stddef.h>
#include<stdlib.h>

#define?offsetof(TYPE,?MEMBER)?((size_t)?&((TYPE*)0)->MEMBER)

/*ptr?成員指針
*?type?結構體?比如struct?Stu
*?member?成員變量,跟指針對應
*?*/

#define?container_of(ptr,?type,?member)?({??????????\
????????const?typeof(?((type?*)0)->member?)?*__mptr?=?(const?typeof(?((type?*)0)->member?)?*)(ptr);?\
????????(type?*)(?(char?*)__mptr?-?offsetof(type,member)?);})



typedef?struct?Stu{
????????int?age;
????????char?name[10];
????????int?id;
????????unsigned?long?phone_num;
}*p_stu,str_stu;

void?print_all(void?*p_str)
{

????p_stu?m1p_stu?=?NULL;

????m1p_stu?=?container_of(p_str,struct?Stu,age);

????printf("age:%d\n",m1p_stu->age);
????printf("name:%s\n",m1p_stu->name);
????printf("id:%d\n",m1p_stu->id);
????printf("phone_num:%d\n",m1p_stu->phone_num);
}


void?main(void)
{
????p_stu?m_stu?=?(p_stu)malloc(sizeof(str_stu));

????m_stu->age?=?25;
????m_stu->id??=?1;
????m_stu->name[0]='w';
????m_stu->name[1]='e';
????m_stu->name[2]='i';
????m_stu->name[3]='q';
????m_stu->name[4]='i';
????m_stu->name[5]='f';
????m_stu->name[6]='a';
????m_stu->name[7]='\0';
????m_stu->phone_num=13267;

????/*傳結構體成員指針進去*/
????print_all(&m_stu->age);

????printf("main?end\n");

????if(m_stu!=NULL)
????????free(m_stu);
}

7、程序輸出

age:25
name:weiqifa
id:1
phone_num:13267
main?end


當你看到這里的時候,說明你已經閱讀完上面的內容

不管怎樣,感謝您有心或者無意的關注和支持

公眾號接入了AI功能,隨意回復任意消息哦(比如笑話,天氣

覺得不錯,請幫忙轉發點好看,您的每一次支持,我都將銘記于心

想獲取學習資料,請點擊狀態欄公眾號福利按鈕






創作挑戰賽新人創作獎勵來咯,堅持創作打卡瓜分現金大獎

總結

以上是生活随笔為你收集整理的linux 内核宏container_of剖析的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。