• Linux内核编码风格翻译(2)

    4) 命名(Naming)

    C is a Spartan language, and so should your naming be. Unlike Modula-2 and Pascal programmers, C programmers do not use cute names like ThisVariableIsATemporaryCounter. A C programmer would call that variable tmp, which is much easier to write, and not the least more difficult to understand.

    C语言属于斯巴达语,因此,命名也应该遵守斯巴达语言的特点。与Modula-2和Pascal程序员不同,C程序员不应该使用类似ThisVariableIsATemporaryCounter之类的可爱名称。C程序员通常使用类似tmp的变量名,它更容易编写,而且也不难理解。

    HOWEVER, while mixed-case names are frowned upon, descriptive names for global variables are a must. To call a global function foo is a shooting offense.

    然而,尽管不赞成使用大小写混合的命名方式,但是全局变量必须使用描述性的名称(descriptive names),将全局函数命名为foo是一种冒犯(shooting offense)。

    GLOBAL variables (to be used only if you really need them) need to have descriptive names, as do global functions. If you have a function that counts the number of active users, you should call that count_active_users() or similar, you should not call it cntusr().

    全局变量(不要轻易定义全局变量,只有真正需要时再定义)和全局函数需要使用描述性的名称(descriptive names),如果你有一个统计活跃用户的函数,你应该将其命名为count_active_user()或与之类似的名字,一定不要命名为cntusr()。

    Encoding the type of a function into the name (so-called Hungarian notation) is brain damaged - the compiler knows the types anyway and can check those, and it only confuses the programmer. No wonder MicroSoft makes buggy programs.

    将函数类型放入函数名中(所谓的匈牙利命名法)是脑残的做法。编译器知道类型也会做类型检查,这种做法只会让程序员感到困惑。调侃微软曾使用匈牙利命名法(No wonder MicroSoft makes buggy programs)。

    LOCAL variable names should be short, and to the point. If you have some random integer loop counter, it should probably be called i. Calling it loop_counter is non-productive, if there is no chance of it being mis-understood. Similarly, tmp can be just about any type of variable that is used to hold a temporary value.

    局部变量尽量简短明了。例如:循环计数器通常命名为i,如果不会被误解命名为loop_counter是不必要的。类似的,用来保存临时值的变量无论什么类型,都可以命名为tmp。

    If you are afraid to mix up your local variable names, you have another problem, which is called the function-growth-hormone-imbalance syndrome. See chapter 6 (Functions).

    如果担心临时变量名字被混淆,那么你遇到了另一个问题:bulabula综合症(function-growth-hormone-imbalance),请阅读第6章(函数)。

    5) Typedefs

    Please don’t use things like vps_t. It’s a mistake to use typedef for structures and pointers. When you see a

    不要使用类似vps_t之类的自定义类型,对于结构体和指针,使用typedef是一个错误,当看到下面的代码时,你能直接读懂a的含义吗?

    
    
    <span class="n">vps_t</span> <span class="n">a</span><span class="p">;</span>

    in the source, what does it mean? In contrast, if it says

    相反,如果看到下面的代码,你应该可以确切的知道a的含义。

    
    
    <span class="k">struct</span> <span class="n">virtual_container</span> <span class="o">*</span><span class="n">a</span><span class="p">;</span>

    you can actually tell what a is.

    Lots of people think that typedefs help readability. Not so. They are useful only for:

    a. totally opaque objects (where the typedef is actively used to hide what the object is).

    Example: pte_t etc. opaque objects that you can only access using the proper accessor functions.

    Note
    Opaqueness and accessor functions are not good in themselves. The reason we have them for things like pte_t etc. is that there really is absolutely zero portably accessible information there.

    b. Clear integer types, where the abstraction helps avoid confusion whether it is int or long. u8/u16/u32 are perfectly fine typedefs, although they fit into category (d) better than here.

    Note
    Again - there needs to be a reason for this. If something is unsigned long, then there’s no reason to do
    typedef unsigned long myflags_t;

    but if there is a clear reason for why it under certain circumstances might be an unsigned int and under other configurations might be unsigned long, then by all means go ahead and use a typedef.

    c. when you use sparse to literally create a new type for type-checking.

    d. New types which are identical to standard C99 types, in certain exceptional circumstances. Although it would only take a short amount of time for the eyes and brain to become accustomed to the standard types like uint32_t, some people object to their use anyway.

    Therefore, the Linux-specific u8/u16/u32/u64 types and their signed equivalents which are identical to standard types are permitted – although they are not mandatory in new code of your own.

    When editing existing code which already uses one or the other set of types, you should conform to the existing choices in that code.

    e. Types safe for use in userspace.

    In certain structures which are visible to userspace, we cannot require C99 types and cannot use the u32 form above. Thus, we use __u32 and similar types in all structures which are shared with userspace.

    很多人认为typedef有助于提升可读性。不是这样,它们仅仅在下面的场景中对提升可读性是有帮助的:

    a. 完全不透明的对象(typedef为了隐藏对象的细节)

    例如:pte_t等不透明的对象,你只能使用适当的访问器函数来访问。

    注意:
    不透明和访问器函数本身并不好。类似pte_t等之所以这么做,是因为哪里存在完全不可移植的信息。

    b. 清除integer类型,抽象帮助避免混淆是int还是long。

    u8/u16/u32是非常好的typedef,这条规则放到下文的d中比放在这里更合适。

    注意:
    如果某些类型很明确只能是unsigned long类型,那么我们没必要这么做:
    typedef unsigned long myflags_t;

    但是,如果有明确的说明,在某些配置下它可能是unsigned int,而在其他配置下可能是unsigned long,那么一定要继续使用typedef。

    c. 为了做类型检查,使用typedef创建一个新类型。

    d. 某些特殊情况下,使用typedef定义与标准的C99类型相同的新类型

    虽然眼睛和大脑只需要很短的时间就能习惯像uint32_t这样的标准类型,但是有些人还是反对使用他们。

    允许使用与标准类型相同的特定于Linux的u8/u16/u32/u64及对应的有符号类型,尽管他们在您的新代码中不是强制性的。

    编辑使用某种自定义基础类型的已有代码时,应该遵守该代码中的现有选择。

    e. 使用typedef定义在用户空间安全使用的类型

    在用户空间可见的某些结构中,不能依赖C99中的类型,也不能使用u32,因此我们在与用户空间共享的所有结构体中使用了__u32和类似的类型。

    Maybe there are other cases too, but the rule should basically be to NEVER EVER use a typedef unless you can clearly match one of those rules.

    也许还有其他情况,但是基本的规则是永远不要使用typedef,除非您可以清楚地匹配上述的某条规则。

    In general, a pointer, or a struct that has elements that can reasonably be directly accessed should never be a typedef.

    通常情况下,指针和需要直接访问成员的结构体不应该使用typedef。

    6) 函数(Functions)

    Functions should be short and sweet, and do just one thing. They should fit on one or two screenfuls of text (the ISO/ANSI screen size is 80x24, as we all know), and do one thing and do that well.

    函数应该简单明了,并且只做一件事。一个函数应该只占1屏或2屏(ISO/ANSI屏幕大小是80x24),只做一件事并把它做好。

    The maximum length of a function is inversely proportional to the complexity and indentation level of that function. So, if you have a conceptually simple function that is just one long (but simple) case-statement, where you have to do lots of small things for a lot of different cases, it’s OK to have a longer function.

    函数的最大长度与复杂度和缩进级别成反比。但是,如果你有一个概念上简单,但内容是长长的但是很简单的case语句,你需要为不同的case做很多小事,那么有一个较长的函数是OK的。

    However, if you have a complex function, and you suspect that a less-than-gifted first-year high-school student might not even understand what the function is all about, you should adhere to the maximum limits all the more closely. Use helper functions with descriptive names (you can ask the compiler to in-line them if you think it’s performance-critical, and it will probably do a better job of it than you would have done).

    然而,如果你有一个复杂的函数,你怀疑一个不太有天赋的高中一年级的学生不能完全理解这个函数,你应该最大限度地遵守函数最大限制的这条规则。(如果您认为性能很重要,你可以要求编译器内联他们,这可能会比你做的更好)。

    Another measure of the function is the number of local variables. They shouldn’t exceed 5-10, or you’re doing something wrong. Re-think the function, and split it into smaller pieces. A human brain can generally easily keep track of about 7 different things, anything more and it gets confused. You know you’re brilliant, but maybe you’d like to understand what you did 2 weeks from now.

    函数的另一个度量是局部变量的数量。他们不应该超过5-10个,否则你就做错了。重新思考这个函数,并拆分它。人脑通常可以容易地跟踪7个不同的事物,超过这个数量将变得混乱。你知道你很聪明,2周后可能你就不知道你在干什么了。

    In source files, separate functions with one blank line. If the function is exported, the EXPORT macro for it should follow immediately after the closing function brace line. E.g.:

    代码中,函数之间加入一个空行。如果要导出该函数,EXPORT宏应该紧随右括号并独占一行,例如:

    
    
    <span class="kt">int</span> <span class="nf">system_is_up</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span>
    <span class="p">{</span>
            <span class="k">return</span> <span class="n">system_state</span> <span class="o">==</span> <span class="n">SYSTEM_RUNNING</span><span class="p">;</span>
    <span class="p">}</span>
    <span class="n">EXPORT_SYMBOL</span><span class="p">(</span><span class="n">system_is_up</span><span class="p">);</span>

    In function prototypes, include parameter names with their data types. Although this is not required by the C language, it is preferred in Linux because it is a simple way to add valuable information for the reader.

    函数原型中,应包含参数名及其类型。尽管C语言不需要这么做,但是在Linux中这是推荐的做法,它以简单的方式为读者提供了有价值的信息。

    7) 函数集中退出(Centralized exiting of functions)

    Albeit deprecated by some people, the equivalent of the goto statement is used frequently by compilers in form of the unconditional jump instruction.

    尽管有些人不赞成使用goto语句,但是编译器经常以无条件跳转指令的形式使用与goto语句等效的语句。

    The goto statement comes in handy when a function exits from multiple locations and some common work such as cleanup has to be done. If there is no cleanup needed then just return directly.

    当一个函数从多个位置退出并且有一些共通的工作(比如清理)需要做时,goto语句非常方便。如果不需要清理,那么直接返回即可。

    Choose label names which say what the goto does or why the goto exists. An example of a good name could be out_free_buffer: if the goto frees buffer. Avoid using GW-BASIC names like err1: and err2:, as you would have to renumber them if you ever add or remove exit paths, and they make correctness difficult to verify anyway.

    goto语句的标签命名,体现goto做了什么或者goto为什么存在。比如:out_free_buffer是一个好的名字:如果goto释放buffer。避免使用像err1:或err2这样的GW-BASIC名称,,因为如果你增加或移除退出路径时,你需要对他们重新编号,而且很难验证正确性。

    The rationale for using gotos is:

    • unconditional statements are easier to understand and follow
    • nesting is reduced
    • errors by not updating individual exit points when making modifications are prevented
    • saves the compiler work to optimize redundant code away 😉

    使用goto的理由是:

    • 无条件语句更容易理解和遵守
    • 减少了嵌套
    • 防止在进行修改时没更新退出点而导致的错误
    • 节省编译器优化冗余代码的工作
    
    
    <span class="kt">int</span> <span class="nf">fun</span><span class="p">(</span><span class="kt">int</span> <span class="n">a</span><span class="p">)</span>
    <span class="p">{</span>
            <span class="kt">int</span> <span class="n">result</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
            <span class="kt">char</span> <span class="o">*</span><span class="n">buffer</span><span class="p">;</span>

            <span class="n">buffer</span> <span class="o">=</span> <span class="n">kmalloc</span><span class="p">(</span><span class="n">SIZE</span><span class="p">,</span> <span class="n">GFP_KERNEL</span><span class="p">);</span>
            <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">buffer</span><span class="p">)</span>
                    <span class="k">return</span> <span class="o">-</span><span class="n">ENOMEM</span><span class="p">;</span>

            <span class="k">if</span> <span class="p">(</span><span class="n">condition1</span><span class="p">)</span> <span class="p">{</span>
                    <span class="k">while</span> <span class="p">(</span><span class="n">loop1</span><span class="p">)</span> <span class="p">{</span>
                            <span class="p">...</span>
                    <span class="p">}</span>
                    <span class="n">result</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
                    <span class="k">goto</span> <span class="n">out_free_buffer</span><span class="p">;</span>
            <span class="p">}</span>
            <span class="p">...</span>
    <span class="nl">out_free_buffer</span><span class="p">:</span>
            <span class="n">kfree</span><span class="p">(</span><span class="n">buffer</span><span class="p">);</span>
            <span class="k">return</span> <span class="n">result</span><span class="p">;</span>
    <span class="p">}</span>

    A common type of bug to be aware of is one err bugs which look like this:

    一个已知的常见的bug,像这样:

    
    
    <span class="nl">err</span><span class="p">:</span>
            <span class="n">kfree</span><span class="p">(</span><span class="n">foo</span><span class="o">-&gt;</span><span class="n">bar</span><span class="p">);</span>
            <span class="n">kfree</span><span class="p">(</span><span class="n">foo</span><span class="p">);</span>
            <span class="k">return</span> <span class="n">ret</span><span class="p">;</span>

    The bug in this code is that on some exit paths foo is NULL. Normally the fix for this is to split it up into two error labels err_free_bar: and err_free_foo::

    代码中的错误是:在某些退出路径上foo是空,通常,解决方法是将其拆分为err_free_bar和err_free_foo两个标签。

    
    
    <span class="nl">err_free_bar</span><span class="p">:</span>
           <span class="n">kfree</span><span class="p">(</span><span class="n">foo</span><span class="o">-&gt;</span><span class="n">bar</span><span class="p">);</span>
    <span class="nl">err_free_foo</span><span class="p">:</span>
           <span class="n">kfree</span><span class="p">(</span><span class="n">foo</span><span class="p">);</span>
           <span class="k">return</span> <span class="n">ret</span><span class="p">;</span>

    Ideally you should simulate errors to test all exit paths.

    理想情况下,您应该模拟错误来测试所有退出路径。

    8) 注释(Commenting)

    Comments are good, but there is also a danger of over-commenting. NEVER try to explain HOW your code works in a comment: it’s much better to write the code so that the working is obvious, and it’s a waste of time to explain badly written code.

    注释是好习惯,但不要过度。注释中不要试图解析您的代码如何(HOW)工作,最好的方式是代码本身就清晰地说明了它是如何工作的,解析写的不好的代码是在浪费时间。

    Generally, you want your comments to tell WHAT your code does, not HOW. Also, try to avoid putting comments inside a function body: if the function is so complex that you need to separately comment parts of it, you should probably go back to chapter 6 for a while. You can make small comments to note or warn about something particularly clever (or ugly), but try to avoid excess. Instead, put the comments at the head of the function, telling people what it does, and possibly WHY it does it.

    通常情况下,注释用来说明你的代码在做什么(WHAT),而不是怎么做。避免将注释放在函数体中:如果代码非常复杂,需要单独注释其中的部分,请重新阅读第六章。你可以对一些特别聪明(或丑陋)的事情做一些小的注释来提醒或警告,但要尽量避免过度。相反,把注释放在函数的头上,告诉人们它做了什么(WHAT),可能还有它为什么这么做(WHY)。

    When commenting the kernel API functions, please use the kernel-doc format. See the files at Documentation/doc-guide/ and scripts/kernel-doc for details.

    给Kernel的API加注释时,请使用Kernel-doc格式,请查看Documentation/doc-guide/ 和 scripts/kernel-doc来获得更多细节。

    The preferred style for long (multi-line) comments is:

    
    
    <span class="cm">/*
    </span><span class="cm"> * This is the preferred style for multi-line
    </span><span class="cm"> * comments in the Linux kernel source code.
    </span><span class="cm"> * Please use it consistently.
    </span><span class="cm"> *
    </span><span class="cm"> * Description:  A column of asterisks on the left side,
    </span><span class="cm"> * with beginning and ending almost-blank lines.
    </span><span class="cm"> */</span>

    多行注释的首选样式:

    
    
    <span class="cm">/*
    </span><span class="cm"> * Linux内核代码中,这是多行注释的首选样式。
    </span><span class="cm"> * 请坚持使用它。
    </span><span class="cm"> *
    </span><span class="cm"> * 描述:左边有一列星号,开头和结尾都是几乎为空的行。
    </span><span class="cm"> */</span>

    For files in net/ and drivers/net/ the preferred style for long (multi-line) comments is a little different.

    
    
    <span class="cm">/* The preferred comment style for files in net/ and drivers/net
    </span><span class="cm"> * looks like this.
    </span><span class="cm"> *
    </span><span class="cm"> * It is nearly the same as the generally preferred comment style,
    </span><span class="cm"> * but there is no initial almost-blank line.
    </span><span class="cm"> */</span>

    对于net/和drivers/net/中的文件,长(多行)注释的首选样式略有不同

    
    
    <span class="cm">/* net/和drivers/net中的文件的首选样式是这样的。
    </span><span class="cm"> *
    </span><span class="cm"> * 与通常的注释风格非常接近,
    </span><span class="cm"> * 但是没有一个开头几乎是空白的行。
    </span><span class="cm"> */</span>

    It’s also important to comment data, whether they are basic types or derived types. To this end, use just one data declaration per line (no commas for multiple data declarations). This leaves you room for a small comment on each item, explaining its use.

    为数据加注释也很重要,无论他是基础类型还是派生类型。为此,每行只声明一个数据(不要使用逗号分割形式的多数据声明)。这样你就可以为每个项目单独加注释来解释它的用途。

    To be continue.

原创文章,转载请注明: 转载自指南者

本文链接地址: Linux内核编码风格翻译(2)



  • + 华为 FreeBuds Pro 使用体验
  • + 如何看待江苏常州警方为营救轻生男子拉下电闸,附近老人因呼吸机断电死亡?
  • + PayPal注册教程:手把手教你注册大陆PayPal
  • + 总结操作国外lead任务不加钱的一些原因
  • + 本地计算发展到网络计算,到现在的云计算,同时操作系统也在不断变化。不同的计算环境下,操作系统有何区别?
  • + 我购买nord已经有一段时间了,经常出现无法连接的现象,该怎么操作?
  • + 买云服务器有推荐吗?国内知道有腾讯云、阿里云。。。等等,不知道该选哪个好了,另外优惠吗》
  • + 亚马逊账号关联怎么避免?
  • + 求拨号vps adsl的 哪家好?不是土豪,性价比高的求推荐?
  • + 2020年9月,现在做亚马逊测评,使用VPS会被风控吗?
  • + 买云服务器有推荐吗?国内知道有腾讯云、阿里云。。。等等,不知道该选哪个好了,另外优惠吗》
  • + 新人学生党,如何充分利用云服务器?
  • 评论

    你必须先登录