资源预览内容
第1页 / 共78页
第2页 / 共78页
第3页 / 共78页
第4页 / 共78页
第5页 / 共78页
第6页 / 共78页
第7页 / 共78页
第8页 / 共78页
第9页 / 共78页
第10页 / 共78页
亲,该文档总共78页,到这儿已超出免费预览范围,如果喜欢就下载吧!
资源描述
第第2章章 Linux内核内核进程管理进程管理1;.本章主要介绍:本章主要介绍:v进程概念进程概念v进程的组成进程的组成v进程的状态和调度进程的状态和调度v进程间关系进程间关系v中断处理与定时器中断处理与定时器v系统调用系统调用v进程间通信进程间通信21 进程概念进程概念 20世纪世纪60年代,进程年代,进程(process)一词首先在麻省理工学院的一词首先在麻省理工学院的MULTICS和和IBM的的CTSS/360系统中被引入。系统中被引入。 对进程下个准确定义不容易,但有必要强调一下进程具有的两个重要特性。对进程下个准确定义不容易,但有必要强调一下进程具有的两个重要特性。31. 独立性独立性 进程进程是系统中独立存在的实体,它可以拥有自己独立的资源,比如文件和设备描述是系统中独立存在的实体,它可以拥有自己独立的资源,比如文件和设备描述符等。符等。 在没有经过进程本身允许的情况下,其他进程不能访问到这些资源。这一点上和线在没有经过进程本身允许的情况下,其他进程不能访问到这些资源。这一点上和线程有很大的不同。程有很大的不同。线程线程是共享资源的程序实体,创建一个线程所花费的系统开销要比创建一个进程小是共享资源的程序实体,创建一个线程所花费的系统开销要比创建一个进程小得多。得多。 一个程序至少有一个进程一个程序至少有一个进程,一个进程至少有一个线程。一个进程至少有一个线程。 线程是进程的一个实体线程是进程的一个实体,是是CPU调度和分派的基本单位,它是比进程更小的能独立运行调度和分派的基本单位,它是比进程更小的能独立运行的基本单位。线程自己基本上不拥有系统资源,只拥有一点在运行中必不可少的资源的基本单位。线程自己基本上不拥有系统资源,只拥有一点在运行中必不可少的资源(如程如程序计数器序计数器,一组寄存器和栈一组寄存器和栈),但是它可与同属一个进程的其他的线程共享进程所拥有的全部,但是它可与同属一个进程的其他的线程共享进程所拥有的全部资源。资源。42. 动态性动态性 进程与程序的区别在于,进程与程序的区别在于,程序程序只是一个静态的指令集合,而只是一个静态的指令集合,而进程进程是一个正在系统中是一个正在系统中活动的指令集合。活动的指令集合。 在进程中加入了时间的概念。进程具有自己的生命周期和各种不同的状态,这些概在进程中加入了时间的概念。进程具有自己的生命周期和各种不同的状态,这些概念在程序中都是不具备的。念在程序中都是不具备的。5 由于以上两个性质,又可以衍生出进程的第三个重要特性,即由于以上两个性质,又可以衍生出进程的第三个重要特性,即并发性并发性。 若干个进程可以在单处理机状态上并发执行。注意并发性若干个进程可以在单处理机状态上并发执行。注意并发性(concurrency)和多处理和多处理机并行机并行(parallel)是两个不同的概念。是两个不同的概念。 6 并行并行指在同一时刻内,有多条指令在多个处理机上同时执行;指在同一时刻内,有多条指令在多个处理机上同时执行; 并发并发指在同一时刻内可能只有一条指令执行,但多个进程的指令被快速轮换执行,指在同一时刻内可能只有一条指令执行,但多个进程的指令被快速轮换执行,使得在宏观上具有多个进程同时执行的效果。使得在宏观上具有多个进程同时执行的效果。72 进程的组成进程的组成 作为申请系统资源的基本单位,进程必须有一个对应的物理内存空间。作为申请系统资源的基本单位,进程必须有一个对应的物理内存空间。 而对这样的一块空间,首先要用数据结构进行描述,才能进一步对之进行管理。而对这样的一块空间,首先要用数据结构进行描述,才能进一步对之进行管理。8 在在Linux中,进程以进程号中,进程以进程号PID(process ID)作为标识。作为标识。 任何对进程进行的操作都要给予其相应的任何对进程进行的操作都要给予其相应的PID号。号。 每个进程都属于一个用户,进程要配备其所属的用户编号每个进程都属于一个用户,进程要配备其所属的用户编号UID。 此外,每个进程都属于多个用户组,所以进程还要配备其归属的用户组编号此外,每个进程都属于多个用户组,所以进程还要配备其归属的用户组编号GID的的数组数组 9 UID和和GID都分都分4种,种, UID包括包括uid,euid,suid和和fsuid, GID包括包括gid,egid,sgid和和fsgid。 一般来说一般来说uid=euid=fsuid,gid=egid=fsgid。10进程标识:进程标识:vuid和和gid是运行进程的用户标识和用户组标识。是运行进程的用户标识和用户组标识。veuid和和egid又称为有效的又称为有效的uid和和gid。出于系统安全权限的考虑,运行程序时要检查。出于系统安全权限的考虑,运行程序时要检查euid和和egid的合法性。通常,的合法性。通常,uid等于等于euid,gid等于等于egid。有时候,系统会赋予一般用户暂时拥。有时候,系统会赋予一般用户暂时拥有有root的的uid和和gid(作为用户进程的(作为用户进程的euid和和egid),以便于进行运作。),以便于进行运作。vsuid和和sgid是根据是根据POSIX标准引入的,在系统调用改变标准引入的,在系统调用改变uid和和gid时,用于保留真正的时,用于保留真正的uid和和gid。vfsuid和和fsgid称为文件系统的称为文件系统的uid和和gid,用于对文件系统操作时的合法性检查,是,用于对文件系统操作时的合法性检查,是LINUX独特的标识类型。它们一般分别和独特的标识类型。它们一般分别和euid和和egid一致,但在一致,但在NFS文件系统中文件系统中NFS服务器需要服务器需要作为一个特殊的进程访问文件,这时只修改客户进程的作为一个特殊的进程访问文件,这时只修改客户进程的fsuid和和fsgid。11进程运行的环境称为进程上下文进程运行的环境称为进程上下文(context)。Linux中进程的上下文由中进程的上下文由进程控制块进程控制块PCB(process control block)、正文段正文段(text segment)、数据段数据段(data segment)以及以及用户堆栈用户堆栈(stack)组成。组成。 其中其中:正文段正文段存放该进程的可执行代码存放该进程的可执行代码;数据段数据段存放进程中静态产生的数据结构存放进程中静态产生的数据结构;PCB包括进程的编号、状态、优先级以及正文段和数据段中数据分布的大概情况。包括进程的编号、状态、优先级以及正文段和数据段中数据分布的大概情况。12 个称做进程表个称做进程表(process table)的链表结构将系统中所有的的链表结构将系统中所有的PCB块联系起来,块联系起来,如如图图2-1所示所示。 13Linux源代码中也将进程称作任务源代码中也将进程称作任务(Task),Linux上的所有进程都是由上的所有进程都是由task_struct结构体来管理。生成一个进程时会产生一个结构体来管理。生成一个进程时会产生一个task_struct结构体,此后通过结构体,此后通过task_struct结构来管理进程。当然文件也是通过结构来管理进程。当然文件也是通过task_struct结构来管理。结构来管理。 Linux中的中的PCB块又称为块又称为task_struct结构,在结构,在2.4版本内核中,每个版本内核中,每个task_struct占占1680个字节。个字节。Linux根据系统物理内存的大小限制已打开进程的总数目。根据系统物理内存的大小限制已打开进程的总数目。 15 系统每次访问一个进程时,内核根据系统每次访问一个进程时,内核根据PID在进程表中查找相应的进程在进程表中查找相应的进程PCB块块(具体具体查找过程通过一个查找过程通过一个PID的的hash表实现表实现),再通过,再通过PCB块找到其对应的代码段与数据段,并进块找到其对应的代码段与数据段,并进行操作。行操作。163 进程的状态和调度进程的状态和调度17Linux系统信号系统信号信号主要用于通知进程异步事件的发生。在信号主要用于通知进程异步事件的发生。在Linux中可以识别中可以识别29种不同的信号,这些种不同的信号,这些信号中的大部分都有了预先定义好的意义,信号中的大部分都有了预先定义好的意义,进程可以显式的用进程可以显式的用kill或或killpg系统调用来向另一个进程发信号。系统调用来向另一个进程发信号。 18 进程可以通过提供信号处理函数来取代对于任意信号的缺省反应,这种缺省反应一进程可以通过提供信号处理函数来取代对于任意信号的缺省反应,这种缺省反应一般都是终止进程。般都是终止进程。 信号发生时,内核中断当前的进程,进程执行处理函数来响应信号,结束后恢复正信号发生时,内核中断当前的进程,进程执行处理函数来响应信号,结束后恢复正常的进程处理。常的进程处理。 信号有自己的名称和特定的编号,见信号有自己的名称和特定的编号,见表表3-1所示所示。19进程状态进程状态进程是一个动态的实体,故而它是有生命的。进程是一个动态的实体,故而它是有生命的。从创建到消亡,是一个进程的整个生命周期。从创建到消亡,是一个进程的整个生命周期。在这个周期中,进程可能会经历各种不同的状态。在这个周期中,进程可能会经历各种不同的状态。一般来说,所有进程都要经历以下一般来说,所有进程都要经历以下3种状态。种状态。 21 就绪就绪(ready)态:态: 指进程已经获得所有所需的其他资源,并正在申请处理机资源,准备开始运行。指进程已经获得所有所需的其他资源,并正在申请处理机资源,准备开始运行。 这种情况下,称进程处于就绪态。这种情况下,称进程处于就绪态。22 阻塞阻塞(blocked)态:态: 指进程因为需要等待所需资源而放弃处理机,或者进程本不拥有处理机,且其他资指进程因为需要等待所需资源而放弃处理机,或者进程本不拥有处理机,且其他资源也没有满足,从而即使得到处理机资源也不能开始运行。源也没有满足,从而即使得到处理机资源也不能开始运行。 这种情况下,称进程处于阻塞态。阻塞状态又称休眠状态或者等待状态。这种情况下,称进程处于阻塞态。阻塞状态又称休眠状态或者等待状态。23 运行态:运行态: 进程得到了处理机,并不需要等待其他任何资源,正在执行的状态,称之为运行态。进程得到了处理机,并不需要等待其他任何资源,正在执行的状态,称之为运行态。 只有在运行态时,进程才可以使用所申请到的资源。只有在运行态时,进程才可以使用所申请到的资源。24内核以此感知进程的存在内核以此感知进程的存在财产登记卡,记录着进程所占用的各项资源财产登记卡,记录着进程所占用的各项资源Task_struct数据结构数据结构25进程结构进程结构vtask_struct数据结构数据结构进程标志符进程标志符PID进程所占的内存区域进程所占的内存区域相关文件的文件描述符相关文件的文件描述符安全信息安全信息进程环境进程环境信号处理信号处理资源安排资源安排同步处理同步处理进程状态进程状态include/Linux/sched.h26task_struct (include/linux/sch.h)27进程的地址空间进程的地址空间vtask_struct中有中有 struct mm_struct *mm; 内存描述符内存描述符 mm_struct 里面有一个字段里面有一个字段mmp 指向内存线形区链表的首部。指向内存线形区链表的首部。28进程的状态进程的状态vtask_struct 中的中的state 表示进程当前的状态表示进程当前的状态Linux中的进程主要有中的进程主要有5个状态个状态:(include/linux/sched.h)#define TASK_RUNNING0#define TASK_INTERRUPTIBLE1 /等待资源等待资源#define TASK_UNINTERRUPTIBLE2#define TASK_STOPPED4 /等待信号等待信号#define TASK_ZOMBIE829 RUNNING: 正在运行,或者在就绪队列中等待运行的进程。正在运行,或者在就绪队列中等待运行的进程。 也就是上面提到的运行态和就绪态进程的综合。也就是上面提到的运行态和就绪态进程的综合。 一个进程处于一个进程处于RUNNING状态,并不代表它一定在被执行。状态,并不代表它一定在被执行。 31 由于在多任务系统中,各个就绪进程需要并发执行,所以在某个特定时刻,这些处由于在多任务系统中,各个就绪进程需要并发执行,所以在某个特定时刻,这些处于于RUNNING状态的进程之中,只有一个能够得到处理机,而其他进程必须在一个就绪队状态的进程之中,只有一个能够得到处理机,而其他进程必须在一个就绪队列中等待。列中等待。 即使是在多处理机的系统中,即使是在多处理机的系统中,Linux也只能同时让一个处理机执行任务。也只能同时让一个处理机执行任务。32 UNINTERRUPTABLE: 不可中断阻塞状态。不可中断阻塞状态。 处于这种状态的进程正在等待队列中,当资源有效时,可由操作系统进行唤醒,否处于这种状态的进程正在等待队列中,当资源有效时,可由操作系统进行唤醒,否则,将一直处于等待状态。则,将一直处于等待状态。33 INTERRUPTABLE: 可中断阻塞状态。可中断阻塞状态。 与不可中断阻塞状态一样,处于这种状态的进程也在等待队列中,当资源有效时,与不可中断阻塞状态一样,处于这种状态的进程也在等待队列中,当资源有效时,可以由操作系统进行唤醒。可以由操作系统进行唤醒。 与不可中断阻塞状态有所不同的是,处于此状态中的进程亦可被其他进程的信号和与不可中断阻塞状态有所不同的是,处于此状态中的进程亦可被其他进程的信号和定时中断唤醒。定时中断唤醒。34 STOPPED: 挂起状态。挂起状态。 进程被暂停,需要通过其他进程的信号才能被唤醒。进程被暂停,需要通过其他进程的信号才能被唤醒。 导致这种状态的原因有两种。导致这种状态的原因有两种。 其一是受到了相关信号其一是受到了相关信号(SIGSTOP、SIGSTP、SIGTTIN 或或SIGTTOU)的反应;的反应; 其二是受到父进程其二是受到父进程ptrace调用的控制,而暂时将处理机交给控制进程。调用的控制,而暂时将处理机交给控制进程。35 ZOMBIE: 僵尸状态。僵尸状态。 表示进程结束但尚未消亡的一种状态。表示进程结束但尚未消亡的一种状态。 此时进程已经结束运行并释放此时进程已经结束运行并释放大部分资源,但尚未释放进程控制块。大部分资源,但尚未释放进程控制块。36进程调度进程调度 调度程序调度程序(scheduler)用来实现进程状态之间的转换。用来实现进程状态之间的转换。 在在Linux中,调度程序由系统调用中,调度程序由系统调用schedule()来完成。来完成。 schedule()是一个怪异的函数,它与一般是一个怪异的函数,它与一般C语言函数不同,因为它的调用和返回不语言函数不同,因为它的调用和返回不在同一个进程中。在同一个进程中。37 用户进程由用户进程由fork()系统调用实现。用户进程由系统调用实现。用户进程由do_fork()函数创建,它也是函数创建,它也是fork系统系统调用的执行者。调用的执行者。 fork()创建一个新的进程,继承父进程的现有资源,初始化进程时钟、信号、时间创建一个新的进程,继承父进程的现有资源,初始化进程时钟、信号、时间等数据。等数据。 完成子进程初始化后,父进程将它挂到就绪队列,返回子进程的完成子进程初始化后,父进程将它挂到就绪队列,返回子进程的PID。38 进程创建时的状态为进程创建时的状态为UNINTERRUPTIBLE,在,在fork()结束前被父进程唤醒后,变为结束前被父进程唤醒后,变为RUNNING。 处于处于RUNNING状态的进程被移到就绪队列中,在适当时候由状态的进程被移到就绪队列中,在适当时候由schedule()按处理机按处理机调度算法选中,获得处理机。调度算法选中,获得处理机。39 获得处理机而正在运行的进程若申请不到某个资源,则调用获得处理机而正在运行的进程若申请不到某个资源,则调用sleep()进行休眠,其进行休眠,其PCB挂到相应的等待队列,状态变为挂到相应的等待队列,状态变为UNINTERRUPTIBLE或者或者INTERRUPTIBLE。 sleep()将调用将调用schedule()函数把休眠进程释放的处理机分配给就绪队列中的某个进函数把休眠进程释放的处理机分配给就绪队列中的某个进程程.40 状态为状态为INTERRUPTIBLE的休眠进程当它申请的资源有效时被唤醒,也可以由信号的休眠进程当它申请的资源有效时被唤醒,也可以由信号或定时中断唤醒。或定时中断唤醒。 而状态为而状态为UNINTERRUPTIBLE的休眠进程只有当它申请的资源有效时被唤醒,不的休眠进程只有当它申请的资源有效时被唤醒,不能被信号和定时中断唤醒。能被信号和定时中断唤醒。 唤醒后,进程状态改为唤醒后,进程状态改为RUNNING,并进入就绪队列。,并进入就绪队列。41 进程执行系统调用进程执行系统调用exit()或收到外部的杀死进程信号或收到外部的杀死进程信号SIG_KILL时,进程状态变为时,进程状态变为ZOMBIE,释放所申请资源。,释放所申请资源。 同时启动同时启动schedule()把处理机分配给就绪队列中其他进程。把处理机分配给就绪队列中其他进程。42 若进程通过系统调用设置了跟踪标志位,则在系统调用返回前,进入跟踪状态,进若进程通过系统调用设置了跟踪标志位,则在系统调用返回前,进入跟踪状态,进程状态变为程状态变为STOPPED,处理机分配给就绪队列中其他进程。,处理机分配给就绪队列中其他进程。 只有通过其他进程发送只有通过其他进程发送SIG_KILL信号或继续信号信号或继续信号SIG_CONT,才能把,才能把STOPPED进程唤醒。重新进入就绪队列。进程唤醒。重新进入就绪队列。43 对每一个进程,其对每一个进程,其PCB块中都可以记录一种调度策略。块中都可以记录一种调度策略。 进程调度算法可采用先进先出算法进程调度算法可采用先进先出算法(FIFO)或轮转法或轮转法(round-robin),有实时,有实时(这里的这里的“实时实时”,只是一种说法。实际上,未经改造的,只是一种说法。实际上,未经改造的Linux很难实现很难实现“实时实时”)和非实时两种形和非实时两种形式。式。 44 若采用若采用Linux的轮转法,当时间片到时的轮转法,当时间片到时(10ms的整数倍的整数倍),由时钟中断触发,引起新,由时钟中断触发,引起新一轮调度,把当前进程挂到就绪队列队尾。一轮调度,把当前进程挂到就绪队列队尾。 在在schedule()中有一个中有一个goodness()函数,可以用来保证实时的进程可以得到优先函数,可以用来保证实时的进程可以得到优先调用。调用。 然而这只是在调用上优先,事实上在内核态下,实时进程并不能对普通进程进行抢然而这只是在调用上优先,事实上在内核态下,实时进程并不能对普通进程进行抢占。占。 所以所以Linux中的实时并不是真正意义上的实时。中的实时并不是真正意义上的实时。454 进程间关系进程间关系Linux中除了中除了0号进程是启动时由系统创建,其余进程都是由其他进程自行创建的。号进程是启动时由系统创建,其余进程都是由其他进程自行创建的。为了表示这种创建关系,用父进程指代缔造者,用子进程指代被创建出的为了表示这种创建关系,用父进程指代缔造者,用子进程指代被创建出的新进程。新进程。如果进程如果进程A是进程是进程B的间接父进程,则的间接父进程,则A称做称做B的祖先,的祖先,B为为A的后代。的后代。既然提到了父子关系,那么这两个进程之间自然是有着如同父子一样的继承性。既然提到了父子关系,那么这两个进程之间自然是有着如同父子一样的继承性。46v进程的进程的“宗族宗族”关系关系树型组织树型组织task_struct中的中的 struct task_struct *p_pptr, *p_cptr, *p_ysptr, *p_osptr;vp_pptr: parent (父进程父进程)vp_cptr: child (指向自己最年轻、最新的子进程指向自己最年轻、最新的子进程)vp_ysptr:指向比自己年轻的兄弟进程:指向比自己年轻的兄弟进程vp_osptr:指向比自己老的兄弟进程:指向比自己老的兄弟进程47 在数据结构上,父进程在数据结构上,父进程PCB中的指针中的指针p_cptr指向最近创建的一个子进程的指向最近创建的一个子进程的PCB块,块,而每个子进程而每个子进程PCB中的指针中的指针p_pptr都指向其父进程的都指向其父进程的PCB块。块。 这一对指针构成了进程的父子关系,这一对指针构成了进程的父子关系,如图如图2-3所示所示。 48vtask数组(实际是双向链表指针)数组(实际是双向链表指针)包含指向系统中所有包含指向系统中所有task_struct结构的指针结构的指针数组大小限制了系统中的进程数目数组大小限制了系统中的进程数目将所有任务串连起来将所有任务串连起来vPid hash表表通过通过pid查找进程时,利用查找进程时,利用hash快速定位双向指针快速定位双向指针vrun_list动态的将任务链入动态的将任务链入prio_array中的某个优先级队列中中的某个优先级队列中50vcurrent指针指针当前运行的进程的结构用当前运行的进程的结构用current指针表示指针表示vinit进程进程系统初始化后,建立的第一个进程系统初始化后,建立的第一个进程第一个第一个task_struct: INIT_TASK51 系统启动时,内核被加载到内存后,由系统启动时,内核被加载到内存后,由start_kernel函数函数(完成内核初始化工作完成内核初始化工作)从从无到有地自行创建了一个内核进程,叫做无到有地自行创建了一个内核进程,叫做0号进程,其所运行的代码是号进程,其所运行的代码是init_task()函数,在函数,在很多链表中起表头的作用。只有当没有其他进程处于可运行状态时,调度程序才选择很多链表中起表头的作用。只有当没有其他进程处于可运行状态时,调度程序才选择0号号进程。进程。 该进程的作用是作为一切其他进程的父进程,就像亚当夏娃是一切人类的祖先那样。该进程的作用是作为一切其他进程的父进程,就像亚当夏娃是一切人类的祖先那样。 0号进程不能自动生成,必须手工将其设置到进程表中去,才能启动进程管理机制。号进程不能自动生成,必须手工将其设置到进程表中去,才能启动进程管理机制。52 在启动进程管理机制以后,就可以由进程自行创建新的子进程。在启动进程管理机制以后,就可以由进程自行创建新的子进程。 创建新进程的调用是创建新进程的调用是fork()。fork一词在英文中是一词在英文中是“分叉分叉”的意思。的意思。 同样,在同样,在Linux中,中,fork()调用也起了一个调用也起了一个“分叉分叉”的作用。的作用。 当进程当进程A调用调用fork()生成进程生成进程B时,时,fork()函数同时在函数同时在A和和B两个进程中返回。两个进程中返回。 其中,父进程其中,父进程A里的里的fork()返回了子进程的返回了子进程的PID,而子进程,而子进程B里的里的fork()返回返回0。如果。如果出现错误,出现错误,fork()返回一具负值。返回一具负值。53 然而,然而,fork()函数究竟做了些什么呢函数究竟做了些什么呢? 我们发现,经过我们发现,经过fork()以后,父进程和子进程拥有相同内容的代码段、数据段和用以后,父进程和子进程拥有相同内容的代码段、数据段和用户堆栈,就像父进程把自己克隆了一遍。户堆栈,就像父进程把自己克隆了一遍。 事实上,父进程只复制了自己的事实上,父进程只复制了自己的PCB块,而代码段、数据段、用户堆栈内存空间块,而代码段、数据段、用户堆栈内存空间并没有复制一份,而是与子进程共享并没有复制一份,而是与子进程共享 .fork函数演示见文函数演示见文件件fork.swf54 从从概概念念上上讲讲,fork()就就像像细细胞胞的的裂裂变变,调调用用fork()的的进进程程就就是是父父进进程程,而而新新裂裂变变出出的的进进程程就就是是子子进进程程。新新创创建建的的进进程程与与父父进进程程几几乎乎完完全全相相同同,只只有有少少量量属属性性必必须须不不同同。例例,每每个个进进程程的的PID必须是唯一的。必须是唯一的。 调用调用fork()后,子进程被创建,此时父进程和子进程都从这个系统调用内部继续运行。为了后,子进程被创建,此时父进程和子进程都从这个系统调用内部继续运行。为了区分父子进程,区分父子进程,fork()给两个进程返回不同的值。对父进程,给两个进程返回不同的值。对父进程,fork()返回新创建子进程的进程返回新创建子进程的进程标识符(标识符(PID),而对子进程,),而对子进程,fork()返回值返回值0 。55 只有当子进程在运行中出现写操作时,才会产生中断,并为子进程分配内存空间。只有当子进程在运行中出现写操作时,才会产生中断,并为子进程分配内存空间。 由于父进程的由于父进程的PCB和子进程的一样,所以在和子进程的一样,所以在PCB中所记录的父进程占有中所记录的父进程占有的资源,也是与子进程共享使用的。的资源,也是与子进程共享使用的。 这里的这里的“共享共享”一词就意味着一词就意味着“竞争竞争”。 56 有时候为了避免父进程和子进程竞争相同的资源或者出于代码串行性考虑,我们希有时候为了避免父进程和子进程竞争相同的资源或者出于代码串行性考虑,我们希望父进程可以等待子进程运行结束后再继续执行。望父进程可以等待子进程运行结束后再继续执行。 调用调用vfork()可以使在子进程创建后,随即向父进程发送可以使在子进程创建后,随即向父进程发送SIG_STOP信号,使父进信号,使父进程进入挂起状态,直到子进程发送信号表示其已经结束为止。程进入挂起状态,直到子进程发送信号表示其已经结束为止。57 如果系统中只提供如果系统中只提供fork()调用,那么整个操作系统的所有进程就都只能运行同一个调用,那么整个操作系统的所有进程就都只能运行同一个程序了,因为其代码段都是复制或者共享的。程序了,因为其代码段都是复制或者共享的。 Linux为了创建进程运行新的程序,采用为了创建进程运行新的程序,采用fork+exec方法。方法。 execve()支持在新的进程创建后,动态装入新的可执行文件作为自己新的代码段。支持在新的进程创建后,动态装入新的可执行文件作为自己新的代码段。 并且并且execve()支持多种可执行文件的格式。支持多种可执行文件的格式。585 进程间通讯进程间通讯 用户态进程间处于并发状态。用户态进程间处于并发状态。 为了协调进程的运行,需要实现进程之间通信的机制。为了协调进程的运行,需要实现进程之间通信的机制。59在在Linux中,进程间通信有以下几种方法:中,进程间通信有以下几种方法: 1.管道机制管道机制 该机制最适用于解决生产者该机制最适用于解决生产者消费者问题。消费者问题。 管道是一种在进程之间单向流动数据的结构。管道是一种在进程之间单向流动数据的结构。 源进程向管道写数据,而内核会自动将这些数据引导向目标进程。源进程向管道写数据,而内核会自动将这些数据引导向目标进程。 在在POSIX标准中,管道必须是单向的。标准中,管道必须是单向的。 60 虽然通过虽然通过pipe()调用会产生两个描述符调用会产生两个描述符(写管道和读管道写管道和读管道),但是在写之前必须关闭,但是在写之前必须关闭读管道,反之亦然。读管道,反之亦然。 而在而在Linux中,使用一个管道的同时还可以使用另一个管道,这就大大增强了管道中,使用一个管道的同时还可以使用另一个管道,这就大大增强了管道使用的简便性。使用的简便性。 612. 先进先出先进先出(FIFO)机制机制 管道机制的最大缺点是不能由多个进程共享,除非此管道为这些进程共同的祖先所管道机制的最大缺点是不能由多个进程共享,除非此管道为这些进程共同的祖先所创建。创建。 为了解决这个问题,为了解决这个问题,Linux中引入了中引入了FIFO机制机制(又称为又称为named pipe,命名管道,命名管道)。 62 FIFO为为“first in,first out”的简写,指一个在磁盘上的文件,它可以被所有进的简写,指一个在磁盘上的文件,它可以被所有进程所共享。程所共享。 但是但是FIFO与一般文件不同,它还使用了内核中的缓冲区,所以在效率上要比一般与一般文件不同,它还使用了内核中的缓冲区,所以在效率上要比一般共享文件快得多。共享文件快得多。 FIFO和管道都可以使用和管道都可以使用read()和和write()调用来进行读写操作。调用来进行读写操作。633. IPC机制机制 IPC是是“interprocess communication”的缩写形式。的缩写形式。 它包含了一系列系统调用,允许用户态进程通过信号量进行同步,向其他进程发消它包含了一系列系统调用,允许用户态进程通过信号量进行同步,向其他进程发消息,并且可以与其他进程共享一块内存空间息,并且可以与其他进程共享一块内存空间. IPC首先是在一个叫做首先是在一个叫做“Columbus Unix”的系统中实现的,其后在现代的系统中实现的,其后在现代Unix类操作类操作系统中广为流行。系统中广为流行。 64 如上文所述,如上文所述,IPC资源包括信号量,消息队列和共享内存几种。资源包括信号量,消息队列和共享内存几种。 65(1) 消息队列消息队列 消息队列是由内核创建并维护的一个数据结构,它是有标识的。消息队列是由内核创建并维护的一个数据结构,它是有标识的。 任何具有足够权限的进程都可以向消息队列中放置一个消息,同样,任何具有足够任何具有足够权限的进程都可以向消息队列中放置一个消息,同样,任何具有足够权限的进程都可以从中读取一个消息。权限的进程都可以从中读取一个消息。 这样,不同的进程通过访问相同的消息队列便可实现进程间通信。这样,不同的进程通过访问相同的消息队列便可实现进程间通信。66(2) 共享内存共享内存 共享内存区是这几种进程间通信方式中最快的一种。共享内存区是这几种进程间通信方式中最快的一种。 它的特点除了速度快外,而且可传递的信息量大。它的特点除了速度快外,而且可传递的信息量大。 它是通过将一段内存区映射到一个进程的地址空间来实现。它是通过将一段内存区映射到一个进程的地址空间来实现。67 因此,这种进程间通信就不再涉及到内核因此,这种进程间通信就不再涉及到内核(即进程不是通过执行任何进入内核的系即进程不是通过执行任何进入内核的系统调用来传递数据的。统调用来传递数据的。 这样,内核必须建立允许各个进程共享该内存区的内存映射关系,然后一直管理该这样,内核必须建立允许各个进程共享该内存区的内存映射关系,然后一直管理该内存区内存区)。 但同时,也要有效地保证它能同步、有序且没有死锁。但同时,也要有效地保证它能同步、有序且没有死锁。 68用共享内存实现过程如下:用共享内存实现过程如下: 服务器取得访问该共享内存区的权限。服务器取得访问该共享内存区的权限。 服务器从输入文件读取数据到该共享内存区。服务器从输入文件读取数据到该共享内存区。 服务器读入数据完毕时,通知用户进程。服务器读入数据完毕时,通知用户进程。 用户从该共享内存区读出这些数据并输出。用户从该共享内存区读出这些数据并输出。69(3) 信号量信号量 信号量并不是一种信号量并不是一种IPC机制,它是用于提供不同进程间或一给定进程的不同线程间机制,它是用于提供不同进程间或一给定进程的不同线程间同步的一种手段。同步的一种手段。 70信号量主要包括以下几种类型:信号量主要包括以下几种类型: 二值信号量:其值为二值信号量:其值为0或或1。资源被锁住而不可用时,值为。资源被锁住而不可用时,值为0;资源可用时,值为;资源可用时,值为1。 计数信号量:其值在计数信号量:其值在0和某个限制值之间。它的值一般是可用的资源数。和某个限制值之间。它的值一般是可用的资源数。 计数信号量集:由一个或多个信号量构成的一个集合,其中每一个都是计数信号量。每个计数信号量集:由一个或多个信号量构成的一个集合,其中每一个都是计数信号量。每个集合的信号量数都有一个限制值,一般在集合的信号量数都有一个限制值,一般在25个以上。个以上。71 以计数信号量为例,在有信号量机制的程序中,一个进程为了获得共享资源,需要以计数信号量为例,在有信号量机制的程序中,一个进程为了获得共享资源,需要执行以下操作:执行以下操作:72 测试控制该资源的信号量,若此信号量的值为正,则进程可以使用该资源。测试控制该资源的信号量,若此信号量的值为正,则进程可以使用该资源。 进程将信号量值减进程将信号量值减1,表示它使用了一个资源;若此信号量的值为,表示它使用了一个资源;若此信号量的值为0,则进入睡眠,则进入睡眠状态,直至信号量的值大于状态,直至信号量的值大于0为止;若进入为止;若进入1状态,则进程被唤醒。状态,则进程被唤醒。 进程不再使用该资源时,信号量的值就加进程不再使用该资源时,信号量的值就加1。如果这时有进程处在睡眠状态,则唤。如果这时有进程处在睡眠状态,则唤醒它。醒它。73 一个一个IPC资源将永久地驻留在内存中,除非进程显式地释放它。资源将永久地驻留在内存中,除非进程显式地释放它。 并且,并且,IPC资源不仅能被其创建进程的后代所共享,任何进程都可以共享它。资源不仅能被其创建进程的后代所共享,任何进程都可以共享它。746 进程同步进程同步 在在Linux中进程具有并发性,即系统中所有在就绪队列中的进程要按时间片轮流运中进程具有并发性,即系统中所有在就绪队列中的进程要按时间片轮流运行。行。 由于一些资源为所有进程所共享由于一些资源为所有进程所共享(比如文件系统比如文件系统),在并发的情况厂,就需相关中断,在并发的情况厂,就需相关中断事件所打断。事件所打断。75 可串行性是数据库中广泛使用的一种正确标准。可串行性是数据库中广泛使用的一种正确标准。 当事务交叉执行它们的操作,产生的结果和它们串行执行的结果一致时,这些事务当事务交叉执行它们的操作,产生的结果和它们串行执行的结果一致时,这些事务便称为可串行性的。便称为可串行性的。 同样,在操作系统中,假设两个进程同时修改一个文件,进程同样,在操作系统中,假设两个进程同时修改一个文件,进程A和进程和进程B都分别从都分别从偏移量偏移量x处读出数据,加处读出数据,加1后写回。后写回。 76 如果操作是可串行的,那么无论如果操作是可串行的,那么无论A和和B的指令具体在的指令具体在CPU中执行的先后顺序如何,中执行的先后顺序如何,最后最后x的值都应该是被增加的值都应该是被增加2。 然而如果不加任何保护措施,则很有可能出现然而如果不加任何保护措施,则很有可能出现A和和B都分别读出都分别读出x的数据,再各自加的数据,再各自加1,先后写回的情况。,先后写回的情况。 77 在这种情况下,在这种情况下,x最终只增加了最终只增加了1。 为了避免不可串行的情况出现,操作系统中需要将进程的读数据和加为了避免不可串行的情况出现,操作系统中需要将进程的读数据和加1两个操作定两个操作定义为一个原子操作,并进行互斥保护。义为一个原子操作,并进行互斥保护。 总之,总之,Linux系统下,进程同步可以采用互斥锁、读写锁、记录上锁、信号量和条系统下,进程同步可以采用互斥锁、读写锁、记录上锁、信号量和条件变量等方法。件变量等方法。78
收藏 下载该资源
网站客服QQ:2055934822
金锄头文库版权所有
经营许可证:蜀ICP备13022795号 | 川公网安备 51140202000112号