.. include:: ../disclaimer-zh_CN.rst :Original: :ref:`Documentation/process/coding-style.rst <codingstyle>` .. _cn_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> 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; /* fall through */ 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/