数码资讯
一个很好的linux操作系统内核源代码的实现方法
对于许多Linux爱好者来说,他们对核心不感兴趣。本文旨在介绍一种了解linux内核源代码的入门方法,而不是解释Linux复杂的内核机制。 1。核心源程序的文档组织: 1。Linux核心源程序通常安装在 / usr / src / Linux,它有一个非常简单的编号约定:任何偶数的核心(例如2.0.
选购提示
关注价格、性能、续航、售后和真实使用场景,理性比较后再下单。
对于许多Linux爱好者来说,他们对核心不感兴趣。本文旨在介绍一种了解linux内核源代码的入门方法,而不是解释Linux复杂的内核机制。
1。核心源程序的文档组织:
1。Linux核心源程序通常安装在 / usr / src / Linux,它有一个非常简单的编号约定:任何偶数的核心(例如2.0.30)是一个稳定的核心问题,任何奇怪的核心(如2.1.42)是一个发展的核心。
本文是基于稳定2.2.5源代码,并实现平台的第二部分是RedHat Linux 6。
2文件。核心源程序是按树形结构组织的。在源树的顶部,您将看到其中一些目录。
拱:拱子目录包含了所有的核心代码和系统结构,它的每个子目录是一个支持的架构,例如i386是英特尔
CPU和兼容的architecture.pc机一般都是基于这个目录的子目录;
包括:子目录包括内核编译所需的大多数头文件。独立于平台的头文件在包含的Linux子目录和英特尔中。
CPU相关的头文件包含 / asm-i386子目录下,并且包括 / SCSI目录在SCSI设备的头文件目录;
init目录包含:初始化代码的核心(注意:不是引导代码系统),包含两个文件:C和C,这是研究内核工作的一个很好的起点。
mm:这个列表包括所有独立的CPU。
体系结构的内存管理代码,如页式存储管理,内存的分配和释放,等,与建筑相关的内存管理代码位于拱 / / / *毫米,如拱 / / /毫米i386故障C.
内核代码的核心,目录下的文件执行大多数Linux系统的内核函数,其中最重要的文件是计划C;同样,建筑和相关的代码在拱 / * /内核;
驱动程序系统:放置所有设备驱动程序;每个驱动程序和子目录的占用: /块。
块设备驱动程序,如IDE(集成开发环境,C)。如果你想看到所有的设备可能包含一个文件系统初始化,你可以看看device_setup()在司机/块/ genhd.c.it不仅初始化硬盘,而且初始化网络,因为NFS文件系统的安装需要其他的网络:
例如,自由将核心代码库;网络、核心网络相关的代码;工控机,其中包含了进程间通信的核心代码;Fs
所有的文件系统代码和各种类型的文件操作代码,每个子目录支持一个文件系统,如脂肪和ext2;
脚本,其中包含用于配置核心的脚本文件,等等。
一般来说,在每一个目录,有一个依赖文件和Makefile
文件,这两个文件都是在编译时使用的辅助文件。仔细阅读这两个文档,有助于找出每个文件之间的关系和支持关系。此外,在一些目录中也有自述文件。
该文件是对目录下文件的描述,也有利于我们对内核源代码的理解。
两。阅读真正的战斗:向内核添加一个系统调用
虽然Linux的内核代码是用树形结构合理而科学地组织起来的,但是与函数相关的文件被放在同一个子目录下,这使得程序可读性更高。然而,Linux
内核代码太大和复杂,即使文件组织方法非常合理,在不同的目录中仍然有很多关系,分析代码的核心部分通常是看其他几个相关的文档,而这些文档可能不在同一个子目录中。
系统的复杂性和文档之间的关联的复杂性可能很多人都害怕它的主要原因。当然,这个艰巨的劳动报酬是非常迷人的:你可以从计算机知识底学到很多(如下面提到的系统,实现整个指南)在解决一个特定的操作系统的架构和精致的细节,巧妙的算法;但更重要的是:在源代码分析的过程中,你会一点点,细微的专业化;甚至,只要1 / 10的代码的分析,你会深深地体会到,什么样的代码是一个专业的程序员写什么样的代码是一个业余写作。
为了让读者更好地理解这一点,这里有一个具体的内核分析示例,希望通过这个例子,对Linux内核的读者,一些特定于组织的知识,从中读者也可以了解到内核的一些分析方法。
下面是一个分析的例子:
操作平台:
硬件:CPU英特尔奔腾II;
软件:RedHat Linux 6内核版本2.2.5;
B,相关内核源代码分析:
1。系统的启动和初始化:Linux系统下的几种方法:常见的LILO,
加载引导Linux启动引导(bootsect装载机)。后者对应于源程序 / /拱i386启动 / BOOTSECT. S,这是一个实模式汇编。它仅限于空间长度。
拱 / / /内核i386安装,设置,主要是以渐进的方式初始化,准备系统进入保护模式后,系统执行;
拱 / / /头i386内核的(内核存储压缩后应先执行拱 / / / i386启动压缩 /头。S);
setup_idt,一段头定义的汇编程序,负责256个项目的IDT表的建立(中断描述符)
表),持有所有自陷中断入口地址;它包括系统调用的总控制程序system_call
当然,除此之外,还得做一些其他的初始化工作。
2。第一个核心程序,asmlinkage无效__init start_kernel(void),它运行的系统初始化后,定义
在/ usr / / / SRC Linux init /主。C,它调用一个函数在usr / / /弓型Linux i386 / / / C内核陷阱。
__init trap_init虚空(void)组各自捕获中断服务程序的入口地址表
在表中,系统调用的总的控制程序,system_cal,是一个中断服务程序;无效__init trap_init(void)函数调用宏
set_system_gate(syscall_vector,system_call);的系统调用的总控制程序的入口是挂在0x80中断;
syscall_vector是常数定义在0x80 / usr / / /弓型Linux i386 / / / H内核的IRQ;
system_call
这是中断总控制程序的入口地址;中断总控程序在 / usr / / /弓型Linux i386 / / /进入内核的汇编语言的定义;
3、中断控制程序主要负责保存处理器状态执行系统调用之前,检查是否当前的调用是合法的,并调用处理器,它保存在sys_call_table根据系统调用向量。
表中对应的系统服务例程的输入;在系统服务例程返回之后,恢复处理器状态返回到用户程序;
系统调用向量在/ usr / / /包括Src Linux / asm-386 / unistd H;sys_call_table定义。
表是在/ usr / / /弓型Linux i386 / / /进入内核定义的;在同一时间。
系统调用的用户编程接口也在/ usr / / /包括Src Linux / asm-386 / unistd定义。H.
4,我们可以看到,Linux的系统调用也像int 21h中断DOS系统服务。它中断0x80为总入口然后去拯救它。
不同的入口地址中断服务例程在sys_call_table表格各种中断服务。
从上面的源代码的分析,有必要增加一个项目的sys_call_table表添加一个系统调用。
这也节省了入口地址你的系统服务程序,重新编译内核,当然,,,系统服务程序是必不可少的。
因此,在这个版本的Linux内核源代码中,与系统调用相关的源程序文件包括以下内容:
拱 / / / i386启动BOOTSECT. S
拱 / / / i386内核的设置。
拱 / / / i386启动压缩/头的。
拱 / / / i386内核的头。
init
拱 / / / i386内核C陷阱。
拱 / / / i386内核的入口。
拱 / / / i386内核的IRQ。H
包括asm-386 / / unistd。H
当然,这只是一个很少涉及的主要文件。事实上,添加系统调用真的修改只有两个文件,包括asm-386 / / unistd。H和拱 / / /进入i386内核。S.
U3000 U3000
c,对内核源代码的修改:
1。将内核中的系统服务例程添加如下:
asmlinkage int sys_addtotal(int numData)
{
int i = 0,期末余额= 0;
而(我= numData )
期末余额+ =我+ +;
返回期末余额;
}
该函数有一个int类型输入参数numData从0到numData返回累积值。
当然,您也可以将系统服务例程放在它们自己定义的文件或其他文件中,只是在相应的文件中做必要的说明;
2。添加asmlinkage int sys_addtotal的入口地址(int)的sys_call_table表:
最后一行源代码在拱门/ / /进入i386内核的修改之前:
…
长symbol_name(sys_sendfile)。
。长symbol_name(sys_ni_syscall)streams1 / * * /
。长symbol_name(sys_ni_syscall)streams2 / * * /
。长symbol_name(sys_vfork) / * * / 190
报告nr_syscalls-190。
长symbol_name(sys_ni_syscall)。
模。
修订是:…
长symbol_name(sys_sendfile)。
。长symbol_name(sys_ni_syscall)streams1 / * * /
。长symbol_name(sys_ni_syscall)streams2 / * * /
。长symbol_name(sys_vfork) / * * / 190
加上我
长symbol_name(sys_addtotal)。
报告nr_syscalls-191。
长symbol_name(sys_ni_syscall)。
模。
三.相应的补充sys_call_table表项的向量,在包括 / / unistd asm-386。H
对用户进程和其他系统进程进行必要的声明,以进行查询或调用。
的 / usr / / /包括Linux src / / unistd asm-386附加部分。h文件如下:
…
#定义__nr_sendfile 187
#定义__nr_getpmsg 188
#定义__nr_putpmsg 189
#定义__nr_vfork 190
加上我
#定义__nr_addtotal 191
4。测试程序(测试C)如下所示:
#包括
#包括
_syscall1(int,int,addtotal,努姆)
主要()
{
int,j;
做
printf(请输入一个numbern;);
而(scanf(% d我)= = EOF);
如果(((J = addtotal(I))= = 1)
printf(addtotal;在系统调用发生错误();n);
printf(总从0到%d %d nI、J);
}
对新修改的内核进行编译,并将其作为一个新的操作系统引导,运行时可以发现所有程序都正常;在新系统下编译的测试程序(*注:因为原始内核不提供系统调用,所以只有在编译内核中,这个测试程序才能编译、运行)可能如下所示:
$ o o测试测试
测试
请输入一个数字。
三十六
总数从0到36是666。
可以看出,改造是成功的。
此外,相关的源代码的进一步分析表明,在这个版本的内核,从 / usr / / /弓型Linux i386 / / /入境的内核。
在文件的sys_call_table表的设置表明几种服务例程,有许多系统调用定义在 / usr / / /内核Linux SRC /系统C
函数中的相同函数:
asmlinkage int sys_ni_syscall(void)
{
返回enosys;
}
例如,第一百八十八和第一百八十九是相同的。
…
长symbol_name(sys_sendfile)。
。长symbol_name(sys_ni_syscall)streams1 / * * /
。长symbol_name(sys_ni_syscall)streams2 / * * /
。长symbol_name(sys_vfork) / * * / 190
…
两项,在文件 / usr / / /包括Linux src / / unistd asm-386。h,确认如下:
…
#定义__nr_sendfile 187
#定义__nr_getpmsg 188有些人想流* / / *
#定义__nr_putpmsg 189有些人想流* / / *
#定义__nr_vfork 190
可以看出,这个版本的内核源代码,因为asmlinkage int sys_ni_syscall(void)函数不做任何操作,它包括getpmsg。
几个系统调用,包括putpmsg,不手术,即空调需要扩大。
但他们仍然占据sys_call_table表项,这估计是由设计师,方便系统的扩展要求。
所以只有提高服务例程(如增加服务程序getmsg或putpmsg)可以提高系统的角色要求。
1。核心源程序的文档组织:
1。Linux核心源程序通常安装在 / usr / src / Linux,它有一个非常简单的编号约定:任何偶数的核心(例如2.0.30)是一个稳定的核心问题,任何奇怪的核心(如2.1.42)是一个发展的核心。
本文是基于稳定2.2.5源代码,并实现平台的第二部分是RedHat Linux 6。
2文件。核心源程序是按树形结构组织的。在源树的顶部,您将看到其中一些目录。
拱:拱子目录包含了所有的核心代码和系统结构,它的每个子目录是一个支持的架构,例如i386是英特尔
CPU和兼容的architecture.pc机一般都是基于这个目录的子目录;
包括:子目录包括内核编译所需的大多数头文件。独立于平台的头文件在包含的Linux子目录和英特尔中。
CPU相关的头文件包含 / asm-i386子目录下,并且包括 / SCSI目录在SCSI设备的头文件目录;
init目录包含:初始化代码的核心(注意:不是引导代码系统),包含两个文件:C和C,这是研究内核工作的一个很好的起点。
mm:这个列表包括所有独立的CPU。
体系结构的内存管理代码,如页式存储管理,内存的分配和释放,等,与建筑相关的内存管理代码位于拱 / / / *毫米,如拱 / / /毫米i386故障C.
内核代码的核心,目录下的文件执行大多数Linux系统的内核函数,其中最重要的文件是计划C;同样,建筑和相关的代码在拱 / * /内核;
驱动程序系统:放置所有设备驱动程序;每个驱动程序和子目录的占用: /块。
块设备驱动程序,如IDE(集成开发环境,C)。如果你想看到所有的设备可能包含一个文件系统初始化,你可以看看device_setup()在司机/块/ genhd.c.it不仅初始化硬盘,而且初始化网络,因为NFS文件系统的安装需要其他的网络:
例如,自由将核心代码库;网络、核心网络相关的代码;工控机,其中包含了进程间通信的核心代码;Fs
所有的文件系统代码和各种类型的文件操作代码,每个子目录支持一个文件系统,如脂肪和ext2;
脚本,其中包含用于配置核心的脚本文件,等等。
一般来说,在每一个目录,有一个依赖文件和Makefile
文件,这两个文件都是在编译时使用的辅助文件。仔细阅读这两个文档,有助于找出每个文件之间的关系和支持关系。此外,在一些目录中也有自述文件。
该文件是对目录下文件的描述,也有利于我们对内核源代码的理解。
两。阅读真正的战斗:向内核添加一个系统调用
虽然Linux的内核代码是用树形结构合理而科学地组织起来的,但是与函数相关的文件被放在同一个子目录下,这使得程序可读性更高。然而,Linux
内核代码太大和复杂,即使文件组织方法非常合理,在不同的目录中仍然有很多关系,分析代码的核心部分通常是看其他几个相关的文档,而这些文档可能不在同一个子目录中。
系统的复杂性和文档之间的关联的复杂性可能很多人都害怕它的主要原因。当然,这个艰巨的劳动报酬是非常迷人的:你可以从计算机知识底学到很多(如下面提到的系统,实现整个指南)在解决一个特定的操作系统的架构和精致的细节,巧妙的算法;但更重要的是:在源代码分析的过程中,你会一点点,细微的专业化;甚至,只要1 / 10的代码的分析,你会深深地体会到,什么样的代码是一个专业的程序员写什么样的代码是一个业余写作。
为了让读者更好地理解这一点,这里有一个具体的内核分析示例,希望通过这个例子,对Linux内核的读者,一些特定于组织的知识,从中读者也可以了解到内核的一些分析方法。
下面是一个分析的例子:
操作平台:
硬件:CPU英特尔奔腾II;
软件:RedHat Linux 6内核版本2.2.5;
B,相关内核源代码分析:
1。系统的启动和初始化:Linux系统下的几种方法:常见的LILO,
加载引导Linux启动引导(bootsect装载机)。后者对应于源程序 / /拱i386启动 / BOOTSECT. S,这是一个实模式汇编。它仅限于空间长度。
拱 / / /内核i386安装,设置,主要是以渐进的方式初始化,准备系统进入保护模式后,系统执行;
拱 / / /头i386内核的(内核存储压缩后应先执行拱 / / / i386启动压缩 /头。S);
setup_idt,一段头定义的汇编程序,负责256个项目的IDT表的建立(中断描述符)
表),持有所有自陷中断入口地址;它包括系统调用的总控制程序system_call
当然,除此之外,还得做一些其他的初始化工作。
2。第一个核心程序,asmlinkage无效__init start_kernel(void),它运行的系统初始化后,定义
在/ usr / / / SRC Linux init /主。C,它调用一个函数在usr / / /弓型Linux i386 / / / C内核陷阱。
__init trap_init虚空(void)组各自捕获中断服务程序的入口地址表
在表中,系统调用的总的控制程序,system_cal,是一个中断服务程序;无效__init trap_init(void)函数调用宏
set_system_gate(syscall_vector,system_call);的系统调用的总控制程序的入口是挂在0x80中断;
syscall_vector是常数定义在0x80 / usr / / /弓型Linux i386 / / / H内核的IRQ;
system_call
这是中断总控制程序的入口地址;中断总控程序在 / usr / / /弓型Linux i386 / / /进入内核的汇编语言的定义;
3、中断控制程序主要负责保存处理器状态执行系统调用之前,检查是否当前的调用是合法的,并调用处理器,它保存在sys_call_table根据系统调用向量。
表中对应的系统服务例程的输入;在系统服务例程返回之后,恢复处理器状态返回到用户程序;
系统调用向量在/ usr / / /包括Src Linux / asm-386 / unistd H;sys_call_table定义。
表是在/ usr / / /弓型Linux i386 / / /进入内核定义的;在同一时间。
系统调用的用户编程接口也在/ usr / / /包括Src Linux / asm-386 / unistd定义。H.
4,我们可以看到,Linux的系统调用也像int 21h中断DOS系统服务。它中断0x80为总入口然后去拯救它。
不同的入口地址中断服务例程在sys_call_table表格各种中断服务。
从上面的源代码的分析,有必要增加一个项目的sys_call_table表添加一个系统调用。
这也节省了入口地址你的系统服务程序,重新编译内核,当然,,,系统服务程序是必不可少的。
因此,在这个版本的Linux内核源代码中,与系统调用相关的源程序文件包括以下内容:
拱 / / / i386启动BOOTSECT. S
拱 / / / i386内核的设置。
拱 / / / i386启动压缩/头的。
拱 / / / i386内核的头。
init
拱 / / / i386内核C陷阱。
拱 / / / i386内核的入口。
拱 / / / i386内核的IRQ。H
包括asm-386 / / unistd。H
当然,这只是一个很少涉及的主要文件。事实上,添加系统调用真的修改只有两个文件,包括asm-386 / / unistd。H和拱 / / /进入i386内核。S.
U3000 U3000
c,对内核源代码的修改:
1。将内核中的系统服务例程添加如下:
asmlinkage int sys_addtotal(int numData)
{
int i = 0,期末余额= 0;
而(我= numData )
期末余额+ =我+ +;
返回期末余额;
}
该函数有一个int类型输入参数numData从0到numData返回累积值。
当然,您也可以将系统服务例程放在它们自己定义的文件或其他文件中,只是在相应的文件中做必要的说明;
2。添加asmlinkage int sys_addtotal的入口地址(int)的sys_call_table表:
最后一行源代码在拱门/ / /进入i386内核的修改之前:
…
长symbol_name(sys_sendfile)。
。长symbol_name(sys_ni_syscall)streams1 / * * /
。长symbol_name(sys_ni_syscall)streams2 / * * /
。长symbol_name(sys_vfork) / * * / 190
报告nr_syscalls-190。
长symbol_name(sys_ni_syscall)。
模。
修订是:…
长symbol_name(sys_sendfile)。
。长symbol_name(sys_ni_syscall)streams1 / * * /
。长symbol_name(sys_ni_syscall)streams2 / * * /
。长symbol_name(sys_vfork) / * * / 190
加上我
长symbol_name(sys_addtotal)。
报告nr_syscalls-191。
长symbol_name(sys_ni_syscall)。
模。
三.相应的补充sys_call_table表项的向量,在包括 / / unistd asm-386。H
对用户进程和其他系统进程进行必要的声明,以进行查询或调用。
的 / usr / / /包括Linux src / / unistd asm-386附加部分。h文件如下:
…
#定义__nr_sendfile 187
#定义__nr_getpmsg 188
#定义__nr_putpmsg 189
#定义__nr_vfork 190
加上我
#定义__nr_addtotal 191
4。测试程序(测试C)如下所示:
#包括
#包括
_syscall1(int,int,addtotal,努姆)
主要()
{
int,j;
做
printf(请输入一个numbern;);
而(scanf(% d我)= = EOF);
如果(((J = addtotal(I))= = 1)
printf(addtotal;在系统调用发生错误();n);
printf(总从0到%d %d nI、J);
}
对新修改的内核进行编译,并将其作为一个新的操作系统引导,运行时可以发现所有程序都正常;在新系统下编译的测试程序(*注:因为原始内核不提供系统调用,所以只有在编译内核中,这个测试程序才能编译、运行)可能如下所示:
$ o o测试测试
测试
请输入一个数字。
三十六
总数从0到36是666。
可以看出,改造是成功的。
此外,相关的源代码的进一步分析表明,在这个版本的内核,从 / usr / / /弓型Linux i386 / / /入境的内核。
在文件的sys_call_table表的设置表明几种服务例程,有许多系统调用定义在 / usr / / /内核Linux SRC /系统C
函数中的相同函数:
asmlinkage int sys_ni_syscall(void)
{
返回enosys;
}
例如,第一百八十八和第一百八十九是相同的。
…
长symbol_name(sys_sendfile)。
。长symbol_name(sys_ni_syscall)streams1 / * * /
。长symbol_name(sys_ni_syscall)streams2 / * * /
。长symbol_name(sys_vfork) / * * / 190
…
两项,在文件 / usr / / /包括Linux src / / unistd asm-386。h,确认如下:
…
#定义__nr_sendfile 187
#定义__nr_getpmsg 188有些人想流* / / *
#定义__nr_putpmsg 189有些人想流* / / *
#定义__nr_vfork 190
可以看出,这个版本的内核源代码,因为asmlinkage int sys_ni_syscall(void)函数不做任何操作,它包括getpmsg。
几个系统调用,包括putpmsg,不手术,即空调需要扩大。
但他们仍然占据sys_call_table表项,这估计是由设计师,方便系统的扩展要求。
所以只有提高服务例程(如增加服务程序getmsg或putpmsg)可以提高系统的角色要求。
声明:本文内容用于数码产品信息整理与选购参考,具体价格、库存、售后政策以官方渠道和电商页面实时信息为准。