现如今,电脑的使用越来越普遍,几乎每家每户都有电脑,而电脑的操作离不开操作系统,在这里,秋天网 Qiutian.ZqNF.Com小编就向大家介绍linux内核。

很多linux 爱好者对内核很感兴趣却无从下手,本文旨在介绍一种解读linux内核源码的入门方法,而不是讲解linux复杂的内核机制。

1.核心源程序的文件组织

(1)linux核心源程序通常都安装在/usr/src/linux下,而且它有一个非常简单的编号约定:任何偶数的核心(中间数字)如:2.0.30都是一个稳定的发行的核心,而任何奇数的核心如:2.1.42都是一个开发中的核心。

本文基于稳定的2.2.5源代码,第二部分的实现平台为redhat linux 6.0。

(2)核心源程序的文件按树形结构进行组织,在源程序树的最上层你会看到这样一些目录:

arch:arch子目录包括了所有和体系结构相关的核心代码。它的每一个子目录都代表一种支持的体系结构,例如i386就是关于intel cpu及与之相兼容体系结构的子目录。pc机一般都基于此目录;

include:include子目录包括编译核心所需要的大部分头文件。与平台无关的头文件在include/linux子目录下,与intel cpu相关的头文件在include/asm-i386子目录下,而include/scsi目录则是有关scsi设备的头文件目录;

init:这个目录包含核心的初始化代码(注:不是系统的引导代码),包含两个文件main.c和version.c,这是研究核心如何工作的一个非常好的起点;

mm:这个目录包括所有独立于cpu 体系结构的内存管理代码,如页式存储管理内存的分配和释放等,而和体系结构相关的内存管理代码则位于arch/*/mm/,例如arch/i386/mm/fault.c;

kernel:主要的核心代码,此目录下的文件实现了大多数linux系统的内核函数,其中最重要的文件当属sched.c,同样,和体系结构相关的代码在arch/*/kernel中;

drivers:放置系统所有的设备驱动程序;每种驱动程序又各占用一个子目录,如/block下为块设备驱动程序,比如ide(ide.c)。如果你希望查看所有可能包含文件系统的设备是如何初始化的,你可以看drivers/block/genhd.c中的device_setup()函数。它不仅初始化硬盘,也初始化网络,因为安装nfs文件系统的时候需要使用网络。

其他目录如lib:放置核心的库代码;net:核心与网络相关的代码;ipc:包含核心的进程间通信的代码;fs:所有的文件系统代码和各种类型的文件操作代码,它的每一个子目录支持一个文件系统,例如fat和ext2、s,此目录包含用于配置核心的脚本文件等。

一般在每个目录下都有一个.depend 文件和一个makefile 文件,这两个文件都是编译时使用的辅助文件,仔细阅读这两个文件对弄清各个文件之间的联系和依托关系很有帮助,而且在有的目录下还有readme 文件,它是对该目录下的文件的一些说明,同样有利于我们对内核源码的理解。

2.解读实战:为你的内核增加一个系统调用

虽然linux 的内核源码用树形结构组织得非常合理、科学,把与功能相关联的文件都放在同一个子目录下,这样使得程序更具可读性。然而,linux 的内核源码实在是太大而且非常复杂,即便采用了很合理的文件组织方法,在不同目录下的文件之间还是有很多的关联,分析核心的一部分代码通常要查看其他的几个相关的文件,而且可能这些文件还不在同一个子目录下。

下面举一个具体的内核分析实例,希望能通过这个实例,使读者对linux 的内核组织有些具体的认识,读者从中也可以学到一些对内核的分析方法。

以下即为分析实例:

(1)操作平台

硬件:cpu intel pentium ii;

软件:redhat linux 6.0,内核版本2.2.5

(2)相关内核源代码分析

①系统的引导和初始化:linux 系统的引导有好几种方式,常见的有lilo、loadin引导和linux的自举引导(bootsect-loader),而后者所对应源程序为arch/i386/boot/bootsect.s,它为实模式的汇编程序,限于篇幅在此不做分析。无论是哪种引导方式,最后都要跳转到arch/i386/kernel/setup.s。setup.s主要是进行实模式下的初始化,为系统进入保护模式做准备。此后,系统执行arch/i386/kernel/head.s (对经压缩后存放的内核要先执行arch/i386/boot/compressed/head.s);head.s 中定义的一段汇编程序setup_idt,它负责建立一张256项的idt表(interrupt deor table),此表保存着所有自陷和中断的入口地址,其中包括系统调用总控程序system_call 的入口地址。当然,除此之外,head.s还要做一些其他的初始化工作。

②系统初始化后运行的第一个内核程序asmlinkage void __init start_kernel(void) 定义在/usr/src/linux/init/main.c中,它通过调用usr/src/linux/arch/i386/kernel/traps.c 中的一个函数void __init trap_init(void) 把各个自陷和中断服务程序的入口地址设置到idt表中,其中系统调用总控程序system_cal就是中断服务程序之一;void __init trap_init(void)函数则通过调用一个宏set_system_gate(syscall_vector,&system_call),把系统调用总控程序的入口挂在中断0x80上。

其中syscall_vectr是定义在/usr/src/linux/arch/i386/kernel/irq.h中的一个常量0x80,而system_call 即为中断总控程序的入口地址,中断总控程序用汇编语言定义在/usr/src/linux/arch/i386/kernel/entry.s中。

③中断总控程序主要负责保存处理机执行系统调用前的状态,检验当前调用是否合法,并根据系统调用向量,使处理机跳转到保存在sys_call_table 表中的相应系统服务例程的入口,从系统服务例程返回后恢复处理机状态退回用户程序。

而系统调用向量则定义在/usr/src/linux/include/asm-386/unistd.h 中,sys_call_table 表定义在/usr/src/linux/arch/i386/kernel/entry.s 中,同时在/usr/src/linux/include/asm-386/unistd.h 中也定义了系统调用的用户编程接口。

④由此可见,linux的系统调用也像dos系统的int 21h中断服务,大把0x80中断作为总的入口,然后转到保存在sys_call_table表中的各种中断服务例程的入口地址,提供各种不同的中断服务。

提供上源代码分析可知,要增加一个系统调用就必须在sys_call_table表中增加一项,并在其中保存好自己的系统服务例程的入口地址,然后重新编译内核,当然,系统服务例程是必不可少的。

由此可知,在此版linux内核源程序<2.2.5>中,与系统调用相关的源程序文件就包括以下这些:

* arch/i386/boot/bootsect.s

* rch/i386/kernel/setup.s

* rch/i386/boot/compressed/head.s

* rch/i386/kernel/head.s

* nit/main.c

* rch/i386/kernel/traps.c

* rch/i386/kernel/entry.s

* rch/i386/kernel/irq.h

* nclude/asm-386/unistd.h

当然,这只是涉及到的几个主要文件。而事实上,增加系统调用真正要修改的文件只有include/asm-386/unistd.h 和arch/i386/kernel/entry.s两个。

(3)源码的修改

①kernel/sys.c中增加系统服务例程如下:

asmlinkage int sys_addtotal(int numdata)

{ int i=0,enddata=0;

while(i<=numdata)

enddata+=i++;

return enddata; }

该函数有一个int 型入口参数numdata , 并返回从0 到numdata 的累加值,然而也可以把系统服务例程放在一个自己定义的文件或其他文件中,只是要在相应文件中作必要的说明。

②把smlinkage int sys_addtotal( int) 的入口地址加到sys_call_table表中。

arch/i386/kernel/entry.s 中的最后几行源代码修改前为:

.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

修改后为:

.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

③把增加的sys_call_table 表项所对应的向量,在include/asm-386/unistd.h 中进行必要申明,以供用户进程和其他系统进程查询或调用。

增加后的部分/usr/src/linux/include/asm-386/unistd.h 文件如下:

#define __nr_sendfile 187

#define __nr_getpmsg 188

#define __nr_putpmsg 189

#define __nr_vfork 190

/* add by i */

#define __nr_addtotal 191

④测试程序(test.c)如下:

#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); }

对修改后的新的内核进行编译,并引导它作为新的操作系统,运行几个程序后可以发现一切正常;在新的系统下对测试程序进行编译(注:由于原内核并未提供此系统调用,所以只有在编译后的新内核下,此测试程序才可能被编译通过),运行情况如下:

$gcc .test test.c

$./test

please input a number

36

total from 0 to 36 is 666

修改成功后对相关源码进一步分析可知,在此版本的内核中,从/usr/src/linux/arch/i386/kernel/entry.s 文件中对sys_call_table 表的设置可以看出,有好几个系统调用的服务例程都是定义在/usr/src/linux/kernel/sys.c 中的同一个函数:

asmlinkage int sys_ni_syscall(void)

{ return -enosys; }

例如第188项和第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 */

而这两项在文件/usr/src/linux/include/asm-386/unistd.h 中却申明如下:

#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

由此可见,在此版本的内核源代码中,由于asmlinkage int sys_ni_syscall(void) 函数并不进行任何操作,所以包括getpmsg, putpmsg 在内的好几个系统调用都是不进行任何操作的,即有待扩充的空调用;但它们却仍然占用着sys_call_table表项,估计这是设计者们为了方便扩充系统调用而安排的,所以只需增加相应服务例程(如增加服务例程getmsg或putpmsg),就可以达到增加系统调用的作用。

3.结束语

要完全解读庞大复杂的linux内核,一篇文章远远不能介绍清楚,而且与系统调用相关的代码也只是内核中极其微小的一部分,重要的是方法,掌握好的分析方法,所以上述分析只是起个引导作用,而真正的分析还有待读者自己的努力。

linux 内核 介绍,Linux内核详细介绍相关推荐

  1. linux中各目录及详细介绍

    linux中各目录及详细介绍 一.Linux文件系统的层次结构 在Linux或UNIX操作系统中,所有的文件和目录都被组织成一个以根节点开始的倒置的树状结构,如图: 二.目录 1.目录的定义 目录相当 ...

  2. linux中的chmod命令详细介绍、使用及实例

    linux中的chmod命令详细介绍.使用及实例 chmod命令 chmod用于改变 linux 系统文件或目录的访问权限,可以用它控制文件或目录的访问权限.该命令有两种用法:一种是包含字母的文字设定 ...

  3. linux awk 内置函数详细介绍(实例)

    原文地址为: linux awk 内置函数详细介绍(实例) 这节详细介绍awk内置函数,主要分以下3种类似:算数函数.字符串函数.其它一般函数.时间函数 一.算术函数: 以下算术函数执行与 C 语言中 ...

  4. grub2 linux 参数,对grub2的详细介绍

    翻译了grub2官方手册的绝大部分内容,然后自己整理了一下.因为内容有点杂,所以章节安排上可能不是太合理,敬请谅解. 本文目录: 1.1 基础内容 1.2 安装grub2 1.3 grub2配置文件 ...

  5. Linux shell脚本基础学习详细介绍(完整版)一

    Linux shell脚本基础学习这里我们先来第一讲,介绍shell的语法基础,开头.注释.变量和 环境变量,向大家做一个基础的介绍,虽然不涉及具体东西,但是打好基础是以后学习轻松地前提. 1. Li ...

  6. Linux shell脚本基础学习详细介绍(完整版)2

    详细介绍Linux shell脚本基础学习(五) Linux shell脚本基础前面我们在介绍Linux shell脚本的控制流程时,还有一部分内容没讲就是有关here document的内容这里继续 ...

  7. Linux shell脚本基础学习详细介绍(完整版)

    Linux shell脚本基础学习这里我们先来第一讲,介绍shell的语法基础,开头.注释.变量和 环境变量,向大家做一个基础的介绍,虽然不涉及具体东西,但是打好基础是以后学习轻松地前提. 1. Li ...

  8. linux 查看u盘文件,详细介绍Linux系统下检测U盘是不是已经连接的方法

    Linux操作系统是基于Unix操作系统发展而来的一种克隆系统,它诞生于1991年的10月5日(这是第一次正式向外公布的时间).今天就跟着小编一起来看一看:详细介绍linux系统下检测U盘是不是已经连 ...

  9. linux od命令详解,详细介绍Linux od命令

    转:http://blog.csdn.net/zuixinnet/article/details/8994002 随着计算机飞速的发展,很多人开始学习Linux,怎样才能学好Linux,一定要学好Li ...

  10. 重新命名的linux命令,Linux 重命名命令自制详细介绍

    Linux 重命名命令 相比于Windows上的ren命名,Linux还真的是没有一个特定的重命名的命令.(虽然可以间接的使用mv来实现).下面我就来自己写一个简单的重命名命令. 准备工作 操作系统: ...

最新文章

  1. 90后清华女校友范楚楚获ACM 2020唯一博士论文奖!出任MIT助理教授后再摘桂冠
  2. Linux常见命令(五)——rmdir
  3. ISA2004发布应用程序服务器
  4. 使用Socket及ServerSocket创建简单的服务器
  5. [Leetcode][程序员面试金典][面试题16.11][JAVA][跳水板][数学][动态规划]
  6. python水平_如何在python中水平透视表
  7. C++ string 介绍
  8. mkdir 函数_PHP mkdir()函数与示例
  9. 关于new 和delete的灾祸
  10. mysql开启事务并锁表_MySQL:如何锁定表并启动事务?
  11. yaahp层次分析法步骤_什么是层次分析法?(文末附yaahp软件)
  12. Swift Alamofire SwiftyJson
  13. wxWidgets+wxSmith版电子词典
  14. java 小技巧_成为JAVA高手的25个小窍门
  15. 如何使浏览器打开时,默认的文档模式就是标准模式
  16. 第3-4课:爱因斯坦的思考题(上)
  17. 储能国际IPFS百城节点峰会广州站完美落幕,引领IPFS新时代!
  18. 【大数据入门核心技术-Tez】(三)Tez与Hive整合
  19. 【181007】VC++ 打飞机游戏源码
  20. ST官网获取并生成常用PCB EDA工具的原理图库和封装库方法

热门文章

  1. Elastic Job 入门
  2. 怎样学好计算机——计算机达人成长之路(23)
  3. 服务器启动socket服务报错 java.net.BindException:Cannot assign requested address
  4. 备忘: Visual Studio 2013 VC++ IDE 使用小贴示。
  5. hive变量传递的源码实现
  6. Android学习笔记之如何将数据保存到SDCard
  7. android使用protobuf实现网络订单管理功能
  8. JavaWeb-Web请求过程
  9. 攻击需要成本吗_光伏智能温室建设成本高吗?需要哪种钢结构?
  10. mysql 中 where条件的OR 和 and 加括号的说法