在现代操作系统中,操作系统和应用程序执行的函数之间具有明确的界限。每当进程希望执行一个任务(如分配内存,从磁盘中读取数据,或通过网络发送数据)时,必须使用一组系统定义好的编程接口访问操作系统。malloc()、read()等系统函数,都是提供操作系统服务的系统调用。我们可以直接通过应用程序,或间接通过高级开发框架(如Mac OS X上的Cocoa框架)执行这些系统调用。Cocoa框架内部也是基于相同的系统调用实现的,它们访问操作系统服务时也要调用底层函数(如read())。 但因为用户进程无法直接访问硬件或操作系统结构,所以它们需要调用read()等函数打破进程地址空间的限制。利用函数调用操作系统服务时,控制权从用户应用程序转至操作系统的特权部分(即内核)。将控制权转至内核这一过程通常需要借助CPU,CPU为此提供了一个指令。例如,现代Mac中的英特尔CPU提供一个syscall指令,它会跳转到操作系统启动时设置的函数。该内核函数首先需要知道用户进程执行哪一个系统调用(由调用进程写入CPU寄存器中的值确定),然后读取传送给该系统调用的函数参数(同样,这一过程也由调用进程通过CPU寄存器设置)。内核随后代表用户进程执行该函数调用,并将控制权及结果代码返回给该进程,如图1-4所示。 内核是一个享有特权的进程,能够执行一些用户进程无权执行的操作,这些操作是配置系统时必不可少的。当控制权转移到内核时(如系统调用之后),CPU在内核代码执行期间进入特权模式,并在返回用户进程之前,恢复到权限受限的状态。 由于内核在代表进程执行系统调用时,其权限级别高于用户进程,所以必须注意不要无意中造成安全漏洞。如果欺骗内核执行用户进程不应该执行的任务,就会发生这种情况,如打开一个用户没有读权限的文件,或向内核提供一个进程地址空间外的目标缓冲区。对于第一种情况,尽管内核进程本身有权打开系统上的任何文件,但因为它代表权限较低的用户进程执行操作,所以需要拒绝该请求。对于第二种情况下,如果内核访问一个无效的地址,将会产生无法恢复的错误,导致内核恐慌。 内核错误是灾难性的,需要重启整个系统。为了防止内核错误发生,每当内核代表用户进程执行请求时,一定要对进程提供的参数进行验证,而不应该假定它们有效。同样,每当驱动程序接受来自用户进程的控制请求时,也需要在内核实现的系统调用中进行验证,接下来几章会介绍这部分内容。
OS X与iOS内核编程——1.4 操作系统服务
书名: OS X与iOS内核编程
作者:
出版社: 人民邮电出版社
原作名: OS X and iOS kernel programming
译者: 贾 伟 | Douglas Clarke
出版年: 2013-6
页数: 400
定价: 89.00
装帧: 平装
ISBN: 9787115318244