精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

吐血整理的萬字Linux內核源碼規范

系統 Linux
曾經在開發Linux內核驅動的時候,創建了一個補丁文件,但是在把補丁打到主分支的時候提示很多編碼風格的錯誤問題,后來重做了補丁才解決了問題,這也是沒有嚴格按照的Linux編碼風格從而導致的問題。因為當時代碼量不大,所以解決問題的時間相對較少。在代碼量增大的情況下可以借助工具進行自動修改。
 
本文轉載自微信公眾號「小麥大叔」,作者菜刀和小麥 。轉載本文請聯系小麥大叔公眾號。  
  •  從編碼風格錯誤開始
  • 快速修改編碼風格的工具
    • scripts/checkpatch.pl
    • scripts/Lindent
    • astyle
  • Linux 內核代碼風格
  • 1 縮進
  • 2 把長的行和字符串打散
  • 3 大括號和空格的放置
  • 4 命名
  • 5 Typedef
  • 6 函數
  • 7 集中的函數退出途徑
  • 8 注釋
  • 9 你已經把事情弄糟了
  • 10 Kconfig 配置文件
  • 11 數據結構
  • 12 宏,枚舉和RTL
  • 13 打印內核消息
  • 14 分配內存
  • 15 內聯弊病
  • 16 函數返回值及命名
  • 17 不要重新發明內核宏
  • 18 編輯器模式行和其他需要羅嗦的事情
  • 19 內聯匯編
  • 20 條件編譯

從編碼風格錯誤開始

曾經在開發Linux內核驅動的時候,創建了一個補丁文件,但是在把補丁打到主分支的時候提示很多編碼風格的錯誤問題,后來重做了補丁才解決了問題,這也是沒有嚴格按照的Linux編碼風格從而導致的問題。因為當時代碼量不大,所以解決問題的時間相對較少。在代碼量增大的情況下可以借助工具進行自動修改。

快速修改編碼風格的工具

scripts/checkpatch.pl

這是一個檢查patch是否符合內核編碼規范的腳本。默認的調用也確實如此。如果用來檢查原文件,需要加上-f的選項。

scripts/Lindent

源碼路徑下的scripts目錄中的工具Lindent可以用來自動修改縮進問題。不過使用Lindent要求系統安裝indent這個工具。Ubuntu系統下可以使用apt-get install indent進行「安裝」。

astyle

比較推薦使用這個工具,因為比較相當方便,可以一鍵式轉換成linux,java,gnu等等風格。

下載地址項目地址文檔說明

如何使用,可以參考具體文檔說明,寫的比較詳細。

總而言之,應該顧全大局,在進行內核開發和驅動開發的時候,嚴格遵守Linux的編碼規范,避免由于編碼不規范帶來的種種問題,可以參考內核路徑下Documentation/CodingStyle文檔,以下轉自Linux內核文檔,最權威的文檔路徑,很全很強大,原來在這個網站上已經有中文版了,感謝萬分,具體的更新可以跳轉到你懂的網址。

「來自 Documentation/process/coding-style.rst 的中文翻譯」

Linux 內核代碼風格

這是一個簡短的文檔,描述了 linux 內核的首選代碼風格。代碼風格是因人而異的, 而且我不愿意把自己的觀點強加給任何人,但這就像我去做任何事情都必須遵循的原則 那樣,我也希望在絕大多數事上保持這種的態度。請 (在寫代碼時) 至少考慮一下這里 的代碼風格。

首先,我建議你打印一份 GNU 代碼規范,然后不要讀。燒了它,這是一個具有重大象征性意義的動作。

不管怎樣,現在我們開始:

1 縮進

制表符是 8 個字符,所以縮進也是 8 個字符。有些異端運動試圖將縮進變為 4 (甚至 2!) 字符深,這幾乎相當于嘗試將圓周率的值定義為 3。

理由:縮進的全部意義就在于清楚的定義一個控制塊起止于何處。尤其是當你盯著你的 屏幕連續看了 20 小時之后,你將會發現大一點的縮進會使你更容易分辨縮進。

現在,有些人會抱怨 8 個字符的縮進會使代碼向右邊移動的太遠,在 80 個字符的終端 屏幕上就很難讀這樣的代碼。這個問題的答案是,如果你需要 3 級以上的縮進,不管用 何種方式你的代碼已經有問題了,應該修正你的程序。

簡而言之,8 個字符的縮進可以讓代碼更容易閱讀,還有一個好處是當你的函數嵌套太 深的時候可以給你警告。留心這個警告。

在 switch 語句中消除多級縮進的首選的方式是讓 switch 和從屬于它的 case 標簽對齊于同一列,而不要 兩次縮進 case 標簽。比如:

  1. switch (suffix) { 
  2. case 'G'
  3. case 'g'
  4.         mem <<= 30; 
  5.         break; 
  6. case 'M'
  7. case 'm'
  8.         mem <<= 20; 
  9.         break; 
  10. case 'K'
  11. case 'k'
  12.         mem <<= 10; 
  13.         /* fall through */ 
  14. default
  15.         break; 

不要把多個語句放在一行里,除非你有什么東西要隱藏:

  1. if (condition) do_this; 
  2.   do_something_everytime; 

也不要在一行里放多個賦值語句。內核代碼風格超級簡單。就是避免可能導致別人誤讀 的表達式。

除了注釋、文檔和 Kconfig 之外,不要使用空格來縮進,前面的例子是例外,是有意為 之。

選用一個好的編輯器,不要在行尾留空格。

2 把長的行和字符串打散

代碼風格的意義就在于使用平常使用的工具來維持代碼的可讀性和可維護性。

每一行的長度的限制是 80 列,我們強烈建議您遵守這個慣例。

長于 80 列的語句要打散成有意義的片段。除非超過 80 列能顯著增加可讀性,并且不 會隱藏信息。子片段要明顯短于母片段,并明顯靠右。這同樣適用于有著很長參數列表 的函數頭。然而,絕對不要打散對用戶可見的字符串,例如 printk 信息,因為這樣就 很難對它們 grep。

3 大括號和空格的放置

C 語言風格中另外一個常見問題是大括號的放置。和縮進大小不同,選擇或棄用某種放 置策略并沒有多少技術上的原因,不過首選的方式,就像 Kernighan 和 Ritchie 展示 給我們的,是把起始大括號放在行尾,而把結束大括號放在行首,所以:

  1. if (x is true) { 
  2.         we do y 

這適用于所有的非函數語句塊 (if, switch, for, while, do)。比如:

  1. switch (action) { 
  2. case KOBJ_ADD: 
  3.         return "add"
  4. case KOBJ_REMOVE: 
  5.         return "remove"
  6. case KOBJ_CHANGE: 
  7.         return "change"
  8. default
  9.         return NULL

不過,有一個例外,那就是函數:函數的起始大括號放置于下一行的開頭,所以:

  1. int function(int x) 
  2.         body of function 

全世界的異端可能會抱怨這個不一致性是... 呃... 不一致的,不過所有思維健全的人 都知道 (a) K&R 是 「正確的」 并且 (b) K&R 是正確的。此外,不管怎樣函數都是特 殊的 (C 函數是不能嵌套的)。

注意結束大括號獨自占據一行,除非它后面跟著同一個語句的剩余部分,也就是 do 語 句中的 “while” 或者 if 語句中的 “else”,像這樣:

  1. do { 
  2.         body of do-loop 
  3. } while (condition); 

  1. if (x == y) { 
  2.         .. 
  3. else if (x > y) { 
  4.         ... 
  5. else { 
  6.         .... 

理由:K&R。

也請注意這種大括號的放置方式也能使空 (或者差不多空的) 行的數量最小化,同時不 失可讀性。因此,由于你的屏幕上的新行是不可再生資源 (想想 25 行的終端屏幕),你 將會有更多的空行來放置注釋。

當只有一個單獨的語句的時候,不用加不必要的大括號。

  1. if (condition) 
  2.         action(); 

  1. if (condition) 
  2.         do_this(); 
  3. else 
  4.         do_that(); 

這并不適用于只有一個條件分支是單語句的情況;這時所有分支都要使用大括號:

  1. if (condition) { 
  2.         do_this(); 
  3.         do_that(); 
  4. else { 
  5.         otherwise(); 

3.1 空格

Linux 內核的空格使用方式 (主要) 取決于它是用于函數還是關鍵字。(大多數) 關鍵字 后要加一個空格。值得注意的例外是 sizeof, typeof, alignof 和 「attribute」,這 些關鍵字某些程度上看起來更像函數 (它們在 Linux 里也常常伴隨小括號而使用,盡管 在 C 里這樣的小括號不是必需的,就像 struct fileinfo info; 聲明過后的 sizeof info)。

所以在這些關鍵字之后放一個空格:

  1. if, switch, casefor, do, while 

但是不要在 sizeof, typeof, alignof 或者 「attribute」 這些關鍵字之后放空格。例如,

  1. s = sizeof(struct file); 

不要在小括號里的表達式兩側加空格。這是一個 「反例」 :

  1. s = sizeof( struct file ); 

當聲明指針類型或者返回指針類型的函數時, * 的首選使用方式是使之靠近變量名 或者函數名,而不是靠近類型名。例子:

  1. char *linux_banner; 
  2. unsigned long long memparse(char *ptr, char **retptr); 
  3. char *match_strdup(substring_t *s); 

在大多數二元和三元操作符兩側使用一個空格,例如下面所有這些操作符:

  1. =  +  -  <  >  *  /  %  |  &  ^  <=  >=  ==  !=  ?  : 

但是一元操作符后不要加空格:

  1. &  *  +  -  ~  !  sizeof  typeof  alignof  __attribute__  defined 

后綴自加和自減一元操作符前不加空格:

  1. ++  -- 

前綴自加和自減一元操作符后不加空格:

  1. ++  -- 

. 和 -> 結構體成員操作符前后不加空格。

不要在行尾留空白。有些可以自動縮進的編輯器會在新行的行首加入適量的空白,然后 你就可以直接在那一行輸入代碼。不過假如你最后沒有在那一行輸入代碼,有些編輯器 就不會移除已經加入的空白,就像你故意留下一個只有空白的行。包含行尾空白的行就 這樣產生了。

當 git 發現補丁包含了行尾空白的時候會警告你,并且可以應你的要求去掉行尾空白;不過如果你是正在打一系列補丁,這樣做會導致后面的補丁失敗,因為你改變了補丁的 上下文。

4 命名

C 是一個簡樸的語言,你的命名也應該這樣。和 Modula-2 和 Pascal 程序員不同, C 程序員不使用類似 ThisVariableIsATemporaryCounter 這樣華麗的名字。C 程序員會 稱那個變量為 tmp ,這樣寫起來會更容易,而且至少不會令其難于理解。

不過,雖然混用大小寫的名字是不提倡使用的,但是全局變量還是需要一個具描述性的 名字。稱一個全局函數為 foo 是一個難以饒恕的錯誤。

全局變量 (只有當你 「真正」 需要它們的時候再用它) 需要有一個具描述性的名字,就 像全局函數。如果你有一個可以計算活動用戶數量的函數,你應該叫它count_active_users() 或者類似的名字,你不應該叫它 cntuser() 。

在函數名中包含函數類型 (所謂的匈牙利命名法) 是腦子出了問題——編譯器知道那些類 型而且能夠檢查那些類型,這樣做只能把程序員弄糊涂了。難怪微軟總是制造出有問題 的程序。

本地變量名應該簡短,而且能夠表達相關的含義。如果你有一些隨機的整數型的循環計 數器,它應該被稱為 i 。叫它 loop_counter 并無益處,如果它沒有被誤解的 可能的話。類似的, tmp 可以用來稱呼任意類型的臨時變量。

如果你怕混淆了你的本地變量名,你就遇到另一個問題了,叫做函數增長荷爾蒙失衡綜 合癥。請看第六章 (函數)。

5 Typedef

不要使用類似 vps_t 之類的東西。

對結構體和指針使用 typedef 是一個 「錯誤」 。當你在代碼里看到:

  1. vps_t a; 

這代表什么意思呢?

相反,如果是這樣

  1. struct virtual_container *a; 

你就知道 a 是什么了。

很多人認為 typedef 能提高可讀性 。實際不是這樣的。它們只在下列情況下有用:

1.完全不透明的對象 (這種情況下要主動使用 typedef 來 「隱藏」 這個對象實際上 是什么)。

例如:pte_t 等不透明對象,你只能用合適的訪問函數來訪問它們。

❝不透明性和 “訪問函數” 本身是不好的。我們使用 pte_t 等類型的原因在于真 的是完全沒有任何共用的可訪問信息。❞

2.清楚的整數類型,如此,這層抽象就可以 「幫助」 消除到底是 int 還是 long 的混淆。

u8/u16/u32 是完全沒有問題的 typedef,不過它們更符合類別 (d) 而不是這里。

❝要這樣做,必須事出有因。如果某個變量是 unsigned long ,那么沒有必要typedef unsigned long myflags_t;❞

不過如果有一個明確的原因,比如它在某種情況下可能會是一個 unsigned int 而在其他情況下可能為 unsigned long ,那么就不要猶豫,請務必使用 typedef。

3.當你使用 sparse 按字面的創建一個 「新」 類型來做類型檢查的時候。

4.和標準 C99 類型相同的類型,在某些例外的情況下。

雖然讓眼睛和腦筋來適應新的標準類型比如 uint32_t 不需要花很多時間,可 是有些人仍然拒絕使用它們。

因此,Linux 特有的等同于標準類型的 u8/u16/u32/u64 類型和它們的有符號 類型是被允許的——盡管在你自己的新代碼中,它們不是強制要求要使用的。

當編輯已經使用了某個類型集的已有代碼時,你應該遵循那些代碼中已經做出的選 擇。

5.可以在用戶空間安全使用的類型。

在某些用戶空間可見的結構體里,我們不能要求 C99 類型而且不能用上面提到的 u32類型。因此,我們在與用戶空間共享的所有結構體中使用 __u32 和類似 的類型。

可能還有其他的情況,不過基本的規則是 「永遠不要」 使用 typedef,除非你可以明 確的應用上述某個規則中的一個。

總的來說,如果一個指針或者一個結構體里的元素可以合理的被直接訪問到,那么它們 就不應該是一個 typedef。

6 函數

函數應該簡短而漂亮,并且只完成一件事情。函數應該可以一屏或者兩屏顯示完 (我們 都知道 ISO/ANSI 屏幕大小是 80x24),只做一件事情,而且把它做好。

一個函數的最大長度是和該函數的復雜度和縮進級數成反比的。所以,如果你有一個理 論上很簡單的只有一個很長 (但是簡單) 的 case 語句的函數,而且你需要在每個 case 里做很多很小的事情,這樣的函數盡管很長,但也是可以的。

不過,如果你有一個復雜的函數,而且你懷疑一個天分不是很高的高中一年級學生可能 甚至搞不清楚這個函數的目的,你應該嚴格遵守前面提到的長度限制。使用輔助函數, 并為之取個具描述性的名字 (如果你覺得它們的性能很重要的話,可以讓編譯器內聯它 們,這樣的效果往往會比你寫一個復雜函數的效果要好。)

函數的另外一個衡量標準是本地變量的數量。此數量不應超過 5-10 個,否則你的函數 就有問題了。重新考慮一下你的函數,把它分拆成更小的函數。人的大腦一般可以輕松 的同時跟蹤 7 個不同的事物,如果再增多的話,就會糊涂了。即便你聰穎過人,你也可 能會記不清你 2 個星期前做過的事情。

在源文件里,使用空行隔開不同的函數。如果該函數需要被導出,它的 「EXPORT」 宏 應該緊貼在它的結束大括號之下。比如:

  1. int system_is_up(void) 
  2.         return system_state == SYSTEM_RUNNING; 
  3. EXPORT_SYMBOL(system_is_up); 

在函數原型中,包含函數名和它們的數據類型。雖然 C 語言里沒有這樣的要求,在 Linux 里這是提倡的做法,因為這樣可以很簡單的給讀者提供更多的有價值的信息。

7 集中的函數退出途徑

雖然被某些人聲稱已經過時,但是 goto 語句的等價物還是經常被編譯器所使用,具體 形式是無條件跳轉指令。

當一個函數從多個位置退出,并且需要做一些類似清理的常見操作時,goto 語句就很方 便了。如果并不需要清理操作,那么直接 return 即可。

選擇一個能夠說明 goto 行為或它為何存在的標簽名。如果 goto 要釋放 buffer, 一個不錯的名字可以是 out_free_buffer: 。別去使用像 err1: 和 err2: 這樣的GW_BASIC 名稱,因為一旦你添加或刪除了 (函數的) 退出路徑,你就必須對它們 重新編號,這樣會難以去檢驗正確性。

使用 goto 的理由是:

  • 無條件語句容易理解和跟蹤
  • 嵌套程度減小
  • 可以避免由于修改時忘記更新個別的退出點而導致錯誤
  • 讓編譯器省去刪除冗余代碼的工作 ;)
  1. int fun(int a) 
  2.         int result = 0; 
  3.         char *buffer; 
  4.  
  5.         buffer = kmalloc(SIZE, GFP_KERNEL); 
  6.         if (!buffer) 
  7.                 return -ENOMEM; 
  8.  
  9.         if (condition1) { 
  10.                 while (loop1) { 
  11.                         ... 
  12.                 } 
  13.                 result = 1; 
  14.                 goto out_free_buffer; 
  15.         } 
  16.         ... 
  17. out_free_buffer: 
  18.         kfree(buffer); 
  19.         return result; 

一個需要注意的常見錯誤是 一個 err 錯誤 ,就像這樣:

  1. err: 
  2.         kfree(foo->bar); 
  3.         kfree(foo); 
  4.         return ret; 

這段代碼的錯誤是,在某些退出路徑上 foo 是 NULL。通常情況下,通過把它分離 成兩個錯誤標簽 err_free_bar: 和 err_free_foo: 來修復這個錯誤:

  1. err_free_bar: 
  2.        kfree(foo->bar); 
  3. err_free_foo: 
  4.        kfree(foo); 
  5.        return ret; 

理想情況下,你應該模擬錯誤來測試所有退出路徑。

8 注釋

注釋是好的,不過有過度注釋的危險。永遠不要在注釋里解釋你的代碼是如何運作的:更好的做法是讓別人一看你的代碼就可以明白,解釋寫的很差的代碼是浪費時間。

一般的,你想要你的注釋告訴別人你的代碼做了什么,而不是怎么做的。也請你不要把 注釋放在一個函數體內部:如果函數復雜到你需要獨立的注釋其中的一部分,你很可能 需要回到第六章看一看。你可以做一些小注釋來注明或警告某些很聰明 (或者槽糕) 的 做法,但不要加太多。你應該做的,是把注釋放在函數的頭部,告訴人們它做了什么, 也可以加上它做這些事情的原因。

當注釋內核 API 函數時,請使用 kernel-doc 格式。請看 Documentation/doc-guide/ 和 scripts/kernel-doc 以獲得詳細信息。

長 (多行) 注釋的首選風格是:

  1. /* 
  2.  * This is the preferred style for multi-line 
  3.  * comments in the Linux kernel source code. 
  4.  * Please use it consistently. 
  5.  * 
  6.  * Description:  A column of asterisks on the left side, 
  7.  * with beginning and ending almost-blank lines. 
  8.  */ 

對于在 net/ 和 drivers/net/ 的文件,首選的長 (多行) 注釋風格有些不同。

  1. /* The preferred comment style for files in net/ and drivers/net 
  2.  * looks like this. 
  3.  * 
  4.  * It is nearly the same as the generally preferred comment style, 
  5.  * but there is no initial almost-blank line. 
  6.  */ 

注釋數據也是很重要的,不管是基本類型還是衍生類型。為了方便實現這一點,每一行 應只聲明一個數據 (不要使用逗號來一次聲明多個數據)。這樣你就有空間來為每個數據 寫一段小注釋來解釋它們的用途了。

9 你已經把事情弄糟了

這沒什么,我們都是這樣??赡苣愕氖褂昧撕荛L時間 Unix 的朋友已經告訴你 GNU emacs 能自動幫你格式化 C 源代碼,而且你也注意到了,確實是這樣,不過它 所使用的默認值和我們想要的相去甚遠 (實際上,甚至比隨機打的還要差——無數個猴子 在 GNU emacs 里打字永遠不會創造出一個好程序) (譯注:Infinite Monkey Theorem)

所以你要么放棄 GNU emacs,要么改變它讓它使用更合理的設定。要采用后一個方案, 你可以把下面這段粘貼到你的 .emacs 文件里。

  1. (defun c-lineup-arglist-tabs-only (ignored) 
  2.   "Line up argument lists by tabs, not spaces" 
  3.   (let* ((anchor (c-langelem-pos c-syntactic-element)) 
  4.          (column (c-langelem-2nd-pos c-syntactic-element)) 
  5.          (offset (- (1+ column) anchor)) 
  6.          (steps (floor offset c-basic-offset))) 
  7.     (* (max steps 1) 
  8.        c-basic-offset))) 
  9.  
  10. (add-hook 'c-mode-common-hook 
  11.           (lambda () 
  12.             ;; Add kernel style 
  13.             (c-add-style 
  14.              "linux-tabs-only" 
  15.              '("linux" (c-offsets-alist 
  16.                         (arglist-cont-nonempty 
  17.                          c-lineup-gcc-asm-reg 
  18.                          c-lineup-arglist-tabs-only)))))) 
  19.  
  20. (add-hook 'c-mode-hook 
  21.           (lambda () 
  22.             (let ((filename (buffer-file-name))) 
  23.               ;; Enable kernel mode for the appropriate files 
  24.               (when (and filename 
  25.                          (string-match (expand-file-name "~/src/linux-trees"
  26.                                        filename)) 
  27.                 (setq indent-tabs-mode t) 
  28.                 (setq show-trailing-whitespace t) 
  29.                 (c-set-style "linux-tabs-only"))))) 

這會讓 emacs 在 ~/src/linux-trees 下的 C 源文件獲得更好的內核代碼風格。

不過就算你嘗試讓 emacs 正確的格式化代碼失敗了,也并不意味著你失去了一切:還可 以用 indent 。

不過,GNU indent 也有和 GNU emacs 一樣有問題的設定,所以你需要給它一些命令選 項。不過,這還不算太糟糕,因為就算是 GNU indent 的作者也認同 K&R 的權威性 (GNU 的人并不是壞人,他們只是在這個問題上被嚴重的誤導了),所以你只要給 indent 指定選項 -kr -i8 (代表 K&R,8 字符縮進),或使用 scripts/Lindent 這樣就可以以最時髦的方式縮進源代碼。

indent 有很多選項,特別是重新格式化注釋的時候,你可能需要看一下它的手冊。不過記?。篿ndent 不能修正壞的編程習慣。

10 Kconfig 配置文件

對于遍布源碼樹的所有 Kconfig* 配置文件來說,它們縮進方式有所不同。緊挨著 config 定義的行,用一個制表符縮進,然而 help 信息的縮進則額外增加 2 個空 格。舉個例子:

  1. config AUDIT 
  2.       bool "Auditing support" 
  3.       depends on NET 
  4.       help 
  5.         Enable auditing infrastructure that can be used with another 
  6.         kernel subsystem, such as SELinux (which requires this for 
  7.         logging of avc messages output).  Does not do system-call 
  8.         auditing without CONFIG_AUDITSYSCALL. 

而那些危險的功能 (比如某些文件系統的寫支持) 應該在它們的提示字符串里顯著的聲 明這一點:

  1. config ADFS_FS_RW 
  2.       bool "ADFS write support (DANGEROUS)" 
  3.       depends on ADFS_FS 
  4.       ... 

要查看配置文件的完整文檔,請看 Documentation/kbuild/kconfig-language.txt。

11 數據結構

如果一個數據結構,在創建和銷毀它的單線執行環境之外可見,那么它必須要有一個引 用計數器。內核里沒有垃圾收集 (并且內核之外的垃圾收集慢且效率低下),這意味著你 絕對需要記錄你對這種數據結構的使用情況。

引用計數意味著你能夠避免上鎖,并且允許多個用戶并行訪問這個數據結構——而不需要 擔心這個數據結構僅僅因為暫時不被使用就消失了,那些用戶可能不過是沉睡了一陣或 者做了一些其他事情而已。

注意上鎖 「不能」 取代引用計數。上鎖是為了保持數據結構的一致性,而引用計數是一 個內存管理技巧。通常二者都需要,不要把兩個搞混了。

很多數據結構實際上有 2 級引用計數,它們通常有不同 類 的用戶。子類計數器統 計子類用戶的數量,每當子類計數器減至零時,全局計數器減一。

這種 多級引用計數 的例子可以在內存管理 (struct mm_struct: mm_users 和 mm_count),和文件系統 (struct super_block: s_count 和 s_active) 中找到。

記?。喝绻硪粋€執行線索可以找到你的數據結構,但這個數據結構沒有引用計數器, 這里幾乎肯定是一個 bug。

12 宏,枚舉和RTL

用于定義常量的宏的名字及枚舉里的標簽需要大寫。

  1. #define CONSTANT 0x12345 

在定義幾個相關的常量時,最好用枚舉。

宏的名字請用大寫字母,不過形如函數的宏的名字可以用小寫字母。

一般的,如果能寫成內聯函數就不要寫成像函數的宏。

含有多個語句的宏應該被包含在一個 do-while 代碼塊里:

  1. #define CONSTANT 0x12345 

使用宏的時候應避免的事情:

影響控制流程的宏:

  1. #define FOO(x)                                  \ 
  2.         do {                                    \ 
  3.                 if (blah(x) < 0)                \ 
  4.                         return -EBUGGERED;      \ 
  5.         } while (0) 

「非?!?不好。它看起來像一個函數,不過卻能導致 調用 它的函數退出;不要打 亂讀者大腦里的語法分析器。

依賴于一個固定名字的本地變量的宏:

  1. #define FOO(val) bar(index, val) 

可能看起來像是個不錯的東西,不過它非常容易把讀代碼的人搞糊涂,而且容易導致看起 來不相關的改動帶來錯誤。

  1. 作為左值的帶參數的宏:FOO(x) = y;如果有人把 FOO 變成一個內聯函數的話,這 種用法就會出錯了。
  2. 忘記了優先級:使用表達式定義常量的宏必須將表達式置于一對小括號之內。帶參數 的宏也要注意此類問題。
  1. #define CONSTANT 0x4000 
  2. #define CONSTEXP (CONSTANT | 3) 

在宏里定義類似函數的本地變量時命名沖突:

  1. #define FOO(x)                          \ 
  2. ({                                      \ 
  3.         typeof(x) ret;                  \ 
  4.         ret = calc_ret(x);              \ 
  5.         (ret);                          \ 
  6. }) 

ret 是本地變量的通用名字 - __foo_ret 更不容易與一個已存在的變量沖突。

cpp 手冊對宏的講解很詳細。gcc internals 手冊也詳細講解了 RTL,內核里的匯編語 言經常用到它。

13 打印內核消息

內核開發者應該是受過良好教育的。請一定注意內核信息的拼寫,以給人以好的印象。不要用不規范的單詞比如 dont,而要用 do not 或者 don't 。保證這些信 息簡單明了,無歧義。

內核信息不必以英文句號結束。

在小括號里打印數字 (%d) 沒有任何價值,應該避免這樣做。

里有一些驅動模型診斷宏,你應該使用它們,以確保信息對應于正確 的設備和驅動,并且被標記了正確的消息級別。這些宏有:dev_err(), dev_warn(), dev_info() 等等。對于那些不和某個特定設備相關連的信息, 定義了 pr_notice(), pr_info(), pr_warn(), pr_err()和其他。

寫出好的調試信息可以是一個很大的挑戰;一旦你寫出后,這些信息在遠程除錯時能提 供極大的幫助。然而打印調試信息的處理方式同打印非調試信息不同。其他 pr_XXX() 函數能無條件地打印,pr_debug() 卻不;默認情況下它不會被編譯,除非定義了 DEBUG 或設定了 CONFIG_DYNAMIC_DEBUG。實際這同樣是為了 dev_dbg(),一個相關約定是在一 個已經開啟了 DEBUG 時,使用 VERBOSE_DEBUG 來添加 dev_vdbg()。

許多子系統擁有 Kconfig 調試選項來開啟 -DDEBUG 在對應的 Makefile 里面;在其他 情況下,特殊文件使用 #define DEBUG。當一條調試信息需要被無條件打印時,例如, 如果已經包含一個調試相關的 #ifdef 條件,printk(KERN_DEBUG ...) 就可被使用。

14 分配內存

內核提供了下面的一般用途的內存分配函數:kmalloc(), kzalloc(), kmalloc_array(), kcalloc(), vmalloc() 和 vzalloc()。請參考 API 文檔以獲取有關它們的詳細信息。

傳遞結構體大小的首選形式是這樣的:

  1. p = kmalloc(sizeof(*p), ...); 

另外一種傳遞方式中,sizeof 的操作數是結構體的名字,這樣會降低可讀性,并且可能 會引入 bug。有可能指針變量類型被改變時,而對應的傳遞給內存分配函數的 sizeof 的結果不變。

強制轉換一個 void 指針返回值是多余的。C 語言本身保證了從 void 指針到其他任何 指針類型的轉換是沒有問題的。

分配一個數組的首選形式是這樣的:

  1. p = kmalloc_array(n, sizeof(...), ...); 

分配一個零長數組的首選形式是這樣的:

  1. p = kcalloc(n, sizeof(...), ...); 

兩種形式檢查分配大小 n * sizeof(...) 的溢出,如果溢出返回 NULL。

15 內聯弊病

有一個常見的誤解是 內聯 是 gcc 提供的可以讓代碼運行更快的一個選項。雖然使用內聯函數有時候是恰當的 (比如作為一種替代宏的方式,請看第十二章),不過很多情 況下不是這樣?!竔nline 的過度使用會使內核變大」,從而使整個系統運行速度變慢。因為體積大內核會占用更多的指令高速緩存,而且會導致 pagecache 的可用內存減少。想象一下,一次 pagecache 未命中就會導致一次磁盤尋址,將耗時 5 毫秒。

「5 毫秒的 時間內 CPU 能執行很多很多指令?!?/p>

一個基本的原則是如果一個函數有 3 行以上,就不要把它變成內聯函數。這個原則的一 個例外是,如果你知道某個參數是一個編譯時常量,而且因為這個常量你確定編譯器在 編譯時能優化掉你的函數的大部分代碼,那仍然可以給它加上 inline 關鍵字。kmalloc() 內聯函數就是一個很好的例子。

人們經常主張給 static 的而且只用了一次的函數加上 inline,如此不會有任何損失, 因為沒有什么好權衡的。雖然從技術上說這是正確的,但是實際上這種情況下即使不加 inline gcc 也可以自動使其內聯。而且其他用戶可能會要求移除 inline,由此而來的 爭論會抵消 inline 自身的潛在價值,得不償失。

16 函數返回值及命名

函數可以返回多種不同類型的值,最常見的一種是表明函數執行成功或者失敗的值。這樣 的一個值可以表示為一個錯誤代碼整數 (-Exxx=失敗,0=成功) 或者一個 成功 布爾值 (0=失敗,非0=成功)。

混合使用這兩種表達方式是難于發現的 bug 的來源。如果 C 語言本身嚴格區分整形和 布爾型變量,那么編譯器就能夠幫我們發現這些錯誤... 不過 C 語言不區分。為了避免 產生這種 bug,請遵循下面的慣例:

  1. 如果函數的名字是一個動作或者強制性的命令,那么這個函數應該返回錯誤代 
  2. 碼整數。如果是一個判斷,那么函數應該返回一個 "成功" 布爾值。 

如果函數的名字是一個動作或者強制性的命令,那么這個函數應該返回錯誤代碼整數。如果是一個判斷,那么函數應該返回一個 "成功" 布爾值。

比如, add work 是一個命令,所以 add_work() 在成功時返回 0,在失敗時返回 -EBUSY。類似的,因為 PCI device present 是一個判斷,所以 pci_dev_present() 在成功找到一個匹配的設備時應該返回 1,如果找不到時應該返回 0。

所有 EXPORTed 函數都必須遵守這個慣例,所有的公共函數也都應該如此。私有 (static) 函數不需要如此,但是我們也推薦這樣做。

返回值是實際計算結果而不是計算是否成功的標志的函數不受此慣例的限制。一般的, 他們通過返回一些正常值范圍之外的結果來表示出錯。典型的例子是返回指針的函數, 他們使用 NULL 或者 ERR_PTR 機制來報告錯誤。

17 不要重新發明內核

宏頭文件 include/linux/kernel.h 包含了一些宏,你應該使用它們,而不要自己寫一些 它們的變種。比如,如果你需要計算一個數組的長度,使用這個宏

  1. #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) 

類似的,如果你要計算某結構體成員的大小,使用

  1. #define FIELD_SIZEOF(t, f) (sizeof(((t*)0)->f)) 

還有可以做嚴格的類型檢查的 min() 和 max() 宏,如果你需要可以使用它們。你可以 自己看看那個頭文件里還定義了什么你可以拿來用的東西,如果有定義的話,你就不應 在你的代碼里自己重新定義。

18 編輯器模式行和其他需要羅嗦的事情有一些編輯器可以解釋嵌入在源文件里的由一些特殊標記標明的配置信息。比如,emacs 能夠解釋被標記成這樣的行:

  1. -*- mode: c -*- 

或者這樣的:

  1. /* 
  2. Local Variables: 
  3. compile-command: "gcc -DMAGIC_DEBUG_FLAG foo.c" 
  4. End
  5. */ 

Vim 能夠解釋這樣的標記:

  1. /* vim:set sw=8 noet */ 

不要在源代碼中包含任何這樣的內容。

每個人都有他自己的編輯器配置,你的源文件不 應該覆蓋別人的配置。這包括有關縮進和模式配置的標記。人們可以使用他們自己定制 的模式,或者使用其他可以產生正確的縮進的巧妙方法。

19 內聯匯編

在特定架構的代碼中,你可能需要內聯匯編與 CPU 和平臺相關功能連接。需要這么做時 就不要猶豫。然而,當 C 可以完成工作時,不要平白無故地使用內聯匯編。在可能的情 況下,你可以并且應該用 C 和硬件溝通。

請考慮去寫捆綁通用位元 (wrap common bits) 的內聯匯編的簡單輔助函數,別去重復 地寫下只有細微差異內聯匯編。記住內聯匯編可以使用 C 參數。

大型,有一定復雜度的匯編函數應該放在 .S 文件內,用相應的 C 原型定義在 C 頭文 件中。匯編函數的 C 原型應該使用 asmlinkage 。

你可能需要把匯編語句標記為 volatile,用來阻止 GCC 在沒發現任何副作用后就把它 移除了。你不必總是這樣做,盡管,這不必要的舉動會限制優化。

在寫一個包含多條指令的單個內聯匯編語句時,把每條指令用引號分割而且各占一行, 除了最后一條指令外,在每個指令結尾加上 nt,讓匯編輸出時可以正確地縮進下一條 指令:

  1. asm ("magic %reg1, #42\n\t" 
  2.      "more_magic %reg2, %reg3" 
  3.      : /* outputs */ : /* inputs */ : /* clobbers */); 

20 條件編譯

只要可能,就不要在 .c 文件里面使用預處理條件 (#if, #ifdef);這樣做讓代碼更難 閱讀并且更難去跟蹤邏輯。替代方案是,在頭文件中用預處理條件提供給那些 .c 文件 使用,再給 #else 提供一個空樁 (no-op stub) 版本,然后在 .c 文件內無條件地調用 那些 (定義在頭文件內的) 函數。這樣做,編譯器會避免為樁函數 (stub) 的調用生成 任何代碼,產生的結果是相同的,但邏輯將更加清晰。

最好傾向于編譯整個函數,而不是函數的一部分或表達式的一部分。與其放一個 ifdef 在表達式內,不如分解出部分或全部表達式,放進一個單獨的輔助函數,并應用預處理 條件到這個輔助函數內。

如果你有一個在特定配置中,可能變成未使用的函數或變量,編譯器會警告它定義了但 未使用,把它標記為 __maybe_unused 而不是將它包含在一個預處理條件中。(然而,如 果一個函數或變量總是未使用,就直接刪除它。)

在代碼中,盡可能地使用 IS_ENABLED 宏來轉化某個 Kconfig 標記為 C 的布爾 表達式,并在一般的 C 條件中使用它:

  1. if (IS_ENABLED(CONFIG_SOMETHING)) { 
  2.         ... 

編譯器會做常量折疊,然后就像使用 #ifdef 那樣去包含或排除代碼塊,所以這不會帶 來任何運行時開銷。然而,這種方法依舊允許 C 編譯器查看塊內的代碼,并檢查它的正 確性 (語法,類型,符號引用,等等)。因此,如果條件不滿足,代碼塊內的引用符號就 不存在時,你還是必須去用 #ifdef。

在任何有意義的 #if 或 #ifdef 塊的末尾 (超過幾行的),在 #endif 同一行的后面寫下 注解,注釋這個條件表達式。例如:

  1. #ifdef CONFIG_SOMETHING 
  2. ... 
  3. #endif /* CONFIG_SOMETHING */ 

 

責任編輯:武曉燕 來源: 小麥大叔
相關推薦

2023-10-31 12:58:00

TypeScriptJavaScript

2021-03-16 08:21:29

Spark系統并行

2023-03-30 08:28:57

explain關鍵字MySQL

2025-09-05 07:23:12

2023-01-06 08:15:58

StreamAPI接口

2021-11-11 09:27:02

技術RedisMySQL

2021-10-18 11:58:56

負載均衡虛擬機

2024-07-19 08:34:18

2024-08-13 15:07:20

2022-09-06 08:02:40

死鎖順序鎖輪詢鎖

2022-02-15 18:45:35

Linux進程調度器

2024-03-07 18:11:39

Golang采集鏈接

2021-01-19 05:49:44

DNS協議

2024-08-30 10:29:21

2024-09-26 13:33:12

2024-12-31 00:00:01

驅動設計應用場景業務邏輯

2022-09-14 09:01:55

shell可視化

2020-03-18 12:47:59

設計模式ERP

2025-07-31 02:11:00

Linux網絡編程C/C++

2023-06-12 08:49:12

RocketMQ消費邏輯
點贊
收藏

51CTO技術棧公眾號

欧美2区3区4区| 国产一二三在线观看| 亚洲精品成人| 精品国产污污免费网站入口| 成人一对一视频| 久久经典视频| 国产一区二区看久久| 午夜精品久久久久久99热| 免费看裸体网站| 美女精品视频在线| 色婷婷综合激情| 警花观音坐莲激情销魂小说 | 久久久久久久久久成人| 国产亚洲字幕| 色婷婷av久久久久久久| 中文字幕日韩精品无码内射| 视频福利在线| 韩国女主播成人在线| 91精品国产成人| 无码黑人精品一区二区| 亚州av日韩av| 精品久久久久久久一区二区蜜臀| 亚洲欧美另类动漫| 国产精品蜜芽在线观看| 亚洲欧洲精品一区二区三区| 精品在线视频一区二区| 国产精品一区二区三区在线免费观看| 9色国产精品| 欧美成人在线网站| 18啪啪污污免费网站| 国产精品视屏| 欧美第一区第二区| 亚洲一区二区在线视频观看| 高潮一区二区| 亚洲丰满少妇videoshd| 五月天在线免费视频| 久青草国产在线| jlzzjlzz亚洲日本少妇| av色综合网| 一区二区三区免费在线| 亚洲一区二区三区四区五区午夜| 乱亲女秽乱长久久久| 九九热免费在线| 米奇777超碰欧美日韩亚洲| 欧美精品一区二区蜜臀亚洲| 中文字幕在线观看视频www| 久久天天久久| 欧美日韩国产高清一区| 免费看黄色一级大片| 亚洲第一二三四区| 91黄色免费网站| 人妻少妇被粗大爽9797pw| 丰乳肥臀在线| 亚洲mv大片欧洲mv大片精品| 青青青在线观看视频| 影音先锋男人在线资源| 亚洲欧美激情小说另类| 亚洲砖区区免费| 免费黄色电影在线观看| 最新不卡av在线| 国产手机视频在线观看| 女同视频在线观看| 午夜精品福利一区二区蜜股av| 免费超爽大片黄| 美女高潮视频在线看| 欧美性黄网官网| 成年人在线观看视频免费| 欧美视频免费看| 91精品福利在线一区二区三区| 善良的小姨在线| 综合激情久久| 亚洲精品黄网在线观看| 国精产品一区一区三区免费视频 | 亚洲精品自拍视频| 人妻大战黑人白浆狂泄| 成人高清电影网站| 久久久精品网站| 国产污视频在线观看| 午夜一级久久| 国产精品一区二区三区久久| 一本色道久久综合精品婷婷| 国产伦理精品不卡| 国产欧美日韩亚洲| 国产人成在线观看| 亚洲色大成网站www久久九九| 狠狠干视频网站| 黄视频免费在线看| 欧美色涩在线第一页| 污视频在线观看免费网站| 国产一区在线电影| 国产一区二区三区在线看| 国精产品久拍自产在线网站| 国产综合久久| 日韩暖暖在线视频| 国产99999| 国产午夜一区二区三区| 成人短视频在线观看免费| 成人小电影网站| 欧美军同video69gay| 老司机免费视频| 四季av在线一区二区三区| 欧美丰满少妇xxxxx| 日本视频免费观看| 高清不卡在线观看av| 日本一区二区在线视频| 国产盗摄在线视频网站| 欧美中文一区二区三区| jjzz黄色片| 99成人在线视频| 97在线看免费观看视频在线观看| 一级片免费网站| 97精品久久久久中文字幕 | 亚洲无线一线二线三线区别av| 亲爱的老师9免费观看全集电视剧| 国产精品久久久久久免费| 99re这里只有精品视频首页| 成年人黄色在线观看| 欧美片第1页| 亚洲成人a**站| 欧美日韩黄色网| 蜜臀av性久久久久蜜臀aⅴ流畅 | 国产精品人成电影在线观看| 乱精品一区字幕二区| 18欧美亚洲精品| 免费看a级黄色片| 色老板在线视频一区二区| 欧美成人免费全部| 一级特黄aaa| 久久午夜老司机| 男女啪啪免费视频网站| 亚洲三级av| 欧美大码xxxx| 99久久久国产精品无码免费| 国产精品乱码人人做人人爱| 毛片av免费在线观看| 日韩母乳在线| 国产69精品久久久| 国产成人自拍一区| 亚洲综合在线五月| wwwxxx色| 午夜日韩在线| 亚洲尤物视频网| sm国产在线调教视频| 69堂成人精品免费视频| 亚洲色偷偷综合亚洲av伊人| 久久精品国产成人一区二区三区| 视频一区亚洲| 黄色日韩网站| 深夜成人在线观看| 91高潮大合集爽到抽搐| 国产精品福利在线播放| 在线免费观看av网| 欧美成人国产| αv一区二区三区| 欧美78videosex性欧美| 精品久久久久久最新网址| 日本一二三区视频| 99re亚洲国产精品| 北条麻妃av高潮尖叫在线观看| 欧美日韩伦理| 国产日韩欧美成人| 国产激情在线| 精品国产一区二区亚洲人成毛片| 久久久久亚洲av成人片| 波多野结衣在线一区| 草草久久久无码国产专区| 亚洲国产精品嫩草影院久久av| 欧美亚洲视频一区二区| 男人的天堂在线| 欧美日韩精品三区| 亚洲欧美小视频| 成人精品小蝌蚪| 国产中文字幕免费观看| 国产在视频线精品视频www666| 国产精品视频免费观看www| 黄色片网站在线| 精品国产伦理网| 无码视频在线观看| 亚洲欧美欧美一区二区三区| 奇米777第四色| 噜噜爱69成人精品| 亚洲美女自拍偷拍| 欧美五码在线| 国产视频观看一区| 俺来俺也去www色在线观看| 亚洲免费电影一区| 国产乱码精品一区二区| 亚洲一区二区三区在线看| 久久丫精品国产亚洲av不卡| 久久99精品久久久久久久久久久久| 国产资源第一页| 亚洲日产av中文字幕| 91精品国产自产在线| 新版的欧美在线视频| 色婷婷综合久久久久| 三级小视频在线观看| 欧美日韩夫妻久久| 女人十八岁毛片| 亚洲欧洲在线观看av| 日韩一级视频在线观看| 狠狠色丁香久久婷婷综合丁香| 免费无遮挡无码永久视频| 99久久亚洲精品| 欧美h视频在线| 亚洲不卡视频| 国产精品亚发布| 98色花堂精品视频在线观看| 久久精品电影网站| 免费毛片在线| 亚洲福利视频专区| 99精品视频免费看| 欧美另类z0zxhd电影| 波多野结衣 久久| 亚洲午夜久久久久久久久电影院| 久久久久99精品成人| 91一区二区三区在线观看| 亚洲精品乱码久久久久久9色| 久久久久99| av免费观看国产| 国精品一区二区三区| 亚洲一区二区自拍偷拍| 欧美禁忌电影| 久久精品一二三区| 99久久香蕉| 亚洲尤物视频网| 精品91福利视频| 国产精品揄拍一区二区| 三上悠亚激情av一区二区三区 | 2022中文字幕| 9999国产精品| 亚洲永久激情精品| 色呦哟—国产精品| 亚洲v国产v在线观看| 国产探花一区在线观看| 欧美日韩一区二区视频在线| 久久精品福利| 久久精品女人的天堂av| 久久香蕉网站| 精品视频导航| 天堂在线精品| 欧美精品二区三区四区免费看视频| 国产调教精品| 九九九九久久久久| 私拍精品福利视频在线一区| 精品国产乱码久久久久久郑州公司| 88久久精品| 国产私拍一区| 青青草这里只有精品| 久久影院理伦片| 国产精品手机在线播放| 麻豆成人av| 国产探花一区在线观看| 一本一本a久久| 午夜激情久久| 成人av在线播放观看| 亚洲视频一二| 老太脱裤让老头玩ⅹxxxx| 日韩天天综合| 丝袜老师办公室里做好紧好爽| 国产模特精品视频久久久久| 激情婷婷综合网| 秋霞影院一区二区| 久国产精品视频| 国产suv一区二区三区88区| 色综合久久五月| 久久久噜噜噜久久中文字幕色伊伊| 影音先锋男人在线| 日韩美女久久久| 国产一级在线播放| 色综合久久中文字幕| 在线视频你懂得| 日韩精品一区二区三区视频| 五月天婷婷激情网| 日韩av中文字幕在线| 国产三级在线观看| 欧美成人免费大片| 欧美成人h版| 91在线|亚洲| 全国精品免费看| 亚洲三区在线观看| 影音国产精品| 天天爽人人爽夜夜爽| 国产不卡视频一区| 国产18无套直看片| 一区二区三区四区蜜桃 | 久久国产直播| 一级做a爱视频| 91在线云播放| 国产精品 欧美激情| 欧美午夜丰满在线18影院| 97精品人妻一区二区三区在线| 精品久久久久久亚洲综合网| 岛国最新视频免费在线观看| 欧美日韩国产成人在线| 香蕉成人av| 国产精品久久亚洲7777| 欧美色就是色| 久久久久久免费看| 久久99深爱久久99精品| 久久国产精品影院| 一区二区三区成人| 中文字幕第99页| 亚洲精品乱码久久久久久金桔影视| 香蕉视频免费在线播放| 国产91精品久久久久| 日韩高清在线观看一区二区| 日本不卡一二三区| 亚洲人体大胆视频| 亚洲天堂伊人网| 国产三级欧美三级日产三级99| 久久久久97国产| 欧美裸体bbwbbwbbw| 黄色在线免费观看大全| 97热精品视频官网| 日韩中文字幕一区二区高清99| 亚洲精品一区国产精品| 国产精品视区| 小毛片在线观看| 伊人开心综合网| 一区二区www| 一本色道久久88综合亚洲精品ⅰ| 暧暧视频在线免费观看| 亚洲字幕一区二区| 欧美成人直播| mm1313亚洲国产精品无码试看| 99国产一区二区三精品乱码| 豆国产97在线 | 亚洲| 日韩一卡二卡三卡| 精品国产丝袜高跟鞋| 国产精品视频xxx| 精品一区二区三区的国产在线观看| 啊啊啊一区二区| 99综合电影在线视频| 国产一级av毛片| 欧美r级电影在线观看| 亚洲奶水xxxx哺乳期| 亚洲aaa激情| 伊人久久大香线| 亚洲男人天堂2021| 亚洲精品网站在线观看| 99热这里只有精品3| 久热精品视频在线| 国产精品一级在线观看| 干日本少妇视频| 高清不卡一二三区| 国产真实乱人偷精品视频| 欧美mv日韩mv国产网站| 福利写真视频网站在线| 国产福利久久精品| 亚洲精品影视| 久久久久亚洲av无码专区桃色| 91电影在线观看| 在线日本视频| 91沈先生作品| 国内精品久久久久久久影视蜜臀 | 国产亚洲精品aa午夜观看| 中文字幕日韩免费| 在线亚洲午夜片av大片| 久久免费资源| 欧美日韩午夜爽爽| 成人av在线看| 欧美在线观看不卡| 在线观看欧美成人| 亚洲欧美综合久久久久久v动漫| 超碰免费在线公开| 国产成人福利片| 国产午夜在线播放| 在线观看国产成人av片| 久久九九精品视频| 一本久道高清无码视频| 26uuu精品一区二区| 中文字幕在线网站| 久久99精品视频一区97 | 亚洲aⅴ天堂av在线电影软件| 久久精品久久精品| 欧美日韩免费一区二区| 亚洲精品wwww| 国产一区二区三区四区五区3d| 精品一区二区三区毛片| 99久久综合99久久综合网站| 青青艹在线观看| 九色91av视频| 亚洲自拍电影| 五月六月丁香婷婷| 欧美日韩国产一区中文午夜| av在线日韩国产精品| 成人av蜜桃| 老**午夜毛片一区二区三区 | 一区二区中文| www.色天使| 日韩视频一区二区| 欧美中文字幕精在线不卡| 欧美日韩一级在线| 91麻豆福利精品推荐| 国产精品久久无码一三区| 性亚洲最疯狂xxxx高清| 欧美激情理论| 黄色在线观看av| 日韩欧美国产综合在线一区二区三区| 欧美成人ⅴideosxxxxx|