Linux系统调用讲义_第1页
Linux系统调用讲义_第2页
Linux系统调用讲义_第3页
Linux系统调用讲义_第4页
Linux系统调用讲义_第5页
已阅读5页,还剩30页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

1、Linux系统调用讲义 Linux下系统调用的实现 Linux中的系统调用 Linux中怎样编译和定制内核 Linux下系统调用的实现 1. Unix/Linux操作系统的体系结构及系统调用介绍 A. 什么是操作系统和系统调用 操作系统是从硬件抽象出来的虚拟机,在该虚拟机上用户可以运行应用程序。它负责直接与硬件交互,向用户程序提供公共服务,并使它们同硬件特性隔离。因为程序不应该依赖于下层的硬件,只有这样应用程序才能很方便的在各种不同的Unix系统之间移动。系统调用是Unix/Linux操作系统向用户程序提供支持的接口,通过这些接口应用程序向操作系统请求服务,控制转向操作系统,而操作系统在完成服

2、务后,将控制和结果返回给用户程序。 B. Unix/Linux系统体系结构 一个Unix/Linux系统分为三个层次:用户、核心以及硬件。 其中系统调用是用户程序与核心间的边界,通过系统调用进程可由用户模式转入核心模式,在核心模式下完成一定的服务请求后在返回用户模式。 系统调用接口看起来和C程序中的普通函数调用很相似,它们通常是通过库把这些函数调用映射成进入操作系统所需要的原语。 这些操作原语只是提供一个基本功能集,而通过库对这些操作的引用和封装,可以形成丰富而且强大的系统调用库。这里体现了机制与策略相分离的编程思想系统调用只是提供访问核心的基本机制,而策略是通过系统调用库来体现。 例:exe

3、cv, execl, execlv, opendir , readdir. C. Unix/Linux运行模式,地址空间和上下文 运行模式(运行态): 一种计算机硬件要运行Unix/Linux系统,至少需要提供两种运行模式:高优先级的核心模式和低优先级的用户模式。 实际上许多计算机都有两种以上的执行模式。如:intel 80x86体系结构就有四层执行特权,内层特权最高。Unix只需要两层即可以了:核心运行在高优先级,称之为核心态;其它外围软件包括shell,编辑程序,Xwindow等等都是在低优先级运行,称之为用户态。之所以采取不同的执行模式主要原因时为了保护,由于用户进程在较低的特权级上运行

4、,它们将不能意外或故意的破坏其它进程或内核。程序造成的破坏会被局部化而不影响系统中其它活动或者进程。当用户进程需要完成特权模式下才能完成的某些功能时,必须严格按照系统调用提供接口才能进入特权模式,然后执行调用所提供的有限功能。 每种运行态都应该有自己的堆栈。在Linux中,分为用户栈和核心栈。用户栈包括在用户态执行时函数调用的参数、局部变量和其它数据结构。有些系统中专门为全局中断处理提供了中断栈,但是x86中并没有中断栈,中断在当前进程的核心栈中处理。 地址空间: 采用特权模式进行保护的根本目的是对地址空间的保护,用户进程不应该能够访问所有的地址空间:只有通过系统调用这种受严格限制的接口,进程

5、才能进入核心态并访问到受保护的那一部分地址空间的数据,这一部分通常是留给操作系统使用。另外,进程与进程之间的地址空间也不应该随便互访。这样,就需要提供一种机制来在一片物理内存上实现同一进程不同地址空间上的保护,以及不同进程之间地址空间的保护。 Unix/Linux中通过虚存管理机制很好的实现了这种保护,在虚存系统中,进程所使用的地址不直接对应物理的存储单元。每个进程都有自己的虚存空间,每个进程有自己的虚拟地址空间,对虚拟地址的引用通过地址转换机制转换成为物理地址的引用。正因为所有进程共享物理内存资源,所以必须通过一定的方法来保护这种共享资源,通过虚存系统很好的实现了这种保护:每个进程的地址空间

6、通过地址转换机制映射到不同的物理存储页面上,这样就保证了进程只能访问自己的地址空间所对应的页面而不能访问或修改其它进程的地址空间对应的页面。 虚拟地址空间分为两个部分:用户空间和系统空间。在用户模式下只能访问用户空间而在核心模式下可以访问系统空间和用户空间。系统空间在每个进程的虚拟地址空间中都是固定的,而且由于系统中只有一个内核实例在运行,因此所有进程都映射到单一内核地址空间。内核中维护全局数据结构和每个进程的一些对象信息,后者包括的信息使得内核可以访问任何进程的地址空间。通过地址转换机制进程可以直接访问当前进程的地址空间(通过MMU),而通过一些特殊的方法也可以访问到其它进程的地址空间。 尽

7、管所有进程都共享内核,但是系统空间是受保护的,进程在用户态无法访问。进程如果需要访问内核,则必须通过系统调用接口。进程调用一个系统调用时,通过执行一组特殊的指令(这个指令是与平台相关的,每种系统都提供了专门的trap命令,基于x86的Linux中是使用int 指令)使系统进入内核态,并将控制权交给内核,由内核替代进程完成操作。当系统调用完成后,内核执行另一组特征指令将系统返回到用户态,控制权返回给进程。 上下文: 一个进程的上下文可以分为三个部分:用户级上下文、寄存器上下文以及系统级上下文。 用户级上下文:正文、数据、用户栈以及共享存储区; 寄存器上下文:程序寄存器(IP),即CPU将执行的下

8、条指令地址,处理机状态寄存器(EFLAGS),栈指针,通用寄存器; 系统级上下文:进程表项(proc结构)和U区,在Linux中这两个部分被合成task_struct,区表及页表(mm_struct , vm_area_struct, pgd, pmd, pte等),核心栈等。 全部的上下文信息组成了一个进程的运行环境。当发生进程调度时,必须对全部上下文信息进行切换,新调度的进程才能运行。进程就是上下文的集合的一个抽象概念。 D. 系统调用的功能和分类 操作系统核心在运行期间的活动可以分为两个部分:上半部分(top half)和下半部分(bottom half),其中上半部分为应用程序提供系统

9、调用或自陷的服务,是同步服务,由当前执行的进程引起,在当前进程上下文中执行并允许直接访问当前进程的数据结构;而下半部分则是由处理硬件中断的子程序,是属于异步活动,这些子程序的调用和执行与当前进程无关。上半部分允许被阻塞,因为这样阻塞的是当前进程;下半部分不允许被阻塞,因为阻塞下半部分会引起阻塞一个无辜的进程甚至整个核心。 系统调用可以看作是一个所有Unix/Linux进程共享的子程序库,但是它是在特权方式下运行,可以存取核心数据结构和它所支持的用户级数据。系统调用的主要功能是使用户可以使用操作系统提供的有关设备管理、文件系统、进程控制进程通讯以及存储管理方面的功能,而不必要了解操作系统的内部结

10、构和有关硬件的细节问题,从而减轻用户负担和保护系统以及提高资源利用率。 系统调用分为两个部分:与文件子系统交互的和进程子系统交互的两个部分。其中和文件子系统交互的部分进一步由可以包括与设备文件的交互和与普通文件的交互的系统调用(open, close, ioctl, create, unlink, . . . );与进程相关的系统调用又包括进程控制系统调用(fork, exit, getpid, . . . ),进程间通讯,存储管理,进程调度等方面的系统调用。 2.Linux下系统调用的实现 (以i386为例说明) A.在Linux中系统调用是怎样陷入核心的? 系统调用在使用时和一般的函数调用

11、很相似,但是二者是有本质性区别的,函数调用不能引起从用户态到核心态的转换,而正如前面提到的,系统调用需要有一个状态转换。 在每种平台上,都有特定的指令可以使进程的执行由用户态转换为核心态,这种指令称作操作系统陷入(operating system trap)。进程通过执行陷入指令后,便可以在核心态运行系统调用代码。 在Linux中是通过软中断来实现这种陷入的,在x86平台上,这条指令是int 0x80。也就是说在Linux中,系统调用的接口是一个中断处理函数的特例。具体怎样通过中断处理函数来实现系统调用的入口将在后面详细介绍。 这样,就需要在系统启动时,对INT 0x80进行一定的初始化,下面

12、将描述其过程: 1.使用汇编子程序setup_idt(linux/arch/i386/kernel/head.S)初始化idt表(中断描述符表),这时所有的入口函数偏移地址都被设为ignore_int ( setup_idt: lea ignore_int,%edx movl $(_KERNEL_CS flags。 2.堆栈中的参数 正如前面提到的,SAVE_ALL是系统调用参数的传入过程,当执行完SAVE_ALL并且再由CALL指令调用其处理函数时,堆栈的结构应该如上图所示。这时的堆栈结构看起来和执行一个普通带参数的函数调用是一样的,参数在堆栈中对应的顺序是(arg1, ebx),(arg2

13、, ecx),(arg3, edx). . . . . .,这正是SAVE_ALL压栈的反顺序,这些参数正是用户在使用系统调用时试图传送给核心的参数。下面是在核心的调用处理函数中使用参数的两种典型方法: asmlinkage int sys_fork(struct pt_regs regs); asmlinkage int sys_open(const char * filename, int flags, int mode); 在sys_fork中,把整个堆栈中的内容视为一个struct pt_regs类型的参数,该参数的结构和堆栈的结构是一致的,所以可以使用堆栈中的全部信息。而在sys_o

14、pen中参数filename, flags, mode正好对应与堆栈中的ebx, ecx, edx的位置,而这些寄存器正是用户在通过C库调用系统调用时给这些参数指定的寄存器。 _asm_ _volatile_( int $0x80nt :=a(retval) :0(_NR_open), b(filename), c(flags), d(mode); 3.核心如何使用用户空间的参数在使用系统调用时,有些参数是指针,这些指针所指向的是用户空间DS寄存器的段选择子所描述段中的地址,而在2.2之前的版本中,核心态的DS段寄存器的中的段选择子和用户态的段选择子描述的段地址不同(前者为0xC, 后者为0x

15、),这样在使用这些参数时就不能读取到正确的位置。所以需要通过特殊的核心函数(如:memcpy_fromfs, mencpy_tofs)来从用户空间数据段读取参数,在这些函数中,是使用FS寄存器来作为读取参数的段寄存器的,FS寄存器在系统调用进入核心态时被设成了USER_DS(DS被设成了KERNEL_DS)。在2.2之后的版本用户态和核心态使用的DS中段选择子描述的段地址是一样的(都是0x),所以不需要再经过上面那样烦琐的过程而直接使用参数了。2.2及以后的版本linux/arch/i386/head.S ENTRY(gdt_table) .quad 0x00000/* NULL descri

16、ptor */ .quad 0x00000/* not used */ .quad 0x00cf9affff /* 0x10 kernel 4GB code at 0x */ .quad 0x00cfffff /* 0x18 kernel 4GB data at 0x */ .quad 0x00cffaffff /* 0x23 user 4GB code at 0x */ .quad 0x00cffffff /* 0x2b user 4GB data at 0x */ 2.0 linux/arch/i386/head.S ENTRY(gdt) .quad 0x00000 /* NULL des

17、criptor */ .quad 0x00000 /* not used */ .quad 0xc0c39affff /* 0x10 kernel 1GB code at 0xC */ .quad 0xc0cffff /* 0x18 kernel 1GB data at 0xC */ .quad 0x00cbfaffff /* 0x23 user 3GB code at 0x */ .quad 0x00cbfffff /* 0x2b user 3GB data at 0x * 在2.0版的内核中SAVE_ALL宏定义还有这样几条语句: movl $ STR(KERNEL_DS) ,%edxnt

18、 mov %dx,%dsnt mov %dx,%esnt movl $ STR(USER_DS) ,%edxnt mov %dx,%fsnt movl $0,%edxnt E.调用返回 调用返回的过程要做的工作比其响应过程要多一些,这些工作几乎是每次从核心态返回用户态都需要做的,这里将简要的说明: 1.判断有没有软中断,如果有则跳转到软中断处理; 2.判断当前进程是否需要重新调度,如果需要则跳转到调度处理; 3.如果当前进程有挂起的信号还没有处理,则跳转到信号处理; 4.使用用RESTORE_ALL来弹出所有被SAVE_ALL压入核心栈的内容并且使用iret返回用户态。 F.实例介绍 前面介绍

19、了系统调用相关的数据结构以及在Linux中使用一个系统调用的过程中每一步是怎样处理的,下面将把前面的所有概念串起来,说明怎样在Linux中增加一个系统调用。 这里实现的系统调用hello仅仅是在控制台上打印一条语句,没有任何功能。 1.修改linux/include/i386/unistd.h,在里面增加一条语句: #define _NR_hello ?(这个数字可能因为核心版本不同而不同) 2.在某个合适的目录中(如:linux/kernel)增加一个hello.c,修改该目录下的Makefile(把相映的.o文件列入Makefile中就可以了)。 3.编写hello.c . . . . .

20、 . asmlinkage int sys_hello(char * str) printk(“My syscall: hello, I know what you say to me: %s ! n”, str); return 0; 4.修改linux/arch/i386/kernel/entry.S,在里面增加一条语句: ENTRY(sys_call_table) . . . . . . .long SYMBOL_NAME(sys_hello) 并且修改: .rept NR_syscalls-? /* ? = ? +1 */ .long SYMBOL_NAME(sys_ni_syscal

21、l) 5.在linux/include/i386/中增加hello.h,里面至少应包括这样几条语句:#include #ifdef _KERNEL #else inline _syscall1(int, hello, char *, str); #endif 这样就可以使用系统调用hello了 Back Linux中的系统调用 1 进程相关的系统调用 Fork & vfork & clone 进程是一个指令执行流及其执行环境,其执行环境是一个系统资源的集合,这些资源在Linux中被抽象成各种数据对象:进程控制块、虚存空间、文件系统,文件I/O、信号处理函数。所以创建一个进程的过程就是这些数据对

22、象的创建过程。 在调用系统调用fork创建一个进程时,子进程只是完全复制父进程的资源,这样得到的子进程独立于父进程,具有良好的并发性,但是二者之间的通讯需要通过专门的通讯机制,如:pipe,fifo,System V IPC机制等,另外通过fork创建子进程系统开销很大,需要将上面描述的每种资源都复制一个副本。这样看来,fork是一个开销十分大的系统调用,这些开销并不是所有的情况下都是必须的,比如某进程fork出一个子进程后,其子进程仅仅是为了调用exec执行另一个执行文件,那么在fork过程中对于虚存空间的复制将是一个多余的过程(由于Linux中是采取了copy-on-write技术,所以这

23、一步骤的所做的工作只是虚存管理部分的复制以及页表的创建,而并没有包括物理也面的拷贝);另外,有时一个进程中具有几个独立的计算单元,可以在相同的地址空间上基本无冲突进行运算,但是为了把这些计算单元分配到不同的处理器上,需要创建几个子进程,然后各个子进程分别计算最后通过一定的进程间通讯和同步机制把计算结果汇总,这样做往往有许多格外的开销,而且这种开销有时足以抵消并行计算带来的好处。 这说明了把计算单元抽象到进程上是不充分的,这也就是许多系统中都引入了线程的概念的原因。在讲述线程前首先介绍以下vfork系统调用,vfork系统调用不同于fork,用vfork创建的子进程共享地址空间,也就是说子进程完

24、全运行在父进程的地址空间上,子进程对虚拟地址空间任何数据的修改同样为父进程所见。但是用vfork创建子进程后,父进程会被阻塞直到子进程调用exec或exit。这样的好处是在子进程被创建后仅仅是为了调用exec执行另一个程序时,因为它就不会对父进程的地址空间有任何引用,所以对地址空间的复制是多余的,通过vfork可以减少不必要的开销。 在Linux中, fork和vfork都是调用同一个核心函数 do_fork(unsigned long clone_flag, unsigned long usp, struct pt_regs) 其中clone_flag包括CLONE_VM, CLONE_FS

25、, CLONE_FILES, CLONE_SIGHAND, CLONE_PID,CLONE_VFORK等等标志位,任何一位被置1了则表明创建的子进程和父进程共享该位对应的资源。所以在vfork的实现中,cloneflags = CLONE_VFORK | CLONE_VM | SIGCHLD,这表示子进程和父进程共享地址空间,同时do_fork会检查CLONE_VFORK,如果该位被置1了,子进程会把父进程的地址空间锁住,直到子进程退出或执行exec时才释放该锁。 在讲述clone系统调用前先简单介绍线程的一些概念。 线程是在进程的基础上进一步的抽象,也就是说一个进程分为两个部分:线程集合和资

26、源集合。线程是进程中的一个动态对象,它应该是一组独立的指令流,进程中的所有线程将共享进程里的资源。但是线程应该有自己的私有对象:比如程序计数器、堆栈和寄存器上下文。 线程分为三种类型: 内核线程、轻量级进程和用户线程。 内核线程: 它的创建和撤消是由内核的内部需求来决定的,用来负责执行一个指定的函数,一个内核线程不需要和一个用户进程联系起来。它共享内核的正文段核全局数据,具有自己的内核堆栈。它能够单独的被调度并且使用标准的内核同步机制,可以被单独的分配到一个处理器上运行。内核线程的调度由于不需要经过态的转换并进行地址空间的重新映射,因此在内核线程间做上下文切换比在进程间做上下文切换快得多。 轻

27、量级进程: 轻量级进程是核心支持的用户线程,它在一个单独的进程中提供多线程控制。这些轻量级进程被单独的调度,可以在多个处理器上运行,每一个轻量级进程都被绑定在一个内核线程上,而且在它的生命周期这种绑定都是有效的。轻量级进程被独立调度并且共享地址空间和进程中的其它资源,但是每个LWP都应该有自己的程序计数器、寄存器集合、核心栈和用户栈。 用户线程: 用户线程是通过线程库实现的。它们可以在没有内核参与下创建、释放和管理。线程库提供了同步和调度的方法。这样进程可以使用大量的线程而不消耗内核资源,而且省去大量的系统开销。用户线程的实现是可能的,因为用户线程的上下文可以在没有内核干预的情况下保存和恢复。

28、每个用户线程都可以有自己的用户堆栈,一块用来保存用户级寄存器上下文以及如信号屏蔽等状态信息的内存区。库通过保存当前线程的堆栈和寄存器内容载入新调度线程的那些内容来实现用户线程之间的调度和上下文切换。 内核仍然负责进程的切换,因为只有内核具有修改内存管理寄存器的权力。用户线程不是真正的调度实体,内核对它们一无所知,而只是调度用户线程下的进程或者轻量级进程,这些进程再通过线程库函数来调度它们的线程。当一个进程被抢占时,它的所有用户线程都被抢占,当一个用户线程被阻塞时,它会阻塞下面的轻量级进程,如果进程只有一个轻量级进程,则它的所有用户线程都会被阻塞。 明确了这些概念后,来讲述Linux的线程和cl

29、one系统调用。 在许多实现了MT的操作系统中(如:Solaris,Digital Unix等), 线程和进程通过两种数据结构来抽象表示: 进程表项和线程表项,一个进程表项可以指向若干个线程表项, 调度器在进程的时间片内再调度线程。 但是在Linux中没有做这种区分, 而是统一使用task_struct来管理所有进程/线程,只是线程与线程之间的资源是共享的,这些资源可是是前面提到过的:虚存、文件系统、文件I/O以及信号处理函数甚至PID中的几种。 也就是说Linux中,每个线程都有一个task_struct,所以线程和进程可以使用同一调度器调度。其实Linux核心中,轻量级进程和进程没有质上的

30、差别,因为Linux中进程的概念已经被抽象成了计算状态加资源的集合,这些资源在进程间可以共享。如果一个task独占所有的资源,则是一个HWP,如果一个task和其它task共享部分资源,则是LWP。 clone系统调用就是一个创建轻量级进程的系统调用: int clone(int (*fn)(void * arg), void *stack, int flags, void * arg); 其中fn是轻量级进程所执行的过程,stack是轻量级进程所使用的堆栈,flags可以是前面提到的CLONE_VM, CLONE_FS, CLONE_FILES, CLONE_SIGHAND,CLONE_PI

31、D的组合。Clone 和fork,vfork在实现时都是调用核心函数do_fork。 do_fork(unsigned long clone_flag, unsigned long usp, struct pt_regs); 和fork、vfork不同的是,fork时clone_flag = SIGCHLD; vfork时clone_flag = CLONE_VM | CLONE_VFORK | SIGCHLD; 而在clone中,clone_flag由用户给出。 下面给出一个使用clone的例子。 Void * func(int arg) . . . . . . int main() int

32、 clone_flag, arg; . . . . . . clone_flag = CLONE_VM | CLONE_SIGHAND | CLONE_FS | CLONE_FILES; stack = (char *)malloc(STACK_FRAME); stack += STACK_FRAME; retval = clone(void *)func, stack, clone_flag, arg); . . . . . . 看起来clone的用法和pthread_create有些相似,两者的最根本的差别在于clone是创建一个LWP,对核心是可见的,由核心调度,而pthread_cre

33、ate通常只是创建一个用户线程,对核心是不可见的,由线程库调度。 Nanosleep & sleep sleep和nanosleep都是使进程睡眠一段时间后被唤醒,但是二者的实现完全不同。 Linux中并没有提供系统调用sleep,sleep是在库函数中实现的,它是通过调用alarm来设定报警时间,调用sigsuspend将进程挂起在信号SIGALARM上,sleep只能精确到秒级上。 nanosleep则是Linux中的系统调用,它是使用定时器来实现的,该调用使调用进程睡眠,并往定时器队列上加入一个time_list型定时器,time_list结构里包括唤醒时间以及唤醒后执行的函数,通过na

34、nosleep加入的定时器的执行函数仅仅完成唤醒当前进程的功能。系统通过一定的机制定时检查这些队列(比如通过系统调用陷入核心后,从核心返回用户态前,要检查当前进程的时间片是否已经耗尽,如果是则调用schedule()函数重新调度,该函数中就会检查定时器队列,另外慢中断返回前也会做此检查),如果定时时间已超过,则执行定时器指定的函数唤醒调用进程。当然,由于系统时间片可能丢失,所以nanosleep精度也不是很高。 alarm也是通过定时器实现的,但是其精度只精确到秒级,另外,它设置的定时器执行函数是在指定时间向当前进程发送SIGALRM信号。 2.存储相关的系统调用mmap:文件映射 在讲述文件

35、映射的概念时,不可避免的要牵涉到虚存(SVR 4的VM)。实际上,文件映射是虚存的中心概念,文件映射一方面给用户提供了一组措施,似的用户将文件映射到自己地址空间的某个部分,使用简单的内存访问指令读写文件;另一方面,它也可以用于内核的基本组织模式,在这种模式种,内核将整个地址空间视为诸如文件之类的一组不同对象的映射。 Unix中的传统文件访问方式是,首先用open系统调用打开文件,然后使用read,write以及lseek等调用进行顺序或者随即的I/O。这种方式是非常低效的,每一次I/O操作都需要一次系统调用。另外,如果若干个进程访问同一个文件,每个进程都要在自己的地址空间维护一个副本,浪费了内

36、存空间。而如果能够通过一定的机制将页面映射到进程的地址空间中,也就是说首先通过简单的产生某些内存管理数据结构完成映射的创建。当进程访问页面时产生一个缺页中断,内核将页面读入内存并且更新页表指向该页面。而且这种方式非常方便于同一副本的共享。 下面给出以上两种方式的对比图: VM是面向对象的方法设计的,这里的对象是指内存对象:内存对象是一个软件抽象的概念,它描述内存区与后备存储之间的映射。系统可以使用多种类型的后备存储,比如交换空间,本地或者远程文件以及帧缓存等等。VM系统对它们统一处理,采用同一操作集操作,比如读取页面或者回写页面等。每种不同的后备存储都可以用不同的方法实现这些操作。这样,系统定

37、义了一套统一的接口,每种后备存储给出自己的实现方法。 这样,进程的地址空间就被视为一组映射到不同数据对象上的的映射组成。所有的有效地址就是那些映射到数据对象上的地址。这些对象为映射它的页面提供了持久性的后备存储。映射使得用户可以直接寻址这些对象。 值得提出的是,VM体系结构独立于Unix系统,所有的Unix系统语义,如正文,数据及堆栈区都可以建构在基本VM系统之上。同时,VM体系结构也是独立于存储管理的,存储管理是由操作系统实施的,如:究竟采取什么样的对换和请求调页算法,究竟是采取分段还是分页机制进行存储管理,究竟是如何将虚拟地址转换成为物理地址等等(Linux中是一种叫Three Level

38、 Page Table的机制),这些都与内存对象的概念无关。 下面介绍Linux中VM的实现。 如下图所示,一个进程应该包括一个mm_struct(memory manage struct),该结构是进程虚拟地址空间的抽象描述,里面包括了进程虚拟空间的一些管理信息:start_code, end_code, start_data, end_data, start_brk, end_brk等等信息。另外,也有一个指向进程虚存区表(vm_area_struct :virtual memory area)的指针,该链是按照虚拟地址的增长顺序排列的。 在Linux进程的地址空间被分作许多区(vma),

39、每个区(vma)都对应虚拟地址空间上一段连续的区域,vma是可以被共享和保护的独立实体,这里的vma就是前面提到的内存对象。这里给出vm_area_struct的结构,其中,前半部分是公共的,与类型无关的一些数据成员,如:指向mm_struct的指针,地址范围等等,后半部分则是与类型相关的成员,其中最重要的是一个指向vm_operation_struct向量表的指针vm_ops,vm_pos向量表是一组虚函数,定义了与vma类型无关的接口。每一个特定的子类,即每种vma类型都必须在向量表中实现这些操作。这里包括了:open, close, unmap, protect, sync, nopag

40、e, wppage, swapout这些操作。 struct vm_area_struct /*公共的,与vma类型无关的 */ struct mm_struct * vm_mm; unsigned long vm_start; unsigned long vm_end; struct vm_area_struct *vm_next; pgprot_t vm_page_prot; unsigned long vm_flags; short vm_avl_height; struct vm_area_struct * vm_avl_left; struct vm_area_struct * vm_avl_right; struct vm_area_struct *vm_next_share

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论