2017-2018-1 20179215《Linux內核原理與分析》第十周作業
阿新 • • 發佈:2017-12-03
自動填充 可移植性 智能 x86 調試 推薦 狀態 討論 ani
第17章 設備與模塊
一、設備類型
?除了以上3種典型的設備之外,其實Linux中還有一些其他的設備類型,其中見的較多的應該算是"偽設備"。所謂"偽設備",其實就是一些虛擬的設備,僅提供訪問內核功能而已,沒有物理設備與之關聯。典型的"偽設備"就是 /dev/random(內核隨機數發生器), /dev/null(空設備), /dev/zero(零設備), /dev/full(滿設備)
二、內核模塊
?Linux內核是模塊化組成的,內核中的模塊可以按需加載,從而保證內核啟動時不用加載所有的模塊,即減少了內核的大小,也提高了效率。通過編寫內核模塊來給內核增加功能或者接口是個很好的方式,既不用重新編譯內核,也方便調試和刪除。
?內核模塊可以帶參數也可以不帶參數,不帶參數的內核模塊比較簡單。帶參數的內核模塊:內核中已經提供了簡單的框架來給我們聲明參數。
?1.module_param(name, type, perm) : 定義一個模塊參數
?參數 name :: 既是用戶可見的參數名,也是模塊中存放模塊參數的變量名
?參數 type :: 參數的類型(byte, short, int, uint, long, ulong, charp, bool...) byte型存放在char變量中,bool型存放在int變量中
?參數 perm :: 指定模塊在 sysfs 文件系統中對應的文件權限(關於 sysfs 的內容後面介紹)
static int stu_id = 0; // 默認id
module_param(stu_id, int, 0644);
?2.module_ param_ named(name, variable, type, perm) : 定義一個模塊參數,並且參數對內對外的名稱不一樣
? 參數 name :: 用戶可見的參數名
?參數 variable :: 模塊中存放模塊參數的變量名
?參數 type和perm :: 同 module_param 中的 type 和 perm
static char* stu_name_in = "default name"; // 默認名字 module_param_named(stu_name_out, stu_name_in ,charp, 0644); /* stu_name_out 是對用戶開放的名稱 *stu_name_in 是內核模塊內部使用的名稱 */
?3. module_ param_ string(name, string, len, perm) : 拷貝字符串到指定的字符數組
?參數 name :: 用戶可見的參數名
?參數 string :: 模塊中存放模塊參數的變量名
?參數 len :: string 參數的緩沖區長度
?參數 perm :: 同 module_param 中的 perm
static char str_in[BUF_LEN];
module_param_string(str_out, str_in, BUF_LEN, 0);
/* perm=0 表示完全禁止 sysfs 項 */
?4.module_ param_ array(name, type, nump, perm) : 定義數組類型的模塊參數
?參數 name :: 同 module_param 中的 name
?參數 type :: 同 module_param 中的 type
?參數 nump :: 整型指針,存放數組的長度
?參數 perm :: 同 module_param 中的 perm
#define MAX_ARR_LEN 5
static int arr_len;
static int arr_in[MAX_ARR_LEN];
module_param_array(arr_in, int, &arr_len, 0644);
? 5.module_ param_ array_named(name, array, type, nump, perm) : 定義數組類型的模塊參數,並且數組參數對內對外的名稱不一樣
?參數 name :: 數組參數對外的名稱
?參數 array :: 數組參數對內的名稱
?參數 type,nump,perm :: 同 module_ param_ array 中的 type,nump,perm
#define MAX_ARR_LEN 5
static int arr_len;
static int arr_in[MAX_ARR_LEN];
module_param_array_named(arr_out, arr_in, int, &arr_len, 0644);
?6.參數描述宏
?可以通過 MODULE_ PARM_ DESC() 來給內核模塊的參數添加一些描述信息。這些描述信息在編譯完內核模塊後,可以通過modinfo命令查看。
static int stu_id = 0; // 默認id
module_param(stu_id, int, 0644);
MODULE_PARM_DESC(stu_id, "學生ID,默認為 0"); // 這句就是描述內核模塊參數 stu_id 的語句
<font color==blue size=4>內核模塊相關操作:
?1.模塊安裝
make modules_install <-- 把隨內核編譯出來的模塊安裝到合適的目錄中( /lib/modules/version/kernel )
?2. 模塊依賴性
?linux中自動生產模塊依賴性的命令:
depmod <-- 產生內核依賴關系信息
depmod -A <-- 只為新模塊生成依賴信息(速度更快)
?3. 模塊的載入
?內核模塊實驗時已經用過:
insmod module.ko
<-- 推薦使用以下的命令, 自動加載依賴的模塊
modprobe module [module parameters]
?4. 模塊的卸載
?內核模塊實驗時已經用過:
rmmod module.ko
<-- 推薦使用以下的命令, 自動卸載依賴的模塊
modprobe -r module
?5. 模塊導出符號表
?內核模塊被載入後,就動態的加載到內核中,為了能讓其他內核模塊使用其功能,需要將其中函數導出。內核模塊中導出函數的方法:
EXPORT_SYMBOL(函數名) <-- 接在要導出的函數後面即可
EXPORT_SYMBOL_GPL(函數名) <-- 和EXPORT_SYMBOL一樣,區別在於只對標記為GPL協議的模塊可見
內核對象:
?2.6內核中增加了一個引人註目的新特性--統一設備模型(device model)。
?統一設備模型的最初動機是為了實現智能的電源管理,linux 內核為了實現智能電源管理,需要建立表示系統中所有設備拓撲關系的樹結構,這樣在關閉電源時,可以從樹的節點開始關閉。實現了統一設備模型之後,還給內核帶來了如下的好處:
1.代碼重復最小化(統一處理的東西多了)
2. 可以列舉系統中所有設備,觀察它們的狀態,並查看它們連接的總線
3. 可以將系統中的全部設備以樹的形式完整,有效的展示出來--包括所有總線和內部連接
4. 可以將設備和其對應的驅動聯系起來,反之亦然
5. 可以將設備按照類型加以歸類,無需理解物理設備的拓撲結構
6. 可以沿設備樹的葉子向其根的反向依次遍歷,以保證能以正確的順序關閉設備電源
第19章 可移植性
?linux內核的移植性非常好, 目前的內核也支持非常多的體系結構(有20多個).但是剛開始時, linux也只支持 intel i386 架構, 從 v1.2版開始支持 Digital Alpha, Intel x86, MIPS和SPARC(雖然支持的還不是很完善).從 v2.0版本開始加入了對 Motorala 68K和PowerPC的官方支持, v2.2版本開始新增了 ARMS, IBM S390和UltraSPARC的支持.v2.4版本支持的體系結構數達到了15個, v2.6版本支持的體系結構數目提高到了21個.考慮到內核支持如此之多的架構, 在內核開發的時候就需要考慮編碼的可移植性。
?數據對齊也是增強可移植性的一個重要方面(有的體系結構對數據對齊要求非常嚴格, 載入未對齊的數據可導致性能下降, 甚至錯誤)。數據對齊的意思就是: 數據的內存地址可以被 4 整除。
?對於結構體, 保證結構體中每個元素能夠正確對齊即可。如果結構體中的元素沒有對齊, 編譯器會自動填充結構體, 保證它是對齊的. 比如下面的代碼, 預計應該輸出12, 實際卻輸出了24。
#include <stdio.h>
struct animal_struct
{
char dog; /* 1個字節 */
unsigned long cat; /* 8個字節 */
unsigned short pig; /* 2個字節 */
char fox; /* 1個字節 */
};
int main(int argc, char *argv[])
{
/* 在我的64bit 系統中是按8位對齊, 下面的代碼輸出 24 */
printf ("sizeof(animal_struct)=%d\n", sizeof(struct animal_struct));
return 0;
}
?結構體應該被填充成如下形式:
struct animal_struct
{
char dog; /* 1個字節 */
/* 此處填充了7個字節 */
unsigned long cat; /* 8個字節 */
unsigned short pig; /* 2個字節 */
char fox; /* 1個字節 */
/* 此處填充了5個字節 */
};
?通過調整結構體中元素順序, 可以減少填充的字節數, 比如上述結構體如果定義成如下順序:
struct animal_struct
{
unsigned long cat; /* 8個字節 */
unsigned short pig; /* 2個字節 */
char dog; /* 1個字節 */
char fox; /* 1個字節 */
/* 此處填充了4個字節 */
};
註意: 雖然調整結構體中元素的順序可以減少填充的字節, 從而降低內存的消耗.但是對於內核中已有的那些結構, 千萬不能隨便調整其元素順序, 因為內核中很多現存的方法都是通過元素在結構體中位置偏移來獲取元素的.
第20章 補丁,開發和社區
1. 加入社區
?如果想為linux貢獻代碼, 那麽加入linux社區是必須的, 加入了社區, 不僅可以及時內核的最新消息, 而且可以及時和社區內有經驗的內核開發者交流經驗.同時也是提交代碼和討論代碼的地方, 了解社區的規則, 融入社區環境之中, 才能更好的學習內核, 體會內核開發的樂趣和成就感.內核社區說白了就是內核郵件列表(LKML linux kernel mail list)
?訂閱郵件列表的網址:http://vger.kernel.org/vger-lists.html 這裏面有linux相關的各種郵件列表
?關於內核的郵件列表是: http://vger.kernel.org/vger-lists.html#linux-kernel
2. 編碼風格
?社區給我們提供了學習和貢獻內核的地方, 但是為了避免不必要的麻煩(被別人指責或者無人理睬), 首先得好好了解一些內核代碼的編碼風格.linux的編碼風格都記錄在 Documentation/CodingStyle 內核開發前要好好研讀以下, 之後有時間我會整理到博客中.
3. 提交補丁
?準備工作都完成之後, 就可以開始內核開發之旅了 :)只要堅持不斷的學習和嘗試, 總有一天會為了內核貢獻自己的代碼, 這時候, 就需要了解如何提交代碼, 也就是內核補丁.如果是發現了BUG或者有改善, 可以將BUG的描述或者改善代碼發送給對應的維護者.(內核各個子系統的維護者信息在內核代碼根目錄下的 MAINTAINERS 文件中)生成BUG或者改善代碼的補丁有2種方法:
(1)、用diff命令創建補丁
生成patch
diff -urN linux-old/ linux-new/ > my-patch # 比對整個內核代碼文件夾
OR
diff -u linux-old/some/file linux-new/some/file > my-patch # 比對某個文件
?應用patch, 應用了patch之後, linux-old 和 linux-new 中的代碼就一樣了
cd linux-old
patch -p1 < ../my-patch # 這個命令是進入linux內核代碼根目錄內執行的
?PS. 還有個很有用的工具 diffstat
diffstat -p1 my-patch # 列出補丁所引起的變更的統計(加入或移去的代碼行)
(2)、用git命令創建補丁
?提交修改的或新增的代碼
git commit -a # 提交所有修改的代碼
OR
git commit linux-src/some/file.c # 提交某個修改的代碼
OR
git add linux-src/some/new-file.c # 把新增的文件加入版本庫
git commit -a # 提交新增的文件
# 生成patch
git format-patch -N # N 是正整數, 這條命令生成最後N次提交產生的補丁
OR
git format-patch -1 # 最後1次提交產生的補丁
2017-2018-1 20179215《Linux內核原理與分析》第十周作業