Easy way to learn Linux OS kernel source code
For a lot of Linux enthusiasts are interested in the kernel is not easy to swallow, the purpose of this article is to introduce a way to interpret the Linux kernel source code, rather than explain the Linux complex kernel mechanism;
A The file organization of the core source program:
1. Linux core source programs are usually installed under/usr/src/linux, and it has a very simple numbering convention: Any even-numbered core (such as 2.0.30) is a stable distribution core, and any odd-numbered core (such as 2.1.42) is the core of a development.
This article is based on the stable 2.2.5 source code, the second part of the implementation platform for RedHat Linux 6.0.
2. The files of the core source program are organized in a tree structure, and in the top level of the source program tree you will see directories such as:
The Arch:arch subdirectory includes all the core code related to the architecture. Each of its subdirectories represents a supported architecture, such as i386, which is about Intel
A subdirectory of the CPU and its compatible architecture. PC machines are generally based on this directory;
The Include:include subdirectory includes most of the header files needed to compile the core. Platform-Independent header files under the Include/linux subdirectory, with Intel
The CPU-related header file is in the include/asm-i386 subdirectory, while the INCLUDE/SCSI directory is the header file directory of the SCSI device;
Init: This directory contains the core initialization code (note: Not the system's boot code), contains two files main.c and VERSION.C, which is a very good starting point for studying how the core works.
Mm: This directory includes all CPU-independent
Architecture of memory management code, such as page storage Management memory allocation and release, and architecture-related memory management code is located in arch/*/mm/, for example arch/i386/mm/fault.c
Kernel: The main core code, the file in this directory implements the kernel functions of most Linux systems, the most important of which is sched.c; Similarly, the architecture-related code is in Arch/*/kernel;
Drivers: Place all device drivers for the system, and each driver consumes one subdirectory: for example,/block
Block device drivers, such as the IDE (IDE.C). If you want to see how all the devices that might contain the file system are initialized, you can look at the Device_setup () in DRIVERS/BLOCK/GENHD.C. It not only initializes the hard disk, but also initializes the network, because the NFS file system needs to be installed on the other network:
For example, Lib places the core library code; NET, core and network-related code; IPC, this directory contains the code for the core interprocess communication; Fs
, all file system codes and various types of file manipulation codes, each of which supports a file system, such as fat and ext2;
Scripts, this directory contains script files for configuring the core, and so on.
In general, in each directory, there is a. depend file and a Makefile
Files, these two files are compile-time use of the auxiliary files, carefully read these two files to clarify the relationship between the file and the dependency is very helpful, and in some directories there is a Readme
File, which is a description of the file under the directory, it is also conducive to our understanding of the kernel source code;
Two Interpreting combat: Add a system call to your kernel
Although, the kernel source of Linux is organized in a tree structure is very reasonable, scientific, the function of the associated files are placed in the same subdirectory, which makes the program more readable. However, Linux
Kernel source is too large and very complex, even with a very reasonable method of file organization, in different directories in the file is still a lot of association, the analysis of the core part of the code will usually see a few other related files, and may not be in the same subdirectory.
The complexity of the system and the intricacies of the document may be the main reason why many people are intimidated by it. Of course, the rewards of this daunting labor are fascinating: not only can you learn a lot of the underlying knowledge of the computer (such as the boot of the system below), you will appreciate the subtlety of the entire operating system architecture and the ingenious algorithm in solving a specific detail problem. And more importantly: in the process of source code analysis, you will be 1.1 points, subtle specialization; even if you analyze One-tenth of the code, you will be deeply aware of what kind of code is a professional programmer, what kind of code is written by an amateur.
In order to enable readers to better understand this feature, here is a concrete example of kernel analysis, hope that through this example, so that the reader of the Linux kernel organization some specific understanding, from the reader can also learn some of the kernel analysis methods.
The following is an example of an analysis:
A, operating platform:
Hardware: CPU Intel Pentium II;
Software: RedHat Linux 6.0; Kernel version 2.2.5
B, the relevant core source code analysis:
1. Booting and initializing the system: There are several ways to boot a Linux system: a common Lilo,
Loadin Boot and Linux bootstrap bootstrap (bootsect-loader), which corresponds to the source program Arch/i386/boot/bootsect. S, which is the assembler of real-mode, confined to the space here not to do analysis; whichever way you boot, you end up jumping to
Arch/i386/kernel/setup. S, Setup. S is primarily the initialization in the in-time mode, which prepares the system to enter the protection mode; After that, the system executes
Arch/i386/kernel/head. S (Arch/i386/boot/compressed/head the kernel to be stored after it has been compressed.) S);
Head. A piece of assembler defined in S Setup_idt, which is responsible for creating a 256-item IDT table (Interrupt descriptor
table), which holds all the ingress addresses for self-trapping and interrupts, including the system Invoke master control program System_call
Of course, besides, head. s also has to do some other initialization work;
2. The first kernel program that runs after system initialization asmlinkage void __init start_kernel (void) is defined in the
In/usr/src/linux/init/main.c, it calls a function in the USR/SRC/LINUX/ARCH/I386/KERNEL/TRAPS.C
void __init trap_init (void) Sets the ingress address of the respective sink and interrupt service program to IDT
Table, where the system invoke master handler System_cal is one of the interrupt service programs; void __init trap_init (void) function calls a macro
Set_system_gate (Syscall_vector,&system_call); The entry of the system call Master control program is hung on the interrupt 0x80;
Where Syscall_vector is a constant 0x80 defined in the/usr/src/linux/arch/i386/kernel/irq.h; and
System_call
That is, the entry address of the interrupt master program, and the interrupt Master program is defined in assembly language in/usr/src/linux/arch/i386/kernel/entry. s medium;
3. Interrupt Master Control program is mainly responsible for preserving the state before the processor execution system call, verifying that the current call is legitimate, and according to the system call vector, so that the processor jump to save in the sys_call_table
The entry of the corresponding system service routine in the table; Recovering the processor status back to the user program after returning from the system service routine;
and the system call vector is defined in/usr/src/linux/include/asm-386/unistd.h; sys_call_table
The table is defined in/usr/src/linux/arch/i386/kernel/entry. S Medium; At the same time
The user programming interface of the system call is also defined in/usr/src/linux/include/asm-386/unistd.h.
4. This shows that the Linux system call is also like the DOS system int 21h interrupt Service, it 0x80 interrupt as the total entrance, and then go to save in
Sys_call_table the entry address of various interrupt service routines in the table to form various interrupt services;
From the above source code analysis, to add a system call must be added to the Sys_call_table table,
and to save the entry address of your own system service routine, and then recompile the kernel, of course, the system service routines are essential.
In this version of the Linux kernel source program, the system calls related to the source program files include the following:
Arch/i386/boot/bootsect. S
Arch/i386/kernel/setup. S
Arch/i386/boot/compressed/head. S
Arch/i386/kernel/head. S
Init/main.c
Arch/i386/kernel/traps.c
Arch/i386/kernel/entry. S
Arch/i386/kernel/irq.h
Include/asm-386/unistd.h
Of course, this is only a few of the main documents involved. In fact, adding system calls to really modify files is only Include/asm-386/unistd.h and arch/i386/kernel/entry. S of two.
C, the changes to the kernel source code:
1. Add the following system service routines in KERNEL/SYS.C:
asmlinkage int sys_addtotal (int numdata)
{
int i=0,enddata=0;
while (I<=numdata)
enddata+=i++;
return enddata;
}
The function has an int type entry parameter numdata, and returns the accumulated value from 0 to Numdata;
Of course, you can also put the system service routines in a self-defined file or other files, just to make the necessary description in the corresponding file;
2. Add the entry address of the asmlinkage int sys_addtotal (int) to the sys_call_table table:
Arch/i386/kernel/entry. The last few lines of the source code in S are modified before:
... ...
. Long Symbol_name (Sys_sendfile)
. Long Symbol_name (sys_ni_syscall)/* STREAMS1 */
. Long Symbol_name (sys_ni_syscall)/* STREAMS2 */
. Long Symbol_name (sys_vfork)/* 190 */
. rept nr_syscalls-190
. Long Symbol_name (Sys_ni_syscall)
. Endr
Modified to: ...
. Long Symbol_name (Sys_sendfile)
. Long Symbol_name (sys_ni_syscall)/* STREAMS1 */
. Long Symbol_name (sys_ni_syscall)/* STREAMS2 */
. Long Symbol_name (sys_vfork)/* 190 */
/* Add by I */
. Long Symbol_name (sys_addtotal)
. rept nr_syscalls-191
. Long Symbol_name (Sys_ni_syscall)
. Endr
3. Add the vector corresponding to the Sys_call_table table entry in the Include/asm-386/unistd.h
For user processes and other system processes to query or invoke:
Add the following section/usr/src/linux/include/asm-386/unistd.h file as follows:
... ...
#define __NR_SENDFILE 187
#define __NR_GETPMSG 188
#define __NR_PUTPMSG 189
#define __nr_vfork 190
/* Add by I */
#define __nr_addtotal 191
4. The test procedure (TEST.C) is as follows:
#include
#include
_SYSCALL1 (Int,addtotal,int, num)
Main ()
{
int i,j;
Do
printf ("Please input a number\n");
while (scanf ("%d", &i) ==eof);
if ((J=addtotal (i)) ==-1)
printf ("Error occurred in syscall-addtotal (); \ n");
printf ("Total from 0-%d is%d \ n", i,j);
}
Compile the modified new kernel and boot it as a new operating system, run several programs to find everything OK, and compile the test program under the new system (* Note: The original kernel does not provide this system call, so only after the new kernel compiled, the test program can be compiled), The operating conditions are as follows:
$GCC-O test test.c
$./test
Please input a number
36
Total from 0 to 666
Visible, the modification succeeds;
Furthermore, further analysis of the relevant source code, in this version of the kernel, from/usr/src/linux/arch/i386/kernel/entry. S
The setup of the Sys_call_table table in the file shows that there are several service routines called by the system that are defined in the/USR/SRC/LINUX/KERNEL/SYS.C
The same function in:
asmlinkage int Sys_ni_syscall (void)
{
Return-enosys;
}
For example, items 188th and 189th are the same:
... ...
. Long Symbol_name (Sys_sendfile)
. Long Symbol_name (sys_ni_syscall)/* STREAMS1 */
. Long Symbol_name (sys_ni_syscall)/* STREAMS2 */
. Long Symbol_name (sys_vfork)/* 190 */
... ...
These two items are stated in document/usr/src/linux/include/asm-386/unistd.h as follows:
... ...
#define __NR_SENDFILE 187
#define __NR_GETPMSG 188/* Some people actually want streams * *
#define __NR_PUTPMSG 189/* Some people actually want streams * *
#define __nr_vfork 190
Thus, in this version of the kernel source code, because the asmlinkage int sys_ni_syscall (void) function does nothing, it includes getpmsg,
Several system calls, including PUTPMSG, are non-operational, i.e. air-conditioning to be expanded;
But they still occupy the sys_call_table table, it is estimated that the designers in order to facilitate the expansion of the system call arrangements;
So you can increase the role of system calls by simply adding the appropriate service routines (such as adding service routines getmsg or putpmsg).
Easy way to learn the Linux kernel source code