RM新时代网站-首页

0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

【Linux內(nèi)核】從小小的宏定義窺探Linux內(nèi)核的精妙設(shè)計(jì)

嵌入式物聯(lián)網(wǎng)開發(fā) ? 來源:嵌入式物聯(lián)網(wǎng)開發(fā) ? 作者:嵌入式物聯(lián)網(wǎng)開發(fā) ? 2022-08-31 13:30 ? 次閱讀
 Linux操作系統(tǒng),可以說它就是程序猿的代碼天堂;這不僅僅因?yàn)樗情_源的,更多的是因?yàn)樗恼Q生,是由世界上無數(shù)的代碼天才共同締造而來;跑在它上面的Linux內(nèi)核,經(jīng)受了世界上各式各樣的服務(wù)器壓力測(cè)試,始終保持著高效、穩(wěn)定、安全的特性,一如既往地服務(wù)全人類。甚至可以說Linux操作系統(tǒng)造福了人類,很難想象,當(dāng)Linux操作系統(tǒng)消失了,這個(gè)世界會(huì)變得怎么樣?

? 作為L(zhǎng)inux操作系統(tǒng)的忠實(shí)粉絲,筆者自大學(xué)時(shí)期就開始研究和使用Linux操作系統(tǒng),出來工作了好幾年,幾乎每天都要跟Linux系統(tǒng)打交道,甚至毫不夸張的是,白天不在Linux系統(tǒng)命令行下敲幾行命令,晚上都會(huì)失眠。

? 學(xué)習(xí)和使用了Linux系統(tǒng)這么些年,一直想找個(gè)機(jī)會(huì),對(duì)Linux的知識(shí)做一番梳理,無奈之前礙于各種時(shí)間因素和自我的惰性,遲遲未有實(shí)質(zhì)性的進(jìn)展。最近才開始狠狠地下定決心,必須邁出扎實(shí)的一步,爭(zhēng)取做出更多的分享,充實(shí)自我的同時(shí),也給同行帶來更多的視野和思路,何樂而不為呢?


? 本文打算從一個(gè)很小的代碼設(shè)計(jì),試圖從中窺探一下Linux內(nèi)核代碼的精妙設(shè)計(jì)。它的名字就叫 max宏定義,請(qǐng)跟隨筆者的思路一步步解開它神秘的面紗。

? 先來一個(gè)它的全貌:

#define max(a, b) ({\
typeof(a) _max1 = (a);\
typeof(b) _max2 = (b);\
(void)(&_max1 == &_max2);\
_max1 > _max2 ? _max1 : _max2; })

[外鏈圖片轉(zhuǎn)存失敗,源站可能有防盜鏈機(jī)制,建議將圖片保存下來直接上傳(img-AQSFb6aB-1661923667090)(data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==)]

? 我們先不一下子就把這段代碼剖析徹底,換個(gè)思維,假設(shè)我們是Linux內(nèi)核的設(shè)計(jì)者,要解決比較2個(gè)數(shù)的大小,代碼應(yīng)該怎么樣入手。我想很多C語(yǔ)言工作者,甚至是初學(xué)C語(yǔ)言的碼農(nóng)也可以寫出這樣的如下代碼:

#define max(a, b) a > b ? a : b

[外鏈圖片轉(zhuǎn)存失敗,源站可能有防盜鏈機(jī)制,建議將圖片保存下來直接上傳(img-jvjzDDsR-1661923667093)(data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==)]

? 初看這個(gè)宏定義,似乎沒有問題;細(xì)細(xì)一看,用個(gè)測(cè)試案例一測(cè)試就發(fā)現(xiàn)端倪了:

/* 假設(shè)有如下的調(diào)用代碼 */
{    
    printf("result = %d\n", max(9!=9, 0==0));

    /* 宏定義展開后是  9!=9 > 0==0 ? 9!=9 : 0==0*/
    /* 輸出結(jié)果是 0*/
}

[外鏈圖片轉(zhuǎn)存失敗,源站可能有防盜鏈機(jī)制,建議將圖片保存下來直接上傳(img-59Bp6ggT-1661923667094)(data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==)]

? 很明顯正確答案應(yīng)該是輸出1,細(xì)心者就很快發(fā)現(xiàn),給a和b加上括號(hào)試試看:

#define max(a, b) (a) > (b) ? (a) : (b)

[外鏈圖片轉(zhuǎn)存失敗,源站可能有防盜鏈機(jī)制,建議將圖片保存下來直接上傳(img-k7e0TQdq-1661923667097)(data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==)]

? 調(diào)用代碼測(cè)試下:

/* 假設(shè)有如下的調(diào)用代碼 */
{
    printf("result = %d\n", max(9!=9, 0==0));

    /* 宏定義展開后是  (9!=9) > (0==0) ?(9!=9):(0==0)*/
    /* 輸出正確結(jié)果 1*/

    printf("result = %d\n", 9 + max(9!=9, 0==0));

    /* 宏定義展開后是  9 + (9!=9) > (0==0) ?(9!=9 :(0==0)*/
    /* 輸出結(jié)果是0, 正確的期望值輸出,應(yīng)該是10 (=9+1) */
}

[外鏈圖片轉(zhuǎn)存失敗,源站可能有防盜鏈機(jī)制,建議將圖片保存下來直接上傳(img-m705RxoN-1661923667114)(data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==)]

? 于是又有了下面的改進(jìn):

#define max(a, b) ((a) > (b) ? (a) : (b))

[外鏈圖片轉(zhuǎn)存失敗,源站可能有防盜鏈機(jī)制,建議將圖片保存下來直接上傳(img-xriR0pv8-1661923667116)(data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==)]

? 這個(gè)版本,也是我們?nèi)粘懘a最經(jīng)??吹降陌姹荆覀兪褂脺y(cè)試代碼測(cè)試下看看:

/* 假設(shè)有如下的調(diào)用代碼 */
{
    printf("result = %d\n", 9 + max(9!=9, 0==0));

    /* 宏定義展開后是  9 + ((9!=9) > (0==0) ?(9!=9):(0==0))*/
    /* 輸出正確的期望值10 (=9+1) */

    int a = 8;
    int b = 9;

    printf("result = %d\n", max(a++, b++));

    /* 宏定義展開后是  ((a++) > (b++) ?(a++):(b++))*/
    /* 輸出結(jié)果是10;而正確的期望值輸出,應(yīng)該是9 */
}

[外鏈圖片轉(zhuǎn)存失敗,源站可能有防盜鏈機(jī)制,建議將圖片保存下來直接上傳(img-2JAec1EG-1661923667122)(data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==)]

? 很遺憾,經(jīng)測(cè)試,這個(gè)版本依然有問題,這是因?yàn)楹甓x中的++操作干擾了比較結(jié)果的輸出,我們需要再次改進(jìn)這個(gè)宏定義。應(yīng)該怎么樣改進(jìn)呢?既然是++操作干擾了輸出,那么我們使用2個(gè)中間變量來中轉(zhuǎn)下不就ok了嗎?于是有了下面的版本:

#define max(a, b) ({\
int _a = (a);\
int _b = (b);\
_a > _b ? _a : _b;\
})

[外鏈圖片轉(zhuǎn)存失敗,源站可能有防盜鏈機(jī)制,建議將圖片保存下來直接上傳(img-t7VD8Szr-1661923667123)(data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==)]

? 這樣的寫法,已經(jīng)有點(diǎn)接近Linux內(nèi)核定義的模樣了。再次使用上面的測(cè)試代碼執(zhí)行測(cè)試:

/* 假設(shè)有如下的調(diào)用代碼 */
{    
    int a = 8;
    int b = 9;

    printf("result = %d\n", max(a++, b++));

    /* 宏定義展開后是  ({int _a=a++; int _b=b++; _a > _b ? _a : _b;})*/
    /* 輸出正確的期望值9 */
}

[外鏈圖片轉(zhuǎn)存失敗,源站可能有防盜鏈機(jī)制,建議將圖片保存下來直接上傳(img-dpinR0OT-1661923667125)(data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==)]

? 雖然上面版本的定義解決了++操作符引起的輸出結(jié)果錯(cuò)誤的問題,但是由于宏定義內(nèi)部使用了int型的_a和_b作為中間變量,這就是限制了max宏定義只能用于2個(gè)int型的數(shù)據(jù)做比較,這將大大限制了它的使用范圍。于是,很容易想到一個(gè)解決辦法,將int這個(gè)數(shù)據(jù)類型使用type變量傳進(jìn)去,于是有了下面的版本:

#define max(type, a, b) ({\
type _a = (a);\
type _b = (b);\
_a > _b ? _a : _b;\
})

[外鏈圖片轉(zhuǎn)存失敗,源站可能有防盜鏈機(jī)制,建議將圖片保存下來直接上傳(img-WVFbRavF-1661923667126)(data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==)]

? 這樣的確是解決了如上數(shù)據(jù)類型問題的困惑,但是這樣我們的宏定義是以多一個(gè)參數(shù)輸入為犧牲代價(jià)的。那么,我們有沒有什么辦法,可以不將type輸入,而直接從輸入的a和b中獲取它們的數(shù)據(jù)類型呢?答案是肯定有的!

? GNU C作為C語(yǔ)言的擴(kuò)展版本,增加了若干非常有用的擴(kuò)展語(yǔ)法,其中typeof關(guān)鍵字就是其中的一個(gè)。比如定義一個(gè)變量int a; 則typeof(a)就可以取得a變量的類型,即int;比如直接使用typeof(unsigned char *),得到的輸出就是數(shù)據(jù)類型unsigned char *,非常的實(shí)用。于是我們將typeof應(yīng)用到max宏中,于是就有下面的優(yōu)良版本:

#define max(a, b) ({\
typeof(a) _a = (a);\
typeof(a) _b = (b);\
_a > _b ? _a : _b;\
})

[外鏈圖片轉(zhuǎn)存失敗,源站可能有防盜鏈機(jī)制,建議將圖片保存下來直接上傳(img-KTnbdo0C-1661923667127)(data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==)]

? 這樣的寫法,雖然避免了我們傳遞a和b變量的數(shù)據(jù)類型進(jìn)去,但是,如下的測(cè)試代碼,結(jié)果會(huì)怎么樣呢?

/* 假設(shè)有如下的調(diào)用代碼 */
{    
    int a = 8;
    float b = 9.0;

    printf("result = %d\n", max(a, b));
    /* 這樣能比較嗎?*/

    int a = 8;
    float b = 9.0;
    float *p = &b;

    printf("result = %d\n", max(a, p));
    /* 這樣又能比較嗎?*/
}

[外鏈圖片轉(zhuǎn)存失敗,源站可能有防盜鏈機(jī)制,建議將圖片保存下來直接上傳(img-j0v9A1VG-1661923667128)(data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==)]

? 很明顯,當(dāng)a是int型,而b是float型,內(nèi)核執(zhí)行比較是可以的;但是如果拿一個(gè)int型的變量跟一個(gè)float *變量做比較,或者兩個(gè)奇奇怪怪的struct類型變量做計(jì)較,這樣肯定是不行的。所以,我們?cè)谠O(shè)計(jì)max宏定義的時(shí)候,需要將這種可能出現(xiàn)的問題盡可能地在編譯階段就暴露出來,于是有了Linux內(nèi)核max宏定義的最佳版本:

#define max(a, b) ({\
typeof(a) _a = (a);\
typeof(a) _b = (b);\
(void) &_a == &_b;\
_a > _b ? _a : _b;\
})

[外鏈圖片轉(zhuǎn)存失敗,源站可能有防盜鏈機(jī)制,建議將圖片保存下來直接上傳(img-YVJm8AAy-1661923667129)(data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==)]

? 我們注意,宏定義的第4行,(void) &_a == &_b; 意思是對(duì)_a和_b的地址做比較,實(shí)際上從運(yùn)行結(jié)果上,這個(gè)肯定是不等的,但是我們關(guān)心的并不是兩者比較的結(jié)果,而是兩者能不能用==比較的問題。當(dāng)_a和_b的數(shù)據(jù)類型一致時(shí),代碼編譯不會(huì)有任何警告;反之,當(dāng)兩者的數(shù)據(jù)類型不一致時(shí),比如之前的a是int型,而b是float型,那么這條語(yǔ)句就會(huì)報(bào)出編譯警告,如果在嚴(yán)格的編譯選項(xiàng)下,這個(gè)警告還可以轉(zhuǎn)換為錯(cuò)誤,要求代碼調(diào)用者去確認(rèn)結(jié)果,是否對(duì)兩個(gè)不同類型的數(shù)據(jù)執(zhí)行max比較的動(dòng)作,從而將隱患消除,提升代碼質(zhì)量。


? 通過跟隨筆者的思路,我們可以細(xì)細(xì)地體會(huì)到,內(nèi)核設(shè)計(jì)者在設(shè)計(jì)這個(gè)max宏時(shí),相信也是走了不少的彎路,從一開始最簡(jiǎn)版本,接著遇到各式各樣的問題,然后一步步解決,一步步完善設(shè)計(jì),最終才有最優(yōu)秀的max宏呈現(xiàn)在我們面前。如此之類的代碼設(shè)計(jì),在Linux內(nèi)核設(shè)計(jì)代碼中比比皆是,今后筆者也會(huì)集中整理此類的優(yōu)秀設(shè)計(jì),致力于將更多的優(yōu)秀內(nèi)核代碼分享給讀者,敬請(qǐng)關(guān)注。文中提及的觀點(diǎn),均為筆者愚見,如有紕漏之處,還望誠(chéng)心指正,謝謝。

審核編輯:湯梓紅
聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場(chǎng)。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請(qǐng)聯(lián)系本站處理。 舉報(bào)投訴
  • 內(nèi)核
    +關(guān)注

    關(guān)注

    3

    文章

    1372

    瀏覽量

    40276
  • Linux
    +關(guān)注

    關(guān)注

    87

    文章

    11292

    瀏覽量

    209323
  • 操作系統(tǒng)
    +關(guān)注

    關(guān)注

    37

    文章

    6801

    瀏覽量

    123283
收藏 人收藏

    評(píng)論

    相關(guān)推薦

    Linux內(nèi)核中C語(yǔ)言的使用技巧

    Linux內(nèi)核可謂是集C語(yǔ)言大成者,從中我們可以學(xué)到非常多的技巧,本文來學(xué)習(xí)一下技巧,文章有點(diǎn)長(zhǎng),但耐心看完后C語(yǔ)言level直接飆升。
    發(fā)表于 07-21 14:56 ?462次閱讀
    <b class='flag-5'>Linux</b><b class='flag-5'>內(nèi)核</b>中C語(yǔ)言<b class='flag-5'>宏</b>的使用技巧

    Linux內(nèi)核中文版教程

    Linux內(nèi)核中文版教程
    發(fā)表于 03-28 09:45 ?0次下載

    Linux內(nèi)核教程

    本章學(xué)習(xí)目標(biāo)掌握LINUX內(nèi)核版本的含義理解并掌握進(jìn)程的概念掌握管道的概念及實(shí)現(xiàn)了解內(nèi)核的數(shù)據(jù)結(jié)構(gòu)了解LINUX內(nèi)核的算法掌握
    發(fā)表于 04-10 16:59 ?0次下載

    linux內(nèi)核的完全注釋

    linux內(nèi)核的完全注釋
    發(fā)表于 10-29 10:02 ?18次下載
    <b class='flag-5'>linux</b><b class='flag-5'>內(nèi)核</b>的完全注釋

    Linux_內(nèi)核注釋

    Linux_內(nèi)核注釋
    發(fā)表于 10-30 09:45 ?9次下載
    <b class='flag-5'>Linux</b>_<b class='flag-5'>內(nèi)核</b>注釋

    Linux內(nèi)核編譯詳談

    Linux內(nèi)核編譯詳談
    發(fā)表于 10-30 09:51 ?7次下載
    <b class='flag-5'>Linux</b><b class='flag-5'>內(nèi)核</b>編譯詳談

    Linux內(nèi)核配置系統(tǒng)詳解

    隨著 Linux 操作系統(tǒng)的廣泛應(yīng)用,特別是 Linux 在嵌入式領(lǐng)域的發(fā)展,越來越多的人開始投身到 Linux 內(nèi)核級(jí)的開發(fā)中。面對(duì)日益龐大的 L
    發(fā)表于 11-01 15:45 ?4次下載

    linux內(nèi)核是什么_linux內(nèi)核學(xué)習(xí)路線

    Linux內(nèi)核是一個(gè)操作系統(tǒng)(OS)內(nèi)核,本質(zhì)上定義為類Unix。它用于不同的操作系統(tǒng),主要是以不同的Linux發(fā)行版的形式。
    發(fā)表于 09-16 15:49 ?2637次閱讀

    linux內(nèi)核參數(shù)設(shè)置_linux內(nèi)核的功能有哪些

    本文主要闡述了linux內(nèi)核參數(shù)設(shè)置及linux內(nèi)核的功能。
    發(fā)表于 09-17 14:40 ?1371次閱讀
    <b class='flag-5'>linux</b><b class='flag-5'>內(nèi)核</b>參數(shù)設(shè)置_<b class='flag-5'>linux</b><b class='flag-5'>內(nèi)核</b>的功能有哪些

    最硬核的Linux內(nèi)核文章

    來源 :頭條號(hào)@Linux學(xué)習(xí)教程,冰凌塊兒 01 前言 本文主要講解什么是Linux內(nèi)核,以及通過多張圖片展示Linux內(nèi)核的作用與功能,
    的頭像 發(fā)表于 10-19 17:46 ?2114次閱讀
    最硬核的<b class='flag-5'>Linux</b><b class='flag-5'>內(nèi)核</b>文章

    快速理解什么是Linux內(nèi)核以及Linux內(nèi)核的內(nèi)容

    01 前言 本文主要講解什么是Linux內(nèi)核,以及通過多張圖片展示Linux內(nèi)核的作用與功能,以便于讀者能快速理解什么是Linux
    的頭像 發(fā)表于 10-21 12:02 ?4286次閱讀
    快速理解什么是<b class='flag-5'>Linux</b><b class='flag-5'>內(nèi)核</b>以及<b class='flag-5'>Linux</b><b class='flag-5'>內(nèi)核</b>的內(nèi)容

    Linux 5.10.5內(nèi)核正式發(fā)布

    1月6日,Linux基金會(huì)宣布,Linux 5.10.5內(nèi)核正式發(fā)布,所有5.10內(nèi)核系列的用戶都必須升級(jí)。
    的頭像 發(fā)表于 01-07 14:36 ?2604次閱讀

    嵌入式LINUX系統(tǒng)內(nèi)核內(nèi)核模塊調(diào)試

    嵌入式LINUX系統(tǒng)內(nèi)核內(nèi)核模塊調(diào)試(嵌入式開發(fā)和硬件開發(fā))-嵌入式LINUX系統(tǒng)內(nèi)核內(nèi)核
    發(fā)表于 07-30 13:55 ?10次下載
    嵌入式<b class='flag-5'>LINUX</b>系統(tǒng)<b class='flag-5'>內(nèi)核</b>和<b class='flag-5'>內(nèi)核</b>模塊調(diào)試

    Linux內(nèi)核文件Cache機(jī)制

    Linux內(nèi)核文件Cache機(jī)制(開關(guān)電源技術(shù)與設(shè)計(jì) 第二版)-Linux內(nèi)核文件Cache機(jī)制? ? ? ? ? ? ? ??
    發(fā)表于 08-31 16:34 ?4次下載
    <b class='flag-5'>Linux</b><b class='flag-5'>內(nèi)核</b>文件Cache機(jī)制

    基于Android的Linux內(nèi)核的電源管理:概述

    1.電源管理的狀態(tài)Android的Linux內(nèi)核為系統(tǒng)提供了4種電源狀態(tài),內(nèi)核的源代碼為其中的3種定義了名字和對(duì)應(yīng)的
    發(fā)表于 01-07 11:14 ?6次下載
    基于Android的<b class='flag-5'>Linux</b><b class='flag-5'>內(nèi)核</b>的電源管理:概述
    RM新时代网站-首页