1、进程的概念 我们编译的代码可执⾏⽂件只是储存在硬盘的静态⽂件,运⾏时被加载到内存,CPU执⾏内存中指令,这个运⾏的程序被称为进程。 进程是对运⾏时程序的封装,操作系统进⾏资源调度和分配的基本单位。 2、进程的实现 中断发⽣后操作系统底层的⼯作步骤 1. 硬件压⼊堆栈程序计数器等 2. 硬件从中断向量装⼊新的程序计数器 3. 汇编语⾔过程保存寄存器值 4. 汇编语⾔过程设置新的堆栈 5. C中断服务例程运⾏(典型地读和缓冲输⼊) 6. 调度程序决定下⼀个将运⾏的进程 7. C过程返回⾄汇编代码 8. 汇编语⾔过程开始运⾏新的当前进程 进程表: 为了实现进程模型,操作系统维护着⼀张表格(⼀个结构数组),即进程表。 每个进程占有⼀个进程表项。(有些著作称这些为进程控制块) 该表项包含了⼀个进程状态的重要信息 包括程序计数器、堆栈指针、内存分配状况、所打开⽂件的状态、账号的调度信息,以及其他在进程由运⾏态转换 到就绪态或阻塞态时必须保存的信息,从⽽保证该进程随后能再次启动,就像从未中断过⼀样 3、并发与并⾏ (1)单个核⼼在很短时间内分别执⾏多个进程,称为并发 (2)多个核⼼同时执⾏多个进程称为并⾏ (3)对于并发来说,CPU需要从⼀个进程切换到另⼀个进程,这个过程需要保存进程的状态信息 4、进程的状态 某个进程在某个时刻所处的状态分为以下⼏种,运⾏态、就绪态、阻塞态。对于阻塞状态; ⽐如read系统调⽤阻塞,进程会占⽤内存空间,这是⼀种浪费⾏为,于是操作系统会有跟内存管理中物理页置换到 磁盘⼀样的⾏为,把阻塞的进程置换到磁盘中,此时进程未占⽤物理内存,我们称之为挂起; 挂起不仅仅可能是物理内存不⾜,⽐如sleep系统调⽤过着⽤户执⾏Ctrl+Z也可能导致挂起。 除了创建和结束⼀般有三个状态: 运⾏态: 该时刻进程占⽤CPU 就绪态: 可运⾏,由于其他进程处于运⾏状态⽽暂时停⽌运⾏ 阻塞态: 该进程正在等待某⼀事件发⽣(如等待输⼊/输出操作的完成)⽽暂时停⽌运⾏ 阻塞态的进程占⽤着物理内存,在虚拟内存管理的操作系统中,通常会把阻塞态的进程的物理内存空间换出到硬 盘,等需要再次运⾏的时候,再从硬盘换⼊到物理内存。 挂起态:新的状态,描述进程没有占⽤实际的物理内存空间的情况,这个状态就是挂起状态 阻塞挂起状态: 进程在外存(硬盘)并等待某个事件的出现 就绪挂起状态: 进程在外存(硬盘),但只要进⼊内存,马上运⾏ 特点: 1. 就绪态和运⾏态可以相互转换,其它的都是单向转换。就绪态的进程通过调度算法从⽽获得CPU 时间,转为运⾏状态; 2. 运⾏态的进程,在分配给它的 CPU 时间⽚⽤完之后就会转为就绪状态,等待下⼀次调度。 3. 阻塞态是缺少需要的资源从⽽由运⾏态转换⽽来,但是该资源不包括 CPU 时间,缺少 CPU 时间会从运⾏态转换为就绪态。 5、进程控制块(PCB) 操作系统对进程的感知,是通过进程控制块PCB数据结构来描述的。它是进程存在的唯⼀标识,其包括以下信息: 1. 进程描述信息: 进程标识符、⽤户标识符等; 2. 进程控制和管理信息: 进程状态,进程优先级等; 3. 进程资源分配清单: 虚拟内存地址空间信息,打开⽂件列表,IO设备信息等; 4. CPU相关信息: 当进程切换时,CPU寄存器的值都被保存在相应PCB中,以便CPU重新执⾏该进程时能从断点 处继续执⾏; PCB通过链表形式组织起来,⽐如有就绪队列、阻塞队列等,⽅便增删,⽅便进程管理。 6、进程状态的切换 进程的状态分类: 1. 就绪状态(ready):等待被调度 2. 运⾏状态(running) 3. 阻塞状态(waiting):等待资源 转换关系: 只有就绪态和运⾏态可以互相转换,其他都是单向转换。 就绪态的进程通过调度算法从⽽获得CPU时间,转为运⾏状态;⽽运⾏状态的进程,在分配给它的CPU时间⽚完之 后就会转为就绪状态,等待下⼀次调度。 进程因为等待资源⽽阻塞,但是该资源不包括 CPU 时间,缺少 CPU 时间会从运⾏态转换为就绪态。 当进程等待的外部事件发⽣时(如⼀些输⼊到达),则又阻塞态转换为就绪态,如果此时没有其他进程运⾏,变转 换为运⾏态,否则该进程将处于就绪态,等待CPU空闲轮到它运⾏。 7、进程切换为何⽐线程慢 涉及到虚拟内存的问题,进程切换涉及虚拟地址空间的切换⽽线程不会。 因为每个进程都有⾃⼰的虚拟地址空间,⽽线程是共享所在进程的虚拟地址空间的,所以同⼀个进程中的线程进⾏ 线程切换时不涉及虚拟地址空间的转换。 把虚拟地址转换为物理地址需要查找页表,页表查找是⼀个很慢的过程(⾄少访问2次内存),因此通常使⽤Cache 来缓存常⽤的地址映射,这样可以加速页表查找,这个cache就是TLB(快表)。 由于每个进程都有⾃⼰的虚拟地址空间,那么显然每个进程都有⾃⼰的页表,那么当进程切换后页表也要进⾏切 换,页表切换后TLB就失效了,cache失效导致命中率降低,那么虚拟地址转换为物理地址就会变慢,表现出来的就 是程序运⾏会变慢,⽽线程切换则不会导致TLB失效,因为线程线程⽆需切换地址空间,这也就是进程切换要⽐同 进程下线程切换慢的原因。 10、守护进程 守护进程是指在后台运⾏的,没有控制终端与它相连的进程。它独⽴于控制终端,周期性地执⾏某种任务。 Linux的⼤多数服务器就是⽤守护进程的⽅式实现的,如web服务器进程http等。 创建守护进程要点: (1)让程序在后台执⾏。 ⽅法是调⽤fork()产⽣⼀个⼦进程,然后使⽗进程退出。 (2)调⽤setsid()创建⼀个新对话期。 守护进程需要摆脱⽗进程的影响,⽅法是调⽤setsid()使进程成为⼀个会话组长。setsid()调⽤成功后,进程成为新的 会话组长和进程组长,并与原来的登录会话、进程组和控制终端脱离。 (3)禁⽌进程重新打开控制终端。 经过1和2,进程已经成为⼀个⽆终端的会话组长,但是它可以重新申请打开⼀个终端。为了避免这种情况发⽣,可 以通过使进程不再是会话组长来实现。再⼀次通过fork()创建新的⼦进程,使调⽤fork的进程退出。 (4)关闭不再需要的⽂件描述符。 ⼦进程从⽗进程继承打开的⽂件描述符。如不关闭,将会浪费系统资源,造成进程所在的⽂件系统⽆法卸下以及引 起⽆法预料的错误。⾸先获得最⾼⽂件描述符值,然后⽤⼀个循环程序,关闭0到最⾼⽂件描述符值的所有⽂件描 述符。 (5)将当前⽬录更改为根⽬录。 (6)⼦进程从⽗进程继承的⽂件创建屏蔽字可能会拒绝某些许可权。 为防⽌这⼀点,使⽤unmask(0)将屏蔽字清零。 (7)处理SIGCHLD信号。 对于服务器进程,在请求到来时往往⽣成⼦进程处理请求。如果⼦进程等待⽗进程捕获状态,则⼦进程将成为僵⼫ 进程(zombie),从⽽占⽤系统资源。如果⽗进程等待⼦进程结束,将增加⽗进程的负担,影响服务器进程的并发 性能。在Linux下可以简单地将SIGCHLD信号的操作设为SIG_IGN。这样,⼦进程结束时不会产⽣僵⼫进程。 11、僵⼫进程多进程程序 ⽗进程⼀般需要跟踪⼦进程的退出状态,当⼦进程退出,⽗进程在运⾏,⼦进程必须等到⽗进程捕获 到了⼦进程的退出状态才真正结束。在⼦进程结束后,⽗进程读取状态前,此时⼦进程为僵⼫进程。 设置僵⼫进程的⽬的是维护⼦进程的信息,以便⽗进程在以后某个时候获取。这些信息⾄少包括进程ID,进程的终 ⽌状态,以及该进程使⽤的CPU时间。所以当终⽌⼦进程的⽗进程调⽤wait或waitpid时就可以得到这些信息。 但是⼦进程停⽌在僵⼫态会占据内核资源,所以需要避免僵⼫进程的产⽣或⽴即结束⼦进程的僵⼫态。 1. ⽗进程调⽤wait/waitpid等函数等待⼦进程结束,如果尚⽆⼦进程退出wait会导致⽗进程阻塞。waitpid只会等 待由pid参数指定的⼦进程,同时也是⾮阻塞,⽬标进程正常退出返回⼦进程PID,还没结束返回0。 2. 在事件已经发⽣情况下执⾏⾮阻塞调⽤可以提⾼程序效率。对waitpid,最好在⼦进程退出后调⽤。使⽤ SIGCHLD信号通知⽗进程,⼦进程结束。 ⽗进程中捕获信号,然后在信号处理函数中调⽤waitpid以彻底结束⼦进程 1. 通过signal(SIGCHLD, SIG_IGN)通知内核对⼦进程的结束不关⼼,由内核回收。如果不想让⽗进程挂起,可以 在⽗进程中加⼊⼀条语句:signal(SIGCHLD,SIG_IGN);表⽰⽗进程忽略SIGCHLD信号,该信号是⼦进程退出的 时候向⽗进程发送的。 2. 忽略SIGCHLD信号,这常⽤于并发服务器的性能的⼀个技巧因为并发服务器常常fork很多⼦进程,⼦进程终结 之后需要服务器进程去wait清理资源。如果将此信号的处理⽅式设为忽略,可让内核把僵⼫⼦进程转交给init进 程去处理,省去了⼤量僵⼫进程占⽤系统资源。 12、多进程 进程结构由以下⼏个部分组成:代码段、堆栈段、数据段。代码段是静态的⼆进制代码,多个程序可以共享。 ⽗进程创建⼦进程之后,⽗、⼦进程除了pid外,⼏乎所有的部分⼏乎⼀样。 ⽗、⼦进程共享全部数据,⼦进程在写数据时会使⽤写时复制技术将公共的数据重新拷贝⼀份,之后在拷贝出的数 据上进⾏操作;不是对同⼀块数据进⾏操作; 如果⼦进程想要运⾏⾃⼰的代码段,还可以通过调⽤execv()函数重新加载新的代码段,之后就和⽗进程独⽴开了。
标签:状态,基础,阻塞,线程,进程,就绪,CPU From: https://www.cnblogs.com/workformyselfandworld/p/17661569.html