系统调用
目录
系统调用
API, Posix和C库
System Call
如何自己搓一个系统调用
Syscall的优缺点
Reference
我们知道,操作系统为底层的硬件架构与上层的应用层进程之间提供了一个抽象。那么这个抽象是如何体现的呢?之前我们谈到过操作系统实际上就是代之应用软件向底层的硬件提出需求。这种代理方式就是向用户层提供一个系统调用。(System Call
)这样搞,我们可以有效的防止一个进程随意的使用硬件,侵占其他进程的找资源和破坏系统整体的运行。
API, Posix和C库
我们一般在用户层次使用API接口完成一次操作。我们列举一个例子:
int printf(const char* format, ...);
这个大家都很熟悉了:就是向标准输出输出格式化的字符串。那么,作为C库的一个标准函数,我们实际上在Linux系统下将需求转发到操作系统提供的write接口,这个write接口将会发起一个系统调用表明自己想要请求的服务。
问题来了,write是可移植的吗?显然!如果我们的上层接口不统一,我们就不得不为了移植我们的代码而做出大量的代码更改以求适应接口。所以,我们就有了Posix标准,所有的操作系统都应该实现这些标准,这样的话代码就会是可移植的(至少在软件层次可以移植!)
你可以看到系统调用的层次是如何的。
System Call
上面说到我们的printf走到write这个地方,经过一定的处理我们准备好了字符和向何处设备写入,我们就需要发起一次系统调用。系统调用就是应用层需要更加底层的服务,软件要向操作系统打招呼。凡是涉及到跟底层硬件的交互都需要系统调用完成:写文件啊(与磁盘交互),还是写内存(跟内存条交互),还是其他的跟操作系统自身的交互(访问操作系统的数据结构)
他可以完成所有明确的操作。比如说返回当前的pid等等。
系统调用因为在汇编文件中按照序号排布,所以从这个含意上,我们使用一个系统调用号的东西来标识一个系统调用(本质上是向寄存器扔进去一个offset,之后发起调用的时候按照排布的表找是何种系统调用)
你可以在arch/对应的架构/kernel/syscall_64.c
下看到系统调用号码!
我们发起系统调用之后,系统正确的找到了系统调用后需要执行的处理函数,举个例子,发起的system_call查看到我们的需求是write,他找到了处理这个系统调用的需求是sys_write函数,随后就会执行sys_write代之完成我们的操作后返回结果给用户上层,
如何自己搓一个系统调用
第一步呢,就是明确我们的系统调用要干什么吗??他需要什么参数?他需要返回什么?需要错误码吗?你如何保证你的实现是可以被移植的!事实上他们都需要考虑!
你如何检测这些参数是有效的,纵读Linux源码会发现一些参数的设置组合可能无效,你应该做何种处理?
你可以自行的查看诸如reboot syscall等是如何处理的。这里作为通用性的阐述不加以分析
实现结束之后,需要注册系统调用:在entry.S文件下,你需要排布一个系统调用,如法炮制!
.long sys_my_call /* ID__CNT */
然后在unistd文件下做出更改:
#define ___NR_my_call YOUR_ID_CNT
Syscall的优缺点
系统调用有它自己的优缺点,它的优点有:
-
系统调用很容易实现,而且很容易使用。
-
另一方面系统调用的性能很好
缺点也很明显:
-
你需要一个系统调用号,而且需要你来进行注册
-
在系统调用的背后需要一个稳定的内核,它需要被写入而且它不应当是不稳定的(换而言之一旦写入就不可以变),因为这将会破坏用户层的代码稳定性
-
每一个架构都需要独立的注册这个系统调用号,并且做出实现以支持这个系统调用
-
不容易直接被文件系统所访问,也不好再系统主干树之外的代码片段使用。
Reference
posix是什么都不知道,还好意思说你懂Linux? - 知乎 (zhihu.com)