The kernel code tried to follow standard rules throughout its evolution. In this chapter, we will just be introduced to them. They are all discussed in a dedicated chapter; starting from Chapter 3, Kernel Facilities and Helper Functions, we get a better overview of the kernel development process and tips, up to Chapter 13, Linux Device Model.
Kernel habits
Coding style
Before going deep into this section, you should always refer to the kernel coding style manual, at Documentation/CodingStyle in the kernel source tree. This coding style is a set of rules you should respect, at least if you need to get patches accepted by kernel developers. Some of these rules concern indentation, program flow, naming conventions, and so on.
The most popular ones are:
- Always use a tab indentation of eight characters, and the line should be 80 columns long. If the indentation prevents you from writing your function, it is because this one has too many nesting levels. One can size the tabs and verify the line size using a scripts/cleanfile script from the kernel source:
scripts/cleanfile my_module.c
- You can also indent the code correctly using the indent tool:
sudo apt-get install indent scripts/Lindent my_module.c
- Every function/variable that is not exported should be declared as static.
- No spaces should be added around (inside) parenthesized expressions. s = size of (struct file); is accepted, whereas s = size of( struct file ); is not.
- Using typdefs is forbidden.
- Always use /* this */ comment style, not // this:
- BAD: // do not use this please
- GOOD: /* Kernel developers like this */
- You should capitalise macros, but functional macros can be in lowercase.
- A comment should not replace code that is not illegible. Prefer rewriting the code rather than adding a comment.
Kernel structure allocation/initialization
The kernel always offers two possible allocation mechanisms for its data structures and facilities.
Some of these structures are:
- Workqueue
- List
- Waitqueue
- Tasklet
- Timer
- Completion
- mutex
- spinlock
Dynamical initializers are all macros, which means they are always capitalized: INIT_LIST_HEAD(), DECLARE_WAIT_QUEUE_HEAD(), DECLARE_TASKLET( ), and so on.
These are all discussed in Chapter 3, Kernel Facilities and Helper Functions. Data structures that represent framework devices are always allocated dynamically, each having its own allocation and deallocation API. These framework device types are:
- Network
- Input device
- Char device
- IIO device
- Class
- Framebuffer
- Regulator
- PWM device
- RTC
The scope of the static objects is visible in the whole driver, and by every device this driver manages. Dynamically allocated objects are visible only by the device that is actually using a given instance of the module.
Classes, objects, and OOP
The kernel implements OOP by means of a device and a class. Kernel subsystems are abstracted by means of classes. There are almost as many subsystems as there are directories under /sys/class/. The struct kobject structure is the centerpiece of this implementation. It even brings in a reference counter, so that the kernel may know how many users actually use the object. Every object has a parent, and has an entry in sysfs (if mounted).
Every device that falls into a given subsystem has a pointer to an operations (ops) structure, which exposes operations that can be executed on this device.