Linux内核设计与实现 第五章 系统调用

在现代操作系统中,内核提供了用户进程与内核进行交互的一组接口。这些接口让应用程序受限地访问硬件设备,提供了创建新进程并与已有进程进行通信的机制,也提供了申请操作系统其他资源的能力。

5.1内核通信

系统调用是除异常和陷入外,用户空间访问内核的唯一合法入口。
系统调用为用户空间提供了一种硬件的抽象接口。硬件抽象接口:是对众多的硬件,抽取出它们的共同的、本质性的特征,而舍弃其非本质的特征。达到用户程序可以轻松的使用硬件,不用去看如不同磁盘的不同使用手册。
系统调用的形式访问硬件,内核可以对要进行的访问进行裁决,保证了系统的稳定和安全。虚拟化技术才可能实现

学生:尊敬的教授,什么是虚拟化?
教授:想象我们有一个桃子。
学生:桃子?(不可思议)
教授:是的,一个桃子,我们称之为【物理】(physical)桃子。但是有很多想吃这个桃子的人,我们希望向每个想吃的人提供一个属于他的桃子,这样才能皆大欢喜。我们把给每个人的桃子称为【虚拟】(virtual)桃子。我们通过某种方式,从这个物理桃子创造出许多虚拟地址桃子。重要的是,这种假象中,每个人看起来都有一个物理桃子,但实际上不是。

以最基本的计算机资源CPU为例,假设一个计算机只有一个CPU(现代计算机一般拥有2个、4个或者更多CPU),虚拟化要做的就是将这个CPU虚拟成多个虚拟CPU并分配给每一个进程使用,因此每个应用程序都以为自己在【独占CPU】(每个进程独享虚拟地址空间)。但实际上只有一个CPU。这样操作系统就创造了美丽的假象–虚拟化了CPU。

5.2API、POSIX、C库

在大多数Unix系统上,根据POSIX定义的API函数和系统调用之间有着直接关系。
C库实现了Unix系统的主要API,包括标准C库函数和系统调用接口。
关于Unix的接口设计有一句格言“提供机制而不是策略”。换句话说,Unix的系统调用抽象出了用于完成某种确定的目的的函数。至于这些函数怎么用完全不需要内核去关心。

5.3系统调用

系统调用还会有一个long类型的返回值。返回负值表明错误,返回0表明成功。
系统调用出现错误时C库会把错误码写入errno全局变量。
内核必须提供系统调用所希望完成的功能,但是它完全可以按照自己预期的方式去实现,只要最后结果正确就行了。
所以的系统调用的定义都需要使用编译指令asmlinkage,通知编译器仅从栈中提取该函数的参数。如:asmlinkage long sys_getpid(void)注意用户空间的系统调用xxx,在内核都定义为sys_xxx。
为了保证32位和64位系统的兼容性,系统调用在用户空间返回int型的值,在内核空间返回long型的值

1)系统调用号

在这里插入图片描述

2)系统调用性能

Linux系统调用比其他许多操作系统执行得要快。
Linux很短的上下文切换时间是一个重要原因,进出内核都被优化得简洁高效。
另外一个原因是系统调用处理程序和每个系统调用本身也都非常简洁。

5.4系统调用处理程序

用户空间的程序无法直接执行内核代码。它们不能直接调用内核空间中的函数,因为内核驻留在受保护的地址空间上。
应用程序应该以某种方式通知系统,告诉内核自己需要执行一个系统调用,希望系统切换到内核态,这样内核就可以代表应用程序在内核空间执行系统调用。
通知内核的机制:软中断。
在x86系统上预定义的软件中断是中断号128.通过int $0x80指令触发中断。这条指令触发一个异常,系统切换到内核态并执行低128号异常处理程序,而该处理程序是系统调用处理程序system_call()。

1)指定恰当的系统调用号

在x86上,系统调用号是通过eax寄存器传递给内核的。陷入内核之前,用户空间就先把系统调用对应的系统调用号放入eax中,这样system_call()一旦运行、,就可以从eax中得到数据。有了调用号就可以用它查询系统调用表。

2)参数传递

最简单的办法是通过寄存器

5.5系统调用的实现

给Linux添加一个新的系统调用是件容易的工作。怎样设计和实现一个系统调用是难题所在。

1)实现系统调用

第一决定系统调用的用途,Linux不提倡一个系统调用通过传递不同的参数值来选择完成不同的工作。
第二确定新系统调用的参数,返回值和错误码。
第三考虑是否对函数做了不必要的限制,系统调用号越通用越好。
第四考虑系统调用的可移植性,对机器的字节长度和字节序做假设。

2)参数验证

系统调用必须保证参数不但合法,而且正确。不然就有漏洞让进程哄骗内核去访问哪些它无权限访问的资源。
将内核空间数据块复制到用户空间:copy_to_user(进程空间中的目的内存地址,内核空间内的源地址,需要拷贝的数据长度)
从用户空间复制数据块到内核空间:copy_form_user(进程空间中的源内存地址,内核空间内的目的地址,需要拷贝的数据长度)
注意,copy_to_user()和copy_form_user()都有可能引起阻塞。当包含用户数据的也被换出到硬盘上,没有在物理内存上的时候,进程就会休眠,直到缺页处理程序将该页从硬盘重新换回物理内存。

5.6系统调用上下文

1)绑定一个系统调用的最后步骤

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

2)从用户空间访问系统调用

通常是调用C库函数,在由库函数实际调用系统调用。
C库函数不支持直接调用系统调用。
不靠C库函数间接调用系统调用的话,可以直接调用此系统调用的宏的形式进行直接调用系统调用。
例如:

#define NR_open 5
_syscall3(long,open,const char* ,filename,int,flags,int,mode)//表示系统调用open的返回值类型long,open的参数1int flags,open的参数2int mode

_syscall3()的第一个参数系统调用的返回值类型,第二个参数是系统调用名称,后续参数是顺序排列的参数类型和名称

3)为什么不通过系统调用的方式实现

新系统调用增添频率很低,也反映出Linux是一个相对较为稳定并且功能已经较为完善的操作系统。
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值