Skip to content

Coding Style

thanos edited this page Mar 20, 2021 · 1 revision

Coding Style

Some guidilines about the Coding Style for the kernel of the Operating System. Pretty much follows linux kernel coding style, except the parts that are specific to the linux kernel.

Indentation

8 characters

Line Length

80 characters

Braces

For all non-function statement blocks:

if (x is true) {
        we do y
}

For all functions:

int function(int x)
{
    body of function
}

For closing braces, where it is followed by a continuation of the same statement:

if (x == y) {
        ..
} else if (x > y) {
        ...
} else {
        ....
}

For single statements, braces are omitted:

if (condition)
        do_this();
else
        do_that();

If only one branch of a conditional statement is a single statement, braces are used both branches:

if (condition) {
        do_this();
        do_that();
} else {
        otherwise();
}

Spaces

A space is used after these keywords:

if, switch, case, for, do, while

But not with:

sizeof, typeof, alignof, __attribute__, ...

Do not add spaces around (inside) parenthesized expressions:

s = sizeof( struct file );  // BAD

For pointers, the preferred use of * is adjacent to the data name or function name and not adjacent to the type name:

char *linux_banner;
unsigned long long memparse(char *ptr, char **retptr);
char *match_strdup(substring_t *s);

Use one space around most binary and ternary operators:

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

But no space after unary operators:

&  *  +  -  ~  !  sizeof  typeof  alignof  __attribute__  defined

No space before the postfix/prefix increment & decrement unary operators:

++  --

No space around the . and -> structure member operators.

Do not leave trailing whitespace at the ends of lines.

Naming

Snake Case:

this_is_a_variable_name

GLOBAL variables

For global functions, if a function counts the number of active users, it should be called that:

count_active_users()

and not:

cntusr() // BAD

LOCAL variables

Variable names should be short, and to the point. If there is some random integer loop counter, it should probably be called:

i

and not:

loop_counter

Typedefs

Typedefs are used on struct or enum to omit the keyword from its declaration.

So for a struct this is not preferred:

struct some_struct {
	int64_t state;
	int64_t counter;
};

struct some_struct a;

Instead this is followed:

typedef struct {
	int64_t state;
	int64_t counter;
} some_struct;

some_struct a;

Functions

Functions should be short and do just one thing.

They should fit on one or two screenfuls of text (the ISO/ANSI screen size is 80x24), and do one thing and do that well.

The maximum length of a function is inversely proportional to the complexity and indentation level of that function. So for simple functions (e.g. with just a long switch case), you can have a longer function. But for complex functions, they should be as smaller as possible. If its not possible, break it to smaller functions or define some helper functions.

The number of local variables in a function shouldn’t exceed 5-10, or you’re doing something wrong.

In source files, separate functions with one blank line.

In function prototypes, the parameter names should be included with their data types. Although this is not required by the C language, it is preferred for readibility.

Commenting

Multi-Line Comments

The following style is followed:

/*
 * This is the preferred style for multi-line
 * comments in the kernel.
 *
 * Description:  A column of asterisks on the left side,
 * with beginning and ending almost-blank lines.
 */

In case of documenting with doxygen the following style is followed:

/**
 * Function that switches from current to the next task.
 * @param next A @ref task_struct pointer, that points to the next task.
 * @details Calls `cpu_next_to`, in order to perform the necessary context switch.
 * @see task_struct
 */

Macros, Enums and RTL

Names of macros defining constants and labels in enums are capitalized:

#define CONSTANT 0x12345

Enums are preferred when defining several related constants.

CAPITALIZED macro names are appreciated but macros resembling functions may be named in lower case.

Generally, inline functions are preferable to macros resembling functions.

Macros with multiple statements should be enclosed in a do - while block:

#define macrofun(a, b, c)                       \
        do {                                    \
                if (a == 5)                     \
                        do_this(b, c);          \
        } while (0)

Function return values and names

Functions can return values of many different kinds, and one of the most common is a value indicating whether the function succeeded or failed. Such a value can be represented as:

  • An error-code integer (-Exxx = failure, 0 = success) or
  • A succeeded boolean (0 = failure, non-zero = success).

Always follow this convention:

If the name of a function is an action or an imperative command,
the function should return an error-code integer.
If the name is a predicate,
the function should return a "succeeded" boolean.

For example:

  • add_work is a command, and the add_work() function returns 0 for success or -EBUSY for failure.
  • PCI_device_present is a predicate, and the pci_dev_present() function returns 1 if it succeeds in finding a matching device or 0 if it doesn’t.

Functions whose return value is the actual result of a computation, rather than an indication of whether the computation succeeded, are not subject to this rule. Generally they indicate failure by returning some out-of-range result. Typical examples would be functions that return pointers; they use NULL or the ERR_PTR mechanism to report failure.

Inline assembly

Don’t hesitate to do so when necessary.

However, don’t use inline assembly gratuitously when C can do the job. You can and should poke hardware from C when possible.

Large, non-trivial assembly functions should go in .S files, with corresponding C prototypes defined in C header files. The C prototypes for assembly functions should use asmlinkage.

You may need to mark your asm statement as volatile, to prevent GCC from removing it if GCC doesn’t notice any side effects.

When writing a single inline assembly statement containing multiple instructions, put each instruction on a separate line in a separate quoted string, and end each string except the last with nt to properly indent the next instruction in the assembly output:

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

Conditional Compilation

The use of preprocessor conditionals (#if, #ifdef) in .c files is used in order to have the same file be compiled for 2 different architectures.

Were possible they are used in a header file defining functions for use in those .c files, providing no-op stub versions in the #else case, and then calling those functions unconditionally from .c files.

It is preferred to compile out entire functions, rather than portions of functions or portions of expressions.

Rather than putting an ifdef in an expression, it is preferred to factor out part or all of the expression into a separate helper function and apply the conditional to that function.

Resources

Most of the guidelines are taken as is from the Linux kernel coding style.

Clone this wiki locally