




已阅读5页,还剩36页未读, 继续免费阅读
版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
第5章 Linux内核简介,主要内容 Linux核心的一般结构 进程的概念、进程的调度和进程通信 文件系统的构成和管理 内存管理 设备驱动及中断处理,5.1 概 述,Linux系统大致可分为三层: 靠近硬件的底层是内核,即Linux操作系统常驻内存部分。 中间层是内核之外的shell层,即操作系统的系统程序部分。 最高层是应用层,即用户程序部分,从结构上看,Linux操作系统是采用单块结构的操作系统。 一般说来,可以将操作系统划分为内核和系统程序两部分。,进程控制系统用于进程管理、进程同步、进程通信、进程调度和内存管理等。 内存管理控制内存分配与回收。 文件系统管理文件、分配文件空间、管理空闲空间、控制对文件的访问并为用户检索数据。 Linux系统支持三种类型的硬件设备:字符设备、块设备和网络设备。 核心底层的硬件控制负责处理中断以及与机器通信。,启动一个Linux系统的过程是:一个不隶属于任何操作系统的加载程序将Linux部分内核调入内存,并将控制权交给内存中Linux内核的第一行代码。此后Linux要将自己的剩余部分全部加载到内存、初始化所有的设备、在内存中建立好所需的数据结构(有关进程、设备、内存等)。 到此为止,Linux内核的工作告一段落,内核已经控制了所有硬件设备。至于操作和使用这些硬件设备,则属于系统部分。 内核加载设备并启动init守护进程,init守护进程会根据配置文件加载文件系统、配置网络、服务进程、终端等。一旦终端初始化完毕,我们会看到系统的欢迎界面了。,系统调用和库函数,系统调用是操作系统提供的、与用户程序之间的接口,也就是操作系统提供给程序员的接口 从感觉上系统调用类似于过程调用,都由程序代码构成,使用方式相同 两者有实质差别:过程调用只能在用户态下运行,不能进入核心态;而系统调用可以实现从用户态到核心态的转变。 系统调用可大致分为五个类别:进程控制、文件管理、设备管理、信息维护和通信,库函数,它们本身并不属于操作系统的内核部分 库函数可以分为下面六大类: 文件管理 状态信息 文件修改 程序设计语言的支持 程序装入和执行 通信,调用方式 例如,creat系统调用可以创建一个新文件,其函数原型说明如下: #include #include #include int open(const char *path, int oflags); 不同的系统调用所需要的头文件(又称前导文件)是不同的。,5.2 进 程 管 理 5.2.1 进程和线程的概念,1进程及其状态 简单说来,进程就是程序的一次执行过程。 进程至少要有三种基本状态。这三种基本状态是:运行态、就绪态和封锁态(或等待态)。 进程的状态可依据一定的条件和原因而变化,进程控制块,进程的物理表示,2Linux进程状态,3进程的模式和类型 在Linux系统中,进程的执行模式划分为用户模式和内核模式 按照进程的功能和运行的程序来分,进程划分为两大类:一类是系统进程,另一类是用户进程,4Linux线程 Linux把线程定义为进程的“执行上下文” 具有一段可执行的程序、专用的系统堆栈空间、私有的“线程控制块”(即thread_struct数据结构) 缺少自己的存储空间,linux进程的概念,Linux操作系统包括三种不同类型的进程,每种进程都有自己的特点和属性: 交互进程:由shell启动的进程。 批处理进程:这种进程和终端没有联系,是一个进程序列。 守护进程(dameon进程):在后台持续运行的进程。,进程管理命令,进程查看命令 ps ps 选项 主要选项的含义如下: -e:显示所有进程; -h:不显示标题; -l:采用详细的格式来显示进程; -a:显示所有终端上的进程,包括其他用户的进程; -r:只显示当前终端上正在运行的进程; -x:显示所有进程,不以终端来区分; -u:以用户为主的格式来显示进程;,删除进程命令kill kill -s | -p -a kill -s | -p -a . kill -l 信号 选项的含义如下: -s:指定需要送出的信号。既可以是信号名也可以是信号名对应的数字。 -p:指定kill命令只显示命名进程的pid,并不真正送出任何信号。 -l:显示信号名称列表,该列表也可以在/usr/include/linux/signal.h文件中找到。,系统监视,系统监控命令top :能显示实时的进程列表,而且还能实时监视系统资源,包括内存、交换分区和CPU的使用率等。,5.2.2 Linux进程的结构,1task_struct结构 task_struct结构包含下列几方面的信息: 进程状态 调度信息 标志符(pid) 内部进程通讯 链接信息 时间和计时器 文件系统 虚拟内存 处理器信息,2进程系统堆栈 每个进程都有一个系统堆栈,用来保存中断现场信息和进程进入内核模式后执行子程序(函数)嵌套调用的返回现场信息。 每个进程的系统堆栈和task_struct数据结构之间存在紧密联系,因而二者物理存储空间也连在一起 系统堆栈的大小静态确定,用户堆栈可在运行时动态扩展,5.2.3 对进程的操作,1进程的创建 各个进程构成了树形的进程族系 内核在引导并完成了基本的初始化以后,就有了系统的第一个进程init(即初始化进程,实际上是内核线程)。除此之外,所有其他的进程和内核线程都由这个原始进程或其子孙进程所创建。 除初始化进程外,其他进程都是用系统调用fork( )和clone( )创建的。 fork( )是全部复制 ,而clone( ) 有选择地复制,1. fork函数,fork系统调用的作用是复制一个进程。当一个进程调用它,完成后就出现两个几乎一模一样的进程,我们也由此得到了一个新进程。 在Linux 中,创造新进程的方法只有一个,就是fork。其他一些库函数,如system(),看起来似乎它们也能创建新的进程,实际上也在内部调用了fork。包括我们在命令行下运行应用程序,新的进程也是由shell调用fork制造出来的。,1. fork函数,/* example_fork.c */ #include #inlcude main() pid_t pid; /*此时仅有一个进程*/ pid=fork(); /*此时已经有两个进程在同时运行*/ if(pid0) printf(“error in fork!“); else if(pid=0) printf(“I am the child process, my process ID is %dn“,getpid(); else printf(“I am the parent process, my process ID is %dn“,getpid(); ,1. fork函数,在语句pid=fork()之前,只有一个进程在执行这段代码,但在这条语句之后,就变成两个进程在执行了,这两个进程的代码部分完全相同,将要执行的下一条语句都是if(pid=0)。 两个进程中,原先就存在的那个被称作“父进程”,新出现的那个被称作“子进程”。父子进程的区别除了进程标志符(process ID)不同外,变量pid的值也不相同,pid存放的是fork的返回值。 fork调用的一个奇妙之处就是它仅仅被调用一次,却能够返回两次,它可能有三种不同的返回值: 在父进程中,fork返回新创建子进程的进程ID; 在子进程中,fork返回0; 如果出现错误,fork返回一个负值; fork出错可能有两种原因:(1)当前的进程数已经达到了系统规定的上限,这时errno的值被设置为EAGAIN。(2)系统内存不足,这时errno的值被设置为ENOMEM。,2. getpid和getppid函数,#include #include pid_t getpid(void); pid_t getpgrp(void); pid_t getppid(void); 说明:分别返回调用进程的进程标识符、进程组标识符和其父进程标识符。,2进程的等待 父进程可用系统调用wait ( )等待它的任一个子进程终止,也可以用系统调用wait ( )等待某个特定的子进程终止。 wait ( )算法如下: (1)如果父进程没有子进程,则出错返回。 (2)如果发现有一个终止的子进程,则取出子进程的进程号,把子进程的CPU使用时间等加到父进程上,释放子进程占用的task_struct和系统空间堆栈,以供新进程使用。 (3)如果发现有子进程,但都不处于终止态,则父进程睡眠,等待由相应的信号唤醒。,5. wait和waitpid函数,#include #include pid_t wait(int *status); pid_t waitpid(pid_t pid, int *status, int options); 一个进程在终止时会关闭所有文件描述符,释放在用户空间分配的内存,但它的PCB还保留着,内核在其中保存了一些信息:如果是正常终止则保存着退出状态,如果是异常终止则保存着导致该进程终止的信号是哪个。这个进程的父进程可以调用wait或waitpid获取这些信息,然后彻底清除掉这个进程。 如果一个进程已经终止,但是它的父进程尚未调用wait或waitpid对它进行清理,这时的进程状态称为僵尸(Zombie)进程。任何进程在刚终止时都是僵尸进程,正常情况下,僵尸进程都立刻被父进程清理了. 如果一个父进程终止,而它的子进程还存在(这些子进程或者仍在运行,或者已经是僵尸进程了),则这些子进程的父进程改为init进程。,5. wait和waitpid函数,这两个函数的区别是: 1.如果父进程的所有子进程都还在运行,调用wait将使父进程阻塞,而调用waitpid时如果在options参数中指定WNOHANG可以使父进程不阻塞而立即返回0。 2.wait等待第一个终止的子进程,而waitpid可以通过pid参数指定等待哪一个子进程。 可见,调用wait和waitpid不仅可以获得子进程的终止信息,还可以使父进程阻塞等待子进程终止,起到进程间同步的作用。如果参数status不是空指针,则子进程的终止信息通过这个参数传出,如果只是为了同步而不关心子进程的终止信息,可以将status参数指定为NULL。,5. wait和waitpid函数,对于waitpid函数中的pid参数的作用见下表: pid = -1 等待任一子进程。 pid 0 等待其进程ID与pid相等的子进程 pid = 0 等待其组ID等于调用进程组ID的任一的子进程 pid -1 等待其组ID等于pid绝对值的任一的子进程 对于waitpid函数中的options参数的作用见下表: WCONTINUED 若实现支持作业控制,那么由pid指定的任一子进程在暂停后已经继续,但是状态没报告,则返回其状态 WNOHANG 若由pid指定的子进程并不是立即可用的,则waitpid不阻塞,此时返回值为0 WUNTRACED 若实现支持作业控制,那么由pid指定的任一子进程已经处于暂停状态并没报告过,则返回其状态,3进程的终止 进程可使用系统调用exit( )终止自己 其实现算法如下: (1)撤消所有的信号量。 (2)释放其所有的资源,包括存储空间、已打开的文件、工作目录、信号处理表等。 (3)置进程状态为“终止态”(TASK_ZOMBIE)。 (4)向它的父进程发送子进程终止的信号。 (5)执行进程调度。,4. exit和_exit函数,void exit(int status); void _exit(int status); exit()函数定义在 stdlib.h中,而_exit()定义在unistd.h中。_exit()函数的作用简单:直接使进程停止运行,清除其使用的内存空间,并销毁其在内核中的各种数据结构;exit()函数则在这些基础上作了一些包装,在执行退出之前加了若干道工序。 exit()函数与_exit()函数最大的区别就在于exit()函数在调用exit系统调用之前要检查文件的打开情况,把文件缓冲区中的内容写回文件,就是图中的“清理I/O缓冲”一项。,4进程映像的更换 改换进程映像的工作很复杂,是由系统调用execve( )实现的,它用一个可执行文件的副本来覆盖该进程的内存空间。 ELF可执行文件格式示意图 execve( )系统调用的基本算法如下: (1)验证文件的可执行性,即用户 有权执行它。 (2)读文件头,检查它是一个可装入模块。 (3)释放原有的内存空间。 (4)按照可执行文件的要求分配新的内存空间,并装入内存。,3. exec函数族,在Linux中,并不存在exec()函数,exec指的是一组函数,一共有6个,分别是: #include extern char *environ; int execl(const char *path, const char *arg, .); int execlp(const char *file, const char *arg, .); int execle(const char *path, const char *arg, ., char * const envp); int execv(const char *path, char *const argv); int execvp(const char *file, char *const argv); int execve(const char *path, char *const argv, char *const envp); 其中只有execve是真正意义上的系统调用,其它都是在此基础上经过包装的库函数。 exec函数族的作用是根据指定的文件名找到可执行文件,并用它来取代调用进程的内容,换句话说,就是在调用进程内部执行一个可执行文件。这里的可执行文件既可以是二进制文件,也可以是任何Linux下可执行的脚本文件。,3. exec函数族,函数名与参数的关系: 细看一下,这6个函数都是以exec开头(表示属于exec函数组),前3个函数接着字母l,后3个接着字母v。 l表示list(列举参数),v表示vector(参数向量表)。它们的区别在于,execv开头的函数是以“char *argv”(vector)形式传递命令行参数,而execl开头的函数采用了罗列(list)的方式,把参数一个一个列出来,然后以一个NULL表示结束。 字母p是指在环境变量PATH的目录里去查找要执行的可执行文件。2个以p结尾的函数execlp和execvp,和execl与execv的差别很小,除execlp和execvp之外的4个函数都要求,它们的第1个参数path必须是一个完整的路径,如“/bin/ls“;而execlp和execvp的第1个参数file可以仅仅只是一个文件名,如“ls“,这两个函数可以自动到环境变量PATH指定的目录里去查找。 字母e是指给可执行文件指定环境变量。在全部6个函数中,只有execle和execve使用了char *envp传递环境变量,用指定的环境变量去替代默认的那些。,3. exec函数族,返回值 exec函数族的函数执行成功后不会返回,因为调用进程的实体,包括代码段,数据段和堆栈等都已经被新的内容取代,只有进程ID等一些表面上的信息仍保持原样。调用失败时,会设置errno并返回-1,然后从原程序的调用点接着往下执行。 与其他系统调用比起来,exec很容易失败,被执行文件的位置、权限等很多因素都能导致调用失败。因此,使用exec函数族时,一定要加错误判断语句。 最常见的错误: 找不到文件或路径,此时errno被设置为ENOENT; 数组argv和envp忘记用NULL结束,此时errno被设置为EFAULT; 没有对要执行文件的运行权限,此时errno被设置为EACCES。,3. exec函数族,应用 如果一个进程想执行另一个程序,它就可以fork出一个新进程,然后调用任何一个exec函数。 为此,Linux还专门对fork作了优化:通常fork会将调用进程的所有内容原封不动的拷贝到新产生的子进程中去,这些拷贝的动作很消耗时间,而如果fork完之后我们马上就调用exec,那这些辛辛苦苦拷贝来的东西就会被立刻抹掉,这看起来非常不划算,于是人们设计了一种“写时复制(copy-on-write)“技术,使得fork结束后并不立刻复制父进程的内容到子进程,而是到了真正使用时才复制,这样如果下一条语句是exec,它就不会作无用功了。其实“写时复制“还是有复制,进程的mm结构、页表都还是被复制了。,3. exec函数族,例程 /* example_exec.c */ #include void main(void) if (fork() = 0) if (execl(“/bin/echo“, “echo“, “executed by execl“, NULL) 0) if (execlp(“echo“, “echo“, “executed by execlp“, NULL) 0) if (execle(“/usr/bin/env“,
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 婚姻忠诚协议中家庭医疗决策权重约定书
- 商务楼宇外立面清洗维护与节能减排服务协议
- 环保文化节策划与执行合作协议
- 汽车共享平台新能源汽车调度租赁专项协议
- 离职人员保密协议与竞业禁止合同(物流配送业)
- 治疗案例临床解析与经验分享
- 护理术后宣教事件案例
- 酒店大堂照明设施合同(2篇)
- 新噪声管理条例解读
- 双胎宝宝护理日常
- 中国企业科创力研究报告2024
- 细胞培养技术的基础试题及答案
- (广东二模)2025年广东省高三高考模拟测试(二)历史试卷(含答案)
- GB/T 14601-2025电子特气氨
- 湖北省武汉第二中学2025届高三3月高考模拟考试数学试题试卷
- 培训机构兼职老师聘用协议书范本
- 透析患者贫血的护理查房
- 2025年上半年生态环境部信息中心招聘工作人员22人重点基础提升(共500题)附带答案详解
- (高清版)DB11∕T1008-2024建筑光伏系统安装及验收规程
- 天然气泄露调查报告范文
- 《心脏骤停的急救护理》课件
评论
0/150
提交评论