-
Notifications
You must be signed in to change notification settings - Fork 3
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.
8 characters
80 characters
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();
}
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.
Snake Case:
this_is_a_variable_name
For global functions, if a function counts the number of active users, it should be called that:
count_active_users()
and not:
cntusr() // BAD
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 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 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.
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
*/
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)
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 theadd_work()
function returns0
for success or-EBUSY
for failure. -
PCI_device_present
is a predicate, and thepci_dev_present()
function returns1
if it succeeds in finding a matching device or0
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.
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 */);
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.
Most of the guidelines are taken as is from the Linux kernel coding style.
Author: thanoskoutr
Wiki Documentation: https://github.com/thanoskoutr/armOS/wiki
Doxygen Documentation: https://thanoskoutr.github.io/armOS/