操作系统系统调用实验

操作系统实验一(记录)

思路:
1.实验目标及要求
2.实验结果演示
3.实验流程
4.遇到的问题及解决
5.实验思考

一、实验目标

题目5:把指定文件变为长度为0的空文件

要求:

添加系统调用:

\1. 程序能正常演示,功能正确实现;

\2. 熟悉系统调用详细处理过程;

\3. 系统调用添加过程,包括要修改的文件、修改的内容等;

\4. 能解释所有添加的代码;

\5. 能详细分析解释代码中所用到的内核函数的源码。

二、实验结果演示

1.新建一个文本文件test.txt
img
2.如图,可以看见文件大小为22bytes

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-s15ZyFsX-1611652521505)(…/typora_tupian/QQ%E5%9B%BE%E7%89%8720201128110945.png)]

3.编译用户测试程序

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HGCs6pac-1611652521510)(…/typora_tupian/QQ%E5%9B%BE%E7%89%8720201128111502.png)]

4.编译测试程序
5.生成a.out文件

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nwN0iPDE-1611652521512)(…/typora_tupian/QQ%E5%9B%BE%E7%89%8720201128111619.png)]

6.运行测试程序:
7.查看结果:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-r8BPKMwW-1611652521515)(…/typora_tupian/QQ%E5%9B%BE%E7%89%8720201128111930.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LRZfrgUR-1611652521517)(…/typora_tupian/QQ%E5%9B%BE%E7%89%8720201128112108.png)]

文件长度变为0bytes,打开文件后无内容

实验成功!!

三、实验经过

1.系统调用过程

系统调用是啥?

我在用户态,想要实现一个在核心态下实现的功能,所以需要调用内核函数,进入内核态运行。

通过一条访管指令“int $0x80”来调用——产生访管中断——暂停当前进程——传入系统调用号——查找相应的服务例程的入口——进入核心态——执行函数调用

用户态到核心态的转变:

用户态——用户空间——Shell

核心态——内核空间——kernel

GUI–图形界面

Linux操作系统一般是通过软件中断从用户态切换到内核态

用户态到内核态的切换,其实就是一个进程通过系统调用到内核的一些接口。从而实现切换

系统调用是对接口的封装,接口是对内核操作的封装(接口相当于银行柜员,用户想要去银行处理业务,但是不能自己处理,需要银行柜员帮忙处理,系统调用相当于主管,我们要找哪个柜员需要通过主管)

先通过软件中断调用了0x80的这个编程异常,这个编程异常对应的是中断描述符表IDT中的第128项——也就是对应的系统门描述符。门描述符中含有一个预设的内核空间地址,它指向了系统调用处理程序:system_call()(别和系统调用服务程序混淆,这个程序在entry.S文件中用汇编语言编写)。走到这一步,我们应该画个图来理解。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nKdWl8C4-1611652521519)(…/typora_tupian/20181018173953114)]

现在我们就知道通过系统描述符中的内核空间来找到系统调用程序,从而进入到内核态。可很显然,所有的系统调用都会统一地转到这个地址,但Linux一共有319个系统调用都从这里进入内核后又该如何派发到它们到各自的服务程序去呢?

解决这个问题的方法很简单就是: 首先Linux为每个系统调用都进行了编号 (0—NR_syscall),同时在内核中保存了一张系统调用表 ,该表中保存了系统调用编号和其对应的服务例程,因此在系统调入通过系统门陷入内核前,需要把系统调用号一并传入内核。在x86上,这个传递动作是通过在执行int0x80前把调用号装入eax寄存器实现的。这样系统调用处理程序一旦运行,就可以从eax中得到数据,然后再去系统调用表中寻找相应服务例程了。为了方便理解,我把这些操作用图表示出来。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-g75DErXw-1611652521520)(…/typora_tupian/20181018183154271)]

除了需要传递系统调用号以外,许多系统调用还需要传递一些参数到内核,比如sys_write(unsigned int fd, const char * buf, size_t count)调用就需要传递文件描述符fd、要写入的内容buf、以及写入字节数count等几个内容到内核。碰到这种情况,Linux会有6个寄存器可被用来传递这些参数:eax (存放系统调用号)、 ebxecxedxesiedi来存放这些额外的参数(以字母递增的顺序)。具体做法是在system_call( )中使用SAVE_ALL宏把这些寄存器的值保存在内核态堆栈中。

做好上述工作之后于,再执行系统调用处理程序,这样就从用户态切换到内核态。

因此:实验的思路就是1.先进入系统调用表,增加系统调用编号和其对应的服务例程,2.然后需要将想要调用的也就是刚刚写的系统调用号通过寄存器先传入内核 3.正常进行系统编译

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rWKsoye2-1611652521521)(…/typora_tupian/20180603145400866)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-y2NSspNM-1611652521521)(…/typora_tupian/1-7.png)]

总而言之:

在用户态:

1.写了一段代码,进行系统调用,假设调用号是333

2.执行这段代码的时候,到进行系统调用号的那一步时,将系统调用表和系统调用号对应的参数通过eax等寄存器存入内核

3.之后,软件中断调用了0x80这个编程异常,这个编程异常对应中断描述符表IDT中的第128项——也就是对应的系统门描述符。门描述符中含有一个预设的内核空间地址,指向系统调用处理程序

4.到系统调用处理程序后,因为所有的系统调用都会统一地转到这个地址,所以要查系统调用表(已经传入内核了),根据系统调用号进入对应的服务例程,执行系统调用。

5.调用后,返回中断处理程序,一步步返回到用户态,继续执行下面的代码。

2.实验环境

我分别在两台电脑上进行了实验

其一;

Ubantu64位:ubuntu-18.04.1-desktop-amd64.iso
待编译的内核:linux-4.15.0
虚拟机:WMware-15

其二:

Ubuntu:ubuntu-20.04.1-desktop-amd64.iso
待编译内核:Linux-5.4.0
虚拟机:WMware-15

tips:虚拟机建议配置磁盘空间不宜过小,内核编译过程中会产生较多的临时文件,若空间太小,可能在编译内核的时候发生磁盘空间不够的情况,甚至可能导致Ubuntu系统无法开机(我就发生了这样的错误,从网上搜寻安装虚拟机教程时,默认给20G的磁盘空间,自己装的时候要注意给多点),内存太小会影响编译速度,建议40GB以上,我给了80GB,内存给了3GB。

3.下载内核源码
1.从官网下载:https://www.kernel.org

但从官网下载内核源码太慢了,得将近5个小时,查询后用命令行去其清华镜像仓库下载内核源码。但效果仍然不好,建议直接在终端里使用apt-get下载

2.命令行中使用apt-get下载

输入命令:

sudo apt-get install linux-source-5.4.0

但是出现了以下问题:

解决措施如下:参考于网络


Ubuntu中,有时候运用sudo apt-get install 安装软件时,会出现一下的情况

E: Could not get lock /var/lib/dpkg/lock - open (11: Resource temporarily unavailable)
E: Unable to lock the administration directory (/var/lib/dpkg/), is another process using it?

在这个时候,主要是因为apt还在运行,此时的解决方案是

1、找到并且杀掉所有的apt-getapt进程

运行下面的命令来生成所有含有 apt 的进程列表,你可以使用psgrep命令并用管道组合来得到含有apt或者apt-get的进程。

ps -A | grep apt

找出所有的 apt 以及 apt-get 进程

$ sudo kill -9 processnumber
或者
$ sudo kill -SIGKILL processnumber
比如,下面命令中的9是 SIGKILL 的信号数,它会杀掉第一个 apt 进程
$ sudo kill -9 进程ID
或者
$ sudo kill -SIGKILL  进程ID

2、删除锁定文件

锁定的文件会阻止Linux系统中某些文件或者数据的访问,这个概念也存在于 Windows 或者其他的操作系统中。

一旦你运行了 apt-get 或者 apt 命令,锁定文件将会创建于 /var/lib/apt/lists//var/lib/dpkg//var/cache/apt/archives/ 中。

这有助于运行中的 apt-get 或者 apt 进程能够避免被其它需要使用相同文件的用户或者系统进程所打断。当该进程执行完毕后,锁定文件将会删除。当你没有看到 apt-get 或者 apt 进程的情况下在上面两个不同的文件夹中看到了锁定文件,这是因为进程由于某个原因被杀掉了,因此你需要删除锁定文件来避免该错误。

首先运行下面的命令来移除 /var/lib/dpkg/ 文件夹下的锁定文件:

$ sudo rm /var/lib/dpkg/lock

之后像下面这样强制重新配置软件包:

$ sudo dpkg --configure -a

也可以删除 /var/lib/apt/lists/ 以及缓存文件夹下的锁定文件:

$ sudo rm /var/lib/apt/lists/lock
$ sudo rm /var/cache/apt/archives/lock

接下来,更新你的软件包源列表:

$ sudo apt update
或者
$ sudo apt-get update

总结一下,对于 Ubuntu(以及它的衍生版)用户在使用 apt-get 或者 apt 也叫 aptitude 命令[7]时遇到的问题,我们已经用两种方法来解决了。


依据以上方法,可以解决apt-get的问题,大概30分钟左右,源代码可以下好。

4.解压缩内核源代码

文件下载好后:

image-20201106231328169

home目录下有充分的root权利,建议把它解压到home文件下,Linux里的压缩文件是以后缀tar.bz2结尾的,这里有六个文件,只有最后一个是需要的,其他的是因为使用apt-get下载会自动多下点别的,我不清楚是什么

我将文件解压到home/wjw下了

解压命令:

tar -xjf linux-source-5.4.0.tar.bz2 -C /home/wjw

关于解压命令:

例如:将y文件夹下的压缩包x.tar.bz2解压到z里

1.进入y文件夹
2.命令行:
tar -xjf x.tar.bz2 -C /z
5.linux添加系统调用号,修改系统调用表

在文件**linux/arch/x86/entry/syscalls/syscall_64.tbl**中

建议安装vscode来查看和修改文件

因为题目是修改长度为0,所以取名change0

在较新的内核版本下操作:

335 common change0  __x64_sys_change0
--------------------------------------------------------------------------------------
335							  	 系统调用号
common—————分为32/64/common		CPU处理位数 common就是两个都可以
change0							 取的系统调用名
__x64_sys_change0				 服务例程入口地址
image-20201128131516199

注意:在4的版本的linux下,不用加**_64,只要sys_change0**

在修改内核源码的时候,如这一步的操作,建议复制334的系统调用号再在此基础上修改,不然不知道内核中的间隔是空格还是Tab,比较容易格式出错。

6.申明系统调用服务例程原型

在文件**linux/includelinux/syscalls.h**中

在文件末尾添加:

asmlinkage long sys_change0(void *path, unsigned int length)
--------------------------------------------------------------------------------------
asmlinkage:
一个限定词,通知编译器仅仅从堆栈提取该函数的参数,而不是从寄存器中,因为从执行服务例程之前系统就已经将通过寄存器传递过来的参数值压入内核堆栈了.
是和系统调用号一起传入的吗?对,参数通过ebx ecx rdx esi edi等寄存器传递,系统调用号和相关参数在内中断开始时放入内存。
对于系统调用服务例程,可以直接从 system_call 函数压入的堆栈中获得参数,对参数的修改也可以一直在堆栈中进行。在 system_call 函数退出后,用户应用可以直接从寄存器中获得被修改过的参数。
并不是所有的系统调用服务例程都有实际的内容,有一个服务例程 sys_ni_syscall 除了返回-ENOSYS外不做任何其他工作,在kernel/sys_ni.c 文件中定义。
long:
系统调用在用户态和内核态返回值不同,用户态返回int,内核态返回long
sys_change0:
系统调用服务例程原型,对应系统调用表中的
void *path:
要修改的指定文件地址,之所以用void*是因为方便地址传入不会发生错误
unsigned int length:
文件大小,之所以加unsigned,是因为该目录下的其他函数传入关于长度的长度时都采取的是unsigned int
注意:参数之间都是'', ''逗号后空一格。
7、实现系统调用服务例程

路径:linux/kernel/sys.c

为**change0编写服务例程sys_change0**

SYSCALL_DEFINE2(change0, void __user*, path, unsigned int, length)
{
	struct file *fp;						//创建一个指向文件的指针
	char * cu_any =(char*)kmalloc(length,GFP_KERNEL);//在内核开辟一个用户态文件地址一样大的空间
	copy_from_user(cu_any,path,length);		//将用户态文件地址字符串复制到内核态刚刚创建的空间里
	fp = filp_open(cu_any,O_TRUNC,0);	//在内核态以截断式打开该文件并用指针指向它
	filp_close(fp, NULL);					//关闭打开的文件
	kfree(cu_any);							//释放内核空间(文件地址字符串)
	return 0;
}
-------------------------------------------------------------------------------------
SYSCALL_DEFINE2:
宏,因为有两个参数,所以DEFINE后面是2

1.change0:
函数名,对应系统调用表中的

2.void __user*:
 __user宏表示此指针为用户空间对应进程的线性地址。(通过查进程的页表得到物理地址)
 __user宏简单告诉编译器(通过 noderef)不应该解除这个指针的引用(因为在当前地址空间中它是没有意义的)
 我的理解:现在是在内核空间,如果不加__user 就会默认是在内核空间中的地址。而 void __user* 说明这是一个用户空间的地址,不能直接进行拷贝等,要使用例如copy_from_user,copy_to_user等函数。
 
3.path:
文件用户空间的地址,之所以和void __user*分开是因为内核中的其他服务例程都是这样定义的。连起来的意思就是定义一个void*的指针,这个指针指向用户态,存放用户态的文件地址,不能直接进行拷贝要使用别的函数转换。

4.unsigned int:
同理,内核中其他的服务例程调用都是这样定义的

5.length:
文件地址字符串的长度

6.struct file *fp:
声明fp是指针,指向FILE类型的对象,也就是指向文件的指针。

7.char * cu_any =(char*)kmalloc(length,GFP_KERNEL);
定义了一个char *类型的cu_any,并为其开辟一个空间

其中:
kmalloc(length,GFP_KERNEL):
内核提供的内存分配函数,常用的以字节为单位分配。
函数原型:
void *kmalloc(size_t size, gfp_t flags);
size_t size:需要多少字节的内存
gfp_t flags:要分配的内存类型
这里传入指定文件长度,故用length
GFP_KERNEL:查询资料知道这是最一般使用的标志,意思是这个分配代表运行在内核空间。详细后面再说
总之:这里在内核开辟了和传入文件大小一致的内存空间

(char*)
因为定义的空间是char *类型,因此这里进行一个类型转换

8.copy_from_user(cu_any,path,length);
将用户态下文件复制并传入内核
函数原型:
unsigned long __copy_from_user (void * to, const void __user * from, unsigned long n);
void * to:目标地址,在内核空间中,因此我参数用的是新建立的cu_any
const void __user * from:源地址,在用户空间中.传入用户空间地址,void __user*, path,格式相符
unsigned long n:要复制的字节数。unsigned int, length,传入文件长度,这也是为什么声明系统调用原型要写这两个参数

9.fp = filp_open(cu_any,O_TRUNC,0777);
使fp指针指向以截断式的方法打开的文件
函数原型:
struct file *filp_open(const char *filename, int flags, umode_t mode)
const char *filename:要打开或创建文件的名称(包括路径部分,此时文件以从用户态复制到内核态,是存在cu_any里
int flags:参数文件的打开方式,其取值与标准库中的open相应参数类似,可以取O_CREAT,O_RDWR,O_RDONLY等,O_TRUNC代表截断式,若文件存在,则长度被截为0,属性不变,因此用来刚好
umode_t mode:创建文件时使用,设置创建文件的读写权限,0777代表可读可写可执行,其实一般写0。
该函数返回strcut file*结构指针,供后继函数操作使用,该返回值用IS_ERR()来检验其有效性。

10.filp_close(fp, NULL)
关闭fp指向的文件
函数原型:
int filp_close(struct file *filp, fl_owner_t id)
struct file *filp:filp_open()函数的返回值,也就是把打开的文件关上
fl_owner_t id:根据查询资料,表示文件的关闭结果,值0或NULL即代表成功关闭

11.kfree(cu_any)
释放内核中的内存
8.内核函数介绍:
1.关于kmalloc

https://www.kernel.org/doc/htmldocs/kernel-api/API-kmalloc.html

https://blog.csdn.net/macrossdzh/article/details/5627274

对于常用的以字节为单位的分配来说,内核提供的函数时kmalloc(),其与用户空间的malloc()非常相似,但它多了一个flags函数。kmalloc()函数是一个简单的接口,用它可以获得以字节为单位的一块内存。
kmalloc()在#include <linux/slab.h> 中声明

void *kmalloc(size_t size, gfp_t flags);

该函数返回一个void *的指针,指向内存块,所分配的内存在物理上是连续的。
出错时返回NULL,表示内存不够。
size_t size:需要多少字节的内存
gfp_t flags:要分配的内存类型
该flags参数可能是以下之一:
GFP_KERNEL-内核内存的正常分配. 可能睡眠
GFP_USER-用来为用户空间页来分配内存; 它可能睡眠.
GFP_ATOMIC-用来从中断处理和进程上下文之外的其他代码中分配内存. 从不睡眠.
GFP_HIGHUSER -如同 GFP_USER, 但是从高端内存分配, 如果有. 高端内存在下一个子节描述.。
GFP_NOIO -尝试获取内存时根本不执行任何I / O操作。
GFP_NOFS -尝试获取内存时请勿进行任何fs调用。
GFP_NOWAIT -分配不会睡眠。
__GFP_THISNODE -仅分配节点本地内存。
GFP_DMA-适用于DMA的分配。仅应用于kmalloc高速缓存
等等
关于GFP_KERNEL:
最一般使用的标志, GFP_KERNEL, 意思是这个分配(内部最终通过调用 __get_free_pages 来进行, 它是 GFP_ 前缀的来源) 代表运行在内核空间的进程而进行的. 换句话说, 这意味着调用函数是代表一个进程在执行一个系统调用. 使用 GFP_KENRL 意味着 kmalloc 能够使当前进程在少内存的情况下睡眠来等待一页. 一个使用 GFP_KERNEL 来分配内存的函数必须, 因此, 是可重入的并且不能在原子上下文中运行. 当当前进程睡眠, 内核采取正确的动作来定位一些空闲内存, 或者通过刷新缓存到磁盘或者交换出去一个用户进程的内存.

2.关于copy_from_user(cu_any,path,length)

https://www.kernel.org/doc/htmldocs/kernel-api/API—copy-from-user.html

https://blog.csdn.net/liuhangtiant/article/details/85227125

https://www.cnblogs.com/LxwEmbedded/p/4532924.html

__copy_from_user —从用户空间复制数据块,而无需进行检查。

unsigned long __copy_from_user (void * to, const void __user * from, unsigned long n);

完整源码:
static inline unsigned long __must_check copy_from_user(void *to, const void __user *from, unsigned long n)
{
	unsigned long res = n;
	kasan_check_write(to, n);

	if (access_ok(VERIFY_READ, from, n)) {
		check_object_size(to, n, false);
		res = __arch_copy_from_user(to, from, n);
	}
	if (unlikely(res))
		memset(to + (n - res), 0, res);
	return res;
	
}
其中:
void * to:目标地址,在内核空间中。
const void __user * from:源地址,在用户空间中
unsigned long n:要复制的字节数。
说明:
copy_from_user函数的目的是从用户空间拷贝数据到内核空间,失败返回没有被拷贝的字节数,成功返回0.
该函数同时需要进行内核关于异常出错的处理.从用户空间拷贝数据到内核中时必须非常小心,如果用户空间的数据地址是个非法的地址,或是超出用户空间的范围,或是那些地址还没有被映射到,都可能对内核产生很大的影响,如oops,或者被造成系统安全的影响.所以copy_from_user函数的功能就不只是从用户空间拷贝数据那样简单了,它还要做一些指针检查以及处理这些问题的方法.

3.关于filp_open

https://elixir.bootlin.com/linux/latest/source/fs/open.c#L1127

https://www.cnblogs.com/leaven/archive/2010/05/26/1744274.html

struct file *filp_open(const char *filename, int flags, umode_t mode)
{
	struct filename *name = getname_kernel(filename);
	struct file *file = ERR_CAST(name);
	
	if (!IS_ERR(name)) {
		file = file_open_name(name, flags, mode);
		putname(name);
	}
	return file;
}
const char *filename:要打开或创建文件的名称(包括路径部分
int flags:参数文件的打开方式,其取值与标准库中的open相应参数类似,可以取O_CREAT,O_RDWR等
umode_t mode:创建文件时使用,设置创建文件的读写权限

关于O_TRUNC:
int _open(char *pathname,int access)为读或写打开一个文件,
  按后按access来确定是读文件还是写文件,access值见下表
  ┌──────┬────────────────────┐
  │access值  │意义                  │
  ├──────┼────────────────────┤
  │O_RDONLY │读文件 │
  │O_WRONLY │写文件 │
  │O_RDWR │即读也写 │
  │O_NOINHERIT │若文件没有传递给子程序,则被包含 │
  │O_DENYALL │只允许当前处理必须存取的文件 │
  │O_DENYWRITE │只允许从任何其它打开的文件读 │

  │O_DENYREAD │只允许从任何其它打开的文件写 │
  │O_DENYNONE │允许其它共享打开的文件 │
  └──────┴────────────────────┘
  int open(char *pathname,int access[,int permiss])为读或写打开一个文件,
  按后按access来确定是读文件还是写文件,access值见下表
  ┌────┬────────────────────┐
  │access值│意义 │
  ├────┼────────────────────┤
  │O_RDONLY│读文件 │
  │O_WRONLY│写文件 │
  │O_RDWR │即读也写 │
  │O_NDELAY│没有使用;对UNIX系统兼容 │
  │O_APPEND│即读也写,但每次写总是在文件尾添加 │
  │O_CREAT │若文件存在,此标志无用;若不存在,建新文件 │
  │O_TRUNC │若文件存在,则长度被截为0,属性不变 │
  │O_EXCL │未用;对UNIX系统兼容 │
  │O_BINARY│此标志可显示地给出以二进制方式打开文件 │
  │O_TEXT │此标志可用于显示地给出以文本方式打开文件│
  └────┴────────────────────┘
  permiss为文件属性,可为以下值:
  S_IWRITE允许写 S_IREAD允许读 S_IREAD|S_IWRITE允许读、写
  int creat(char *filename,int permiss) 建立一个新文件filename,并设定读写性。permiss为文件读写性,可以为以下值S_IWRITE允许写 S_IREAD允许读 S_IREAD|S_IWRITE允许读、写
  int _creat(char *filename,int attrib) 建立一个新文件filename,并设定文件属性。attrib为文件属性,可以为以下值FA_RDONLY只读 FA_HIDDEN隐藏 FA_SYSTEM系统
  int creatnew(char *filenamt,int attrib) 建立一个新文件filename,并设定文件属性。attrib为文件属性,可以为以下值FA_RDONLY只读 FA_HIDDEN隐藏 FA_SYSTEM系统

4.关于filp_closs()

https://www.cnblogs.com/chorm590/p/12565991.html

int filp_close(struct file *filp, fl_owner_t id)
{
	int retval = 0;

	if (!file_count(filp)) {
		printk(KERN_ERR "VFS: Close: file count is 0\n");
		return 0;
	}

	if (filp->f_op->flush)
		retval = filp->f_op->flush(filp, id);

	if (likely(!(filp->f_mode & FMODE_PATH))) {
		dnotify_flush(filp, id);
		locks_remove_posix(filp, id);
	}
	fput(filp);
	return retval;
}

参数1就是filp_open()函数的返回值。
参数2一般填0即可。
返回值表示这个文件的关闭结果,值0表示成功关闭。

5.关于kfree()

https://www.kernel.org/doc/htmldocs/kernel-api/API-kfree.html

void kfree(void kfree (	const void * objp) ;
如果objp为NULL,则不执行任何操作。
只能用来释放kmalloc()申请的动态空间。不然会导致内核崩溃,出现oops信息
9.重新编译内核
1.清除残留的.config文件

开始编译前要清理残留的.config和.o文件,每次编译内核都要清理一遍

可能会提醒安装ncurses包,所以建议先安装好再清理

命令:

sudo apt-get install libncurses5-dev

若安装出现问题,参考下载内核源码时的操作:

1:查找所有apt相关的进程,并用命令杀死。

命令:

ps afx|grep apt
sudo kill -9 2873
--------------------------------------------------------------------------------------
2873是进程号

2:删除锁定文件

sudo rm /var/lib/dpkg/lock
sudo dpkg --configure -a
sudo apt update

3:问题解决,执行apt install 命令成功

安装好包后,执行

make mrproper
2.配置内核

提前安装好相关的包

sudo apt-get install libncurses5-dev
--------------------------------------------------------------------------------------
若上一步没装没报错,这里应该会出错,所以建议提前装

要在root权限下make menuconfig

sudo make menuconfig

仍然出错了,如下:

image-20201107224902291

提示找不到flex

于是参考资料:


列一下Make Menu过程中遇到错误(Ubuntu18.04):

Q1:3.14.38的内核

root@simon-virtual-machine:/home/simon/FeiLing/src/linux-3.14.38# make menuconfig
*** Unable to find the ncurses libraries or the
*** required header files.
*** 'make menuconfig' requires the ncurses libraries.
***
*** Install ncurses (ncurses-devel) and try again.
***
/home/simon/FeiLing/src/linux-3.14.38/scripts/kconfig/Makefile:199: recipe for target 'scripts/kconfig/dochecklxdialog' failed
make[1]: *** [scripts/kconfig/dochecklxdialog] Error 1
Makefile:512: recipe for target 'menuconfig' failed
make: *** [menuconfig] Error 2

A1: sudo apt-get install ncurses-devel

Q2:linux-4.17.2内核

root@simon-virtual-machine:/home/simon/Src/linux-4.17.2# make menuconfig
YACC scripts/kconfig/zconf.tab.c
/bin/sh: 1: bison: not found
scripts/Makefile.lib:196: recipe for target 'scripts/kconfig/zconf.tab.c' failed
make[1]: *** [scripts/kconfig/zconf.tab.c] Error 127
Makefile:528: recipe for target 'menuconfig' failed
make: *** [menuconfig] Error 2

A2:apt-get install bison -y

Q3:

root@simon-virtual-machine:/home/simon/Src/linux-4.17.2# make menuconfig
YACC scripts/kconfig/zconf.tab.c
LEX scripts/kconfig/zconf.lex.c
/bin/sh: 1: flex: not found
scripts/Makefile.lib:188: recipe for target 'scripts/kconfig/zconf.lex.c' failed
make[1]: *** [scripts/kconfig/zconf.lex.c] Error 127
Makefile:528: recipe for target

A3: sudo apt-get install flex

输入命令:

sudo apt-get install flex

安装好后继续配置内核

但是又报错了,如下:

image-20201107231231745

于是输入命令

sudo apt-get install bison -y

继续配置内核

出现如下框:

image-20201107231348545

按照书上选择

image-20201107231427220

save后

image-20201107231452815

选择ok

但:

image-20201107231515929

查找资料:

应该是没有问题的

https://blog.csdn.net/kang___xi/article/details/80556633

这部分不理解是什么意思,查找资料如下:

https://blog.csdn.net/wangyachao0803/article/details/81380889

文章太长,附上链接

后面再次尝试没有出现如上错误。

3.编译内核

先安装好openssl

sudo apt-get install libssl-dev

开始编译:

make -j8
--------------------------------------------------------------------------------------
这里参考虚拟机给的是几个内核,就j后面写几

大约30分钟后编译完成,未报错

(之前不断尝试修改内核源码的时候报了很多错,但并未记录下来)

4.编译模块
meke modules

之前编译模块时出现了如下错误:

image-20201127132841564

查询资料得知:

输入命令:

sudo make CONFIG_DEBUG_SECTTION_MISMATH=y

会重新编译一次内核,这次非常慢!!

编译好后再编译模块,无问题。

5.安装内核

安装模块:

make modules_install

安装内核:

make install
6.配置引导程序
update-grub2
7.重启系统
reboot

新内核开机后;

uname-a

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZFRG2Rzf-1611652521523)(…/typora_tupian/image-20201108110136519.png)]

至此,尚未修改系统调用的内核编译完成。

10.编写用户态程序测试新系统调用

具体步骤参考:实验结果演示 部分

测试程序如下:

#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/syscall.h>
#define __NR_mysyscall 333
int main()  {
    const char * path = "/home/weiwei/test.txt";
    syscall(__NR_mysyscall,(void *)path,strlen(path));
}
-------------------------------------------------------------------------------------
这是在旧版本的linux编写的测试,新版本应将系统调用号改为335
11.思考

其实在做的时候,我用更高版本的linux系统实验时,执行测试程序时,被killed了,内核编译没有问题,后续我再去研究研究,暂时得先做别的实验了。

  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 6
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值