曦之微言

朝乾夕惕,刚中而志行

  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

从应用开发者的角度来看系统调用,它是一个具备一定基础功能的库函数,通过输入参数得到结果输出;而系统开发者所要考虑的是如何用程序来操控计算机硬件实现系统调用的功能。

我们就拿getchar()库函数举例说明FreeBSD系统调用实现过程:

首先写出上层应用代码:

 1 #include <stdlib.h>
 2 #include <stdio.h>
 3 #include <unistd.h>
 4 #include <signal.h>
 5 
 6 /*SIGALRM消息处理函数*/
 7 void alarm_handle()   
 8 {   
 9 
10 }
11 
12 int main(int argc, char **argv)
13 {
14     int  x;
15 
16     /*设置定时器消息处理函数*/
17     signal(SIGALRM,handle);   
18 
19     /*设定定时器到期时间*/
20     alarm(1);   
21 
22     if ( ( x = getchar() ) < 0 )  {
23         if ( errno == EINTR) {
24             printf("get EINTR signal\n") ;
25         }        
26     }
27 
28     return 0;
29 
30 }

 

当程序执行到第22行的时候,启动系统调用。下面是getchar()库函数大致实现过程描述:

 

1、系统调用申请INT 80中断,我们的main进程进入系统内核以特权模式执行。 

      在 Unix / Linux 环境下程序可以在“用户态”与“内核态”两种模式下执行,“用户态”模式下程序不能直接访问和控制系统硬件资源,而在“内核态”模式下没有这方面的限制。一般情况下我们编写的应用程序一开始都是在“用户态”模式下运行,当程序需要访问系统硬件资源获取数据时(如:通过getchar()获取用户键盘输入字符),就必须使程序转入“内核态”模式执行。FreeBSD系统环境下程序从“用户态”转入“内核态”有三种方式:

  • 硬件中断(hardware interrupet)
  • 硬件陷阱(hardware trap)
  • 软件初始化陷阱(software initiated trap) 

       硬件中断由外部事件造成,比如发生IO事件、时钟事件等等,在我们的例子里就是用户按下键盘就会导致硬件中断;硬件陷阱的产生是由于程序在执行过程中发生内部错误,比如除0错误,空指针错误等等;软件初始化陷阱的产生是由于有进程内有事件需要及时调度软中断处理函数进行处理,通过设置系统内核数据结构中一个flag标志,每次进程从“内核态”退出之前就会调用软中断处理函数。 

       系统调用就是软件初始化陷阱的一种方式。getchar()申请“INT 80”中断后,产生一个硬件中断,硬件中断处理函数Syscall()将CPU设置成“内核模式”,从而可以执行特权指令了,比如访问一些特殊寄存器什么的,接着中断处理函数要保护现场,将CPU程序计数寄存器、CPU程序状态字寄存器、CPU标志位寄存器的内容压入内核栈保存,然后调用汇编程序再保存其他一些记录程序执行情况的信息,比如CPU通用寄存器、用户栈指针什么的也压入内核栈。硬件中断处理函数不再做多余的事情,而是准备直接退出,但是在退出前还有一件关键的使用要做,就是检查我们上面所说的内核数据结构中标志软件初始化陷阱的flag标志位,因为我们在调用getchar()的时候设置了标志位,所以于getchar()有关的软中断处理函数被执行,结果是: 

 

2、系统调用处理函数调用键盘驱动程序,通过IO接口向键盘控制器发出读取指令,用户进程因等待用户输入而进入“阻塞”状态 

       软件初始化中断处理函数启动键盘控制器,并向键盘设备的工作队列(work queue)中加入一个请求项,然后就让出CPU,导致用户进程进入Sleep状态,直到键盘输入导致的IO中断发生。

 

 

3、系统调用处理函数执行过程中一种情况是:用户正常键入字符,键盘中断处理函数启动,唤醒用户进程,系统调用处理函数返回输入字符 

       键盘IO输入完成,键盘IO中断处理函数将输入字符放回工作队列(work queue),标志键盘IO事件完成,唤醒main进程进入“就绪”状态最终进入“运行”态,从而使getchar()软件初始化中断处理函数继续开始执行,将用户键盘数据字符从内核缓存空间中考入用户缓存空间,最后在函数结束前恢复main进程上下文环境,从而使main进程重新回到“用户”态。  

 

 

4、系统调用处理函数执行过程中另一种情况是:用户不做任何操作,而此时用户进程恰巧收到系统消息,结果getchar()终止执行,系统调用处理函数返回EINT错误 

       当发生硬件异常或者软件异常(Trap)时,比如我们例子中的alarm时间到期,trap()处理函数会调用psignal()函数将对应的信号(SIGALRM) 提交给用户进程,直接导致该信号被记录在用户进程中线程集合内的某个线程的td_siglist数据结构中,因为此时用户进程处于“阻塞”态,则处理函数调用setrunnable()将线程唤醒,使用户进程状态改为"就绪"态,程序接着流程1中的系统调用处理函数继续执行,这时系统调用处理函数该退出内核态了,在退出之前,它会调用cursig()函数从td_siglist中获取消息,如果存在未处理的消息,则调用用户定义的消息处理函数(本例中的alarm_handle()),待消息处理函数返回以后,系统调用处理函数在用户函数调用栈中设定getchar()返回值-1,并设定全局变量errno的值为"EINTR", 最后才退出内核态返回用户态。

 

 

 

 
posted on 2012-07-14 20:09  大曦哥  阅读(582)  评论(0编辑  收藏  举报