⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 interrupt-sys_call_6.htm

📁 编写自己的操作系统
💻 HTM
📖 第 1 页 / 共 2 页
字号:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<!-- saved from url=(0063)http://www.huihoo.com/gnu_linux/own_os/interrupt-sys_call_6.htm -->
<HTML><HEAD><TITLE></TITLE>
<META http-equiv=Content-Type content="text/html; charset=gb2312">
<META content="MSHTML 6.00.2800.1106" name=GENERATOR></HEAD>
<BODY><FONT face="Courier New" size=2>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><SPAN lang=EN-US><FONT 
face="Times New Roman" size=6><SPAN style="mso-tab-count: 1"><STRONG>2.6 System 
Call</STRONG></SPAN></FONT></SPAN></P><SPAN lang=EN-US><FONT 
face="Times New Roman" size=5><SPAN style="mso-tab-count: 1">
<P>
<P><FONT size=3>[</FONT><A 
href="index.htm" 
tppabs="http://pagoda-ooos.51.net/os_book/index.htm"><FONT 
size=3>Home</FONT></A><FONT size=3>]&nbsp; [</FONT><A 
href="interrupt_and_exception.htm" 
tppabs="http://pagoda-ooos.51.net/os_book/interrupt_and_exception.htm"><FONT 
size=3>Top</FONT></A><FONT size=3>]&nbsp; [</FONT><A 
href="interrupt-8259_5.htm" 
tppabs="http://pagoda-ooos.51.net/os_book/interrupt/interrupt-8259_5.htm"><FONT 
size=3>Previous</FONT></A><FONT size=3>]</FONT></P>
<P><STRONG>
<HR width="100%" SIZE=2>
</STRONG>
<P></P>
<P></P></SPAN></FONT></SPAN><SPAN lang=EN-US><FONT face="Times New Roman" 
size=5><SPAN style="mso-tab-count: 1">
<P class=MsoNormal 
style="MARGIN: 0cm 0cm 0pt 21pt; TEXT-INDENT: -21pt; tab-stops: list 21.0pt; mso-list: l5 level1 lfo3"><FONT 
face="Times New Roman TUR" size=5><STRONG>2.6.1 
Overview</STRONG></FONT></P></SPAN></FONT></SPAN></FONT>
<P><FONT face="Courier New" 
size=2>操作系统的重要任务之一就是为应用程序提供服务,而提供服务的最直接手段就是允许应用程序能够调用操作系统的相关服务代码。作为程序员,我们知道,一个程序如果想调用一段代码,它必须知道这段代码的入口地址。对于DOS这种非保护模式操作系统,如果我们知道其内核一段代码的入口地址,我们的应用程序只需要执行一条CALL指令,就可以执行这段内核代码,以获取服务。</FONT></P>
<P><FONT face="Courier New" 
size=2>对于这一点,操作系统的设计原则有两种:即对应用程序总是信任的,还是总是不信任的。如果操作系统对于应用程序总是信任的,那就意味着操作系统认为应用程序总是按照它们之间的约定或协议在做事情,应用程序永远不会违反这些协议,无论是有意的还是无意的;但很明显,即使一个程序员总是在善意的写程序,他也无法保证其程序没有错误,无论其水平有多高,经验有多丰富;而对于恶意的程序,这种操作系统几乎不设防,一个水平不用太高的程序员就可以很轻松的写一个程序让系统崩溃。这正是DOS时代病毒肆虐的原因。另外,或许对于一个单任务的操作系统来说,当问题程序运行引起系统崩溃时,只会影响当前存在问题的任务。但对于多任务操作系统来说,一个问题程序引起其它健康程序无法运行,无疑是一种非常不公平的结果。</FONT></P>
<P><FONT face="Courier New" 
size=2>所以,今天运行在PC机以上平台(包括工作站,小型机,大型机)的操作系统对应用程序几乎都是采取不信任原则,也就是说,操作系统假定运行在其上的任何应用程序都有可能是不安全的,这种类型的操作系统非常保守,尽其所能的对内核其其它应用程序给予保护,任何不遵守协议的应用程序都无法正常运行,却有不会影响操作系统自身的安全,以及其它正常应用程序的运行。一个安全的社会总需要完善的法制来保障,这就是生活。</FONT></P>
<P><FONT face="Courier New" 
size=2>在具有保护模式的操作系统中,操作系统内核运行在高特权级别(内核态),而用户应用程序运行在低特权级别(用户态)。应用程序无法直接调用任何操作系统的代码,即使应用程序非常清楚这些代码的入口地址,但由于硬件给予的保护,应用程序对于这些入口地址的调用,会引起异常。操作系统会捕获到这个异常,并在这个异常的处理中将这个进行非法调用的应用程序杀掉。</FONT></P>
<P><FONT face="Courier New" 
size=2>但操作系统必须向应用程序提供服务,否则,应用程序几乎无法作任何有价值的事情。既然直接调用的方法会导致系统的不安全,那么只能进行间接调用,那就是中断。</FONT></P>
<P><FONT face="Courier New" 
size=2>即使对于非保护模式的DOS来说,它也是通过中断的方法向应用程序提供服务,这就是DOS程序员熟悉的INT 
21H。事实上,通过中断的方法,应用程序无需知道相应的操作系统服务例程的入口地址。因为中断服务程序知道它们,而这一切都是由操作系统设定和维护的。应用程序只需要设定好相应的寄存器,然后执行一条INT指令就可以对这些操作系统服务例程进行调用,并通过寄存器获取执行的结果。</FONT></P>
<P><FONT face="Courier New" size=2>对于保护机制的操作系统来说,中断机制本身也是受保护的,在IBM 
PC上,Intel规定多达255个中断号,但只有授权给应用程序保护等级的中断号才是可以被应用程序调用的,对于未被授权的中断号,如果应用程序进行调用,同样会引起保护异常,而导致自己被操作系统杀死。比如,Linux仅仅给应用程序授权了4个中断号——3,4,5,以及80h。前三个中断号为了提供给应用程序调试(单步跟踪)的手段,而80h正是我们本节讨论的系统调用(system 
call)的中断号。</FONT></P>
<P><FONT face="Courier New" 
size=2>应用程序在执行用户态代码时,被称为运行在用户态;在通过系统调用执行内核服务代码时,被称为运行在内核态——这是一个从应用程序角度来观察的结果。如果你从Client/Server的观点来看,当应用程序作为Client执行一个系统调用时,那么相当于向Server(内核)发起一个请求,当系统调用返回结果时,相当于内核给应用程序一个应答。尤其是基于消息机制的操作系统(比如Minix),这样的逻辑更加直观。</FONT></P>
<P><FONT face="Courier New" 
size=2>作为前一种观点的结果,系统调用很自然的被称做应用程序的陷阱(Trap),在某些硬件系统上,有专门的陷阱指令,尽管机制仍然是中断的机制。当应用程序进行系统调用时,就像从用户态通过一个陷阱掉进内核态里一样,很多操作系统文献都这样比喻,很有趣!</FONT><FONT 
face="Courier New" size=2><STRONG>&nbsp;</P>
<HR width="100%" SIZE=2>
</STRONG>
<P></P>
<P><STRONG></STRONG></P><SPAN lang=EN-US><FONT face="Times New Roman" 
size=5><SPAN style="mso-tab-count: 1">
<P class=MsoNormal 
style="MARGIN: 0cm 0cm 0pt 21pt; TEXT-INDENT: -21pt; tab-stops: list 21.0pt; mso-list: l5 level1 lfo3"><FONT 
face="Times New Roman TUR" size=5><STRONG>2.6.2 Mechanism</STRONG></FONT></P>
<P></SPAN></FONT></SPAN></FONT><FONT face="Courier New" 
size=2>前面我们已经讨论,系统调用是通过中断机制实现的,并且一个操作系统的所有系统调用都通过一个中断号来实现。如下的一些例子都是Unix系统的系统调用函数:</FONT></P><FONT 
face="Courier New" size=2>
<UL>
  <LI>int fork(); 
  <LI>void exit(int status); 
  <LI>int open(char* path, int flags); 
  <LI>int lseek(int fildes, off_t offset, int wherence); </LI></UL>
<P>从这些例子可以看出,它们有3个特点:</FONT></P><FONT face="Courier New" size=2>
<UL>
  <LI>参数个数不固定,可以没有参数,也可以有多个参数; 
  <LI>函数的返回值要么为void,要么为32-bit类型(integer); 
  <LI>函数的形式参数都为32-bit类型;</LI></UL>
<P>DOS程序员都知道如何通过INT 21H来进行DOS功能调用,应用程序只需要将功能号装入AX寄存器,将参数(如果存在的话)按照需要装入BX, CX, 
DX, SI, DI寄存器,然后调用INT 
21H指令。等中断服务程序完成后,AX寄存器用来存放返回值(如果有的话),其它寄存器用来存放执行结果(如果存在的话)。依赖于Intel 
80x86芯片的通用寄存器只有(E)AX, 
(E)BX,(E)CX,(E)DX,(E)SI,(E)DI等6个寄存器,其中(E)AX要用来存放功能号,使用这种手段最多只能允许有5个参数。</FONT></P>
<P><FONT face="Courier New" 
size=2>保护模式的操作系统也可以使用相同的手段来进行系统调用的处理。我们以Linux为例,在进行系统调用时,它使用EAX存放系统调用功能号,在内核中每一个功能号对应一个系统调用功能,比如0对应exit,1对应fork,2对应read,如果对应的系统调用功能有参数,那么应用程序需要按照此功能要求的参数个数分别设置EBX, 
ECX, EDX, 
ESI,EDI,基于同样原因,这种方法所允许的参数也最多为5个。为了能够定义6个参数的系统调用功能函数,Linux使用了%ebp,但%ebp对于函数来说是一个非常重要的寄存器,所以使用前必须进行压栈保存。由于这些寄存器都是32-bit宽度的,所以要求参数也都应该为32-bit类型的;然后应用程序执行指令INT 
80h。下面是Linux 2.4相关代码。</FONT></P>
<P><FONT face="Courier New" size=2>#define __NR_exit&nbsp;&nbsp;&nbsp; 1 /* exit 
的系统调用号 */<BR>#define __NR_fork&nbsp;&nbsp;&nbsp; 2 /* fork 的系统调用号 */<BR>#define 
__NR_read&nbsp;&nbsp;&nbsp; 3&nbsp; /* read 的系统调用号 */<BR>#define 
__NR_write&nbsp;&nbsp; 4&nbsp; /* write 的系统调用号 */<BR>#define 
__NR_open&nbsp;&nbsp;&nbsp; 5&nbsp; /* open 的系统调用号 */<BR>#define 
__NR_close&nbsp;&nbsp; 6&nbsp; /* close 的系统调用号 */</FONT></P>
<P><FONT face="Courier New" size=2>/*这里为了节省篇幅,只列出了几个系统调用功能号*/</FONT></P>
<P><FONT face="Courier New" size=2>#define __syscall_return(type, res) \<BR>do { 

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -