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

📄

📁 linux informations
💻
📖 第 1 页 / 共 3 页
字号:
当进程请求分配虚拟内存时,Linux并不直接分配物理内存。它只是创建一个vm_area_struct 结构来描叙此虚拟内存,此结构被连接到进程的虚拟内存链表中。当进程试图对新分配的虚拟内存进行写操作时,系统将产生页面错。处理器会尝试解析此虚拟地址,但是如果找不到对应此虚拟地址的页表入口时,处理器将放弃解析并产生页面错误异常,由Linux核心来处理。Linux则查看此虚拟地址是否在当前进程的虚拟地址空间中。如果是Linux会创建正确的PTE并为此进程分配物理页面。包含在此页面中的代码或数据可能需要从文件系统或者交换磁盘上读出。然后进程将从页面错误处开始继续执行,由于物理内存已经存在,所以不会再产生页面异常。 


4.6  进程创建
系统启动时总是处于核心模式,此时只有一个进程:初始化进程。象所有进程一样,初始化进程也有一个由堆栈、寄存器等表示的机器状态。当系统中有其它进程被创建并运行时,这些信息将被存储在初始化进程的task_struct结构中。在系统初始化的最后,初始化进程启动一个核心线程(init)然后保留在idle状态。 如果没有任何事要做,调度管理器将运行idle进程。idle进程是唯一不是动态分配task_struct的进程,它的 task_struct在核心构造时静态定义并且名字很怪,叫init_task。 

由于是系统的第一个真正的进程,所以init核心线程(或进程)的标志符为1。它负责完成系统的一些初始化设置任务(如打开系统控制台与安装根文件系统),以及执行系统初始化程序,如/etc/init, /bin/init 或者 /sbin/init ,这些初始化程序依赖于具体的系统。init程序使用/etc/inittab作为脚本文件来创建系统中的新进程。这些新进程又创建各自的新进程。例如getty进程将在用户试图登录时创建一个login进程。系 统中所有进程都是从init核心线程中派生出来。 

新进程通过克隆老进程或当前进程来创建。系统调用fork或clone可以创建新任务,复制发生在核心状态下的核心中。在系统调用的结束处有一个新进程等待调度管理器选择它去运行。系统从物理内存中分配出来一个新的task_struct数据结构,同时还有一个或多个包含被复制进程堆栈(用户与核心)的物理页面。然后创建唯一地标记此新任务的进程标志符。但复制进程保留其父进程的标志符也是合理的。新创建的task_struct将被放入task数组中,另外将被复制进程的task_struct中的内容页表拷入新的task_struct中。 

复制完成后,Linux允许两个进程共享资源而不是复制各自的拷贝。这些资源包括文件、信号处理过程和虚拟内存。进程对共享资源用各自的count来记数。在两个进程对资源的使用完毕之前,Linux绝不会释放此资源,例如复制进程要共享虚拟内存,则其task_struct将包含指向原来进程的mm_struct的指针。mm_struct将增加count变量以表示当前进程共享的次数。 

复制进程虚拟空间所用技术的十分巧妙。复制将产生一组新的vm_area_struct结构和对应的mm_struct结构,同时还有被复制进程的页表。该进程的任何虚拟内存都没有被拷贝。由于进程的虚拟内存有的可能在物理内存中,有的可能在当前进程的可执行映象中,有的可能在交换文件中,所以拷贝将是一个困难且繁琐的工作。Linux使用一种"copy on write"技术:仅当两个进程之一对虚拟内存进行写操作时才拷贝此虚拟内存块。但是不管写与不写,任何虚拟内存都可以在两个进程间共享。只读属性的内存,如可执行代码,总是可以共享的。为了使"copy on write"策略工作,必须将那些可写区域的页表入口标记为只读的,同时描叙它们的vm_area_struct数据都被设置为"copy on write"。当进程之一试图对虚拟内存进行写操作时将产生页面错误。这时Linux将拷贝这一块内存并修改两个进程的页表以及虚拟内存数据结构。 


4.7  时钟和定时器
核心跟踪着进程的创建时间以及在其生命期中消耗的CPU时间。每个时钟滴答时,核心将更新当前进程在系统 模式与用户模式下所消耗的时间(记录在jiffies中)。 
除了以上记时器外,Linux还支持几种进程相关的时间间隔定时器。 

进程可以使用这些定时器在到时时向它发送各种信号,这些定时器如下: 


Real 
此定时器按照实时时钟记数,当时钟到期时,向进程发送SIGALRM信号。 
Virtual 
此定时器仅在进程运行时记数,时钟到期时将发送SIGVTALRM信号。 
Profile 
此定时器在进程运行和核心为其运行时都记数。当到时时向进程发送SIGPROF信号。 
以上时间间隔定时器可以同时也可以单独运行,Linux将所有这些信息存储在进程的task_struct数据结构中。通过系统调用可以设置这些时间间隔定时器并启动、终止它们或读取它们的当前值。Virtual和Profile定时器以相同方式处理。 

每次时钟滴答后当前进程的时间间隔定时器将递减,当到时之后将发送适当的信号。 

Real时钟间隔定时器的机制有些不同,这些将在kernel一章中详细讨论。每个进程有其自身的timer_list数 据结构,当时间间隔定时器运行时,它们被排入系统的定时器链表中。当定时器到期后,底层处理过程将把它从队列中删除并调用时间间隔处理过程。此过程将向进程发送SIGALRM信号并重新启动定时器,将其重新放入系统时钟队列。 

  

4.8  程序执行
象Unix一样,Linux程序通过命令解释器来执行。命令解释器是一个用户进程,人们将其称为shell程序。 

在Linux中有多个shell程序,最流行的几个是sh、bash和tcsh。除了几个内置命令如cd和pwd外,命令都是 一个可执行二进制文件。当键入一个命令时,Shell程序将搜索包含在进程PATH环境变量中查找路径中的目 录来定位这个可执行映象文件。如果找到,则它被加载且执行。shell使用上面描叙的fork机制来复制自身 然后用找到的二进制可执行映象的内容来代替其子进程。一般情况下,shell将等待此命令的完成或者子进 程的退出。你可以通过按下control-Z键使子进程切换到后台而shell再次开始运行。同时还可以使用shell 命令bg将命令放入后台执行,shell将发送SIGCONT信号以重新启动进程直到进程需要进行终端输出和输入。 

可执行文件可以有许多格式,甚至是一个脚本文件。脚本文件需要恰当的命令解释器来处理它们;例如 /bin/sh解释shell脚本。可执行目标文件包含可执行代码和数据,这样操作系统可以获得足够的信息将其 加载到内存并执行之。Linux最常用的目标文件是ELF,但是理论上Linux可以灵活地处理几乎所有目标文件 格式。 




图4.3 已注册的二进制格式 


通过使用文件系统,Linux所支持的二进制格式既可以构造到核心又可以作为模块加载。核心保存着一个可以支持的二进制格式的链表(见图4.3),同时当执行一个文件时,各种二进制格式被依次尝试。 

Linux上支持最广的格式是a.out和ELF。执行文件并不需要全部读入内存,而使用一种请求加载技术。进程 使用的可执行映象的每一部分被调入内存而没用的部分将从内存中丢弃。 


4.8.1  ELF
ELF(可执行与可连接格式)是Unix系统实验室设计的一种目标文件格式,现在已成为Linux中使用最多的格式。但与其它目标文件格式相比,如ECOFF和a.out,ELF的开销稍大,它的优点是更加灵活。ELF可执行文件 中包含可执行代码,即正文段:text和数据段:data。位于可执行映象中的表描叙了程序应如何放入进程的 虚拟地址空间中。静态连接映象是通过连接器ld得到,在单个映象中包含所有运行此映象所需代码和数据。 此映象同时也定义了映象的内存分布和首先被执行的代码的地址。 






图4.4 ELF可执行文件格式 

图4.4给出了一个静态连接的ELF可执行映象的构成。 

这是一个打印"Hello World"并退出的简单C程序。文件头将其作为一个带两个物理文件头(e_phnum = 2)的ELF映象来描叙,物理文件头位于映象文件起始位置52字节处。第一个物理文件头描叙的是映象中的可执行代码。它从虚拟地址0x8048000开始,长度为65532字节。这是因为它包含了printf()函数库代码以输出"Hello World"的静态连接映象。映象的入口点,即程序的第一条指令,不是位于映象的起始位置 而在虚拟地址0x8048090(e_entry)处。代码正好接着第二个物理文件头。这个物理文件头描叙了此程序使用的数据,它被加载到虚拟内存中0x8059BB8处。这些数据是可读并可写的。你可能已经注意到了数据 的大小在文件中是2200字节(p_filesz)但是在内存中的大小为4248字节。这是因为开始的2200字节包含的是预先初始化数据而接下来的2048字节包含的是被执行代码初始化的数据。 

当Linux将一个ELF可执行映象加载到进程的虚拟地址空间时,它并不真的加载映象。首先它建立其虚拟内存数据结构:进程的vm_area_struct树和页表。当程序执行时将产生页面错,引起程序代码和数据从物理内存中取出。程序中没有使用到的部分从来都不会加载到内存中去。一旦ELF二进制格式加载器发现这个映象是有效的ELF可执行映象,它将把进程的当前可执行映象从虚拟内存冲刷出去。当进程是一个复制映象时(所有的进程都是),父进程执行的是老的映象程序,例如象bash这样的命令解释器。同时还将清除任何信号处理过程并且关闭打开的文件,在冲刷的最后,进程已经为新的可执行映象作好了准备。不管可执行映象是哪种格式,进程的mm_struct结构中将存入相同信息,它们是指向映象代码和数据的指针。当ELF可执行映象从文件中读出且相关程序代码被映射到进程虚拟地址空间后,这些指针的值都被确定下来。同时vm_area_struct也被建立起来,进程的页表也被修改。mm_struct结构中还包含传递给程序和进程环境变量的参数的指针。 


ELF 共享库
另一方面,动态连接映象并不包含全部运行所需要的代码和数据。其中的一部分仅在运行时才连接到共享库中。ELF共享库列表还在运行时连接到共享库时被动态连接器使用。Linux使用几个动态连接器,如ld.so.1,libc.so.1和ld-linux.so.1,这些都放置在/lib目录中。这些库中包含常用代码,如C语言子程序等。如果没有动态连接,所有程序将不得不将所有库过程拷贝一份并连接进来,这样将需要更多的磁盘与虚拟内存空间。通过动态连接,每个被引用库过程的信息都可以包含在ELF映象列表中。这些信息用来帮助动态连接器定位库过程并将它连入程序的地址空间。 

  


4.8.2  脚本文件
脚本文件的运行需要解释器的帮助。Linux中有许许多多的解释器;例如wish、perl以及命令外壳程序tcsh。 Linux使用标准的Unix规则,在脚本文件的第一行包含了脚本解释器的名字。典型的脚本文件的开头如下: 

#!/usr/bin/wish 

此时命令解释器会试着寻找脚本解释器。 然后它打开此脚本解释器的执行文件得到一个指向此文件的VFS inode并使此脚本解释器开始解释此脚本。这时脚本文件名变成了脚本解释器的0号参数(第一个参数)并且其余参数向上挪一个位置(原来的第一个参数变成第二个)。脚本解释器的加载过程与其他可执行文件相同。Linux会逐个尝试各种二进制可执行格式直到它可以执行。 

  

File translated from TEX by TTH, version 1.0. 


--------------------------------------------------------------------------------

Top of Chapter, Table of Contents, Show Frames, No Frames
?1996-1999 David A Rusling copyright notice. 

⌨️ 快捷键说明

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