📄 index.html
字号:
<!doctype html public "-//w3c//dtd html 4.0 transitional//en">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=gb_2312-80">
<meta name="Generator" content="Microsoft Word 97">
<meta name="GENERATOR" content="Mozilla/4.51 [b]C-CCK-MCD (Win98; I) [Netscape]">
<title>Linux核心介绍讲义</title>
</head>
<body>
<dir>
<dir>
<dir>
<dir>
<dir>
<dl><a NAME="title"></a><b><font size=+2>Linux<font face="隶书">系统调用讲义</font></font></b>
<p><b><font size=+1><a href="#c1">Linux<font face="宋体">下系统调用的实现</font></a></font></b>
<br><b><font size=+1><a href="#c2">Linux<font face="宋体">中的系统调用</font></a></font></b>
<br><b><font size=+1><a href="#c3">Linux<font face="宋体">中怎样编译和定制内核</font></a></font></b>
<br> </dl>
</dir>
</dir>
</dir>
</dir>
</dir>
<ul>
<li>
<a NAME="c1"></a><b><font size=+1>Linux<font face="宋体">下系统调用的实现</font></font></b></li>
</ul>
<ol>
<li>
<b>Unix/Linux<font face="宋体">操作系统的体系结构及系统调用介绍</font></b></li>
</ol>
<ol TYPE="A">
<ol TYPE="A">
<li>
<b><font face="宋体"><font size=+0>什么是操作系统和系统调用</font></font></b></li>
<b></b>
<p><br><b> </b>操作系统是从硬件抽象出来的虚拟机,在该虚拟机上用户可以运行应用程序。它负责直接与硬件交互,向用户程序提供公共服务,并使它们同硬件特性隔离。因为程序不应该依赖于下层的硬件,只有这样应用程序才能很方便的在各种不同的Unix系统之间移动。系统调用是Unix/Linux操作系统向用户程序提供支持的接口,通过这些接口应用程序向操作系统请求服务,控制转向操作系统,而操作系统在完成服务后,将控制和结果返回给用户程序。
<br><b> </b>
<li>
<b><font size=+0>Unix/Linux<font face="宋体">系统体系结构</font></font></b></li>
<p><br>一个<font size=+0>Unix/Linux<font face="宋体">系统分为三个层次:用户、核心以及硬件。</font></font>
<br><img SRC="a1.gif" height=298 width=479>
<br><font face="宋体"><font size=+0> 其中系统调用是用户程序与核心间的边界,通过系统调用进程可由用户模式转入核心模式,在核心模式下完成一定的服务请求后在返回用户模式。</font></font>
<p><font size=+0><font face="宋体"> 系统调用接口看起来和</font>C<font face="宋体">程序中的普通函数调用很相似,它们通常是通过库把这些函数调用映射成进入操作系统所需要的原语。</font></font>
<p><font face="宋体"><font size=+0> 这些操作原语只是提供一个基本功能集,而通过库对这些操作的引用和封装,可以形成丰富而且强大的系统调用库。这里体现了机制与策略相分离的编程思想——系统调用只是提供访问核心的基本机制,而策略是通过系统调用库来体现。</font></font>
<p><font size=+0><font face="宋体">例:</font>execv, execl, execlv, opendir
, readdir...</font>
<p><b><font face="宋体"><font size=+0> </font></font></b>
<li>
<b><font face="宋体"><font size=+0>Unix/Linux运行模式,地址空间和上下文</font></font></b></li>
<br>
<p>
<p><b><font face="宋体"><font size=+0>运行模式(运行态):</font></font></b>
<p><font size=+0><font face="宋体"> 一种计算机硬件要运行</font>Unix/Linux<font face="宋体">系统,至少需要提供两种运行模式:高优先级的核心模式和低优先级的用户模式。</font></font>
<p><font size=+0><font face="宋体"> 实际上许多计算机都有两种以上的执行模式。如:</font>intel
80x86<font face="宋体">体系结构就有四层执行特权,内层特权最高。</font>Unix<font face="宋体">只需要两层即可以了:核心运行在高优先级,称之为核心态;其它外围软件包括</font>shell<font face="宋体">,编辑程序,</font>Xwindow<font face="宋体">等等都是在低优先级运行,称之为用户态。之所以采取不同的执行模式主要原因时为了保护,由于用户进程在较低的特权级上运行,它们将不能意外或故意的破坏其它进程或内核。程序造成的破坏会被局部化而不影响系统中其它活动或者进程。当用户进程需要完成特权模式下才能完成的某些功能时,必须严格按照系统调用提供接口才能进入特权模式,然后执行调用所提供的有限功能。</font></font>
<p><font size=+0><font face="宋体"> 每种运行态都应该有自己的堆栈。在</font>Linux<font face="宋体">中,分为用户栈和核心栈。用户栈包括在用户态执行时函数调用的参数、局部变量和其它数据结构。有些系统中专门为全局中断处理提供了中断栈,但是</font>x86<font face="宋体">中并没有中断栈,中断在当前进程的核心栈中处理。</font></font>
<p><b><font face="宋体"><font size=+0>地址空间:</font></font></b>
<p><font face="宋体"><font size=+0> 采用特权模式进行保护的根本目的是对地址空间的保护,用户进程不应该能够访问所有的地址空间:只有通过系统调用这种受严格限制的接口,进程才能进入核心态并访问到受保护的那一部分地址空间的数据,这一部分通常是留给操作系统使用。另外,进程与进程之间的地址空间也不应该随便互访。这样,就需要提供一种机制来在一片物理内存上实现同一进程不同地址空间上的保护,以及不同进程之间地址空间的保护。</font></font>
<p><font size=+0> Unix/Linux<font face="宋体">中通过虚存管理机制很好的实现了这种保护,在虚存系统中,进程所使用的地址不直接对应物理的存储单元。每个进程都有自己的虚存空间,每个进程有自己的虚拟地址空间,对虚拟地址的引用通过地址转换机制转换成为物理地址的引用。正因为所有进程共享物理内存资源,所以必须通过一定的方法来保护这种共享资源,通过虚存系统很好的实现了这种保护:每个进程的地址空间通过地址转换机制映射到不同的物理存储页面上,这样就保证了进程只能访问自己的地址空间所对应的页面而不能访问或修改其它进程的地址空间对应的页面。</font></font>
<p><font size=+0><font face="宋体"> 虚拟地址空间分为两个部分:用户空间和系统空间。在用户模式下只能访问用户空间而在核心模式下可以访问系统空间和用户空间。系统空间在每个进程的虚拟地址空间中都是固定的,而且由于系统中只有一个内核实例在运行,因此所有进程都映射到单一内核地址空间。内核中维护全局数据结构和每个进程的一些对象信息,后者包括的信息使得内核可以访问任何进程的地址空间。通过地址转换机制进程可以直接访问当前进程的地址空间(通过</font>MMU<font face="宋体">),而通过一些特殊的方法也可以访问到其它进程的地址空间。</font></font>
<p><font size=+0><font face="宋体"> 尽管所有进程都共享内核,但是系统空间是受保护的,进程在用户态无法访问。进程如果需要访问内核,则必须通过系统调用接口。进程调用一个系统调用时,通过执行一组特殊的指令(这个指令是与平台相关的,每种系统都提供了专门的</font>trap<font face="宋体">命令,基于</font>x86<font face="宋体">的</font>Linux<font face="宋体">中是使用</font>int
<font face="宋体">指令)使系统进入内核态,并将控制权交给内核,由内核替代进程完成操作。当系统调用完成后,内核执行另一组特征指令将系统返回到用户态,控制权返回给进程。</font></font>
<p><b><font face="宋体"><font size=+0>上下文:</font></font></b>
<p><font face="宋体"><font size=+0> 一个进程的上下文可以分为三个部分:用户级上下文、寄存器上下文以及系统级上下文。</font></font>
<p><font face="宋体"><font size=+0> 用户级上下文:正文、数据、用户栈以及共享存储区;</font></font>
<p><font size=+0><font face="宋体"> 寄存器上下文:程序寄存器(</font>IP<font face="宋体">),即</font>CPU<font face="宋体">将执行的下条指令地址,处理机状态寄存器(</font>EFLAGS<font face="宋体">),栈指针,通用寄存器;</font></font>
<p><font size=+0><font face="宋体"> 系统级上下文:进程表项</font>(proc<font face="宋体">结构</font>)<font face="宋体">和</font>U<font face="宋体">区,在</font>Linux<font face="宋体">中这两个部分被合成</font>task_struct<font face="宋体">,区表及页表</font>(mm_struct
, vm_area_struct, pgd, pmd, pte<font face="宋体">等</font>)<font face="宋体">,核心栈等。</font></font>
<p><font face="宋体"><font size=+0> 全部的上下文信息组成了一个进程的运行环境。当发生进程调度时,必须对全部上下文信息进行切换,新调度的进程才能运行。进程就是上下文的集合的一个抽象概念。</font></font>
<p><font face="宋体"><font size=+0> </font></font>
<li>
<b><font face="宋体"><font size=+0>系统调用的功能和分类</font></font></b></li>
</ol>
</ol>
<dir>
<dir><font size=+0><font face="宋体"> 操作系统核心在运行期间的活动可以分为两个部分:上半部分</font>(top
half)<font face="宋体">和下半部分</font>(bottom half)<font face="宋体">,其中上半部分为应用程序提供系统调用或自陷的服务,是同步服务,由当前执行的进程引起,在当前进程上下文中执行并允许直接访问当前进程的数据结构;而下半部分则是由处理硬件中断的子程序,是属于异步活动,这些子程序的调用和执行与当前进程无关。上半部分允许被阻塞,因为这样阻塞的是当前进程;下半部分不允许被阻塞,因为阻塞下半部分会引起阻塞一个无辜的进程甚至整个核心。</font></font>
<p><font size=+0><font face="宋体"> 系统调用可以看作是一个所有</font>Unix/Linux<font face="宋体">进程共享的子程序库,但是它是在特权方式下运行,可以存取核心数据结构和它所支持的用户级数据。系统调用的主要功能是使用户可以使用操作系统提供的有关设备管理、文件系统、进程控制进程通讯以及存储管理方面的功能,而不必要了解操作系统的内部结构和有关硬件的细节问题,从而减轻用户负担和保护系统以及提高资源利用率。</font></font>
<p><font size=+0><font face="宋体"> 系统调用分为两个部分:与文件子系统交互的和进程子系统交互的两个部分。其中和文件子系统交互的部分进一步由可以包括与设备文件的交互和与普通文件的交互的系统调用(</font>open,
close, ioctl, create, unlink, . . . <font face="宋体">);与进程相关的系统调用又包括进程控制系统调用(</font>fork,
exit, getpid, . . . <font face="宋体">),进程间通讯,存储管理,进程调度等方面的系统调用。</font></font>
<br><b><font face="宋体"></font></b> </dir>
</dir>
<dir><b>2.Linux<font face="宋体">下系统调用的实现</font></b>
<br><font face="宋体"> (以</font>i386<font face="宋体">为例说明)</font>
<br><b><font size=+0><font face="宋体">
A.在</font>Linux<font face="宋体">中系统调用是怎样陷入核心的?</font></font></b></dir>
<dir>
<dir> 系统调用在使用时和一般的函数调用很相似,但是二者是有本质性区别的,函数调用不能引起从用户态到核心态的转换,而正如前面提到的,系统调用需要有一个状态转换。
<p> 在每种平台上,都有特定的指令可以使进程的执行由用户态转换为核心态,这种指令称作操作系统陷入(<font size=+0>operating
system trap<font face="宋体">)。进程通过执行陷入指令后,便可以在核心态运行系统调用代码。</font></font>
<p><font size=+0><font face="宋体"> 在</font>Linux<font face="宋体">中是通过软中断来实现这种陷入的,在</font>x86<font face="宋体">平台上,这条指令是</font>int
0x80<font face="宋体">。也就是说在</font>Linux<font face="宋体">中,系统调用的接口是一个中断处理函数的特例。具体怎样通过中断处理函数来实现系统调用的入口将在后面详细介绍。</font></font>
<p><font size=+0><font face="宋体"> 这样,就需要在系统启动时,对</font>INT
0x80<font face="宋体">进行一定的初始化,下面将描述其过程:</font></font>
<p><font size=+0><font face="宋体">1.使用汇编子程序</font>setup_idt<font face="宋体">(</font>linux/arch/i386/kernel/head.S<font face="宋体">)初始化</font>idt<font face="宋体">表(中断描述符表),这时所有的入口函数偏移地址都被设为</font>ignore_int</font></dir>
</dir>
<ol TYPE="I">
<ol TYPE="I">
<br>( setup_idt:
<p>lea ignore_int,%edx
<p>movl $(__KERNEL_CS << 16),%eax
<p>movw %dx,%ax /* selector = 0x0010 = cs */
<p>movw $0x8E00,%dx /* interrupt gate - dpl=0, present */
<p>lea SYMBOL_NAME(idt_table),%edi
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -