搜档网
当前位置:搜档网 › 内核模块的编写和运行

内核模块的编写和运行

内核模块的编写和运行
内核模块的编写和运行

第五章模块编程实验

【实验目的】

通过学习内核模块的编写和运行,了解模块是Linux OS的一种特有的机制,可根据用户的实际需要在不需要对内核进行重新编译的情况下,模块能在内核中被动态地加载和卸载。编写一个模块,将它作为Linux OS内核空间的扩展来执行,并通过insmod命令来手工加载,通过命令rmmod来手工卸载。

【准备知识】

Linux模块是一些可以作为独立程序来编译的函数和数据类型的集合。在装载这些模块时,将它的代码链接到内核中。Linux模块有两种装载方式:静态装载(内核启动时装载)和动态装载(在内核运行过程中装载)。若在模块装载之前就调用了动态模块的一个函数,则此调用将失败;若模块已被装载,则内核就可以使用系统调用,并将其传递到模块中的相应函数。模块通常用来实现设备驱动程序(这要求模块的API和设备驱动程序的API相一致)。模块可用来实现所期望的任何功能。

一.模块的组织结构

模块一旦被装载进系统,就在内核地址空间中管态下执行。它就像任何标准的内核代码一样成为内核的一部分,并拥有其它内核代码相同的权限和职责(当然也会引起系统的崩溃)。若模块知道内核数据结构的地址,则它可以读写内核数据结构。但Linux是一个整体式的内核(monolithic kernel)结构,整个内核是一个单独的且非常大的程序,从而存在一个普遍的问题:在一个文件中实现的函数可能需要在其它文件中定义的数据。在传统的程序中,这个问题是通过链接编辑器在生成可执行对象文件时,使用链接编辑器可以解析的外部(全局)变量来解决的。又因为模块的设计和实现与内核无关,所以模块不能靠静态链接通过变量名引用内核数据结构。恰好相反,Linux内核采用了另外一种机制:实现数据结构的文件可以导出结构的符号名(可以从文件/proc/ksyms或文件/…/kernel/ksyms.c中以文本方式读取这个公开符号表),这样在运行时就可以使用这个结构了。不过在编写模块的过程中,编写(修改)导出变量时要格外注意,因为通过修改变量会修改内核的状态,其结果可能并不是内核设计者所期望的。在确信自己了解修改内核变量的后果之前,应该对这些变量只进行读操作。

模块作为一种抽象数据类型,它具有一个可以通过静态内核中断的接口。最小的

模块结构必须包括两个函数,它们在系统装载模块和卸载模块时调用,分别是

init_module()和cleanup_module()。可以编写一个只包括这两个函数的模块,这样

该模块中唯一会被调用的函数就是模块被装载时所调用的函数init_module()和模

块被卸载时所调用的函数cleanup_module()。并且用函数init_module()来启动模块

装载期间的操作,用函数cleanup_module()来停止这些操作。

由于模块可以实现相当复杂的功能,故可以在模块中加入很多新函数以实现所要

期望的功能。不过加入模块的每个新函数都必须在该模块装载到内核中时进行注册。

若该模块是静态装载的,则该模块的所有函数都是在内核启动时进行注册的;若该模

块是动态装载的,则这些新函数必须在装载这个模块时动态注册。当然,如果该模块

被动态卸载了,则该模块的函数都必须从系统中注销。通过这种方式,当这个模块不

在系统中时,就不能调用该模块的函数。其中注册工作通常是在函数init_module()

中完成的,而注销工作通常是在函数cleanup_module()中完成的。

由上述定义的模块应有如下的格式:

#include #include ……// 其它header信息

int init_module( )

{ …… // 装载时,初始化模块的编码

} ……

…… // 期望该模块所能实现的一些功能函数,如open()、release()、write()、

// read()、ioctl()等函数

……

void cleanup_module( )

{ …… // 卸载时,注销模块的编码

}。

二.模块的编译

一旦设计并编写好模块,必须将其编译成一个适合内核装载的对象文件。由于编

写模块是用C语言来完成的,故采用gcc编译器来进行编译。若需要通知编译程序

把这个模块作为内核代码而不是普通的用户代码来编译,则就需向gcc编译器传递参

数“-D_ _KERNEL_ _”;若需要通知编译程序这个文件是一个模块而不是一个普通文

件,则就需向gcc编译器传递参数“-DMODULE”;若需要对模块程序进行优化编译、

连接,则就需使用“-O2”参数;若还需要对装载后的模块进行调试,则就需使用“-g”

参数;同时需要使用“-Wall”参数来向装载程序传递all,使用“-c”开关通知编

译程序在编译完这个模块文件后不调用链接程序。

一般编译模块文件的命令格式如下:

#gcc -O2 -g -Wall -DMODULE -D_ _KERNEL_ _ -c filename.c

//filename.c为自己编写的模块程序源代码文件

执行命令后就会得到文件filename.o,该文件就是一个可装载的目标代码文件。

三.模块的装载

内核模块的装载方式有两种。一种是使用insmod命令手工装载模块;另一种是

请求装载demand loading(在需要时装载模块),即当有必要装载某个模块时,若用

户安装了核心中不存在的文件系统时,核心将请求内核守护进程kerneld准备装载适

当的模块。该内核守护进程是一个带有超级用户权限的普通用户进程。此实验中我们

主要采用insmod命令手工装载模块。

系统启动时,kerneld开始执行,并为内核打开一个IPC通道,内核通过向kerneld

发送消息请求执行各种任务。kerneld的主要功能是装载和卸载内核模块,kerneld

自身并不执行这些任务,它通过某些程序(如insmod)来完成。Kerneld只是内核的

代理,只为内核进行调度。

insmod程序必须找到请求装载的内核模块(该请求装载的模块一般被保存在

/lib/modules/kernel-version中)。这些模块与系统中其它程序一样是已连接的目

标文件,但不同的是它们被连接成可重定位映象(即映象没有被连接到在特定的地址

上运行,其文件格式是a.out或ELF)。亦就是说,模块在用户空间(使用适当的标

志)进行编译,结果产生一个可执行格式的文件。在用insmod命令装载一个模块时,

将会发生如下事件:

(1)新模块(通过内核函数create_module())加入到内核地址空间。

(2)insmod执行一个特权级系统调用get_kernel_syms()函数以找到内核的输

出符号(一个符号表示为符号名和符号值,如地址值)。

(3)create_module()为这个模块分配内存空间,并将新模块添加在内核模块链

表的尾部,然后将新模块标记为UNINITIALIZED(模块未初始化)。

(4)通过init_module()系统调用装载模块。(该模块定义的符号在此时被导出,供其它可能后来装载的模块使用)

(5)insmod为新装载的模块调用init_module()函数,然后将新模块标志为RUNNING(模块正在运行)。

在执行完insmod命令后,就可在/proc/modules文件中看到装载的新模块了。(为证实其正确性,可在执行insmod命令之前先查看/proc/modules文件,执行之后再查看比较)

四.模块的卸载

当一个模块不需要使用时,可以使用rmmod命令卸载该模块。由于无需连接,故它的任务比加载模块要简单得多。但如果请求装载模块在其使用计数为0时,kerneld 将自动从系统中卸载该模块。卸载时调用模块的cleanup_module()释放分配给该模块的内核资源,并将其标志为DELETED(模块被卸载);同时断开内核模块链表中的连接,修改它所依赖的其它模块的引用,重新分配模块所占的内核内存。

五.模块连接到内核的示意图

该图比较明显地展示了模块连接到内核所使用的命令和函数,以及各个函数之间的调用关系。通过该图,可以比较清晰地看出模块连接到内核的整个连接过程,这也有助于内核模块的编写。

六.模块程序中管理模块的几个文件操作

在内核内部用一个file结构来识别模块,而且内核使用file_operatuions结构

来访问模块程序中的函数。file_operatuions结构是一个定义在中的

函数指针表。管理模块的文件操作,通常也称为“方法”,它们都为struct

file_operations提供函数指针。在struct file_operations中的操作一般按如下

顺序出现,除非说明,它们返回0值时表示访问成功;发生错误时返回一个负的错误

值(目前共有13个操作):

int (*lseek) ()、int (*read)()、int (*write)()、int (*readdir)()、int

(*select)()、int (*ioctl)()、int (*mmap)()、int (*open)()、void (*release)()、

int (*fsync)()、int (*fasync)()、int (*check_media_change)()、int

(*revalidate)() 下面我们只简单介绍其中的几个操作,其它在以后涉及时再介绍: 1、方法int (*read)(struct inode *,struct file *,char *, int)

该方法用来从模块中读取数据。当其为NULL指针时将引起read系统调用返回

-EINVAL(“非法参数”)。函数返回一个非负值表示成功地读取了多少字节。

2、方法int (*write)(struct inode *,struct file *,const char *, int)

该方法用来向模块发送数据。当其为NULL指针时将引起write系统调用返回

-EINVAL。如果函数返回一个非负值,则表示成功地写入了多少字节。

3、方法int (*open)(struct inode *,struct file *)

该方法是用来打开模块的操作,它是操作在模块节点上的第一个操作,即使这样,

该方法还是可以为NULL指针。如果为NULL指针,则表示该模块的打开操作永远成功,

但系统不会通知你的模块程序。

4、方法void (*release)(struct inode *,struct file *)

该方法是用来关闭模块的操作。当节点被关闭时就调用这个操作。与open类似,

release也可以为NULL指针。

当在你的模块中需要上面这些方法时,相应的方法若没有,则在struct

file_operations中相应的地方将其令为NULL指针。这样我们需要的大概象下面这

样:

struct file_operations modulename_fops ={

NULL, // modulename_lseek modulename_read, modulename_write, NULL, // modulename_readdir NULL, // modulename_select NULL, // modulename_ioctl NULL, // modulename_mmap modulename_open, modulename_release, NULL, // modulename_fsync NULL, // modulename_fasync NULL, // modulename_check_media_change NULL // modulename_revalidate }

【实验内容】

1、编写一个简单的内核模块,该模块至少需要有两个函数:一个是init_module()

函数,在把模块装载到内核时被调用,它为内核的某些东西注册一个处理程序,或是

用自身的代码取代某个内核函数;另一个是cleanup_module()函数,在卸载模块时

被调用,其任务是清除init_module()函数所做的一切操作。编写完成后进行该模块

的编译、装载和卸载操作。

2、向上面模块中再添加一些新函数,如open()、release()、write()和read()

函数,并编写一个函数来测试你的模块能否实现自己添加的函数的功能。其中

open()、release()和write()函数都可以是空操作或较少的操作,它们仅仅为结构

file_operations提供函数指针。

【实验指导】

1 一个简单的内核模块

1.1 必要的header文件:

除了前面讲到的头文件#include 和#include

外,如果你的内核打开了版本检查,那么我们就还必须增加头文件

#include ,否则就会出错。

1.2 init_module()函数:

由于题目的要求不高,故可只在该函数里完成一个打印功能,如printk(“Hello!

This is a testing module!\n”);等。为便于检查模块是否装载成功,我们可以给

一个返回值,如return 0;若返回一个非0值,则表示init_module()失败,从而不

能装载模块。

1.3 cleanup_module()函数:

只需用一条打印语句来取消init_module()函数所做的打印功能操作就可以了,

如printk(“Sorry! The testing module is unloaded now!\n”);等。

1.4 模块的编写:

此处把该模块文件取名为testmodule.c

#include // 在内核模块中共享

#include // 一个模块

//处理CONFIG_MODVERSIONS #if CONFIG_MODVERSIONS == 1 #define MODVERSIONS #include #endif int init_module() // 初始化模块

{ printk(“Hello! This is a testing module! \n”);

return 0;

} void cleanup_module() // 取消init_module()函数所做的打印功能操作

{ printk(“Sorry! The testing module is unloading now! \n”);

}

1.5 模块的编译、装载和卸载:

[root@linux /]# gcc –o2 –Wall –DMODULE –D__KERNEL__-c testmodule.c [root@linux /]# ls –s //在当前目录下查看生成的目标文件testmodule.o 现在,模块testmodule已经编译好了。用下面命令将它装载到系统中:

[root@linux /]# insmod –f testmodule.o 如果装载成功,则在/proc/modules文件中就可看到模块testmodule,并可看到

它的主设备号。同时在终端显示:

Hello! This is a testing module!

如果要卸载,就用如下命令:

[root@linux /]# rmmod testmodule 如果卸载成功,则在/proc/devices文件中就可看到模块testmodule已经不存

在了。同时在终端显示:

Sorry! The testing module is unloading now!

2 向testmodule模块中添加新函数open()、release()、write()和

read()

2.1 函数open( )

int open(struct inode *inode,struct file *filp) { MOD_INC_USE_COUNT; // 增加该模块的用户数目

printk(“This module is in open!\n”);

return 0;

}

2.2 函数release( )

void release(struct inode *inode,struct file *filp) { MOD_DEC_USE_COUNT; //该模块的用户数目减1 printk(“This module is in release!\n”);

return 0;

#ifdef DEBUG printk(“release(%p,%p)\n”,inode,filp);

#endif

}

2.3 函数 read()

int read(struct inode *inode,struct file *filp,char *buf,int count) { int leave;

if(verify_area(VERIFY_WRITE,buf,count) == DEFAULT) return DEFAULT;

for(leave=count;leave>0;leave --) { __put_user(1,buf,1);

buf ++;

}

return count;

} 2.4 函数write()

int write(struct inode *inode,struct file *filp,const char *buf,int count) { return count;

}

3 模块的测试

在该模块程序编译加载后,再在/dev目录下创建模块设备文件moduledev,使用

命令: #mknod /dev/moduledev c major minor ,其中“c”表示moduledev是

字符设备,“major”是moduledev的主设备号。(该字符设备驱动程序编译加载后,

可在/proc/modules文件中获得主设备号,或者使用命令: [root@linux /]#cat

/proc/modules | awk ”\\$2==\” moduledev\”{ print\\$1}”获得主设备号)

#include #include #include #include

main ( ) { int i,testmoduledev;

char buf[10];

testmoduledev=open(“/dev/moduledev”,O_RDWR);

if(testmoduledev == -1) { printf(“Can’t open the file! \n”);

exit(0);

} read(testmoduledev,buf,10);

for(i=0;i<10;i++) printf(“%d\n”,buf[i]);

close(testmoduledev);

return 0;

}

Linux内核—文件系统模块的设计和开发

Linux内核—文件系统模块的设计和开发 郑小辉 摘要:目前,Linux技术已经成为IT技术发展的热点,投身于Linux技术研究的社区、研究机构和软件企业越来越多,支持Linux的软件、硬件制造商和解决方案提供商也迅速增加,Linux在信息化建设中的应用范围也越来越广,Linux产业链已初步形成,并正在得到持续的完善。随着整个Linux产业的发展,Linux技术也处在快速的发展过程中,形成了若干技术热点。 本文介绍了Linux的发展和特点,以及与其他文件系统的区别。文中主要是对Linux2.4.0内核文件系统源代码的分析,并参考其文件格式设计一个简洁的文件系统。源代码的分析主要介绍了VFS文件系统的结构,Linux自己的Ext2文件系统结构,以及文件系统中的主要函数操作。 在设计的简洁文件系统中,通过调用一些系统函数实现了用户的登录、浏览目录、创建目录、更改目录、创建文件以及退出系统功能。 关键字:Linux 源代码分析文件系统Ext2 Linux内核

Linux kernel -Design and development for the File System Module Zheng xiaohui Abstract: Currently, Linux IT technology has become a hot development technology. Participating in Linux technology research communities, research institutes and software enterprises are in support of Linux more and more, software and hardware manufacturers and solution providers have increased rapidly, In the development of the information industry the Linux application is also increasing, Linux industry chain has taken shape, and is sustained improvemently. With the entire industry in the development of Linux, and Linux is also at the rapid development process, formed a number of technical points. This paper presents the development of Linux and features, and with other file system differences. The main text of the document is Linux2.4.0 system kernel source code analysis, and I reference its file format to design a simple file system. The analysis of the source code mainly on the VFS file system structure, Linux Ext2 its own file system structures, file systems and the main function operation. In the design of the file simple system, some system function is used to achieve function such as: the user's login, browse catalogs, create directories, Change directory, create documents and withdraw from the system function and etc. Key words: Linux, the source code, file system, Ext2, Linux kernel

编译在arm板上运行的内核模块

编译在arm板上运行的内核模块 前两天被这个事情搞晕了,看视频的时候感觉编译一个内核模块很简单的, 就是修改makefile 的两个地方,但是自己一做就出现问题了,因为我是自己自 学的,身边没有可以指导的人,所以很多都要靠自己摸索了,我自己编译的时 候出现很多警告信息和错误,提示找不到头文件,还有一些看不懂的信息,到 处找资料,但是都没有说清楚,看了很久也没看出什么对自己有用的东西,看 的头晕,准备放弃了,今天在学习的时候又去看结果看到一篇博文,才焕然大 悟,makefile 里面要改的源代码路径是移植到arm 板上的linux 源代码,才突然 想起来,我自己改错了,就是要把路径指上你开发板上运行的linux 内核源代 码的顶层路径,我是用的通过nfs 启动系统的,是按照国嵌的视频一步步做的, 所以我的路径在我的nfs 所在的路径。这些问题对于一些学了很久的人来说可 能很低级,但是对于初学者来说可能碰到后半天搞不好,所以写下来供参考。 。。下面是我自己找的一个小实验: #include #include MODULE_LICENSE(“GPL”);MODULE_AUTHOR(“David Xie”);MODULE_DESCRIPTION(“Hello World Module”);MODULE_ALIAS(“a simplest module”);static int __init hello_init(){ printk(KERN_EMERG”Hello World!\n”);return 0;}static void __exit hello_exit(){ printk(KERN_EMERG “Goodbye Cruel World!\n”);}module_init(hello_init);module_exit(hello_exit);第一步是编译,首先要做的是设置交叉编译器,修改makefile,打开makefile 文件, 如下:ifneq ($(KERNELRELEASE),)obj-m := hello.oelseKDIR := /forlinux/kernel/linux-2.6.28all:make -C $(KDIR) M=$(PWD) modules ARCH=arm CROSS_COMPILE=arm-linux-clean:rm -f *.ko *.o *.mod.o *.mod.c *.symversendif 首先需要指定kernel 的源代码路径:我的是KDIR

编译原理复习整理(重点含答案)

1、给出下面语言的相应文法。L1={a n b n c i|n≥1,i≥0} 从n,i的不同取值来把L1分成两部分:前半部分是anbn:A→aAb|ab后半部分是ci:B→Bc|ε所以整个文法G1[S]可以写为:G1(S):S→AB;A→aAb|ab;B→cB|ε 3、构造一个DFA,它接受 ={a,b}上所有包含ab的字符串。 (要求:先将正规式转化为NFA,再将NFA确定化,最小化)

4、对下面的文法G: E →TE ’ E ’→+E|ε T →FT ’ T ’→T|ε F →PF ’ F ’ →*F ’|ε P →(E)|a|b|∧ (1)证明这个文法是LL(1)的。 (2)构造它的预测分析表。 (1)FIRST(E)={(,a,b,^}FIRST(E')={+, ε}FIRST(T)={(,a,b,^}FIRST(T')={(,a,b,^,ε} FIRST(F)={(,a,b,^}FIRST(F')={*,ε}FIRST(P)={(,a,b,^}FOLLOW(E)={#,)} FOLLOW(E')={#,)}FOLLOW(T)={+,),#}FOLLOW(T')={+,),#}FOLLOW(F)={(,a,b,^,+,),#} FOLLOW(F')={(,a,b,^,+,),#}FOLLOW(P)={*,(,a,b,^,+,),#} (2)考虑下列产生式: '→+'→'→'→E E T T F F P E a b ||*|()|^||εεε FIRST(+E)∩FIRST(ε)={+}∩{ε}=φ FIRST(+E)∩FOLLOW(E')={+}∩{#,)}=φ FIRST(T)∩FIRST(ε)={(,a,b,^}∩{ε}=φ FIRST(T)∩FOLLOW(T')={(,a,b,^}∩{+,),#}=φ FIRST(*F')∩FIRST(ε)={*}∩{ε}=φ FIRST(*F')∩FOLLOW(F')={*}∩{(,a,b,^,+,),#}=φ

编译原理知识点汇总

编译原理的复习提纲 1.编译原理=形式语言+编译技术 2.汇编程序: 把汇编语言程序翻译成等价的机器语言程序 3.编译程序: 把高级语言程序翻译成等价的低级语言程序 4.解释执行方式: 解释程序,逐个语句地模拟执行 翻译执行方式: 翻译程序,把程序设计语言程序翻译成等价的目标程序 5.计算机程序的编译过程类似,一般分为五个阶段: 词法分析、语法分析、语义分析及中间代码生成、代码优化、目标代码生成 词法分析的任务: 扫描源程序的字符串,识别出的最小的语法单位(标识符或无正负号数等) 语法分析是: 在词法分析的基础上的,语法分析不考虑语义。语法分析读入词法分析程序识别出的符号,根据给定的语法规则,识别出各个语法结构。 语义分析的任务是检查程序语义的正确性,解释程序结构的含义,语义分析包括检查变量是否有定义,变量在使用前是否具有值,数值是否溢出等。

语法分析完成之后,编译程序通常就依据语言的语义规则,利用语法制导技术把源程序翻译成某种中间代码。所谓中间代码是一种定义明确、便于处理、独立于计算机硬件的记号系统,可以认为是一种抽象机的程序 代码优化的主要任务是对前一阶段产生的中间代码进行等价变换,以便产生速度快、空间小的目标代码 编译的最后一个阶段是目标代码生成,其主要任务是把中间代码翻译成特定的机器指令或汇编程序 编译程序结构包括五个基本功能模块和两个辅助模块 6.编译划分成前端和后端。 编译前端的工作包括词法分析、语法分析、语义分析。编译前端只依赖于源程序,独立于目标计算机。前端进行分析 编译后端的工作主要是目标代码的生成和优化后端进行综合。独立于源程序,完全依赖于目标机器和中间代码。 把编译程序分为前端和后端的优点是: 可以优化配置不同的编译程序组合,实现编译重用,保持语言与机器的独立性。 7.汇编器把汇编语言代码翻译成一个特定的机器指令序列 第二章 1.符号,字母表,符号串,符号串的长度计算P18,子符号串的含义,符号串的简单运算XY,Xn, 2.符号串集合的概念,符号串集合的乘积运算,方幂运算,闭包与正闭包的概念P19,P20A0 ={ε} 3.重写规则,简称规则。非xx(V

探究linux内核,超详细解析子系统

探究linux内核,超详细解析子系统 Perface 前面已经写过一篇《嵌入式linux内核的五个子系统》,概括性比较强,也比较简略,现在对其进行补充说明。 仅留此笔记,待日后查看及补充!Linux内核的子系统 内核是操作系统的核心。Linux内核提供很多基本功能,如虚拟内存、多任务、共享库、需求加载、共享写时拷贝(Copy-On-Write)以及网络功能等。增加各种不同功能导致内核代码不断增加。 Linux内核把不同功能分成不同的子系统的方法,通过一种整体的结构把各种功能集合在一起,提高了工作效率。同时还提供动态加载模块的方式,为动态修改内核功能提供了灵活性。系统调用接口用户程序通过软件中断后,调用系统内核提供的功能,这个在用户空间和内核提供的服务之间的接口称为系统调用。系统调用是Linux内核提供的,用户空间无法直接使用系统调用。在用户进程使用系统调用必须跨越应用程序和内核的界限。Linux内核向用户提供了统一的系统调用接口,但是在不同处理器上系统调用的方法

各不相同。Linux内核提供了大量的系统调用,现在从系统 调用的基本原理出发探究Linux系统调用的方法。这是在一个用户进程中通过GNU C库进行的系统调用示意图,系 统调用通过同一个入口点传入内核。以i386体系结构为例,约定使用EAX寄存器标记系统调用。 当加载了系统C库调用的索引和参数时,就会调用0x80软件中断,它将执行system_call函数,这个函数按照EAX 寄存器内容的标示处理所有的系统调用。经过几个单元测试,会使用EAX寄存器的内容的索引查system_call_table表得到系统调用的入口,然后执行系统调用。从系统调用返回后,最终执行system_exit,并调用resume_userspace函数返回用户空间。 linux内核系统调用的核心是系统多路分解表。最终通过EAX寄存器的系统调用标识和索引值从对应的系统调用表 中查出对应系统调用的入口地址,然后执行系统调用。 linux系统调用并不单层的调用关系,有的系统调用会由

linux内核编译和生成makefile文件实验报告

操作系统实验报告 姓名:学号: 一、实验题目 1.编译linux内核 2.使用autoconf和automake工具为project工程自动生成Makefile,并测试 3.在内核中添加一个模块 二、实验目的 1.了解一些命令提示符,也里了解一些linux系统的操作。 2.练习使用autoconf和automake工具自动生成Makefile,使同学们了解Makefile的生成原理,熟悉linux编程开发环境 三、实验要求 1使用静态库编译链接swap.c,同时使用动态库编译链接myadd.c。可运行程序生成在src/main目录下。 2要求独立完成,按时提交 四、设计思路和流程图(如:包括主要数据结构及其说明、测试数据的设计及测试结果分析) 1.Makefile的流程图: 2.内核的编译基本操作 1.在ubuntu环境下获取内核源码 2.解压内核源码用命令符:tar xvf linux- 3.18.12.tar.xz 3.配置内核特性:make allnoconfig 4.编译内核:make 5.安装内核:make install

6.测试:cat/boot/grub/grub.conf 7.重启系统:sudo reboot,看是否成功的安装上了内核 8.详情及结构见附录 3.生成makefile文件: 1.用老师给的projec里的main.c函数。 2.需要使用automake和autoconf两个工具,所以用命令符:sudo apt-get install autoconf 进行安装。 3.进入主函数所在目录执行命令:autoscan,这时会在目录下生成两个文件 autoscan.log和configure.scan,将configure.Scan改名为configure.ac,同时用gedit打开,打开后文件修改后的如下: # -*- Autoconf -*- # Process this file with autoconf to produce a configure script. AC_PREREQ([2.69]) AC_INIT([FULL-PACKAGE-NAME], [VERSION], [BUG-REPORT-ADDRESS]) AC_CONFIG_SRCDIR([main.c]) AC_CONFIG_HEADERS([config.h]) AM_INIT_AUTOMAKE(main,1.0) # Checks for programs. AC_PROG_CC # Checks for libraries. # Checks for header files. # Checks for typedefs, structures, and compiler characteristics. # Checks for library functions. AC_OUTPUT(Makefile) 4.新建Makefile文件,如下: AUTOMAKE_OPTIONS=foreign bin_PROGRAMS=main first_SOURCES=main.c 5.运行命令aclocal 命令成功之后,在目录下会产生aclocal.m4和autom4te.cache两个文件。 6.运行命令autoheader 命令成功之后,会在目录下产生config.h.in这个新文件。 7.运行命令autoconf 命令成功之后,会在目录下产生configure这个新文件。 8.运行命令automake --add-missing输出结果为: Configure.ac:11:installing./compile’ Configure.ac:8:installing ‘.install-sh’ Configure.ac:8:installing ‘./missing’ Makefile.am:installing ‘./decomp’ 9. 命令成功之后,会在目录下产生depcomp,install-sh和missing这三个新文件和执行下一步的Makefile.in文件。 10.运行命令./configure就可以自动生成Makefile。 4.添加内核模块

编译原理中重点整理

1.翻译程序:将某一种语言(源语言)程序转换为与其逻辑上等价的另一种语言(目标语言) 程序。 编译程序:源语言为高级语言,目标语言为汇编语言或机器语言的翻译程序。 汇编程序:源语言为汇编语言,目标语言为机器语言的翻译程序。 解释程序:源语言程序作为输入,但不产生目标程序,而是边解释边执行源程序本身。 2.解释器与编译器的主要区别在于:运行目标程序时的控制权在解释器而不在目标程序。 3.编译程序的工作过程可划分五个阶段: ①词法分析:从左到右一个字符一个字符的读入源程序,对构成源程序的字符串进行扫描 和分解,从而识别出一个个单词(也称单词符号或简称符号) ②语法分析:在词法分析的基础上将单词序列分解成各类语法短语,如“程序”,“语句”, “表达式”等等 ③语义分析和中间代码生成:语义分析是在语法分析程序确定出语法短语后,审查有无语义 错误,并为代码生成阶段收集类型信息。完成语法分析和语义 处理工作后,编译程序将源程序变成一种内部表示形式,这种 内部表示形式叫做中间语言或称中间代码,它是一种结构简单、 含义明确的记号系统。 ④代码优化:为了使生成的目标代码更为高效,可以对产生的中间代码进行变换或进行改造, 这就是代码的优化。 ⑤目标代码生成:目标代码生成阶段的任务就是是把中间代码变换成特定机器上的绝对指令 代码或可重定位的指令代码或汇编指令代码。 4.前端(Front-End)——与目标机无关的部分 后端(Back-End )——与目标机有关的部分 5.编译系统:编译程序与运行系统合称编译系统 6.遍:对源程序或源程序的中间结果从头到尾扫描一次,并做有关的加工处理,生成新的中 间结果或目标程序。 7.文法是一个四元组:G[S]=(VN, VT, P, S) VN:非终结符集合; VT :终结符集合; P :产生式集合(α→β或α∷=β); S :开始符号(或称根符号,识别符号)。 若S ->α,α∈V*,则称α为文法G的句型 若S ->α,α,α∈VT*,则称α为文法G的句子 语言是所有句子构成的集合,它是所有终结符号串所组成的集合VT*的子集,即L(G) VT* 8.0型文法又叫短语文法,它所确定的语言称为0型语言。 1型文法,上下文敏感文法或上下文有关文法。 2型文法,上下文无关文法 3型文法线性文法、正则文法或正规文法 规范(最右)推导即任何一步α->β都是对α中的最右非终结符进行替换的,规范(最左)归约文法可唯一地确定一个语言 子树与短语:在句型所对应的语法树中,若某些符号按从左到右的顺序组成某棵子树的末端结点,那么由这些末端结点所组成的符号串是相对于子树根结点的短语。 原则上语法树有多少棵子树,就有多少个短语。

史上最全linux内核配置详解

对于每一个配置选项,用户可以回答"y"、"m"或"n"。其中"y"表示将相应特性的支持或设备驱动程序编译进内核;"m"表示将相应特性的支持或设备驱动程序编译成可加载模块,在需要时,可由系统或用户自行加入到内核中去;"n"表示内核不提供相应特性或驱动程序的支持。只有<>才能选择M 1. General setup(通用选项) [*]Prompt for development and/or incomplete code/drivers,设置界面中显示还在开发或者还没有完成的代码与驱动,最好选上,许多设备都需要它才能配置。 [ ]Cross-compiler tool prefix,交叉编译工具前缀,如果你要使用交叉编译工具的话输入相关前缀。默认不使用。嵌入式linux更不需要。 [ ]Local version - append to kernel release,自定义版本,也就是uname -r可以看到的版本,可以自行修改,没多大意义。 [ ]Automatically append version information to the version string,自动生成版本信息。这个选项会自动探测你的内核并且生成相应的版本,使之不会和原先的重复。这需要Perl的支持。由于在编译的命令make-kpkg 中我们会加入- –append-to-version 选项来生成自定义版本,所以这里选N。 Kernel compression mode (LZMA),选择压缩方式。 [ ]Support for paging of anonymous memory (swap),交换分区支持,也就是虚拟内存支持,嵌入式不需要。 [*]System V IPC,为进程提供通信机制,这将使系统中各进程间有交换信息与保持同步的能力。有些程序只有在选Y的情况下才能运行,所以不用考虑,这里一定要选。 [*]POSIX Message Queues,这是POSIX的消息队列,它同样是一种IPC(进程间通讯)。建议你最好将它选上。 [*]BSD Process Accounting,允许进程访问内核,将账户信息写入文件中,主要包括进程的创建时间/创建者/内存占用等信息。可以选上,无所谓。 [*]BSD Process Accounting version 3 file format,选用的话统计信息将会以新的格式(V3)写入,注意这个格式和以前的v0/v1/v2 格式不兼容,选不选无所谓。 [ ]Export task/process statistics through netlink (EXPERIMENTAL),通过通用的网络输出工作/进程的相应数据,和BSD不同的是,这些数据在进程运行的时候就可以通过相关命令访问。和BSD类似,数据将在进程结束时送入用户空间。如果不清楚,选N(实验阶段功能,下同)。 [ ]Auditing support,审计功能,某些内核模块需要它(SELINUX),如果不知道,不用选。 [ ]RCU Subsystem,一个高性能的锁机制RCU 子系统,不懂不了解,按默认就行。 [ ]Kernel .config support,将.config配置信息保存在内核中,选上它及它的子项使得其它用户能从/proc/ config.gz中得到内核的配置,选上,重新配置内核时可以利用已有配置Enable access to .config through /proc/config.gz,上一项的子项,可以通过/proc/ config.gz访问.config配置,上一个选的话,建议选上。 (16)Kernel log buffer size (16 => 64KB, 17 => 128KB) ,内核日志缓存的大小,使用默认值即可。12 => 4 KB,13 => 8 KB,14 => 16 KB单处理器,15 => 32 KB多处理器,16 => 64 KB,17 => 128 KB。 [ ]Control Group support(有子项),使用默认即可,不清楚可以不选。 Example debug cgroup subsystem,cgroup子系统调试例子 Namespace cgroup subsystem,cgroup子系统命名空间 Device controller for cgroups,cgroups设备控制器

简析linux内核的内核执行流程图

简析linux核的执行流程 ----从bootsect.s到main.c(核版本0.11)Linux启动的第一阶段(从开机到main.c) 3个任务: A、启动BIOS,准备实模式下的中断向量表和中断服务程序。 B、从启动盘加载操作系统程序到存。 C、为执行32的main函数做过渡准备。 存变化如下: ①、0xFE000到0xFFFFF是BIOS启动块,其中上电后第一条指令在0xFFFF0。 ②、而后0x00000到0x003FF总共1KB存放中断向量表,而接下去的地址到0x004FF共256B存放BIOS数据,从0x0E05B 开始的约8KB的存中存放中断服务程序。 ③、利用BIOS中断0x19h把硬盘的第一扇区bootsect.s的代码加载到存中,即0x07c00处,后转到该处执行。 ④、将bootsect.s的代码复制到0x90000处。 ⑤、利用中断0x13h将setup.s程序加载到存0x90200处。 ⑥、再将剩余的约240个扇区的容加载到0x10000~0x2EFFF 处。 ⑦、开始转到setup.s处执行,第一件事就利用BIOS提供的中断服务程序从设备上获取核运行的所需系统数据并存在0x90000的地址处,这时将原来bootsect.s的代码覆盖得只剩2Byte的空间。

⑧、关中断并将系统代码复制到0x00000处,将原来放在这里的中断向量表与BIOS数据区覆盖掉,地址围是 0x00000~0x1EFFF。同时制作两表与两寄存器。 ⑨开地址线A20,寻址空间达到4GB,后对8259重新编程,改变中断号。 ⑩、转到head.s(大小是25K+184B)执行,执行该程序完后是这样的: 0x00000~0x04FFF:页目录与4个页表,每一项是4KB,共20KB;0x05000~0x05400:共1KB的空间是软盘缓冲区; 0x05401~0x054b8:共184B没用; 0x054b9~0x05cb8:共2KB的空间存中断描述符表; 0x05cb9~0x064b8:共2KB的空间存全局描述符表; 之后就是main函数的代码了! 第二阶段、从main.c函数到系统准备完毕阶段。 第一步:创建进程0,并让进程0具备在32位保护模式下载主机中的运算能力。流程是: 复制根设备和硬盘参数表(main.c中的102、110、111行) 物理存规划格局(main.c的112行~126行,其中有 rd_init函数定义在kernel/ramdisk.c中,此函数用于虚拟盘初始化;而mem_init函数是用于存管理结构初始化,定义在mem/memory.c中,该函数页面使用

linux内核配置模块编译安装

Linux内核配置编译和加载 Linux内核模块 Linux内核结构非常庞大,包含的组件也非常多,想要把我们需要的部分添加到内核中,有两个方法:直接编译进内核和模块机制 由于直接编译进内核有两个缺点,一是生成的内核过大,二是每次修改内核中功能,就必须重新编译内核,浪费时间。因此我们一般采用模块机制,模块本身不被编译进内核映像,只有在加载之后才会成为内核的一部分,方便了修改调试,节省了编译时间。 配置内核 (1)在drivers目录下创建hello目录存放hello.c源文件 (2)在hello目录下新建Makefile文件和Kconfig文件 Makefile文件内容: obj-y += hello.o //要将hello.c编译得到的hello.o连接进内核 Kconfig文件内容: 允许编译成模块,因此使用了tristate (3)在hello目录的上级目录的Kconfig文件中增加关于新源代码对应项目的编译配置选项 修改即driver目录下的Kconfig文件,添加

source "drivers/hello/Kconfig" //使hello目录下的Kconfig起作用 (4)在hello目录的上级目录的Makefile文件中增加对新源代码的编译条目 修改driver目录下的Makefile文件,添加 obj-$(CONFIG_HELLO_FOR_TEST) += hello/ //使能够被编译命令作用到 (5)命令行输入“make menuconfig”,找到driver device,选择select,发现test menu 已经在配置菜单界面显示出来 (6)选择test menu进入具体的配置,可以选择Y/N/M,这里我选择编译为M,即模块化 (7)保存退出后出现 (8)进入kernels目录中使用“ls -a”查看隐藏文件,发现多出.config隐藏文件,查看.config 文件

Linux内核驱动模块编写概览-ioctl,class_create,device_create

如果你对内核驱动模块一无所知,请先学习内核驱动模块的基础知识。 如果你已经入门了内核驱动模块,但是仍感觉有些模糊,不能从整体来了解一个内核驱动模块的结构,请赏读一下这篇拙文。 如果你已经从事内核模块编程N年,并且道行高深,也请不吝赐教一下文中的疏漏错误。 本文中我将实现一个简单的Linux字符设备,旨在大致勾勒出linux内核模块的编写方法的轮廓。其中重点介绍ioctl的用途。 我把这个简单的Linux字符设备模块命名为hello_mod. 设备类型名为hello_cl ass 设备名为hello 该设备是一个虚拟设备,模块加载时会在/sys/class/中创建名为hello_class 的逻辑设备,在/dev/中创建hello的物理设备文件。模块名为hello_mod,可接受输入字符串数据(长度小于128),处理该输入字符串之后可向外输出字符串。并且可以接受ioctl()函数控制内部处理字符串的方式。 例如: a.通过write函数写入“Tom”,通过ioctl函数设置langtype=chinese,通过read函数读出的数据将会是“你好!Tom/n” b.通过write函数写入“Tom”,通过ioctl函数设置langtype=english,通过read函数读出的数据将会是“hello!Tom/n” c.通过write函数写入“Tom”,通过ioctl函数设置langtype=pinyin,通过read函数读出的数据将会是“ni hao!Tom/n” 一般的内核模块中不会负责设备类别和节点的创建,我们在编译完之后会得到.o或者.k o文件,然后insmod之后需要mk nod来创建相应文件,这个简单的例子 中我们让驱动模块加载时负责自动创建设备类别和设备文件。这个功能有两个步骤, 1)创建设备类别文件class_cr eate(); 2)创建设备文件dev ice_create(); 关于这两个函数的使用方法请参阅其他资料。 linux设备驱动的编写相对wi ndows编程来说更容易理解一点因为不需要处理IR P,应用层函数和内核函数的关联方式浅显易懂。 比如当应曾函数对我的设备调用了open()函数,而最终这个应用层函数会调用我的设备中的自定义open()函数,这个函数要怎么写呢, 我在我的设备中定义的函数名是hello_mod_open,注意函数名是可以随意定义,但是函数签名是要符合内核要求的,具体的定义是怎么样请看 static int hello_mod_open(struct inode *, struct file *); 这样就定义了内核中的open函数,这只是定义还需要与我们自己的模块关联起来,这就要用到一个结构 struct file_operations 这个结构里面的成员是对应于设备操作的各种函数的指针。 我在设备中用到了这些函数所以就如下定义,注意下面的写法不是标准ANSI C的语法,而是GNU扩展语法。 struct file_operations hello_mod_fops = { .owner = THIS_MODULE, .open = hello_mod_open,

Linux内核与跟文件系统的关系

Linux内核与根文件系统的关系 开篇题外话:对于Linux初学者来说,这是一个很纠结的问题,但这也是一个很关键的问题!一语破天机:“尽管内核是Linux 的核心,但文件却是用户与操作系统交互所采用的主要工具。这对Linux 来说尤其如此,这是因为在UNIX 传统中,它使用文件I/O 机制管理硬件 设备和数据文件。” 一.什么是文件系统 文件系统指文件存在的物理空间,linux系统中每个分区都是一个文件系统,都有自己的目 录层次结构。 Linux文件系统中的文件是数据的集合,文件系统不仅包含着文件中的数据而且还有文件系统的结构,所有Linux 用户和程序看到的文件、目录、软连接及文件保护信息等都存储在其 中。这种机制有利于用户和操作系统的交互。 每个实际文件系统从操作系统和系统服务中分离出来,它们之间通过一个接口层:虚拟文件系统或VFS来通讯。VFS使得Linux可以支持多个不同的文件系统,每个表示一个VFS 的通用接口。由于软件将Linux 文件系统的所有细节进行了转换,所以Linux核心的其它部分及系统中运行的程序将看到统一的文件系统。Linux 的虚拟文件系统允许用户同时能透明地安装 许多不同的文件系统。 在Linux文件系统中,EXT2文件系统、虚拟文件系统、/proc文件系统是三个具有代表性的 文件系统。 二.什么是根文件系统 根文件系统首先是一种文件系统,该文件系统不仅具有普通文件系统的存储数据文件的功能,但是相对于普通的文件系统,它的特殊之处在于,它是内核启动时所挂载(mount)的第一个文件系统,内核代码的映像文件保存在根文件系统中,系统引导启动程序会在根文件系统挂载之后从中把一些初始化脚本(如rcS,inittab)和服务加载到内存中去运行。我们要明白文件系统和内核是完全独立的两个部分。在嵌入式中移植的内核下载到开发板上,是没有办法真正的启动Linux操作系统的,会出现无法加载文件系统的错误。 那么根文件系统在系统启动中到底是什么时候挂载的呢?先将/dev/ram0挂载,而后执行/linuxrc.等其执行完后。切换根目录,再挂载具体的根文件系统.根文件系统执行完之后,也就是到了Start_kernel()函数的最后,执行init的进程,也就第一个用户进程。对系统进行各 种初始化的操作。 根文件系统之所以在前面加一个”根“,说明它是加载其它文件系统的”根“,既然是根的话,那么如果没有这个根,其它的文件系统也就没有办法进行加载的。它包含系统引导和使其他文件系统得以挂载(mount)所必要的文件。根文件系统包括Linux启动时所必须的目录和关键性的文件,例如Linux启动时都需要有init目录下的相关文件,在Linux挂载分区时Linux 一定会找/etc/fstab这个挂载文件等,根文件系统中还包括了许多的应用程序bin目录等,任何包括这些Linux 系统启动所必须的文件都可以成为根文件系统。Linux启动时,第一个必须挂载的是根文件系统;若系统不能从指定设备上挂载根文件系统,则系统会出错而退出启动。成功之后可以自动或手动挂载其他的文件系统。因此,一个系统中可以同时存在不同的文件系统。在Linux 中将一个文件系统与一个存储设备关联起来的过程称为挂载(mount)。使用mount 命令将一个文件系统附着到当前文件系统层次结构中(根)。在执行挂装时,要提供文件系统类型、文件系统和一个挂装点。根文件系统被挂载到根目录下“/”上后,在根目录下就有根文件系统的各个目录,文件:/bin /sbin /mnt等,再将其他分区挂接到/mnt 目录上,/mnt目录下就有这个分区的各个目录,文件。

实验2.3_内核模块_实验报告

<内核模块>实验报告 题目: 内核模块实验 1、实验目的 模块是Linux系统的一种特有机制,可用以动态扩展操作系统内核功能。编写实现某些特定功能的模块,将其作为内核的一部分在管态下运行。本实验通过内核模块编程在/porc文件系统中实现系统时钟的读操作接口。 2、实验内容 设计并构建一个在/proc文件系统中的内核模块clock,支持read()操作,read()返回值为一字符串,其中包块一个空格分开的两个子串,分别代表https://www.sodocs.net/doc/e44493163.html,_sec和https://www.sodocs.net/doc/e44493163.html,_usec。 3、实验原理 Linux模块是一些可以作为独立程序来编译的函数和数据类型的集合。在装载这些模块时,将它的代码链接到内核中。Linux模块可以在内核启动时装载,也可以在内核运行的过程中装载。如果在模块装载之前就调用了动态模块的一个函数,那么这次调用将会失败。如果这个模块已被加载,那么内核就可以使用系统调用,并将其传递到模块中的相应函数。 4、实验步骤 编写内核模块 文件中主要包含init_module(),cleanup_module(),proc_read_clock()三个函数。其中init_module(),cleanup_module()负责将模块从系统中加载或卸载,以及增加或删除模块在/proc中的入口。read_func()负责产生/proc/clock被读时的动作。 内核编译部分过程:

过程持续较长时间. ●编译内核模块Makefile文件 Makefile CC=gcc MODCFLAGS := -Wall -D__KERNEL__ -DMODULE –DLINUX clock.o :clock.c /usr/include/linux//version.h $(CC) $(MODCFLAGS) –c clock.c echo insmod clock.o to turn it on echo rmmod clock to turn ig off echo 编译完成之后生成clock.o模块文件。 注:此参考makefile文件包含错误, 于是从网上寻找相关教程自行修改得到合适的Makefile文件 ●内核模块源代码clock.c #define MODULE #define MODULE_VERSION “1.0” #define MODULE_NAME “clock” #include #include #include int proc_read_clock(char* page, char** start, off_t off,int count,int* eof,void* data) { int len; struct timeval xtime;

编译原理知识点总结 哈工程

第一章概论 1.什么是编译器?输入输出? 编译器是将一种语言翻译为另一种语言的计算机程序。 输入:源语言( source language) 编写的程序 输出:目标语言( target language ) 编写的程序。 2.汇编语言的优缺点 优点:汇编语言大大提高了编程的速度和准确度 缺点:编写起来也不容易,阅读和理解很难;而且汇编语言的编写严格依赖于特定的机器,所以为一台计算机编写的代码在应用于另一台计算机时必须完全重写。 3.什么是解释器?与编译器的区别? 解释程序是如同编译器的一种语言翻译程序。 与编译器的区别:它立即执行源程序而不是生成在翻译完成之后才执行的目标代码。 4.乔姆斯基分类结构有几种文法?名称?相互关系? 4种 名称: 0型无限制文法 1型上下文相关文法 2型上下文无关文法 3型正则文法 相互关系:其中的每一个都是其前者的专门化。 5.什么是扫描器?扫描器的功能是什么? 扫描器就是语法分析程序。 功能:依据词法规则,分析由字符组成的源程序,把它分割为一个一个具有独立意义的最小语法单位,即单词。 6.什么是编辑器?IDE中编辑器的新功能 编译器通常接受由任何生成标准文件(例如ASCII 文件)的编辑器编写的源程序。 IDE 中编辑器的新功能:尽管编辑器仍然生成标准文件,但会转向正被讨论的程序设计语言的格式或结构。这样的编辑器称为基于结构的,且它早已包括了编译器的某些操作;因此,程序员就会在程序的编写时而不是在编译时就得知错误了。从编辑器中也可调用编译器以及与它共用的程序,这样程序员无需离开编辑器就可执行程序。

7.什么是调试器,与编译器的关系 调试程序是可在被编译了的程序中判定执行错误的程序。 运行一个带有调试程序的程序与直接执行不同,这是因为调试程序保存着所有的或大多数源代码信息(诸如行数、变量名和过程)。它还可以在预先指定的位置(称为断点)暂停执行,并提供有关已调用的函数以及变量的当前值的信息。为了执行这些函数,编译器必须为调试程序提供恰当的符号信息。 8.编译器有哪几个功能模块?各模块的功能及输入输出 目标代码

linux2.6内核的编译步骤及模块的动态加载-内核源码学习-linux论坛

[原创]linux2.6内核的编译步骤及模块的动态加载-内核源码 学习-linux论坛 05年本科毕业设计做的是Linux下驱动的剖析,当时就买了一本《Linux设备驱动程序(第二版)》,但是没有实现将最简单的helloworld程 序编译成模块,加载到kernel里。不过,现在自己确实打算做一款芯片的Linux的驱动,因此,又开始看了《Linux设备驱动程序》这本书,不过已 经是第三版了。第二版讲的是2.4的内核,第三版讲的是2.6的内核。两个内核版本之间关于编译内核以及加载模块的方法都有所变化。本文是基于2.6的内核,也建议各位可以先看一下《Linux内核设计与实现(第二版)》作为一个基础知识的铺垫。当然,从实践角度来看,只要按着以下的步骤去做也应该可以实现成功编译内核及加载模块。个人用的Linux版本为:Debian GNU/Linux,内核版本为:2.6.20-1-686.第一步,下载Linux内核的源代码,即构建LDD3(Linux Device Drivers 3rd)上面所说的内核树。 如过安装的Linux系统中已经自带了源代码的话,应该在/usr/src目录下。如果该目录为空的话,则需要自己手动下载源代码。下载代码的方法和链接很多,也可以在CU上通过

https://www.sodocs.net/doc/e44493163.html,/search/?key=&;q=kernel&a mp;frmid=53去下载。不过,下载的内核版本最好和所运行的Linux系统的内核版本一致。当然,也可以比Linux系统内核的版本低,但高的话应该不行(个人尚未实践)。 Debian下可以很方便的通过Debian源下载: 首先查找一下可下载的内核源代码: # apt-cache search linux-source 其中显示的有:linux-source-2.6.20,没有和我的内核版本完全匹配,不过也没关系,直接下载就可以了: # apt-get install linux-source-2.6.20 下载完成后,安装在/usr/src下,文件名为: linux-source-2.6.20.tar.bz2,是一个压缩包,解压缩既可以得到整个内核的源代码: # tar jxvf linux-source-2.6.20.tar.bz2

相关主题