分类: LINUX
2008-11-06 20:43:58
什么是系统调用 系 统调用,顾名思义,说的是操作系统提供给用户程序调用的一组“特殊”接口。用户程序可以通过这组“特殊”接口来获得操作系统内核提供的服务,比如用户可以 通过文件系统相关的调用请求系统打开文件、关闭文件或读写文件,可以通过时钟相关的系统调用获得系统时间或设置定时器等。 从逻辑上来说,系统调用可被看成是一个内核与用户空间程序交互的接口——它好比一个中间人,把用户进程的请求传达给内核,待内核把请求处理完毕后再将处理结果送回给用户空间。 系统服务之所以需要通过系统调用来提供给用户空间的根本原因是为了对系统进行“保护”,因为我们知道 Linux 的运行空间分为内核空间与用户空间,它们各自运行在不同的级别中,逻辑上相互隔离。所以用户进程在通常情况下不允许访问内核数据,也无法使用内核函数,它们只能在用户空间操作用户数据,调用用户空间函数。比如我们熟悉的“ hello world” 程序(执行时)就是标准的用户空间进程,它使用的打印函数 printf 就属于用户空间函数,打印的字符“ hello word” 字符串也属于用户空间数据。 但 是很多情况下,用户进程需要获得系统服务(调用系统程序),这时就必须利用系统提供给用户的“特殊接口”——系统调用了,它的特殊性主要在于规定了用户进 程进入内核的具体位置;换句话说,用户访问内核的路径是事先规定好的,只能从规定位置进入内核,而不准许肆意跳入内核。有了这样的陷入内核的统一访问路径 限制才能保证内核安全无虞。我们可以形象地描述这种机制:作为一个游客,你可以买票要求进入野生动物园,但你必须老老实实地坐在观光车上,按照规定的路线 观光游览。当然,不准下车,因为那样太危险,不是让你丢掉小命,就是让你吓坏了野生动物。
系统调用 fork 。 系统调用 fork 是 UNIX 中产生进程的唯一途径。 fork 生成调用进程的一个副本,新生成的进程称为子进程,创建它的进程称为父进程, fork 调用完成后,子进程立即执行,在最初阶段将和父进程共享 CPU 。子进程还得到父进程的部分进程信息的副本,包括环境,打开的文件,真实和有效用户标识,掩码,当前工作目录和信号。 Shell 对输入的命令(来自命令行或者脚本)进行解析时,如果判断为内置命令,则直接由 shell 执行;如果是磁盘上的命令, shell 就调用 fork 来生成自己的一个拷贝,称为子进程,子进程将搜索路径以找到这个命令,并设置用于重定向的文件描述符,管道,命令替换和后台处理。子 shell 运行时,父 shell 通常处于睡眠状态。
当子进程处理重定向,管道和后台处理等细节时,程序安排父 shell 进入睡眠状态。系统调用 wait ,导致父进程挂起,直至它的一个子进程终止。如果 wait 调用成功,它返回死去子进程的 PID 和退出状态。如果父进程没有等待,子进程死亡时就进入僵尸(假死)状态,并且保持这个状态直至父进程调用 wait 或者死亡。欲移除僵尸进程,系统必须重启。如果父进程先于子进程死亡, init 进程会收养孤儿僵尸进程。因此,系统调用 wait 不仅仅用于让父进程进入睡眠状态,还可用于保证进程正常终止。
系统调用 exec 。 exec 本函数调用一个新进程来覆盖掉当前进程映像。 当一个程序想要调用另一个程序时,它通常先调用 fork 产生一个新进程, 然后这个新进程再调用 exec 来运行新的程序 ( 即通常由子进程来调用 exec), 然后父进程通过调用一个 wait 函数等待子进程退出 新程序随时可以调用 exit 终止。子进程终止时,会向父进程发出一个信号 (sigchild) 并等待父进程接受它的退出状态,退出状态是一个 0 到 255 之间的数字,退出状态为 0 说明程序执行成功,不为 0 表示发生了某种错误。
例如,如果在命令行键入命令 ls ,父 shell 将 fork 一个子进程,然后开始睡眠。接下来,子 shell 将在自己的位置 exec (覆盖) ls 程序。 Ls 程序于是替代子 shell ,同时继承了它的所有环境变量,打开文件,用户信息和状态信息。这个新进程运行结束时将调用 exit 退出,父进程也将醒来。这时,屏幕会出现命令提示符, shell 开始等待下一条命令。 Bourne shell 和 Korn shell 把退出状态保存在变量?中。 C shell 则把退出状态保存在变量 status 中。 |