这一章是对进程与线程的一般概述,个别段落摘抄并总结自很多网络文章,不包括进程之间的通信
进程 VS 线程
http://www.cnblogs.com/CareySon/archive/2012/05/04/ProcessAndThread.html
进程独立享有内存地址空间,是资源分配的基础单位,而线程是CPU分配的基本单位。
资源到位,CPU到位
举例:LiveWriter
键盘鼠标输入处理,定时草稿保存
如是存于一个进程中,当保存草稿访问硬盘,阻塞进程,此时不能处理键盘鼠标的输入。
当然采用中断磁盘访问,而且执行输入处理可以实现优化,但是这不是好策略
线程的好处:
1.共享地址空间
2.开销小
3.提高了性能,宏观上并行,微观上串行
从CPU角度线程并无法提升性能,但如果某些线程涉及到等待资源(比如IO,等待输入)时,多线程允许进程中的其它线程继续执行而不是整个进程被阻塞,因此提高了CPU的利用率,从这个角度会提升性能。
4.多核,确实的并行
经典的线程模型:
线程共享进程的大多数资源,但是线程也有自己的寄存器,栈和程序计数器。
进程共享物理内存,打印机,键盘等,线程共享地址空间
| 进程占有的资源 | 线程占有的资源 |
| 地址空间 全局变量 打开的文件 子进程 信号量 账户信息 | 栈 寄存器 状态 程序计数器 |
用户模式线程:
用户空间实现,OS并不能察觉到线程的存在。每个进程维持一个线程表,每个表项对应一个线程,系统空间维护表。但是没有时间中断机制,而且系统调用的话,会阻塞整个进程。调度在同一进程的线程中转换。
内核模式线程:
线程表维护于内核中,轻量进程,由一个内核线程支持。调度在相同进程或不同进程的线程中转换。但是开销更大
混合模式:
用户线程通过轻量进程可以与内核内的线程沟通
线程是进程中不同的执行路径,由于系统的保护,一个进程的corruption不会影响别的进程或OS,而一个进程完蛋,整个线程都完蛋!!
http://yuanshuilee.blog.163.com/blog/static/2176972752013111110038421/
线程是指程序的执行线索,本身没有父子关系,就算是用户级线程,也没有,用户级线程的实现一般是靠线程库来实现的。你调用了该线程库提供的终止线程的操作,一般不会引起别的线程被干掉,因为系统其实不知道发生了这样子的事情。但是用户级线程会有一个弊端,一旦一个线程被阻塞,整个程序(进程)就被阻塞了。用户级线程的优势是调度性能非常高,也就是说,线程切换基本没有开销。
对于核心级线程,一个死掉并不会导致另一个死掉。但是OS/2有一个所谓的主线程的概念,一旦主线程死掉,整个进程就死掉了,当然,隶属于该进程的所有线程也被干掉。核心级的线程和用户级的线程的优缺点正好相反。线程概念上就没有父子之分,它只是被创造出来的一个执行线索。按照WindowsNT的概念,进程仅仅是一个容器,是一个执行环境,真正的执行都是线程。所以,线程一定是由线程创建的。如果创建者死了,被创建者也得死的话,你可以设想一下,你的程序很多东西都会崩溃。
http://andey007518.iteye.com/blog/1413593
死亡状态就是线程释放了实体,即释放了分配给线程对象的内存。线程死亡的原因有两个:
(1)、正常运行的线程完成了它的全部工作,即执行完run()方法中的全部语句,结束了run()方法。
(2)、线程被提前强制性终止,即强制run()方法结束。
==============================================================================================
- 概述
进程, 是一个程序的顺序执行。与进程相关的有进程的地址空间(address space)。在地址空间中,存放有可执行程序,程序的数据和程序的堆栈,进程可以进行读写。除此之外与进程相关的还有其它的资源集,通常包括寄存器,打开文件的清单,warning等,存放与进程表(process table)中。
分时系统(time-slicing/multi-programming system):OS周期性的挂起一个进程(挂起时,之前的所有的信息都必须保存下来)然后启动运行另一个进程。
一个挂起的进程包括:地址空间,又称为磁芯映像(core image),以及对应的进程表项。
警告信号:软件模拟的硬件中断信号,此信号可以引起该进程暂时挂起,无论该进程正在做什么,系统将其寄存器的值保存的到堆栈。
系统管理器授权每个进程使用一个给定的UID标识,每个被启动的进程都有一个启动该进程的用户UID,子进程与父进程一样的UID。用户可以是某个组的成员,每个组也有一个GID标识。为了完成某些任务,相关的进程需要通信(mutual exclusion and synchronization)。
每个进程拥有自己的虚拟CPU。而真正的CPU在进程之间来回转换(be switched back and forth from process to process),这个过程叫做multiprogramming。当同一个进程再次运行时速度不可现,而且每一个进程的执行速度是不确定的(CPU在各个进程之间来回快速切换),所以不能对时序做出任何确定的假设。
每个进程拥有自己的控制流(逻辑程序计数器),实际上只有一个物理程序计数器,在每个程序运行时,它的逻辑程序计数器被装入实际的计数器寄存器中。当程序执行结束或暂停时,物理程序计数器被保存在内存中该进程的逻辑程序计数器中。
- 关键思想
单个处理器可以被多个进程共享,它使用某种调度算法决定何时停止一个进程的工作,并转而为另一个进程提供服务。
如果一个程序运行了两次,则算作两个进程。
- 创建进程
导致创建进程的主要的四个事件:
1. 系统初始化(前台进程与用户交互,后台进程虽然没有与用户交互,却可以完成某些功能)
2. 被正在运行的进程调用的进程创建系统调用(一个正在运行的进程经常会发出系统调用创建进程)
3. 用户请求创建系统调用(用户可以通过键入命令或者点击一个图标就可以启动一个进程)
4.一个批处理作业的初始化 Initialization of a batch job(当OS认为有资源时就可以运行另一个作业时,它创建一个新进程,并运行其输入队列中的下一个作业)
技术上来说,新进程是一个已存在的进程执行了一个用于进程创建的系统调用而创造的。
Unix系统中, 我们可以使用fork,复制创建新进程(同样的存储映像,打开文件,环境字符串等)。接着调用execve之后,可以修改起存储映像。分为两步的好处是可以处理fork之后execve之前的文件描述符等。
Win32中,CreateProcess负责创建新进程,并将正确的程序装入新进程(结构指针)
在Unix或者Windows系统中,进程创建之后,子进程和父进程都各自不同的地址空间。但是不可写的内存区是共享的(使程序正文在两者之间可以共享)。
在fork之后exec之前两个进程用的是相同的物理空间(内存区),子进程的代码段、数据段、堆栈都是指向父进程的物理空间,也就是说,两者的虚拟空间不同,但其对应的物理空间是同一个。当父子进程中有更改相应段的行为发生时,再为子进程相应的段分配物理空间,如果不是因为exec,内核会给子进程的数据段、堆栈段分配相应的物理空间(至此两者有各自的进程空间,互不影响),而代码段继续共享父进程的物理空间(两者的代码完全相同)。而如果是因为exec,由于两者执行的代码不同,子进程的代码段也会分配单独的物理空间。
| 代码: |
| #include <unistd.h> #include <sys/types.h> main () { pid_t pid; pid=fork(); if (pid < 0) printf("error in fork!"); else if (pid == 0) printf("i am the child process, my process id is %d\n",getpid()); else printf("i am the parent process, my process id is %d\n",getpid()); } |
结果是
[root@localhost c]# ./a.out
i am the child process, my process id is 4286
i am the parent process, my process id is 4285
1、申请空白PCB
2、为新进程分配资源(内存空间)
3、初始化进程PCB
a. 初始化标识信息
b. 初始化处理机状态信息:使程序计数器指向程序的入口地址,使栈指针指向栈顶
c.初始化处理机控制信息:进程的状态,优先级
4、将新进程插入就绪队列
- 进程的终止
进程终止的主要条件:
1、正常退出(自愿的)
Unix中,调用是exit。在windows中,相关的调用是ExitProcess。
2、出错退出(自愿的)
通知OS,如果进程想自行解决某些类型的错误,进程会收到中断信号,而不是这类错误出现时终止
3、严重错误(非自愿)
4、被其它进程杀死(非自愿)
Unix中,调用是kill。Win32对应的函数是TerminateProcess。这两种情形的杀手都必须获得确定的授权以便进行动作。在有些系统中,当一个进程终止,不论是自愿的还是其它的原因,由该进程创建的一律进程都被杀死。不过,Unix和Windows都不是这种工作方式。
1、根据被终止进程的标识符,从进程表中检索中该进程的PCB,读取进程的状态
2、若状态是执行,应该立即终止执行,并设置调度标志为真,用于指示该进程被钟之后应重新进行调度
3、对于有些系统,如果该进程有子孙程序,则一起终止
4、将进程拥有的资源,归还给父进程或者还给系统
5、从进程表中移除相关PCB
- 层次结构(Hierarchy)
在Unix中,进程和他的所有子女以及后裔组成一个进程组。当用户从键盘发出一个信号,信号会被送给进程组中当前与键盘相关的所有成员,每个进程可以分别捕获这个信号,忽略这个信号,或者采用默认的动作(被信号杀死)。
Interruption VS Trap:
中断是指计算机在执行过程中,系统发生任何非寻常的或非预期的急需处理的事件(主要是外部原因引起,如机器故障中断,输入/输出中断,外部中断,程序性中断和访问中断。常说的软中断是内部的程序模拟外部中断),使CPU暂时中止当前正在执行的进程而转去执行相应的事件处理程序,待处理完毕后再返回原来中断处继续执行被中断的进程,或调度新的进程执行的过程。可见,中断的发生与当前执行的程序无关,且它是异步发生的。
CPU没执行完一条命令之后都会检查有没有中断请求。
陷入是指计算机在执行过程中,当前程序由于请求操作系统服务或请求使用设备,或程序本身产生错误时,引起的要改变CPU的执行流程的过程。陷入使CPU由用户态改变为核心态。显然,陷入与当前执行的程序密切相关,它是同步发生的
以init为根的一颗树:
一个称为init的特殊进程出现在启动映像中,开始时,它读取一个说明终端数量的文件,然后为每个终端创建一个进程,这些用户等待用户登入,登入成功,这些终端准备接收命令,并且创建更多进程。
Windows中没有进程层次的概念,所有进程的地位都是相同的,父进程得到一个特别的token(句柄handle),这些句柄可以用来控制子进程。但是它有权将token传送给某个其它进程,而Unix中,进程不能剥夺子女的继承权。(can't disinherit his children)。
init进程:它是 内核启动的第一个用户级进程。init有许多很重要的任务,比如象启动getty(用于用户登录)、实现运行级别、以及处理孤立进程。本章解释了怎样配置init以及如何运用不同的运行级别。
- 进程的状态
1、running
2、ready(可运行,由于时间片用完,其它进程正在运行)
3、blocked(除非某种外部事件发送,否则不能运行)
调度程度的主要工作:决定应当运行哪个进程,何时进行以及应该运行多长时间。所有的中断处理,启动进程和停止进程的具体细节都隐藏在调度程序中。
- 进程的实现
OS维护着一张表格,即进程表。每个进程表项对应着一个进程(PCB)。PCB包含了进程状态的重要信息,包括寄存器,打开的文件的状态等信息。下图给出相关信息的大致介绍:
中断向量:与每一个I/O类关联,位于内存底部的固定区域,它包含中断服务程序的入口地址。当一个磁盘中断发生时,如果进程3正在运行,则中断硬件将程序计数器,程序状态字等信息压入栈,并跳转到中断向量所指定的地址的服务程序。(带缓冲的I/O并不是你每次调用都会陷入内核。它会把你写入的数据先放入缓冲,等缓冲区满了,再调用Unix I/O函数实际写入文件。缓冲的读函数每次陷入内核都会读取一块很大的数据或者遇到EOF,标准I/O读函数实际上是每次从缓冲区读取数据,缓冲区没有数据才会陷入内核)
所有的中断从保存寄存器开始,然后从堆栈删除由中断硬件机制存入堆栈的那部分信息,然后将堆栈指针指向一个由进程处理程序所使用的临时堆栈。保存寄存器和设置堆栈指针都必须用汇编语言完成,最后使用一个C过程处理(假设OS由C语言编写)处理特定的中断类型剩下的工作。完成所有的工作之后,使用调用调度程序,决定运行哪个程序。随后将控制转给一段汇编代码将当前的进程装入寄存器值以及内存映射并启动该进程运行。不同的系统之间的有些细节会有所不同。
1、硬件压入堆栈程序计数器等
2、硬件从中断向量装入新的程序计数器
3、汇编语言过程保存寄存器值
4、汇编语言过程设置新的堆栈
5、C中断服务例程运行(典型的读和缓冲输入)
6、调度程序决定下一个将运行的进程
7、C过程返回至汇编代码
8、汇编语言过程开始运行新的当前进程
- 多道程序设计模型
CPU利用率=1-p^n(p为等待I/O操作的事件与其停留在内存中的时间的比,n表示内存中同时有n个进程,也可称为degree of multiprogramming)
p^n是n个进程都在等待I/O.
这个模型并不是精确的,因为他假设n个进程是独立运行的,但是内存中的进程可以多个并行进行,并且会互相影响,比如等待CPU空置,互相通信,更精确的是用排队理论。但是这个模型仍然有效。
例:512MB内存,OS占128MB,最多可以存放3个程序。此时80%的时间用于I/O等待,则CPU的利用率大约是1-0.8^3,即49%。在增加512MB内存后,3道程序变成7道,此时利用率编程79%。吞吐量提升了30%。而在增加512个内存,此时利用率只从79%提升到91%,吞吐量是12%。第二次投资并不是合算的投资。
=================TO BE CONTINUED=================================
本文详细阐述了进程与线程的基本概念,包括它们的区别、资源分配、状态变化、创建与终止过程,以及不同模式下的线程实现方式。此外,还探讨了进程间的层次结构和状态转换。

714

被折叠的 条评论
为什么被折叠?



