色哟哟视频在线观看-色哟哟视频在线-色哟哟欧美15最新在线-色哟哟免费在线观看-国产l精品国产亚洲区在线观看-国产l精品国产亚洲区久久

0
  • 聊天消息
  • 系統消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發帖/加入社區
會員中心
創作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

小編科普一下Linux內核中常用的C語言技巧

CPP開發者 ? 來源:嵌入式Linux充電站 ? 2023-02-08 11:51 ? 次閱讀

Linux內核采用的是GCC編譯器,GCC編譯器除了支持ANSI C,還支持GNU C。在Linux內核中,許多地方都使用了GNU C語言的擴展特性,如typeof、__attribute__、__aligned、__builtin_等,這些都是GNU C語言的特性。

typeof

下面是比較兩個數大小返回最大值的經典宏寫法:

#definemax(a,b)((a)>(b)?(a):(b))

如果a傳入i++,b傳入j++,那么這個比較大小就會出錯。例如:

#definemax(a,b)((a)>(b)?(a):(b))

intx=1,y=2;
printf("max=%d
",max(x++,y++));
printf("x=%d,y=%d
",x,y);

輸出結果:max=3,x=2,y=4。這是錯誤的結果,正常我們希望的是max(1,2),返回max=2。如何修改這個宏呢?

在GNU C語言中,如果知道a和b的類型,可以在宏里面定義一個變量,將a, b賦值給變量,然后再比較。例如:

#definemax(a,b)({
int_a=(a);
int_b=(b);
_a>_b?_a:_b;})

如果不知道具體的數據類型,可以使用typeof類轉換宏,Linux內核中的例子:

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

typeof(a) _a = (a):定義一個a類型的變量_a,將a賦值給_a

typeof(b) _b = (b):定義一個b類型的變量_b,將b賦值給_b

(void) (&_a == &_b):判斷兩個數的類型是否相同,如果不相同,會拋出一個警告。因為a和b的類型不一樣,其指針類型也會不一樣,兩個不一樣的指針類型進行比較操作,會拋出一個編譯警告。

typeof用法舉例:

//typeof的參數可以是表達式或類型

//參數是類型
typeof(int*)a,b;//等價于:int *a,*b;

//參數是表達式
intfoo();
typeof(foo())var;//聲明了int類型的var變量,因為表達式foo()是int類型的。由于表達式不會被執行,所以不會調用foo函數。

零長數組

零長數組,又叫柔性數組。而它的作用主要就是為了滿足需要變長度的結構體,因此有時也習慣性地稱為變長數組

用法:在一個結構體的最后, 申明一個長度為0的數組, 就可以使得這個結構體是可變長的

對于編譯器來說, 此時長度為0的數組并不占用空間, 因為數組名本身不占空間, 它只是一個偏移量, 數組名這個符號本身代表了一個不可修改的地址常量

結構體中定義零長數組:


structpcpu_chunk{
structlist_headlist;
unsignedlongpopulated[];/*變長數組*/
};

數據結構最后一個元素被定義為零長度數組,不占結構體空間。這樣,我們可以根據對象大小動態地分配結構的大小。

structline{
intlength;
charcontents[0];
};

structline*thisline=malloc(sizeof(structline)+this_length);
thisline->length=this_length;

如上例所示,struct line數據結構定義了一個int length變量和一個變長數組contents[0],這個struct line數據結構的大小只包含int類型的大小,不包含contents的大小,也就是**sizeof (struct line) = sizeof (int)**。

創建結構體對象時,可根據實際的需要指定這個可變長數組的長度,并分配相應的空間,如上述實例代碼分配了this_length 字節的內存,并且可以通過contents[index]來訪問第index個地址的數據。

case范圍

GNU C語言支持指定一個case的范圍作為一個標簽,如:

caselow...high:
case'A'...'Z':

這里low到high表示一個區間范圍,在ASCII字符代碼中也非常有用。下面是Linux內核中的代碼例子。



staticintlocal_atoi(constchar*name){
intval=0;
for(;;name++){
switch(*name){
case'0'...'9':
val=10*val+(*name-'0');
break;
default:
returnval;
}
}
}

另外,還可以用整形數來表示范圍,但是這里需要注意在“...”兩邊有空格,否則編譯會出錯。



staticintat91sam9261_udc_init(structat91_udc*udc){
for(i=0;iep[i];
switch(i){
case0:
ep->maxpacket=8;
break;
case1...3:
ep->maxpacket=64;
break;
case4...5:
ep->maxpacket=256;
break;
}
}
}

標號元素

GNU C語言可以通過指定索引或結構體成員名來初始化,不必按照原來的固定順序進行初始化。

結構體成員的初始化在 Linux 內核中經常使用,如在設備驅動中初始化file_operations數據結構:


staticconststructfile_operationszero_fops={
.llseek=zero_lseek,
.read=new_sync_read,
.write=write_zero,
.read_iter=read_iter_zero,
.aio_write=aio_write_zero,
.mmap=mmap_zero,
};

如上述代碼中的zero_fops的成員llseek初始化為zero_lseek函數,read成員初始化為new_sync_read函數,依次類推。當file_operations數據結構的定義發生變化時,這種初始化方法依然能保證已知元素的正確性,對于未初始化成員的值為0或者NULL

可變參數宏

在GNU C語言中,宏可以接受可變數目的參數,主要用在輸出函數里。例如:


#definepr_debug(fmt,...)
dynamic_pr_debug(fmt,##__VA_ARGS__)

“...”代表一個可以變化的參數表,“__VA_ARGS__”是編譯器保留字段,預處理時把參數傳遞給宏。當宏的調用展開時,實際參數就傳遞給dynamic_pr_debug函數了。

函數屬性

GNU C語言允許聲明函數屬性(Function Attribute)變量屬性(Variable Attribute)類型屬性(Type Attribute),以便編譯器進行特定方面的優化和更仔細的代碼檢查。特殊屬性語法格式為:

__attribute__((attribute-list))

attribute-list的定義有很多,如noreturn、format以及const等。此外,還可以定義一些和處理器體系結構相關的函數屬性,如ARM體系結構中可以定義interrupt、isr等屬性。

下面是Linux內核中使用format屬性的一個例子。


intlibcfs_debug_msg(structlibcfs_debug_msg_data*msgdata,constchar*format1,...)__attribute__((format(printf,2,3)));

libcfs_debug_msg()函數里聲明了一個format函數屬性,它會告訴編譯器按照printf的參數表的格式規則對該函數參數進行檢查數字2表示第二個參數為格式化字符串,數字3表示參數“...”里的第一個參數在函數參數總數中排在第幾個

noreturn屬性告訴編譯器,該函數從不返回值,這可以消除一些不必要的警告信息。例如以下函數,函數不會返回:

void__attribute__((noreturn))die(void);

const屬性會讓編譯器只調用該函數一次,以后再調用時只需要返回第一次結果即可,從而提高效率。

staticinlineu32__attribute_const__read_cpuid_cachetype(void){
returnread_cpuid(CTR_EL0);
}

Linux還有一些其他的函數屬性,被定義在compiler-gcc.h文件中。

#define__pure__attribute__((pure))
#define__aligned(x)__attribute__((aligned(x)))
#define__printf(a,b)__attribute__((format(printf,a,b)))
#define__scanf(a,b)__attribute__((format(scanf,a,b)))
#definenoinline__attribute__((noinline))
#define__attribute_const____attribute__((__const__))
#define__maybe_unused__attribute__((unused))
#define__always_unused__attribute__((unused))

變量屬性和類型屬性

變量屬性可以對變量或結構體成員進行屬性設置。類型屬性常見的屬性有alignment、packed和sections等。

alignment屬性規定變量或者結構體成員的最小對齊格式,以字節為單位。

structqib_user_info{
__u32spu_userversion;
__u64spu_base_info;
}__aligned(8);

在這個例子中,編譯器以8字節對齊的方式來分配qib_user_info這個數據結構。

packed屬性可以使變量或者結構體成員使用最小的對齊方式,對變量是以字節對齊,對域是以位對齊

structtest{
chara;
intx[2]__attribute__((packed));
};

x成員使用了packed屬性,它會存儲在變量a后面,所以這個結構體一共占用9字節

內建函數

內建函數以“_builtin_”作為函數名前綴。下面介紹Linux內核常用的一些內建函數。

__builtin_constant_p(x):判斷x是否在編譯時就可以被確定為常量。如果x為常量,該函數返回1,否則返回0。

__builtin_expect(exp, c):

#define__swab16(x)
(__builtin_constant_p((__u16)(x))?
___constant_swab16(x):
__fswab16(x))__builtin_expect(exp,c)

__builtin_expect(exp, c):這里的意思是exp==c的概率很大,用來引導GCC編譯器進行條件分支預測。開發人員知道最可能執行哪個分支,并將最有可能執行的分支告訴編譯器,讓編譯器優化指令序列,使指令盡可能地順序執行,從而提高CPU預取指令的正確率

Linux內核中經常見到likely()和unlikely()函數,本質也是__builtin_expect():

#defineLIKELY(x)__builtin_expect(!!(x),1)//x很可能為真
#defineUNLIKELY(x)__builtin_expect(!!(x),0)//x很可能為假

__builtin_prefetch(const void *addr, int rw, int locality):主動進行數據預取,在使用地址addr的值之前就把其值加載到cache中,減少讀取的延遲,從而提高性能

該函數可以接受3個參數:

第一個參數addr表示要預取數據的地址;

第二個參數rw表示讀寫屬性,1表示可寫,0表示只讀;

第三個參數locality表示數據在cache中的時間局部性,其中0表示讀取完addr的之后不用保留在cache中,而1~3表示時間局部性逐漸增強。如下面的prefetch()和prefetchw()函數的實現。


#defineprefetch(x)__builtin_prefetch(x)
#defineprefetchw(x)__builtin_prefetch(x,1)

下面是使用prefetch()函數進行優化的一個例子。


void__init__free_pages_bootmem(structpage*page,unsignedintorder){
unsignedintnr_pages=1<

在處理struct page數據之前,通過prefetchw()預取到cache中,從而提升性能

asmlinkage

在標準C語言中,函數的形參在實際傳入參數時會涉及參數存放問題。

對于x86架構,函數參數局部變量被一起分配到函數的局部堆棧里。x86中對asmlinkage的定義:


#defineasmlinkageCPP_ASMLINKAGE__attribute__((regparm(0)))

attribute((regparm(0))):告訴編譯器該函數不需要通過任何寄存器來傳遞參數,只通過堆棧來傳遞

對于ARM來說,函數參數的傳遞有一套ATPCS標準,即通過寄存器來傳遞。ARM中的R0~R4寄存器存放傳入參數,當參數超過5個時,多余的參數被存放在局部堆棧中。所以,ARM平臺沒有定義asmlinkage


#defineasmlinkageCPP_ASMLINKAGE
#defineasmlinkageCPP_ASMLINKAGE

UL

在Linux內核代碼中,我們經常會看到一些數字的定義使用了UL后綴修飾。

數字常量會被隱形定義為int類型,兩個int類型相加的結果可能會發生溢出。

因此使用UL強制把int類型數據轉換為unsigned long類型,這是為了保證運算過程不會因為int的位數不同而導致溢出。

1 :表示有符號整型數字1

UL:表示無符號長整型數字1






審核編輯:劉清

聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規問題,請聯系本站處理。 舉報投訴
  • 寄存器
    +關注

    關注

    31

    文章

    5363

    瀏覽量

    120945
  • C語言
    +關注

    關注

    180

    文章

    7614

    瀏覽量

    137432
  • LINUX內核
    +關注

    關注

    1

    文章

    316

    瀏覽量

    21703
  • gcc編譯器
    +關注

    關注

    0

    文章

    78

    瀏覽量

    3414

原文標題:Linux 內核中常用的 C 語言技巧

文章出處:【微信號:CPP開發者,微信公眾號:CPP開發者】歡迎添加關注!文章轉載請注明出處。

收藏 人收藏

    評論

    相關推薦

    C語言中常用的宏定義

    寫好C語言,漂亮的宏定義很重要,使用宏定義可以防止出錯,提高可移植性,可讀性,方便性等等。下面列舉些成熟軟件中常用的宏定義。
    發表于 10-18 10:05 ?1719次閱讀

    Linux內核C語言宏的使用技巧

    Linux內核可謂是集C語言大成者,從中我們可以學到非常多的技巧,本文來學習一下宏技巧,文章有點長,但耐心看完后
    發表于 07-21 14:56 ?495次閱讀
    <b class='flag-5'>Linux</b><b class='flag-5'>內核</b>中<b class='flag-5'>C</b><b class='flag-5'>語言</b>宏的使用技巧

    c語言中常用的宏定義有哪些?

    c語言中常用的宏定義有哪些?
    發表于 04-28 06:01

    Linux內核中GNU C擴展的常用C語言語法分析

    13.1 總結前面12節的課程,主要針對 Linux 內核中 GNU C 擴展的常用 C
    發表于 12-14 06:29

    科普一下RV1126與RV1109具備哪些技術優勢呢

    科普一下RV1126與RV1109具備哪些技術優勢呢?
    發表于 02-21 06:25

    科普一下RK3288安卓主板的優勢特點有哪些呢

    科普一下RK3288安卓主板的優勢特點有哪些呢
    發表于 03-03 13:33

    科普一下RK3399 Audio的功能有哪些呢

    科普一下RK3399 Audio的功能有哪些呢?
    發表于 03-04 12:47

    科普一下RK3399/libdrm/modetest

    科普一下RK3399/libdrm/modetest
    發表于 03-07 07:06

    科普一下RK3328 SoC有何功能呢

    科普一下RK3328 SoC有何功能呢?
    發表于 03-09 07:28

    linux內核C語言的編程風格

    linux 內核C語言的編程風格
    發表于 09-26 14:22 ?0次下載

    初步感知一下C語言

    今天筆者就帶大家跟c語言簡單“相個親”,看看朋友們對c語言的眼緣如何。今天你不需要理解它是什么意思,只是初步感受一下它是什么樣的。
    的頭像 發表于 05-05 20:10 ?1332次閱讀
    初步感知<b class='flag-5'>一下</b><b class='flag-5'>C</b><b class='flag-5'>語言</b>

    單片機中常用C語言語句合集

    單片機中常用C語言語句合集
    發表于 01-12 09:24 ?45次下載

    介紹一下linux內核比較優秀的調試方式KGDB

    printf相信學過C語言的同志再熟悉不過了,然而在linux內核開發中有種非常簡潔的日志輸出函數叫-printk。
    的頭像 發表于 03-08 13:45 ?1916次閱讀

    Linux內核中常用C語言技巧有哪些

    Linux內核采用的是GCC編譯器,GCC編譯器除了支持ANSI C,還支持GNU C。在Linux內核
    的頭像 發表于 05-12 14:45 ?639次閱讀

    淺析Linux內核中常用C語言技巧

    Linux內核采用的是GCC編譯器,GCC編譯器除了支持ANSI C,還支持GNU C。在Linux內核
    發表于 06-25 10:46 ?458次閱讀
    主站蜘蛛池模板: 中文人妻熟妇精品乱又伦 | 中文字幕一区中文亚洲 | 午夜一区二区三区 | 我强进了老师身体在线观看 | 中文无码乱人伦中文视频播放 | 办公室日本肉丝OL在线 | 色呦呦导航 | 一个吃奶两个添下面H | 亚洲人成网77777色在线播放 | 日本一区不卡在线播放视频免费 | 高清欧美性猛交xxxx黑人猛交 | 一区三区三区不卡 | 超碰caoporon最新视频 | 侮辱丰满美丽的人妻 | zooskoo1videos人与狗| YELLOW视频在线观看最新 | 一本之道高清在线观看免费 | 亚洲精品天堂在线 | 亚洲激情网站 | 丝瓜影院观看免费高清国际观察 | 热巴两次用约老师屁股发底线球 | caoporn 超碰在线视频 | 精品国产自在自线官方 | 花蝴蝶高清观看免费 | 日韩视频在线观看 | ppypp午夜限制不卡影院私人 | av天堂电影网| 综合久久伊人 | 亚洲裸舞 hd| 亚洲国产韩国欧美在线不卡 | 亚洲伦理精品久久 | 久久人妻AV一区二区软件 | 国产一区二区三区四区五在线观看 | 亚洲视频中文字幕在线观看 | 久久理论片 | 在教室伦流澡到高潮HNP视频 | 精品国产乱码久久久久久人妻 | 99久久国产综合精品国 | 中文字幕免费在线视频 | 国产色情短视频在线网站 | 国产免费人视频在线观看免费 |