您好,登錄后才能下訂單哦!
前面12節的課程,主要針對 Linux 內核中 GNU C 擴展的一些常用 C 語言語法進行了分析。GNU C 的這些擴展語法,主要用來完善 C 語言標準和編譯優化。而通過 C 標準的發展過程我們又發現,對于一些編譯器擴展的一些特性,或者其它編程語言(如:C++)中的好的特性和語法,C 標準也會適時地吸收進來,作為新的 C 語言標準。
在 GNU C 的這些擴展語法中,attribute 和宏定義是兩大特色。在嵌入式底層系統中,尤其是 Linux 內核和 U-boot 中,大量使用 GNU C 擴展的 attribute屬性去輔助一些底層機制的實現,或者實現一些編譯上的優化。在宏定義方面,通過語句表達式、可變參數宏等特性,我們可以定義一個功能復雜、安全可靠的高質量宏。
本教程所講的一些特性,都是在實際工作或閱讀 Linux 內核驅動源碼時經常遇見的一些特性,掌握了這些擴展特性的使用,以后再遇到類似的“奇葩 C 語言”程序,就知道怎么去分析了。除此之外,GNU C 還有一些其它擴展特性,由于他們在內核中用得不是很多,或者說僅僅是做一些編譯上的優化,即使不知道也不會影響我們理解代碼,限于篇幅關系,所以就暫時不講了,比如下面這些特性。
大家以后遇到類似的擴展,可以到下面這幾個網站上去看看。
下面是幾道 C 語言練習題,大家可以做一做。看看學完本教程后,有沒有真正的掌握。有什么疑問,可以通過讀者圈,或加入QQ群(475504428)與我討論。
1.下面的程序,在不同編譯環境下,比如分別在 C-Free、VC++6.0、TurboC 環境下編譯運行,結果是否相等,為什么?
#include<stdio.h>
int main(void)
{
printf("size: %d\n", sizeof(int);
return 0;
}
2.定義一個宏,求兩個數的最小值。
3.將下面的程序編譯為可以在 ARM 平臺上運行的可執行文件 a.out,并對其進行反匯編,查看變量 global_val 的地址。
int global_val = 10;
int uninit_val;
int main(void)
{
int local_val = 20;
return 0;
}
在一個工程項目中,有兩個源文件如下,分析下面程序的運行結果。
//func.c
int a = 10;
int b;
int c attribute((weak)) = 30;
//main.c
int a;
int b = 20;
int c = 40;
int main(void)
{
printf("a: %d\n",a);
printf("b: %d\n",b);
printf("c: %d\n",c);
return 0;
}
5.定義一個變參函數,實現等級打印控制:ERROR、DEBUG、INFO。用這三個宏分別代表等級打印,比如定義 ERROR 時,只打印錯誤的信息;定義 DEBUG 時,打印錯誤和調試信息;定義 INFO 時,所有的打印信息都打印出來。
6.定義一個變參宏,實現等級打印控制:ERROR、DEBUG、INFO。用這三個宏分別代表等級打印,比如定義 ERROR 宏時,只打印錯誤的信息;定義 DEBUG 時,打印錯誤和調試信息;定義 INFO 時,所有的打印信息都打印出來。
7.下面是 Linux 內核(Linux4.4.0)中的一些宏定義,請分析它們實現的功能。
#define pr_emerg(fmt, ...) \
printk(KERN_EMERG pr_fmt(fmt), ##__VA_ARGS__)
#define pr_alert(fmt, ...) \
printk(KERN_ALERT pr_fmt(fmt), ##__VA_ARGS__)
#define pr_crit(fmt, ...) \
printk(KERN_CRIT pr_fmt(fmt), ##__VA_ARGS__)
#define pr_err(fmt, ...) \
printk(KERN_ERR pr_fmt(fmt), ##__VA_ARGS__)
#define pr_warning(fmt, ...) \
printk(KERN_WARNING pr_fmt(fmt), ##__VA_ARGS__)
#define pr_warn pr_warning
#define pr_notice(fmt, ...) \
printk(KERN_NOTICE pr_fmt(fmt), ##__VA_ARGS__)
#define pr_info(fmt, ...) \
printk(KERN_INFO pr_fmt(fmt), ##__VA_ARGS__)
8.在 Linux 內核啟動過程中,啟動 log 的最后往往會有這么一行信息。
Freeing unused kernel memory: 468K
請用本課程中的 section 屬性聲明,分析這段 log 背后的內核初始化及內存釋放過程。
9.在嵌入式 Linux 驅動開發中,驅動模塊是沒有 main() 入口函數的,請用本課程學過的知識分析:驅動是如何運行和初始化的。
10.驅動分析:在 linux4.4 源碼 linux-4.4/arch/arm/mach-footbridge/ebsa285.c 中,分析下面代碼的含義及 container_of 宏的作用。
MACHINE_START(EBSA285, "EBSA285")
/* Maintainer: Russell King */
.atag_offset = 0x100,
.video_start = 0x000a0000,
.video_end = 0x000bffff,
.map_io = footbridge_map_io,
.init_early = footbridge_sched_clock,
.init_irq = footbridge_init_irq,
.init_time = footbridge_timer_init,
.restart = footbridge_restart,
MACHINE_END
static void ebsa285_led_set(struct \
led_classdev *cdev, enum led_brightness b)
{
struct ebsa285_led *led = container_of(cdev,
struct ebsa285_led, cdev);
if (b == LED_OFF)
hw_led_state |= led->mask;
else
hw_led_state &= ~led->mask;
writeb(hw_led_state, xbus);
}
static enum led_brightness \
ebsa285_led_get(struct led_classdev *cdev)
{
struct ebsa285_led *led = container_of(cdev,
struct ebsa285_led, cdev);
return hw_led_state & led->mask ? LED_OFF : LED_FULL;
}
通過本課程的學習,再加上本節10個習題的練習,相信大家的 C 語言功底肯定又加深了一層!有了這些知識儲備基礎,基本上就掃除了 Linux 內核的閱讀障礙。相信大家在以后的工作、學習中一定會日益精進,不斷突破!
最后祝大家工作順利、學習愉快!
另外,大家如果想系統學習 Linux 內核中的某塊知識,或者說有哪些知識掌握得不是很好,想進階學習,但限于工作、學習繁忙,時間精力有限,無法系統地去學習,也可以跟我聯系交流
本教程根據 C語言嵌入式Linux高級編程視頻教程 第05期 改編,電子版書籍可加入QQ群:475504428 下載,更多嵌入式視頻教程,可關注:
微信公眾號:宅學部落(armlinuxfun)
51CTO學院-王利濤老師:http://edu.51cto.com/sd/d344f
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。