For a lot of Linux enthusiasts, it is very interested in the kernel but does not follow the mouth, this article aims to introduce a way to interpret the Linux kernel source code,
Instead of explaining Linux complex kernel mechanism;
One. Document organization of the core source program:
1. Linux core source program is usually installed under / usr / src / linux, and it has a very simple number agreement: any even number of
Core (e.g., 2.0.30) is a steady core, while any odd core (such as 2.1.42) is a core of development.
Based on stable 2.2.5 source code, the second part of the implementation platform is Redhat Linux 6.0.
2. The document of the core source program is organized by the tree structure. You will see such a directory in the top of the source tree.
● ARCH: The Arch subdirectory includes all core code related to the architecture. Each subdirectory represents a supported system
Structure, such as I386 is a subdirectory for Intel CPUs and compatible architectures. The PC is generally based on this directory;
● Include: The incrude subdirectory includes most of the header files that need to be compiled. Head file with platform-independent header in Include / Linux
In the subdirectory, the header related to the Intel CPU is in the include / ASM-I386 subdirectory, and the include / SCSI directory is related
Skull file directory of 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,
This is a very good starting point to study how the core works.
● mm: This directory includes all memory management code independent of the CPU architecture, such as page-style storage management memory allocation and release, etc .;
The memory management code related to the architecture is located in Arch / * / mm /, such as Arch / I386 / MM / Fault.c.
● kernel: The main core code, this directory implements most Linux system kernel functions, the most important files
Sched.c; Similarly, the code related to the architecture is in Arch / * / kernel;
● Drivers: Place all the device drivers for the system; each driver also occupies a subdirectory: If / block is block device
Drivers, such as IDE (IDE.c). If you want to see how the device that may contain the file system is initialized, you can see
DRIVERS / block / genhd.c device_setup (). It not only initializes the hard disk, but also initializes the network because NFS file is installed.
Network is required
Other: If the lib places the core library code; NET, core and network-related code; IPC, this directory contains the code between the core process;
FS, all file system code and various types of file operation code, each subdirectory supports a file system, such as FAT and EXT2;
Scripts, this directory contains scripting files for configuring cores.
Generally, in each directory, there is a .depend file and a Makefile file, both files are compile
Used auxiliary files, read these two documents, which is very helpful to clarify the links and relying on each file; and in some directory
There is also a readme file, which is some instructions for files in this directory, which is also conducive to our understanding of kernel source.
two. Interpretation of actual combat: adding a system call for your kernel
Although Linux's kernel source code is well organized, scientifically, the files associated with the function are placed in the same subdirectory, which makes the program more readable. However, Linux's kernel source code is really too big and very complicated, even with a very reasonable file organization method,
There is still a lot of associations between files in different directories, and part of the code of the core is usually to view other related files, and may
These files are not in the same subdirectory.
The huge complexity of the system is complex, which may be the main reason for many people. Of course, this kind of worry
The return brought is also very fascinated: You can not only learn the underlying knowledge of many computers (as guidance of the system will say below),
Experience the exquisiteness of the entire operating system architecture and in solving a specific details, the algorithm is ingenious; and more importantly: in the source code analysis
In the process, you will be specialized at a point, the subtleties will be specialized; even after analyzing one quarter of the code, you will deeply understand what
The code is written by a professional programmer, what kind of code is written by a hobby.
In order to make the reader can better experience this feature, the following will give a specific kernel analysis instance, I hope to pass this example to make the reader to Linux.
The organization of the kernel has some specific understandings, and from the readers can learn some analytical methods for kernels.
The following is the analysis example:
[One] Operation Platform:
Hardware: CPU Intel Pentium II;
Software: Redhat Linux 6.0; Nuclear Version 2.2.5
[2] Related kernel source code analysis:
1. Boot and initialization of the system: There are several ways to boot in the Linux system: common Lilo, Loadin boot and Linux bootstrap boot
(Bootsect-loader), and the latter's corresponding source program is Arch / I386 / Boot / Bootsect.s, which is a real-mode assembler, limited to
The space is here not to analyze; no matter which guidance method, finally want to jump to the Arch / I386 / kernel / setup.s, setup.s mainly
Initialization in time mode, prepare for the system to enter the protection mode; thereafter, the system performs Arch / I386 / Kernel / Head.s (compressed compression)
The kernel stored later must first execute Arch / I386 / boot / compressed / head.s); one assembler setup_idt defined in Head.s,
It is responsible for establishing a 256 Idt table (Interrupt Descriptor Table), which saves all the entrance addresses of divide and interrupts;
Including the system calling the entrance address of the total controller system_call; Of course, in addition to this, Head.s will make some other initialization work;
2. The first kernel program for the system is initialized, the first kernel program asmlinkage void __init start_kernel (void) is defined
/usr/src/linux/init/main.c, which calls a function in USR / SRC / Linux / Arch / i386 / kernel / traps.c
Void __init trap_init (void) Sets the entry address of the respective traps and the interrupt service program to the IDT table, where the system calls the presser program
System_cal is one of the interrupt service programs; void __init trap_init (void) function passes a macro
SET_SYSTEM_GATE (syscall_vector, & system_call); hooks the entry of the system calling the presser program on the interrupt 0x80; syscall_vector is defined in /usr/src/linux/arch/i386/kernel/irq.h in a constant 0x80; and system_call
That is, it is the entrance address of the interrupt presser program; the interrupt total control program is defined in the assembly language in /usr/src/linux/Arch/i386/kernel/entry.s;
3. The interrupt prescription is mainly responsible for saving the status before the processor executes the system call, and the current call is legal, and the vector is called according to the system.
Jump to the entry of the corresponding system service routine saved in the sys_call_table table; return the processor status to the user program after returning from the system service routine;
The system call vector is defined in /usr/src/linux/include/ASM-386/unistd.h; SYS_CALL_TABLE table is defined
/usr/src/linux/Arch/i386/kernel/entry.s; at the same time /usr/src/linux/include/ASM-386/unistd.h
The user programming interface of the system call is also defined;
4. It can be seen that Linux system calls are also like the INT 21H interrupt service of the DOS system, which interrupt 0x80 as the total entry, then
Go to the entrance address of the various interrupt service routines saved in the sys_call_table table, form a variety of interrupt services;
It can be seen from the above source code analysis. To add a system call, you must add an item in the sys_call_table table, and save yourself.
The system service routine's entry address, then recompile the kernel, of course, the system service routine is essential.
This can be seen in this version of the Linux kernel source <2.2.5>, the source program file related to the system call includes the following:
1. Arch / i386 / boot / bootsect.s
2. Arch / i386 / kernel / setup.s
3. Arch / i386 / boot / compressed / head.s
4. Arch / i386 / kernel / head.s
5. Init / main.c
6. Arch / i386 / kernel / traps.c
7. Arch / i386 / kernel / entry.s
8. Arch / i386 / kernel / Irq.h
9. INCLUDE / ASM-386 / Unistd.h
Of course, this is just a few main files involved. In fact, increasing system calls really want to modify files only include / asm-386 / unistd.h
And Arch / I386 / kernel / entry.s;
[3] Modification to the kernel source code:
1. Increase the system service routine in kernel / sys.c as follows:
ASMLINKAGE INT SYS_ADDTOTAL (Int Numdata)
{
INT i = 0, enddata = 0;
While (i <= numdata)
Enddata = i ;
Return Enddata;
}
This function has an INT type entry parameter NumData, and returns the accumulated value from 0 to NumData; of course, the system service routine can be
In a defined file or other file, it just wants to explain it in the corresponding file;
2. Add the entry address of the ASMLINKAGE INT SYS_ADDTOTAL (INT) to the SYS_CALL_TABLE table:
The last few line source code in Arch / i386 / kernel / entry.s is modified:
...
.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
After modification: ... ...
.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. Put the vector corresponding to the added sys_call_table entry, in include / ASM-386 / UnisTd.h, for
User processes and other system processes queries or calls:
Added part /usr/src/linux/include/sm-386/unistd.h file is 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 program (Test.c) is as follows:
#include
#include
_SysCall1 (int, addtotal, int, num)
Main ()
{
INT I, J;
DO
Printf ("Please Input a Numbern");
While (Scanf ("% D", & I) == EOF);
IF ((j = addtotal (i)) == - 1)
Printf ("Error Occurred In syscall-addtotal (); n");
Printf ("Total from 0 to% D IS% D N", I, J);
}
Compiling the new kernel after the modified new kernel and guides it as a new operating system. After running a few programs, you can find everything is normal; under the new system
Compiling the test program (* Note: Due to the original kernel, this system is not provided, only the test program can only
It may be compiled), the operation is as follows:
$ GCC -O Test Test.c
$. / TEST
Please Input a Number
36
Total from 0 to 36 IS 666
It can be seen that the revision is successful;
Moreover, further analysis of the relevant source code is known that in this version of the core, from /usr/src/linux/Arch/i386/kernel/entry.s
As can be seen in the file, the settings of the SYS_CALL_TABLE table can be seen that there are several system calls to the service routines are defined.
/usr/src/linux/kernel/sys.c in the same function:
ASMLINKAGE INT SYS_NI_SYSCALL (VOID)
{
Return -enosys;
}
This is, for example, Articles 188 and 189.
...
.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 * /
...
And these two in the file /usr/src/linux/include/sm-386/unistd.h are declared as follows:
...
#define __nr_sendfile 187
#define __nr_getpmsg 188 / * Some People Actual Want Street * /
#define __nr_putpmsg 189 / * Some People Actually Want Street * /
#define __nr_vfork 190
It can be seen that in this version of the kernel source code, since the ASMLINKAGE INT SYS_NI_SYSCALL (Void) function does not perform any operation,
Therefore, several system calls, including getpmsg, putpmsg are not in any operation, that is, the air conditioner to be expanded; but they
But still occupy the sys_call_table entry, which is estimated that the designers are arranged in order to facilitate the expansion of the system; so it only needs to increase the corresponding
Service routines (such as increasing service routines getMSG or PUTPMSG) can achieve increased system calls.
Conclusion: Of course, for a huge complex Linux kernel, an article is far less enough, and the code related to the system call is only the kernel.
Partial part; but important is how the method, master the analysis method; so the above analysis is just a boot role, and the true analysis is still waiting
Readers' own efforts.