程序员自我修养阅读笔记——系统调用与API
Posted grayondream
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了程序员自我修养阅读笔记——系统调用与API相关的知识,希望对你有一定的参考价值。
1 系统调用
1.1 系统调用简介
由操作系统实现提供的所有系统调用所构成的集合即程序接口或应用编程接口(Application Programming Interface,API)。是应用程序同系统之间的接口。操作系统本身是硬件资源的管理者,系统调用就是用户通过系统访问硬件资源和一些核心功能的媒介。一般涉及磁盘访问、网络、进程管理、外设等等系统资源的访问接口。
1.2 系统调用的优缺点
优点:
- 帮助用户安全高效的访问硬件资源;
- 合理的规划资源访问,保证系统安全的运行。
- 同一系统标准统一。
缺点: - 使用不便。操作系统提供的系统调用接口往往过于原始,程序员须要了解很多与操作系统相关的细节。如果没有进行很好的包装,使用起来不方便。
- 各个操作系统之间系统调用不兼容。
2 系统调用原理
2.1 特权级与中断
现代操作系统中,通常有两种特权级别,分别是用户模式和内核模式,也称为用户态和内核态。普通应用程序运行在用户态,诸多操作受限,如访问硬件设备、开关中断、改变特权模式等。OS一般通过中断来从用户态切换到内核态。
中断是一个硬件或软件发出的请求,要求CPU暂停当前的工作去处理更加重要的事情。中断有两个属性,一个称为中断号,一个是中断处理程序(Interrupt Service Routine, ISR)。不同的中断有不同的中断号,不同的中断号对应不同的中断处理程序。在内核中,有一个数组称为中断向量表,向量表的第n项包含指第n号中断处理程序的指针。
中断有两种类型:
- 硬中断:来自于硬件的异常或者其他事件的发生,如电源断电、键盘被按下等
- 软中断:通常是一条指令(i386下是int),带有一个参数记录中断号,使用这条指令用户可以手动触发某个中断执行其中断处理程序。
由于中断号有限,操作系统不会舍得用一个中断号对应一个系统调用,而更倾向于用一个或少数几个中断号对应所有的系统调用。windows绝大多数系统调用由int 0x2E来触发,linux用int 0x80来触发所有系统调用。
每个系统调用都有一个系统调用号,这个系统调用号就是系统调用在系统调用表的位置,例如linux里fork系统调用为2。这个系统调用号在执行int指令前会被放置于某个固定寄存器中,对应的中断代码会取得系统调用号,并且调用正确的函数。以linux以例,这个固定寄存器为eax。
2.2 基于init的linux经典系统调用实现
本机基于fork调用来展现Linux系统调用的流程。这里不会根据linux的源码,我查看源码发现fork的实现和书籍中提到的实现已经有些出入。这里仅仅谈一下基本的流程。
触发中断
通过_syscall*系列函数来执行实际的函数调用,eax寄存器既用来传递参数,又存储函数返回值。将返回值相应地转换为错误码errno,这个错误码既可以是全局变量,也可以存储于TLS。
堆栈切换
在实际执行int 0x80对应的函数之前,CPU要先进行栈的切换。linux下,用户态和内核态使用不同的栈,各自负责各自的函数调用,互不干扰。同理,中断处理程序返回时,程序的当前栈还要从内核栈切换回用户栈。用户栈切换到内核栈的动作实际为:
- 找到当前进程的内核栈;
- 保存当前的ESP, SS值到内核栈;
- 在内核栈中依次压入用户态的寄存器SS, ESP, EFLAGS, CS, EIP;
- 将ESP, SS设置为内核栈的值。
中断处理程序
不同的中断号对应不同的中断处理程序。中断的基本流程为:
- 将用户态寄存器压入内核栈;
- 比较系统调用号与最大系统调用号,以判断系统调用的有效性;
- 若系统调用判断为有效,则通过
sys_call_table
来查找中断处理程序并执行; - 执行完中断处理程序后,恢复步骤1中被保存的用户态寄存器;
- 最后通过iret指令从中断处理程序中返回。
3 windows API
这部分简单了解下就好。
即windows操作系统提供给应用开发者的一些列比较底层的和系统打交道的API。也就是是windows API是对windows系统调用的包装,介于CRT和系统调用之间。这样做是通过添加一层抽象层,windows中称为子系统,来保证不同版本程序的兼容性。windows的兼容性是比较不错的,window10机器上也可以运行xp的应用程序。
以上是关于程序员自我修养阅读笔记——系统调用与API的主要内容,如果未能解决你的问题,请参考以下文章