.. SPDX-License-Identifier: GPL-2.0 .. include:: ../disclaimer-zh_TW.rst :Original: :ref:`Documentation/process/coding-style.rst <codingstyle>` .. _tw_codingstyle: è¯è€…:: ä¸æ–‡ç‰ˆç¶è·è€…: 張樂 Zhang Le <r0bertz@gentoo.org> ä¸æ–‡ç‰ˆç¿»è¯è€…: 張樂 Zhang Le <r0bertz@gentoo.org> ä¸æ–‡ç‰ˆæ ¡è¯è€…: çŽ‹è° Wang Cong <xiyou.wangcong@gmail.com> wheelz <kernel.zeng@gmail.com> 管æ—æ± Xudong Guan <xudong.guan@gmail.com> Li Zefan <lizf@cn.fujitsu.com> Wang Chen <wangchen@cn.fujitsu.com> Hu Haowen <src.res@email.cn> Linux å…§æ ¸ä»£ç¢¼é¢¨æ ¼ ========================= 這是一個簡çŸçš„文檔,æ述了 linux å…§æ ¸çš„é¦–é¸ä»£ç¢¼é¢¨æ ¼ã€‚ä»£ç¢¼é¢¨æ ¼æ˜¯å› äººè€Œç•°çš„ï¼Œ 而且我ä¸é¡˜æ„æŠŠè‡ªå·±çš„è§€é»žå¼·åŠ çµ¦ä»»ä½•äººï¼Œä½†é€™å°±åƒæˆ‘去åšä»»ä½•äº‹æƒ…éƒ½å¿…é ˆéµå¾ªçš„原則 那樣,我也希望在絕大多數事上ä¿æŒé€™ç¨®çš„態度。請 (在寫代碼時) 至少考慮一下這裡 çš„ä»£ç¢¼é¢¨æ ¼ã€‚ 首先,我建è°ä½ 列å°ä¸€ä»½ GNU 代碼è¦ç¯„,然後ä¸è¦è®€ã€‚燒了它,這是一個具有é‡å¤§è±¡å¾µ 性æ„義的動作。 ä¸ç®¡æ€Žæ¨£ï¼Œç¾åœ¨æˆ‘們開始: 1) 縮進 -------------- 制表符是 8 個å—符,所以縮進也是 8 個å—符。有些異端é‹å‹•è©¦åœ–將縮進變爲 4 (甚至 2ï¼) å—符深,這幾乎相當於嘗試將圓周率的值定義爲 3。 ç†ç”±ï¼šç¸®é€²çš„全部æ„義就在於清楚的定義一個控制塊起æ¢æ–¼ä½•è™•ã€‚å°¤å…¶æ˜¯ç•¶ä½ ç›¯è‘—ä½ çš„ å±å¹•é€£çºŒçœ‹äº† 20 å°æ™‚ä¹‹å¾Œï¼Œä½ å°‡æœƒç™¼ç¾å¤§ä¸€é»žçš„ç¸®é€²æœƒä½¿ä½ æ›´å®¹æ˜“åˆ†è¾¨ç¸®é€²ã€‚ ç¾åœ¨ï¼Œæœ‰äº›äººæœƒæŠ±æ€¨ 8 個å—符的縮進會使代碼å‘å³é‚Šç§»å‹•çš„太é ,在 80 個å—符的終端 å±å¹•ä¸Šå°±å¾ˆé›£è®€é€™æ¨£çš„代碼。這個å•é¡Œçš„ç”æ¡ˆæ˜¯ï¼Œå¦‚æžœä½ éœ€è¦ 3 級以上的縮進,ä¸ç®¡ç”¨ 何種方å¼ä½ 的代碼已經有å•é¡Œäº†ï¼Œæ‡‰è©²ä¿®æ£ä½ 的程åºã€‚ 簡而言之,8 個å—符的縮進å¯ä»¥è®“ä»£ç¢¼æ›´å®¹æ˜“é–±è®€ï¼Œé‚„æœ‰ä¸€å€‹å¥½è™•æ˜¯ç•¶ä½ çš„å‡½æ•¸åµŒå¥—å¤ª 深的時候å¯ä»¥çµ¦ä½ è¦å‘Šã€‚留心這個è¦å‘Šã€‚ 在 switch 語å¥ä¸æ¶ˆé™¤å¤šç´šç¸®é€²çš„首é¸çš„æ–¹å¼æ˜¯è®“ ``switch`` 和從屬於它的 ``case`` 標籤å°é½Šæ–¼åŒä¸€åˆ—,而ä¸è¦ ``兩次縮進`` ``case`` 標籤。比如: .. code-block:: c switch (suffix) { case 'G': case 'g': mem <<= 30; break; case 'M': case 'm': mem <<= 20; break; case 'K': case 'k': mem <<= 10; fallthrough; default: break; } ä¸è¦æŠŠå¤šå€‹èªžå¥æ”¾åœ¨ä¸€è¡Œé‡Œï¼Œé™¤éžä½ 有什麼æ±è¥¿è¦éš±è—: .. code-block:: c if (condition) do_this; do_something_everytime; 也ä¸è¦åœ¨ä¸€è¡Œé‡Œæ”¾å¤šå€‹è³¦å€¼èªžå¥ã€‚å…§æ ¸ä»£ç¢¼é¢¨æ ¼è¶…ç´šç°¡å–®ã€‚å°±æ˜¯é¿å…å¯èƒ½å°Žè‡´åˆ¥äººèª¤è®€ 的表é”å¼ã€‚ 除了注釋ã€æ–‡æª”å’Œ Kconfig 之外,ä¸è¦ä½¿ç”¨ç©ºæ ¼ä¾†ç¸®é€²ï¼Œå‰é¢çš„例å是例外,是有æ„爲 之。 é¸ç”¨ä¸€å€‹å¥½çš„編輯器,ä¸è¦åœ¨è¡Œå°¾ç•™ç©ºæ ¼ã€‚ 2) 把長的行和å—符串打散 ------------------------------ ä»£ç¢¼é¢¨æ ¼çš„æ„義就在於使用平常使用的工具來ç¶æŒä»£ç¢¼çš„å¯è®€æ€§å’Œå¯ç¶è·æ€§ã€‚ æ¯ä¸€è¡Œçš„長度的é™åˆ¶æ˜¯ 80 列,我們強烈建è°æ‚¨éµå®ˆé€™å€‹æ…£ä¾‹ã€‚ é•·æ–¼ 80 列的語å¥è¦æ‰“æ•£æˆæœ‰æ„義的片段。除éžè¶…éŽ 80 åˆ—èƒ½é¡¯è‘—å¢žåŠ å¯è®€æ€§ï¼Œä¸¦ä¸”ä¸ æœƒéš±è—ä¿¡æ¯ã€‚å片段è¦æ˜Žé¡¯çŸæ–¼æ¯ç‰‡æ®µï¼Œä¸¦æ˜Žé¡¯é å³ã€‚這åŒæ¨£é©ç”¨æ–¼æœ‰è‘—很長åƒæ•¸åˆ—表 的函數é 。然而,絕å°ä¸è¦æ‰“æ•£å°ç”¨æˆ¶å¯è¦‹çš„å—符串,例如 printk ä¿¡æ¯ï¼Œå› 爲這樣就 很難å°å®ƒå€‘ grep。 3) å¤§æ‹¬è™Ÿå’Œç©ºæ ¼çš„æ”¾ç½® ------------------------------ C èªžè¨€é¢¨æ ¼ä¸å¦å¤–一個常見å•é¡Œæ˜¯å¤§æ‹¬è™Ÿçš„放置。和縮進大å°ä¸åŒï¼Œé¸æ“‡æˆ–棄用æŸç§æ”¾ ç½®ç–ç•¥ä¸¦æ²’æœ‰å¤šå°‘æŠ€è¡“ä¸Šçš„åŽŸå› ï¼Œä¸éŽé¦–é¸çš„æ–¹å¼ï¼Œå°±åƒ Kernighan å’Œ Ritchie 展示 給我們的,是把起始大括號放在行尾,而把çµæŸå¤§æ‹¬è™Ÿæ”¾åœ¨è¡Œé¦–,所以: .. code-block:: c if (x is true) { we do y } 這é©ç”¨æ–¼æ‰€æœ‰çš„éžå‡½æ•¸èªžå¥å¡Š (if, switch, for, while, do)。比如: .. code-block:: c switch (action) { case KOBJ_ADD: return "add"; case KOBJ_REMOVE: return "remove"; case KOBJ_CHANGE: return "change"; default: return NULL; } ä¸éŽï¼Œæœ‰ä¸€å€‹ä¾‹å¤–,那就是函數:函數的起始大括號放置於下一行的開é ,所以: .. code-block:: c int function(int x) { body of function } 全世界的異端å¯èƒ½æœƒæŠ±æ€¨é€™å€‹ä¸ä¸€è‡´æ€§æ˜¯... 呃... ä¸ä¸€è‡´çš„,ä¸éŽæ‰€æœ‰æ€ç¶å¥å…¨çš„人 éƒ½çŸ¥é“ (a) K&R 是 **æ£ç¢ºçš„** 並且 (b) K&R 是æ£ç¢ºçš„。æ¤å¤–,ä¸ç®¡æ€Žæ¨£å‡½æ•¸éƒ½æ˜¯ç‰¹ 殊的 (C 函數是ä¸èƒ½åµŒå¥—çš„)。 注æ„çµæŸå¤§æ‹¬è™Ÿç¨è‡ªå 據一行,除éžå®ƒå¾Œé¢è·Ÿè‘—åŒä¸€å€‹èªžå¥çš„剩餘部分,也就是 do 語 å¥ä¸çš„ "while" 或者 if 語å¥ä¸çš„ "else",åƒé€™æ¨£ï¼š .. code-block:: c do { body of do-loop } while (condition); å’Œ .. code-block:: c if (x == y) { .. } else if (x > y) { ... } else { .... } ç†ç”±ï¼šK&R。 也請注æ„這種大括號的放置方å¼ä¹Ÿèƒ½ä½¿ç©º (或者差ä¸å¤šç©ºçš„) 行的數é‡æœ€å°åŒ–,åŒæ™‚ä¸ å¤±å¯è®€æ€§ã€‚å› æ¤ï¼Œç”±æ–¼ä½ çš„å±å¹•ä¸Šçš„新行是ä¸å¯å†ç”Ÿè³‡æº (想想 25 行的終端å±å¹•)ï¼Œä½ å°‡æœƒæœ‰æ›´å¤šçš„ç©ºè¡Œä¾†æ”¾ç½®æ³¨é‡‹ã€‚ 當åªæœ‰ä¸€å€‹å–®ç¨çš„語å¥çš„時候,ä¸ç”¨åŠ ä¸å¿…è¦çš„大括號。 .. code-block:: c if (condition) action(); å’Œ .. code-block:: c if (condition) do_this(); else do_that(); 這並ä¸é©ç”¨æ–¼åªæœ‰ä¸€å€‹æ¢ä»¶åˆ†æ”¯æ˜¯å–®èªžå¥çš„情æ³ï¼›é€™æ™‚所有分支都è¦ä½¿ç”¨å¤§æ‹¬è™Ÿï¼š .. code-block:: c if (condition) { do_this(); do_that(); } else { otherwise(); } 3.1) ç©ºæ ¼ ******************** Linux å…§æ ¸çš„ç©ºæ ¼ä½¿ç”¨æ–¹å¼ (主è¦) å–決於它是用於函數還是關éµå—。(大多數) é—œéµå— 後è¦åŠ ä¸€å€‹ç©ºæ ¼ã€‚å€¼å¾—æ³¨æ„的例外是 sizeof, typeof, alignof å’Œ __attribute__,這 些關éµå—æŸäº›ç¨‹åº¦ä¸Šçœ‹èµ·ä¾†æ›´åƒå‡½æ•¸ (它們在 Linux 里也常常伴隨å°æ‹¬è™Ÿè€Œä½¿ç”¨ï¼Œå„˜ç®¡ 在 C 里這樣的å°æ‹¬è™Ÿä¸æ˜¯å¿…éœ€çš„ï¼Œå°±åƒ ``struct fileinfo info;`` è²æ˜ŽéŽå¾Œçš„ ``sizeof info``)。 所以在這些關éµå—ä¹‹å¾Œæ”¾ä¸€å€‹ç©ºæ ¼:: if, switch, case, for, do, while 但是ä¸è¦åœ¨ sizeof, typeof, alignof 或者 __attribute__ 這些關éµå—ä¹‹å¾Œæ”¾ç©ºæ ¼ã€‚ 例如, .. code-block:: c s = sizeof(struct file); ä¸è¦åœ¨å°æ‹¬è™Ÿé‡Œçš„表é”å¼å…©å´åŠ ç©ºæ ¼ã€‚é€™æ˜¯ä¸€å€‹ **å例** : .. code-block:: c s = sizeof( struct file ); 當è²æ˜ŽæŒ‡é‡é¡žåž‹æˆ–者返回指é‡é¡žåž‹çš„函數時, ``*`` 的首é¸ä½¿ç”¨æ–¹å¼æ˜¯ä½¿ä¹‹é 近變é‡å 或者函數å,而ä¸æ˜¯é è¿‘é¡žåž‹å。例å: .. code-block:: c char *linux_banner; unsigned long long memparse(char *ptr, char **retptr); char *match_strdup(substring_t *s); 在大多數二元和三元æ“作符兩å´ä½¿ç”¨ä¸€å€‹ç©ºæ ¼ï¼Œä¾‹å¦‚下é¢æ‰€æœ‰é€™äº›æ“作符:: = + - < > * / % | & ^ <= >= == != ? : 但是一元æ“作符後ä¸è¦åŠ ç©ºæ ¼:: & * + - ~ ! sizeof typeof alignof __attribute__ defined å¾Œç¶´è‡ªåŠ å’Œè‡ªæ¸›ä¸€å…ƒæ“作符å‰ä¸åŠ ç©ºæ ¼:: ++ -- å‰ç¶´è‡ªåŠ 和自減一元æ“作符後ä¸åŠ ç©ºæ ¼:: ++ -- ``.`` å’Œ ``->`` çµæ§‹é«”æˆå“¡æ“作符å‰å¾Œä¸åŠ ç©ºæ ¼ã€‚ ä¸è¦åœ¨è¡Œå°¾ç•™ç©ºç™½ã€‚有些å¯ä»¥è‡ªå‹•ç¸®é€²çš„ç·¨è¼¯å™¨æœƒåœ¨æ–°è¡Œçš„è¡Œé¦–åŠ å…¥é©é‡çš„空白,然後 ä½ å°±å¯ä»¥ç›´æŽ¥åœ¨é‚£ä¸€è¡Œè¼¸å…¥ä»£ç¢¼ã€‚ä¸éŽå‡å¦‚ä½ æœ€å¾Œæ²’æœ‰åœ¨é‚£ä¸€è¡Œè¼¸å…¥ä»£ç¢¼ï¼Œæœ‰äº›ç·¨è¼¯å™¨ å°±ä¸æœƒç§»é™¤å·²ç¶“åŠ å…¥çš„ç©ºç™½ï¼Œå°±åƒä½ æ•…æ„留下一個åªæœ‰ç©ºç™½çš„行。包å«è¡Œå°¾ç©ºç™½çš„行就 這樣產生了。 當 git 發ç¾è£œä¸åŒ…å«äº†è¡Œå°¾ç©ºç™½çš„時候會è¦å‘Šä½ ,並且å¯ä»¥æ‡‰ä½ çš„è¦æ±‚去掉行尾空白; ä¸éŽå¦‚æžœä½ æ˜¯æ£åœ¨æ‰“一系列補ä¸ï¼Œé€™æ¨£åšæœƒå°Žè‡´å¾Œé¢çš„補ä¸å¤±æ•—ï¼Œå› çˆ²ä½ æ”¹è®Šäº†è£œä¸çš„ 上下文。 4) 命å ------------------------------ C æ˜¯ä¸€å€‹ç°¡æ¨¸çš„èªžè¨€ï¼Œä½ çš„å‘½å也應該這樣。和 Modula-2 å’Œ Pascal 程å¼è¨è¨ˆå¸«ä¸åŒï¼Œ C 程å¼è¨è¨ˆå¸«ä¸ä½¿ç”¨é¡žä¼¼ ThisVariableIsATemporaryCounter 這樣è¯éº—çš„åå—。C 程å¼è¨è¨ˆå¸«æœƒ 稱那個變é‡çˆ² ``tmp`` ,這樣寫起來會更容易,而且至少ä¸æœƒä»¤å…¶é›£æ–¼ç†è§£ã€‚ ä¸éŽï¼Œé›–然混用大å°å¯«çš„åå—是ä¸æ倡使用的,但是全局變é‡é‚„是需è¦ä¸€å€‹å…·æ述性的 åå—。稱一個全局函數爲 ``foo`` 是一個難以饒æ•çš„錯誤。 å…¨å±€è®Šé‡ (åªæœ‰ç•¶ä½ **真æ£** 需è¦å®ƒå€‘的時候å†ç”¨å®ƒ) 需è¦æœ‰ä¸€å€‹å…·æ述性的åå—,就 åƒå…¨å±€å‡½æ•¸ã€‚å¦‚æžœä½ æœ‰ä¸€å€‹å¯ä»¥è¨ˆç®—活動用戶數é‡çš„å‡½æ•¸ï¼Œä½ æ‡‰è©²å«å®ƒ ``count_active_users()`` 或者類似的åå—ï¼Œä½ ä¸æ‡‰è©²å«å®ƒ ``cntuser()`` 。 在函數åä¸åŒ…å«å‡½æ•¸é¡žåž‹ (所謂的匈牙利命å法) 是腦å出了å•é¡Œâ€”—編è¯å™¨çŸ¥é“那些類 åž‹è€Œä¸”èƒ½å¤ æª¢æŸ¥é‚£äº›é¡žåž‹ï¼Œé€™æ¨£åšåªèƒ½æŠŠç¨‹å¼è¨è¨ˆå¸«å¼„ç³Šå¡—äº†ã€‚é›£æ€ªå¾®è»Ÿç¸½æ˜¯è£½é€ å‡ºæœ‰å•é¡Œ 的程åºã€‚ 本地變é‡å應該簡çŸï¼Œè€Œä¸”èƒ½å¤ è¡¨é”相關的å«ç¾©ã€‚å¦‚æžœä½ æœ‰ä¸€äº›éš¨æ©Ÿçš„æ•´æ•¸åž‹çš„å¾ªç’°è¨ˆ 數器,它應該被稱爲 ``i`` 。å«å®ƒ ``loop_counter`` 並無益處,如果它沒有被誤解的 å¯èƒ½çš„話。類似的, ``tmp`` å¯ä»¥ç”¨ä¾†ç¨±å‘¼ä»»æ„類型的臨時變é‡ã€‚ å¦‚æžœä½ æ€•æ··æ·†äº†ä½ çš„æœ¬åœ°è®Šé‡åï¼Œä½ å°±é‡åˆ°å¦ä¸€å€‹å•é¡Œäº†ï¼Œå«åšå‡½æ•¸å¢žé•·è·çˆ¾è’™å¤±è¡¡ç¶œ åˆç—‡ã€‚請看第å…ç« (函數)。 5) Typedef ----------- ä¸è¦ä½¿ç”¨é¡žä¼¼ ``vps_t`` 之類的æ±è¥¿ã€‚ å°çµæ§‹é«”和指é‡ä½¿ç”¨ typedef 是一個 **錯誤** ã€‚ç•¶ä½ åœ¨ä»£ç¢¼é‡Œçœ‹åˆ°ï¼š .. code-block:: c vps_t a; 這代表什麼æ„æ€å‘¢ï¼Ÿ 相å,如果是這樣 .. code-block:: c struct virtual_container *a; ä½ å°±çŸ¥é“ ``a`` 是什麼了。 很多人èªçˆ² typedef ``能æ高å¯è®€æ€§`` 。實際ä¸æ˜¯é€™æ¨£çš„。它們åªåœ¨ä¸‹åˆ—情æ³ä¸‹æœ‰ç”¨ï¼š (a) 完全ä¸é€æ˜Žçš„å°è±¡ (這種情æ³ä¸‹è¦ä¸»å‹•ä½¿ç”¨ typedef 來 **éš±è—** 這個å°è±¡å¯¦éš›ä¸Š 是什麼)。 例如: ``pte_t`` ç‰ä¸é€æ˜Žå°è±¡ï¼Œä½ åªèƒ½ç”¨åˆé©çš„訪å•å‡½æ•¸ä¾†è¨ªå•å®ƒå€‘。 .. note:: ä¸é€æ˜Žæ€§å’Œ "訪å•å‡½æ•¸" 本身是ä¸å¥½çš„。我們使用 pte_t ç‰é¡žåž‹çš„åŽŸå› åœ¨æ–¼çœŸ 的是完全沒有任何共用的å¯è¨ªå•ä¿¡æ¯ã€‚ (b) 清楚的整數類型,如æ¤ï¼Œé€™å±¤æŠ½è±¡å°±å¯ä»¥ **幫助** 消除到底是 ``int`` 還是 ``long`` 的混淆。 u8/u16/u32 是完全沒有å•é¡Œçš„ typedef,ä¸éŽå®ƒå€‘更符åˆé¡žåˆ¥ (d) 而ä¸æ˜¯é€™è£¡ã€‚ .. note:: è¦é€™æ¨£åšï¼Œå¿…é ˆäº‹å‡ºæœ‰å› ã€‚å¦‚æžœæŸå€‹è®Šé‡æ˜¯ ``unsigned long`` ï¼Œé‚£éº¼æ²’æœ‰å¿…è¦ typedef unsigned long myflags_t; ä¸éŽå¦‚æžœæœ‰ä¸€å€‹æ˜Žç¢ºçš„åŽŸå› ï¼Œæ¯”å¦‚å®ƒåœ¨æŸç¨®æƒ…æ³ä¸‹å¯èƒ½æœƒæ˜¯ä¸€å€‹ ``unsigned int`` 而在其他情æ³ä¸‹å¯èƒ½çˆ² ``unsigned long`` ,那麼就ä¸è¦çŒ¶è±«ï¼Œè«‹å‹™å¿…使用 typedef。 (c) ç•¶ä½ ä½¿ç”¨ sparse 按å—é¢çš„創建一個 **æ–°** 類型來åšé¡žåž‹æª¢æŸ¥çš„時候。 (d) 和標準 C99 類型相åŒçš„類型,在æŸäº›ä¾‹å¤–的情æ³ä¸‹ã€‚ 雖然讓眼ç›å’Œè…¦ç‹ä¾†é©æ‡‰æ–°çš„標準類型比如 ``uint32_t`` ä¸éœ€è¦èŠ±å¾ˆå¤šæ™‚é–“ï¼Œå¯ æ˜¯æœ‰äº›äººä»ç„¶æ‹’絕使用它們。 å› æ¤ï¼ŒLinux 特有的ç‰åŒæ–¼æ¨™æº–é¡žåž‹çš„ ``u8/u16/u32/u64`` 類型和它們的有符號 類型是被å…è¨±çš„â€”â€”å„˜ç®¡åœ¨ä½ è‡ªå·±çš„æ–°ä»£ç¢¼ä¸ï¼Œå®ƒå€‘ä¸æ˜¯å¼·åˆ¶è¦æ±‚è¦ä½¿ç”¨çš„。 當編輯已經使用了æŸå€‹é¡žåž‹é›†çš„å·²æœ‰ä»£ç¢¼æ™‚ï¼Œä½ æ‡‰è©²éµå¾ªé‚£äº›ä»£ç¢¼ä¸å·²ç¶“åšå‡ºçš„é¸ æ“‡ã€‚ (e) å¯ä»¥åœ¨ç”¨æˆ¶ç©ºé–“安全使用的類型。 在æŸäº›ç”¨æˆ¶ç©ºé–“å¯è¦‹çš„çµæ§‹é«”裡,我們ä¸èƒ½è¦æ±‚ C99 類型而且ä¸èƒ½ç”¨ä¸Šé¢æ到的 ``u32`` é¡žåž‹ã€‚å› æ¤ï¼Œæˆ‘們在與用戶空間共享的所有çµæ§‹é«”ä¸ä½¿ç”¨ __u32 和類似 的類型。 å¯èƒ½é‚„有其他的情æ³ï¼Œä¸éŽåŸºæœ¬çš„è¦å‰‡æ˜¯ **æ°¸é ä¸è¦** 使用 typedef,除éžä½ å¯ä»¥æ˜Ž 確的應用上述æŸå€‹è¦å‰‡ä¸çš„一個。 總的來說,如果一個指é‡æˆ–者一個çµæ§‹é«”è£¡çš„å…ƒç´ å¯ä»¥åˆç†çš„被直接訪å•åˆ°ï¼Œé‚£éº¼å®ƒå€‘ å°±ä¸æ‡‰è©²æ˜¯ä¸€å€‹ typedef。 6) 函數 ------------------------------ 函數應該簡çŸè€Œæ¼‚亮,並且åªå®Œæˆä¸€ä»¶äº‹æƒ…。函數應該å¯ä»¥ä¸€å±æˆ–者兩å±é¡¯ç¤ºå®Œ (我們 éƒ½çŸ¥é“ ISO/ANSI å±å¹•å¤§å°æ˜¯ 80x24),åªåšä¸€ä»¶äº‹æƒ…,而且把它åšå¥½ã€‚ 一個函數的最大長度是和該函數的複雜度和縮進級數æˆåæ¯”çš„ã€‚æ‰€ä»¥ï¼Œå¦‚æžœä½ æœ‰ä¸€å€‹ç† è«–ä¸Šå¾ˆç°¡å–®çš„åªæœ‰ä¸€å€‹å¾ˆé•· (但是簡單) çš„ case 語å¥çš„å‡½æ•¸ï¼Œè€Œä¸”ä½ éœ€è¦åœ¨æ¯å€‹ case 里åšå¾ˆå¤šå¾ˆå°çš„事情,這樣的函數儘管很長,但也是å¯ä»¥çš„。 ä¸éŽï¼Œå¦‚æžœä½ æœ‰ä¸€å€‹è¤‡é›œçš„å‡½æ•¸ï¼Œè€Œä¸”ä½ æ‡·ç–‘ä¸€å€‹å¤©åˆ†ä¸æ˜¯å¾ˆé«˜çš„高ä¸ä¸€å¹´ç´šå¸ç”Ÿå¯èƒ½ 甚至æžä¸æ¸…æ¥šé€™å€‹å‡½æ•¸çš„ç›®çš„ï¼Œä½ æ‡‰è©²åš´æ ¼éµå®ˆå‰é¢æ到的長度é™åˆ¶ã€‚使用輔助函數, 並爲之å–個具æ述性的åå— (å¦‚æžœä½ è¦ºå¾—å®ƒå€‘çš„æ€§èƒ½å¾ˆé‡è¦çš„話,å¯ä»¥è®“ç·¨è¯å™¨å…§è¯å®ƒ å€‘ï¼Œé€™æ¨£çš„æ•ˆæžœå¾€å¾€æœƒæ¯”ä½ å¯«ä¸€å€‹è¤‡é›œå‡½æ•¸çš„æ•ˆæžœè¦å¥½ã€‚) 函數的å¦å¤–一個衡é‡æ¨™æº–是本地變é‡çš„數é‡ã€‚æ¤æ•¸é‡ä¸æ‡‰è¶…éŽ 5ï¼10 個,å¦å‰‡ä½ 的函數 就有å•é¡Œäº†ã€‚é‡æ–°è€ƒæ…®ä¸€ä¸‹ä½ 的函數,把它分拆æˆæ›´å°çš„函數。人的大腦一般å¯ä»¥è¼•é¬† çš„åŒæ™‚跟蹤 7 個ä¸åŒçš„事物,如果å†å¢žå¤šçš„話,就會糊塗了。å³ä¾¿ä½ è°ç©ŽéŽäººï¼Œä½ ä¹Ÿå¯ èƒ½æœƒè¨˜ä¸æ¸…ä½ 2 個星期å‰åšéŽçš„事情。 在æºæ–‡ä»¶é‡Œï¼Œä½¿ç”¨ç©ºè¡Œéš”é–‹ä¸åŒçš„函數。如果該函數需è¦è¢«å°Žå‡ºï¼Œå®ƒçš„ **EXPORT** å® æ‡‰è©²ç·Šè²¼åœ¨å®ƒçš„çµæŸå¤§æ‹¬è™Ÿä¹‹ä¸‹ã€‚比如: .. code-block:: c int system_is_up(void) { return system_state == SYSTEM_RUNNING; } EXPORT_SYMBOL(system_is_up); 在函數原型ä¸ï¼ŒåŒ…å«å‡½æ•¸å和它們的數據類型。雖然 C 語言裡沒有這樣的è¦æ±‚,在 Linux 里這是æ倡的åšæ³•ï¼Œå› 爲這樣å¯ä»¥å¾ˆç°¡å–®çš„給讀者æ供更多的有價值的信æ¯ã€‚ 7) 集ä¸çš„函數退出途徑 ------------------------------ 雖然被æŸäº›äººè²ç¨±å·²ç¶“éŽæ™‚,但是 goto 語å¥çš„ç‰åƒ¹ç‰©é‚„是經常被編è¯å™¨æ‰€ä½¿ç”¨ï¼Œå…·é«” å½¢å¼æ˜¯ç„¡æ¢ä»¶è·³è½‰æŒ‡ä»¤ã€‚ 當一個函數從多個ä½ç½®é€€å‡ºï¼Œä¸¦ä¸”需è¦åšä¸€äº›é¡žä¼¼æ¸…ç†çš„常見æ“作時,goto 語å¥å°±å¾ˆæ–¹ 便了。如果並ä¸éœ€è¦æ¸…ç†æ“作,那麼直接 return å³å¯ã€‚ é¸æ“‡ä¸€å€‹èƒ½å¤ 說明 goto 行爲或它爲何å˜åœ¨çš„標籤å。如果 goto è¦é‡‹æ”¾ ``buffer``, 一個ä¸éŒ¯çš„åå—å¯ä»¥æ˜¯ ``out_free_buffer:`` ã€‚åˆ¥åŽ»ä½¿ç”¨åƒ ``err1:`` å’Œ ``err2:`` 這樣的GW_BASIC åç¨±ï¼Œå› çˆ²ä¸€æ—¦ä½ æ·»åŠ æˆ–åˆªé™¤äº† (函數的) é€€å‡ºè·¯å¾‘ï¼Œä½ å°±å¿…é ˆå°å®ƒå€‘ é‡æ–°ç·¨è™Ÿï¼Œé€™æ¨£æœƒé›£ä»¥åŽ»æª¢é©—æ£ç¢ºæ€§ã€‚ 使用 goto çš„ç†ç”±æ˜¯ï¼š - ç„¡æ¢ä»¶èªžå¥å®¹æ˜“ç†è§£å’Œè·Ÿè¹¤ - åµŒå¥—ç¨‹åº¦æ¸›å° - å¯ä»¥é¿å…由於修改時忘記更新個別的退出點而導致錯誤 - 讓編è¯å™¨çœåŽ»åˆªé™¤å†—餘代碼的工作 ;) .. code-block:: c int fun(int a) { int result = 0; char *buffer; buffer = kmalloc(SIZE, GFP_KERNEL); if (!buffer) return -ENOMEM; if (condition1) { while (loop1) { ... } result = 1; goto out_free_buffer; } ... out_free_buffer: kfree(buffer); return result; } 一個需è¦æ³¨æ„的常見錯誤是 ``一個 err 錯誤`` ,就åƒé€™æ¨£ï¼š .. code-block:: c err: kfree(foo->bar); kfree(foo); return ret; 這段代碼的錯誤是,在æŸäº›é€€å‡ºè·¯å¾‘上 ``foo`` 是 NULL。通常情æ³ä¸‹ï¼Œé€šéŽæŠŠå®ƒåˆ†é›¢ æˆå…©å€‹éŒ¯èª¤æ¨™ç±¤ ``err_free_bar:`` å’Œ ``err_free_foo:`` 來修復這個錯誤: .. code-block:: c err_free_bar: kfree(foo->bar); err_free_foo: kfree(foo); return ret; ç†æƒ³æƒ…æ³ä¸‹ï¼Œä½ 應該模擬錯誤來測試所有退出路徑。 8) 注釋 ------------------------------ 注釋是好的,ä¸éŽæœ‰éŽåº¦æ³¨é‡‹çš„å±éšªã€‚æ°¸é ä¸è¦åœ¨æ³¨é‡‹é‡Œè§£é‡‹ä½ 的代碼是如何é‹ä½œçš„: 更好的åšæ³•æ˜¯è®“åˆ¥äººä¸€çœ‹ä½ çš„ä»£ç¢¼å°±å¯ä»¥æ˜Žç™½ï¼Œè§£é‡‹å¯«çš„很差的代碼是浪費時間。 ä¸€èˆ¬çš„ï¼Œä½ æƒ³è¦ä½ çš„æ³¨é‡‹å‘Šè¨´åˆ¥äººä½ çš„ä»£ç¢¼åšäº†ä»€éº¼ï¼Œè€Œä¸æ˜¯æ€Žéº¼åšçš„ã€‚ä¹Ÿè«‹ä½ ä¸è¦æŠŠ æ³¨é‡‹æ”¾åœ¨ä¸€å€‹å‡½æ•¸é«”å…§éƒ¨ï¼šå¦‚æžœå‡½æ•¸è¤‡é›œåˆ°ä½ éœ€è¦ç¨ç«‹çš„注釋其ä¸çš„ä¸€éƒ¨åˆ†ï¼Œä½ å¾ˆå¯èƒ½ 需è¦å›žåˆ°ç¬¬å…ç« çœ‹ä¸€çœ‹ã€‚ä½ å¯ä»¥åšä¸€äº›å°æ³¨é‡‹ä¾†è¨»æ˜Žæˆ–è¦å‘ŠæŸäº›å¾ˆè°æ˜Ž (或者槽糕) çš„ åšæ³•ï¼Œä½†ä¸è¦åŠ å¤ªå¤šã€‚ä½ æ‡‰è©²åšçš„,是把注釋放在函數的é 部,告訴人們它åšäº†ä»€éº¼ï¼Œ 也å¯ä»¥åŠ 上它åšé€™äº›äº‹æƒ…çš„åŽŸå› ã€‚ ç•¶æ³¨é‡‹å…§æ ¸ API 函數時,請使用 kernel-doc æ ¼å¼ã€‚請看 Documentation/doc-guide/ å’Œ scripts/kernel-doc 以ç²å¾—詳細信æ¯ã€‚ é•· (多行) 注釋的首é¸é¢¨æ ¼æ˜¯ï¼š .. code-block:: c /* * This is the preferred style for multi-line * comments in the Linux kernel source code. * Please use it consistently. * * Description: A column of asterisks on the left side, * with beginning and ending almost-blank lines. */ å°æ–¼åœ¨ net/ å’Œ drivers/net/ 的文件,首é¸çš„é•· (多行) æ³¨é‡‹é¢¨æ ¼æœ‰äº›ä¸åŒã€‚ .. code-block:: c /* The preferred comment style for files in net/ and drivers/net * looks like this. * * It is nearly the same as the generally preferred comment style, * but there is no initial almost-blank line. */ 注釋數據也是很é‡è¦çš„,ä¸ç®¡æ˜¯åŸºæœ¬é¡žåž‹é‚„是è¡ç”Ÿé¡žåž‹ã€‚爲了方便實ç¾é€™ä¸€é»žï¼Œæ¯ä¸€è¡Œ 應åªè²æ˜Žä¸€å€‹æ•¸æ“š (ä¸è¦ä½¿ç”¨é€—號來一次è²æ˜Žå¤šå€‹æ•¸æ“š)ã€‚é€™æ¨£ä½ å°±æœ‰ç©ºé–“ä¾†çˆ²æ¯å€‹æ•¸æ“š 寫一段å°æ³¨é‡‹ä¾†è§£é‡‹å®ƒå€‘的用途了。 9) ä½ å·²ç¶“æŠŠäº‹æƒ…å¼„ç³Ÿäº† ------------------------------ 這沒什麼,我們都是這樣。å¯èƒ½ä½ 的使用了很長時間 Unix 的朋å‹å·²ç¶“å‘Šè¨´ä½ ``GNU emacs`` èƒ½è‡ªå‹•å¹«ä½ æ ¼å¼åŒ– C åŽŸå§‹ç¢¼ï¼Œè€Œä¸”ä½ ä¹Ÿæ³¨æ„到了,確實是這樣,ä¸éŽå®ƒ 所使用的默èªå€¼å’Œæˆ‘們想è¦çš„相去甚é (實際上,甚至比隨機打的還è¦å·®â€”—無數個猴å 在 GNU emacs 里打å—æ°¸é ä¸æœƒå‰µé€ 出一個好程åº) (è¯è¨»ï¼šInfinite Monkey Theorem) æ‰€ä»¥ä½ è¦éº¼æ”¾æ£„ GNU emacs,è¦éº¼æ”¹è®Šå®ƒè®“它使用更åˆç†çš„è¨å®šã€‚è¦æŽ¡ç”¨å¾Œä¸€å€‹æ–¹æ¡ˆï¼Œ ä½ å¯ä»¥æŠŠä¸‹é¢é€™æ®µç²˜è²¼åˆ°ä½ çš„ .emacs 文件里。 .. code-block:: none (defun c-lineup-arglist-tabs-only (ignored) "Line up argument lists by tabs, not spaces" (let* ((anchor (c-langelem-pos c-syntactic-element)) (column (c-langelem-2nd-pos c-syntactic-element)) (offset (- (1+ column) anchor)) (steps (floor offset c-basic-offset))) (* (max steps 1) c-basic-offset))) (dir-locals-set-class-variables 'linux-kernel '((c-mode . ( (c-basic-offset . 8) (c-label-minimum-indentation . 0) (c-offsets-alist . ( (arglist-close . c-lineup-arglist-tabs-only) (arglist-cont-nonempty . (c-lineup-gcc-asm-reg c-lineup-arglist-tabs-only)) (arglist-intro . +) (brace-list-intro . +) (c . c-lineup-C-comments) (case-label . 0) (comment-intro . c-lineup-comment) (cpp-define-intro . +) (cpp-macro . -1000) (cpp-macro-cont . +) (defun-block-intro . +) (else-clause . 0) (func-decl-cont . +) (inclass . +) (inher-cont . c-lineup-multi-inher) (knr-argdecl-intro . 0) (label . -1000) (statement . 0) (statement-block-intro . +) (statement-case-intro . +) (statement-cont . +) (substatement . +) )) (indent-tabs-mode . t) (show-trailing-whitespace . t) )))) (dir-locals-set-directory-class (expand-file-name "~/src/linux-trees") 'linux-kernel) 這會讓 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`` 有很多é¸é …,特別是é‡æ–°æ ¼å¼åŒ–æ³¨é‡‹çš„æ™‚å€™ï¼Œä½ å¯èƒ½éœ€è¦çœ‹ä¸€ä¸‹å®ƒçš„手冊。 ä¸éŽè¨˜ä½ï¼š ``indent`` ä¸èƒ½ä¿®æ£å£žçš„編程習慣。 10) Kconfig é…置文件 ------------------------------ å°æ–¼é布æºç¢¼æ¨¹çš„所有 Kconfig* é…置文件來說,它們縮進方å¼æœ‰æ‰€ä¸åŒã€‚緊挨著 ``config`` 定義的行,用一個制表符縮進,然而 help ä¿¡æ¯çš„縮進則é¡å¤–å¢žåŠ 2 個空 æ ¼ã€‚èˆ‰å€‹ä¾‹å:: config AUDIT bool "Auditing support" depends on NET help Enable auditing infrastructure that can be used with another kernel subsystem, such as SELinux (which requires this for logging of avc messages output). Does not do system-call auditing without CONFIG_AUDITSYSCALL. 而那些å±éšªçš„功能 (比如æŸäº›æ–‡ä»¶ç³»çµ±çš„寫支æŒ) 應該在它們的æ示å—ç¬¦ä¸²é‡Œé¡¯è‘—çš„è² æ˜Žé€™ä¸€é»ž:: config ADFS_FS_RW bool "ADFS write support (DANGEROUS)" depends on ADFS_FS ... è¦æŸ¥çœ‹é…置文件的完整文檔,請看 Documentation/kbuild/kconfig-language.rst。 11) 數據çµæ§‹ ------------------------------ 如果一個數據çµæ§‹ï¼Œåœ¨å‰µå»ºå’ŒéŠ·æ¯€å®ƒçš„單線執行環境之外å¯è¦‹ï¼Œé‚£éº¼å®ƒå¿…é ˆè¦æœ‰ä¸€å€‹å¼• ç”¨è¨ˆæ•¸å™¨ã€‚å…§æ ¸é‡Œæ²’æœ‰åžƒåœ¾æ”¶é›† (ä¸¦ä¸”å…§æ ¸ä¹‹å¤–çš„åžƒåœ¾æ”¶é›†æ…¢ä¸”æ•ˆçŽ‡ä½Žä¸‹),這æ„å‘³è‘—ä½ çµ•å°éœ€è¦è¨˜éŒ„ä½ å°é€™ç¨®æ•¸æ“šçµæ§‹çš„使用情æ³ã€‚ 引用計數æ„å‘³è‘—ä½ èƒ½å¤ é¿å…上鎖,並且å…許多個用戶並行訪å•é€™å€‹æ•¸æ“šçµæ§‹â€”—而ä¸éœ€è¦ 擔心這個數據çµæ§‹åƒ…åƒ…å› çˆ²æš«æ™‚ä¸è¢«ä½¿ç”¨å°±æ¶ˆå¤±äº†ï¼Œé‚£äº›ç”¨æˆ¶å¯èƒ½ä¸éŽæ˜¯æ²‰ç¡äº†ä¸€é™£æˆ– 者åšäº†ä¸€äº›å…¶ä»–事情而已。 注æ„上鎖 **ä¸èƒ½** å–代引用計數。上鎖是爲了ä¿æŒæ•¸æ“šçµæ§‹çš„一致性,而引用計數是一 個內å˜ç®¡ç†æŠ€å·§ã€‚通常二者都需è¦ï¼Œä¸è¦æŠŠå…©å€‹æžæ··äº†ã€‚ 很多數據çµæ§‹å¯¦éš›ä¸Šæœ‰ 2 級引用計數,它們通常有ä¸åŒ ``é¡ž`` 的用戶。å類計數器統 計å類用戶的數é‡ï¼Œæ¯ç•¶å類計數器減至零時,全局計數器減一。 這種 ``多級引用計數`` 的例åå¯ä»¥åœ¨å…§å˜ç®¡ç† (``struct mm_struct``: mm_users å’Œ mm_count),和文件系統 (``struct super_block``: s_count å’Œ s_active) ä¸æ‰¾åˆ°ã€‚ 記ä½ï¼šå¦‚æžœå¦ä¸€å€‹åŸ·è¡Œç·šç´¢å¯ä»¥æ‰¾åˆ°ä½ 的數據çµæ§‹ï¼Œä½†é€™å€‹æ•¸æ“šçµæ§‹æ²’有引用計數器, 這裡幾乎肯定是一個 bug。 12) å®ï¼Œæžšèˆ‰å’ŒRTL ------------------------------ 用於定義常é‡çš„å®çš„åå—åŠæžšèˆ‰é‡Œçš„標籤需è¦å¤§å¯«ã€‚ .. code-block:: c #define CONSTANT 0x12345 在定義幾個相關的常é‡æ™‚,最好用枚舉。 å®çš„åå—請用大寫å—æ¯ï¼Œä¸éŽå½¢å¦‚函數的å®çš„åå—å¯ä»¥ç”¨å°å¯«å—æ¯ã€‚ 一般的,如果能寫æˆå…§è¯å‡½æ•¸å°±ä¸è¦å¯«æˆåƒå‡½æ•¸çš„å®ã€‚ å«æœ‰å¤šå€‹èªžå¥çš„å®æ‡‰è©²è¢«åŒ…å«åœ¨ä¸€å€‹ do-while 代碼塊里: .. code-block:: c #define macrofun(a, b, c) \ do { \ if (a == 5) \ do_this(b, c); \ } while (0) 使用å®çš„時候應é¿å…的事情: 1) 影響控制æµç¨‹çš„å®ï¼š .. code-block:: c #define FOO(x) \ do { \ if (blah(x) < 0) \ return -EBUGGERED; \ } while (0) **éžå¸¸** ä¸å¥½ã€‚它看起來åƒä¸€å€‹å‡½æ•¸ï¼Œä¸éŽå»èƒ½å°Žè‡´ ``調用`` 它的函數退出;ä¸è¦æ‰“ 亂讀者大腦里的語法分æžå™¨ã€‚ 2) ä¾è³´æ–¼ä¸€å€‹å›ºå®šåå—的本地變é‡çš„å®ï¼š .. code-block:: c #define FOO(val) bar(index, val) å¯èƒ½çœ‹èµ·ä¾†åƒæ˜¯å€‹ä¸éŒ¯çš„æ±è¥¿ï¼Œä¸éŽå®ƒéžå¸¸å®¹æ˜“把讀代碼的人æžç³Šå¡—,而且容易導致看起 來ä¸ç›¸é—œçš„改動帶來錯誤。 3) 作爲左值的帶åƒæ•¸çš„å®ï¼š FOO(x) = y;如果有人把 FOO 變æˆä¸€å€‹å…§è¯å‡½æ•¸çš„話,這 種用法就會出錯了。 4) 忘記了優先級:使用表é”å¼å®šç¾©å¸¸é‡çš„å®å¿…é ˆå°‡è¡¨é”å¼ç½®æ–¼ä¸€å°å°æ‹¬è™Ÿä¹‹å…§ã€‚帶åƒæ•¸ çš„å®ä¹Ÿè¦æ³¨æ„æ¤é¡žå•é¡Œã€‚ .. code-block:: c #define CONSTANT 0x4000 #define CONSTEXP (CONSTANT | 3) 5) 在å®é‡Œå®šç¾©é¡žä¼¼å‡½æ•¸çš„本地變é‡æ™‚命åè¡çªï¼š .. code-block:: c #define FOO(x) \ ({ \ typeof(x) ret; \ ret = calc_ret(x); \ (ret); \ }) ret 是本地變é‡çš„通用åå— - __foo_ret æ›´ä¸å®¹æ˜“與一個已å˜åœ¨çš„變é‡è¡çªã€‚ cpp 手冊å°å®çš„講解很詳細。gcc internals 手冊也詳細講解了 RTLï¼Œå…§æ ¸é‡Œçš„å½™ç·¨èªž 言經常用到它。 13) 列å°å…§æ ¸æ¶ˆæ¯ ------------------------------ å…§æ ¸é–‹ç™¼è€…æ‡‰è©²æ˜¯å—éŽè‰¯å¥½æ•™è‚²çš„。請一定注æ„å…§æ ¸ä¿¡æ¯çš„拼寫,以給人以好的å°è±¡ã€‚ ä¸è¦ç”¨ä¸è¦ç¯„的單詞比如 ``dont``,而è¦ç”¨ ``do not`` 或者 ``don't`` 。ä¿è‰é€™äº›ä¿¡ æ¯ç°¡å–®æ˜Žäº†,ç„¡æ§ç¾©ã€‚ å…§æ ¸ä¿¡æ¯ä¸å¿…以英文å¥è™ŸçµæŸã€‚ 在å°æ‹¬è™Ÿé‡Œåˆ—å°æ•¸å— (%d) 沒有任何價值,應該é¿å…這樣åšã€‚ <linux/device.h> 里有一些驅動模型診斷å®ï¼Œä½ 應該使用它們,以確ä¿ä¿¡æ¯å°æ‡‰æ–¼æ£ç¢º çš„è¨å‚™å’Œé©…動,並且被標記了æ£ç¢ºçš„消æ¯ç´šåˆ¥ã€‚這些å®æœ‰ï¼šdev_err(), dev_warn(), dev_info() ç‰ç‰ã€‚å°æ–¼é‚£äº›ä¸å’ŒæŸå€‹ç‰¹å®šè¨å‚™ç›¸é—œé€£çš„ä¿¡æ¯ï¼Œ<linux/printk.h> 定義 了 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 文檔以ç²å–有關它們的詳細信æ¯ã€‚ 傳éžçµæ§‹é«”大å°çš„首é¸å½¢å¼æ˜¯é€™æ¨£çš„: .. code-block:: c p = kmalloc(sizeof(*p), ...); å¦å¤–一種傳éžæ–¹å¼ä¸ï¼Œsizeof çš„æ“作數是çµæ§‹é«”çš„åå—,這樣會é™ä½Žå¯è®€æ€§ï¼Œä¸¦ä¸”å¯èƒ½ 會引入 bug。有å¯èƒ½æŒ‡é‡è®Šé‡é¡žåž‹è¢«æ”¹è®Šæ™‚,而å°æ‡‰çš„傳éžçµ¦å…§å˜åˆ†é…函數的 sizeof çš„çµæžœä¸è®Šã€‚ 強制轉æ›ä¸€å€‹ void 指é‡è¿”回值是多餘的。C 語言本身ä¿è‰äº†å¾ž void 指é‡åˆ°å…¶ä»–任何 指é‡é¡žåž‹çš„轉æ›æ˜¯æ²’有å•é¡Œçš„。 分é…一個數組的首é¸å½¢å¼æ˜¯é€™æ¨£çš„: .. code-block:: c p = kmalloc_array(n, sizeof(...), ...); 分é…一個零長數組的首é¸å½¢å¼æ˜¯é€™æ¨£çš„: .. code-block:: c p = kcalloc(n, sizeof(...), ...); 兩種形å¼æª¢æŸ¥åˆ†é…å¤§å° n * sizeof(...) 的溢出,如果溢出返回 NULL。 15) å…§è¯å¼Šç—… ------------------------------ 有一個常見的誤解是 ``å…§è¯`` 是 gcc æ供的å¯ä»¥è®“代碼é‹è¡Œæ›´å¿«çš„一個é¸é …。雖然使 用內è¯å‡½æ•¸æœ‰æ™‚候是æ°ç•¶çš„ (比如作爲一種替代å®çš„æ–¹å¼ï¼Œè«‹çœ‹ç¬¬åäºŒç« ),ä¸éŽå¾ˆå¤šæƒ… æ³ä¸‹ä¸æ˜¯é€™æ¨£ã€‚inline çš„éŽåº¦ä½¿ç”¨æœƒä½¿å…§æ ¸è®Šå¤§ï¼Œå¾žè€Œä½¿æ•´å€‹ç³»çµ±é‹è¡Œé€Ÿåº¦è®Šæ…¢ã€‚ å› çˆ²é«”ç©å¤§å…§æ ¸æœƒå 用更多的指令高速緩å˜ï¼Œè€Œä¸”會導致 pagecache çš„å¯ç”¨å…§å˜æ¸›å°‘。 想åƒä¸€ä¸‹ï¼Œä¸€æ¬¡ pagecache 未命ä¸å°±æœƒå°Žè‡´ä¸€æ¬¡ç£ç¢Ÿå°‹å€ï¼Œå°‡è€—時 5 毫秒。5 毫秒的 時間內 CPU 能執行很多很多指令。 一個基本的原則是如果一個函數有 3 行以上,就ä¸è¦æŠŠå®ƒè®Šæˆå…§è¯å‡½æ•¸ã€‚這個原則的一 å€‹ä¾‹å¤–æ˜¯ï¼Œå¦‚æžœä½ çŸ¥é“æŸå€‹åƒæ•¸æ˜¯ä¸€å€‹ç·¨è¯æ™‚常é‡ï¼Œè€Œä¸”å› çˆ²é€™å€‹å¸¸é‡ä½ 確定編è¯å™¨åœ¨ ç·¨è¯æ™‚èƒ½å„ªåŒ–æŽ‰ä½ çš„å‡½æ•¸çš„å¤§éƒ¨åˆ†ä»£ç¢¼ï¼Œé‚£ä»ç„¶å¯ä»¥çµ¦å®ƒåŠ 上 inline é—œéµå—。 kmalloc() å…§è¯å‡½æ•¸å°±æ˜¯ä¸€å€‹å¾ˆå¥½çš„例å。 人們經常主張給 static 的而且åªç”¨äº†ä¸€æ¬¡çš„å‡½æ•¸åŠ ä¸Š inline,如æ¤ä¸æœƒæœ‰ä»»ä½•æ失, å› çˆ²æ²’æœ‰ä»€éº¼å¥½æ¬Šè¡¡çš„ã€‚é›–ç„¶å¾žæŠ€è¡“ä¸Šèªªé€™æ˜¯æ£ç¢ºçš„,但是實際上這種情æ³ä¸‹å³ä½¿ä¸åŠ inline gcc 也å¯ä»¥è‡ªå‹•ä½¿å…¶å…§è¯ã€‚而且其他用戶å¯èƒ½æœƒè¦æ±‚移除 inline,由æ¤è€Œä¾†çš„ çˆè«–會抵消 inline 自身的潛在價值,得ä¸å„Ÿå¤±ã€‚ 16) 函數返回值åŠå‘½å ------------------------------ 函數å¯ä»¥è¿”回多種ä¸åŒé¡žåž‹çš„值,最常見的一種是表明函數執行æˆåŠŸæˆ–者失敗的值。這樣 的一個值å¯ä»¥è¡¨ç¤ºçˆ²ä¸€å€‹éŒ¯èª¤ä»£ç¢¼æ•´æ•¸ (-Exxxï¼å¤±æ•—,0ï¼æˆåŠŸ) 或者一個 ``æˆåŠŸ`` 布爾值 (0ï¼å¤±æ•—,éž0ï¼æˆåŠŸ)。 æ··åˆä½¿ç”¨é€™å…©ç¨®è¡¨é”æ–¹å¼æ˜¯é›£æ–¼ç™¼ç¾çš„ bug 的來æºã€‚如果 C èªžè¨€æœ¬èº«åš´æ ¼å€åˆ†æ•´å½¢å’Œ 布爾型變é‡ï¼Œé‚£éº¼ç·¨è¯å™¨å°±èƒ½å¤ 幫我們發ç¾é€™äº›éŒ¯èª¤... ä¸éŽ C 語言ä¸å€åˆ†ã€‚爲了é¿å… 產生這種 bug,請éµå¾ªä¸‹é¢çš„慣例:: 如果函數的åå—是一個動作或者強制性的命令,那麼這個函數應該返回錯誤代 碼整數。如果是一個判斷,那麼函數應該返回一個 "æˆåŠŸ" 布爾值。 比如, ``add work`` 是一個命令,所以 add_work() 在æˆåŠŸæ™‚返回 0,在失敗時返回 -EBUSYã€‚é¡žä¼¼çš„ï¼Œå› çˆ² ``PCI device present`` 是一個判斷,所以 pci_dev_present() 在æˆåŠŸæ‰¾åˆ°ä¸€å€‹åŒ¹é…çš„è¨å‚™æ™‚應該返回 1,如果找ä¸åˆ°æ™‚應該返回 0。 所有 EXPORTed å‡½æ•¸éƒ½å¿…é ˆéµå®ˆé€™å€‹æ…£ä¾‹ï¼Œæ‰€æœ‰çš„公共函數也都應該如æ¤ã€‚ç§æœ‰ (static) 函數ä¸éœ€è¦å¦‚æ¤ï¼Œä½†æ˜¯æˆ‘們也推薦這樣åšã€‚ 返回值是實際計算çµæžœè€Œä¸æ˜¯è¨ˆç®—是å¦æˆåŠŸçš„標誌的函數ä¸å—æ¤æ…£ä¾‹çš„é™åˆ¶ã€‚一般的, 他們通éŽè¿”回一些æ£å¸¸å€¼ç¯„åœä¹‹å¤–çš„çµæžœä¾†è¡¨ç¤ºå‡ºéŒ¯ã€‚典型的例å是返回指é‡çš„函數, 他們使用 NULL 或者 ERR_PTR æ©Ÿåˆ¶ä¾†å ±å‘ŠéŒ¯èª¤ã€‚ 17) ä¸è¦é‡æ–°ç™¼æ˜Žå…§æ ¸å® ------------------------------ é 文件 include/linux/kernel.h 包å«äº†ä¸€äº›å®ï¼Œä½ 應該使用它們,而ä¸è¦è‡ªå·±å¯«ä¸€äº› å®ƒå€‘çš„è®Šç¨®ã€‚æ¯”å¦‚ï¼Œå¦‚æžœä½ éœ€è¦è¨ˆç®—ä¸€å€‹æ•¸çµ„çš„é•·åº¦ï¼Œä½¿ç”¨é€™å€‹å® .. code-block:: c #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) é¡žä¼¼çš„ï¼Œå¦‚æžœä½ è¦è¨ˆç®—æŸçµæ§‹é«”æˆå“¡çš„大å°ï¼Œä½¿ç”¨ .. code-block:: c #define sizeof_field(t, f) (sizeof(((t*)0)->f)) 還有å¯ä»¥åšåš´æ ¼çš„類型檢查的 min() å’Œ max() å®ï¼Œå¦‚æžœä½ éœ€è¦å¯ä»¥ä½¿ç”¨å®ƒå€‘ã€‚ä½ å¯ä»¥ 自己看看那個é æ–‡ä»¶é‡Œé‚„å®šç¾©äº†ä»€éº¼ä½ å¯ä»¥æ‹¿ä¾†ç”¨çš„æ±è¥¿ï¼Œå¦‚æžœæœ‰å®šç¾©çš„è©±ï¼Œä½ å°±ä¸æ‡‰ åœ¨ä½ çš„ä»£ç¢¼é‡Œè‡ªå·±é‡æ–°å®šç¾©ã€‚ 18) 編輯器模å¼è¡Œå’Œå…¶ä»–需è¦ç¾…嗦的事情 -------------------------------------------------- 有一些編輯器å¯ä»¥è§£é‡‹åµŒå…¥åœ¨æºæ–‡ä»¶é‡Œçš„由一些特殊標記標明的é…置信æ¯ã€‚比如,emacs èƒ½å¤ è§£é‡‹è¢«æ¨™è¨˜æˆé€™æ¨£çš„行: .. code-block:: c -*- mode: c -*- 或者這樣的: .. code-block:: c /* Local Variables: compile-command: "gcc -DMAGIC_DEBUG_FLAG foo.c" End: */ Vim èƒ½å¤ è§£é‡‹é€™æ¨£çš„æ¨™è¨˜ï¼š .. code-block:: c /* vim:set sw=8 noet */ ä¸è¦åœ¨åŽŸå§‹ç¢¼ä¸åŒ…å«ä»»ä½•é€™æ¨£çš„內容。æ¯å€‹äººéƒ½æœ‰ä»–自己的編輯器é…ç½®ï¼Œä½ çš„æºæ–‡ä»¶ä¸ 應該覆蓋別人的é…置。這包括有關縮進和模å¼é…置的標記。人們å¯ä»¥ä½¿ç”¨ä»–們自己定製 的模å¼ï¼Œæˆ–者使用其他å¯ä»¥ç”¢ç”Ÿæ£ç¢ºçš„縮進的巧妙方法。 19) å…§è¯å½™ç·¨ ------------------------------ 在特定架構的代碼ä¸ï¼Œä½ å¯èƒ½éœ€è¦å…§è¯å½™ç·¨èˆ‡ CPU 和平å°ç›¸é—œåŠŸèƒ½é€£æŽ¥ã€‚需è¦é€™éº¼åšæ™‚ å°±ä¸è¦çŒ¶è±«ã€‚然而,當 C å¯ä»¥å®Œæˆå·¥ä½œæ™‚,ä¸è¦å¹³ç™½ç„¡æ•…地使用內è¯å½™ç·¨ã€‚在å¯èƒ½çš„情 æ³ä¸‹ï¼Œä½ å¯ä»¥ä¸¦ä¸”應該用 C 和硬體æºé€šã€‚ 請考慮去寫æ†ç¶é€šç”¨ä½å…ƒ (wrap common bits) çš„å…§è¯å½™ç·¨çš„簡單輔助函數,別去é‡è¤‡ 地寫下åªæœ‰ç´°å¾®å·®ç•°å…§è¯å½™ç·¨ã€‚記ä½å…§è¯å½™ç·¨å¯ä»¥ä½¿ç”¨ C åƒæ•¸ã€‚ 大型,有一定複雜度的彙編函數應該放在 .S 文件內,用相應的 C 原型定義在 C é æ–‡ 件ä¸ã€‚彙編函數的 C 原型應該使用 ``asmlinkage`` 。 ä½ å¯èƒ½éœ€è¦æŠŠå½™ç·¨èªžå¥æ¨™è¨˜çˆ² volatileï¼Œç”¨ä¾†é˜»æ¢ GCC 在沒發ç¾ä»»ä½•å‰¯ä½œç”¨å¾Œå°±æŠŠå®ƒ ç§»é™¤äº†ã€‚ä½ ä¸å¿…總是這樣åšï¼Œå„˜ç®¡ï¼Œé€™ä¸å¿…è¦çš„舉動會é™åˆ¶å„ªåŒ–。 在寫一個包å«å¤šæ¢æŒ‡ä»¤çš„單個內è¯å½™ç·¨èªžå¥æ™‚,把æ¯æ¢æŒ‡ä»¤ç”¨å¼•è™Ÿåˆ†å‰²è€Œä¸”å„å 一行, 除了最後一æ¢æŒ‡ä»¤å¤–,在æ¯å€‹æŒ‡ä»¤çµå°¾åŠ 上 \n\t,讓彙編輸出時å¯ä»¥æ£ç¢ºåœ°ç¸®é€²ä¸‹ä¸€æ¢ 指令: .. code-block:: c asm ("magic %reg1, #42\n\t" "more_magic %reg2, %reg3" : /* outputs */ : /* inputs */ : /* clobbers */); 20) æ¢ä»¶ç·¨è¯ ------------------------------ åªè¦å¯èƒ½ï¼Œå°±ä¸è¦åœ¨ .c 文件裡é¢ä½¿ç”¨é 處ç†æ¢ä»¶ (#if, #ifdef);這樣åšè®“代碼更難 閱讀並且更難去跟蹤é‚輯。替代方案是,在é 文件ä¸ç”¨é 處ç†æ¢ä»¶æ供給那些 .c 文件 使用,å†çµ¦ #else æä¾›ä¸€å€‹ç©ºæ¨ (no-op stub) 版本,然後在 .c 文件內無æ¢ä»¶åœ°èª¿ç”¨ 那些 (定義在é 文件內的) 函數。這樣åšï¼Œç·¨è¯å™¨æœƒé¿å…爲æ¨å‡½æ•¸ (stub) çš„èª¿ç”¨ç”Ÿæˆ ä»»ä½•ä»£ç¢¼ï¼Œç”¢ç”Ÿçš„çµæžœæ˜¯ç›¸åŒçš„,但é‚è¼¯å°‡æ›´åŠ æ¸…æ™°ã€‚ 最好傾å‘於編è¯æ•´å€‹å‡½æ•¸ï¼Œè€Œä¸æ˜¯å‡½æ•¸çš„一部分或表é”å¼çš„一部分。與其放一個 ifdef 在表é”å¼å…§ï¼Œä¸å¦‚分解出部分或全部表é”å¼ï¼Œæ”¾é€²ä¸€å€‹å–®ç¨çš„輔助函數,並應用é è™•ç† æ¢ä»¶åˆ°é€™å€‹è¼”助函數內。 å¦‚æžœä½ æœ‰ä¸€å€‹åœ¨ç‰¹å®šé…ç½®ä¸ï¼Œå¯èƒ½è®Šæˆæœªä½¿ç”¨çš„函數或變é‡ï¼Œç·¨è¯å™¨æœƒè¦å‘Šå®ƒå®šç¾©äº†ä½† 未使用,把它標記爲 __maybe_unused 而ä¸æ˜¯å°‡å®ƒåŒ…å«åœ¨ä¸€å€‹é 處ç†æ¢ä»¶ä¸ã€‚(然而,如 果一個函數或變é‡ç¸½æ˜¯æœªä½¿ç”¨ï¼Œå°±ç›´æŽ¥åˆªé™¤å®ƒã€‚) 在代碼ä¸ï¼Œå„˜å¯èƒ½åœ°ä½¿ç”¨ IS_ENABLED å®ä¾†è½‰åŒ–æŸå€‹ Kconfig 標記爲 C 的布爾 表é”å¼ï¼Œä¸¦åœ¨ä¸€èˆ¬çš„ C æ¢ä»¶ä¸ä½¿ç”¨å®ƒï¼š .. code-block:: c if (IS_ENABLED(CONFIG_SOMETHING)) { ... } ç·¨è¯å™¨æœƒåšå¸¸é‡æ‘ºç–Šï¼Œç„¶å¾Œå°±åƒä½¿ç”¨ #ifdef 那樣去包å«æˆ–排除代碼塊,所以這ä¸æœƒå¸¶ 來任何é‹è¡Œæ™‚開銷。然而,這種方法ä¾èˆŠå…許 C ç·¨è¯å™¨æŸ¥çœ‹å¡Šå…§çš„ä»£ç¢¼ï¼Œä¸¦æª¢æŸ¥å®ƒçš„æ£ ç¢ºæ€§ (語法,類型,符號引用,ç‰ç‰)ã€‚å› æ¤ï¼Œå¦‚æžœæ¢ä»¶ä¸æ»¿è¶³ï¼Œä»£ç¢¼å¡Šå…§çš„引用符號就 ä¸å˜åœ¨æ™‚ï¼Œä½ é‚„æ˜¯å¿…é ˆåŽ»ç”¨ #ifdef。 在任何有æ„義的 #if 或 #ifdef 塊的末尾 (超éŽå¹¾è¡Œçš„),在 #endif åŒä¸€è¡Œçš„後é¢å¯«ä¸‹ 註解,注釋這個æ¢ä»¶è¡¨é”å¼ã€‚例如: .. code-block:: c #ifdef CONFIG_SOMETHING ... #endif /* CONFIG_SOMETHING */ 附錄 I) åƒè€ƒ ------------------- The C Programming Language, 第二版 作者:Brian W. Kernighan å’Œ Denni M. Ritchie. Prentice Hall, Inc., 1988. ISBN 0-13-110362-8 (軟皮), 0-13-110370-9 (硬皮). The Practice of Programming 作者:Brian W. Kernighan å’Œ Rob Pike. Addison-Wesley, Inc., 1999. ISBN 0-201-61586-X. GNU 手冊 - éµå¾ª K&R 標準和æ¤æ–‡æœ¬ - cpp, gcc, gcc internals and indent, 都å¯ä»¥å¾ž https://www.gnu.org/manual/ 找到 WG14 是 C 語言的國際標準化工作組,URL: http://www.open-std.org/JTC1/SC22/WG14/ Kernel process/coding-style.rst,作者 greg@kroah.com 發表於 OLS 2002: http://www.kroah.com/linux/talks/ols_2002_kernel_codingstyle_talk/html/