进程和线程有什么区别?
答案:进程就是一个程序运行起来的状态,线程是一个进程中的不同的执行路径。
专业版:进程是OS分配资源的基本单位,线程是执行调度的基本单位。分配资源最重要的是:独立的内存空间,线程调度执行(线程共享进程的内存空间,没有自己独立的内存空间)
理论上,一个程序是可以启动多个进程的。例如,点击QQ,内存中创建一个进程。再点击一次,再创建一个。
不同的进程会被OS分配到一些资源。例如占有的寄存器和PC,不过最重要的资源是自己独立的内存地址空间。
进程到内存后,CPU开始读内存指令。把指令拿过来放到PC中,一条条执行。
进程放到内存后,main函数所在的地方,是启动点。那是我们的主线程。一旦开始执行后,一个进程里面可能有其他不同的线程,到底执行哪个线程,就需要CPU进行调度。
线程的实现
不同的操作系统,线程的实现不太一样。
在Linux里实现:
就是一个普通的进程,只不过与其他进程共享资源(内存空间,全局数据等)
在其它系统都有LWP的实现(轻量级进程 Light Weight Process)
从更高层面理解:一个进程中不同的执行路线,就叫做一个线程。
纤程(协程)
JVM跑在用户态,OS跑在内核态。JVM的线程和OS的线程是一一对应的。(即目前Hotspot目前是一对一)一般起一万个都很卡了。
Fiber:用户态的线程。线程里面的线程。在用户空间进行切换调度,不需要与OS打交道,只需要在自己的用户空间就可以完成。JVM自己管理,自己切换。起几万个,几十万个都没问题。
优势:
- 占有资源很少 OS要起一个线程,首先1M内存就被占用了。 而Fiber只需要4K
- 切换比较简单
- 由于非常轻量级,可以启动很多个
10W+
目前2020/3/22 自然而然支持内置纤程的语言:Kotlin Scala Go Python(加上某个lib)… Java?(open jdk : loom项目在做尝试)
JAVA在类库级别可以支持,不成熟
JDK14没有纤程
具体实现:相当于JVM自己实现了小小的OS级别的调度程序。
Java中对于纤程的支持:没有内置,盼望内置
利用Quaser库(不成熟)
1 | import co.paralleluniverse.fibers.Fiber; |
1 | import co.paralleluniverse.fibers.Fiber; |
最好模型可以起10个线程,然后每个线程执行1000个任务。这样既利用了操作系统在内核级别的对线程的调度,又充分利用了JVM在用户空间对纤程的调度。
纤程的应用场景
纤程vs线程池:很短的计算任务,不需要和内核打交道(比如不需要读文件等),并发量高
进程
linux中也称task,是系统分配资源的基本单位。
资源包括:独立地址空间、内核数据结构(PCB…)、全局变量、数据段…
每一个进程有一个PCB(Process Control Block,进程描述符)
Linux管理时,把所有相应信息都记录到PCB表中。大小不固定
注:每一个线程也都有一个PCB
内核线程:内核启动后经常要做一些后台操作,比如计时,定期清理垃圾,这些由Kernel Tread完成。就是内核内部所使用的线程。
进程创建和启动
系统函数fork() exec()
内核暴露出来的接口,我们对其进行调用,来让内核帮我们干一些事情,就是系统函数。
从A中fork B,A为B的父进程。
僵尸进程 孤儿进程
僵尸进程:父进程产生子进程后,会维护子进程的PCB结构。子进程退出,由父进程释放。但如果父进程没有释放,那么子进程就成为一个僵尸进程。
一般情况没有什么影响。结束后相关资源已经都释放了,基本不再占资源,唯一占的资源就是PCB了。
C程序用wait()函数可以手动释放
kill掉父进程,就可以一起干掉僵尸进程
下面的例子就会产生僵尸进程:
1 |
|
孤儿进程:子进程结束前,父进程已经退出
一般情况下,会把孤儿进程都交给一个特殊进程(命令行底下一般是init进程 1号)处理。
影响不大。换了个爹。
下面的例子就会产生孤儿进程:
1 |
|
执行结果:
child ppid is 19071
parent id is 19071
parent ppid is 1457
注:
- 1457是UI界面(图形界面)孤儿的爹。如果UI界面都死了,那么就变成1号了
- 1457的父亲是1
进程(任务)调度
内核进程调度器决定:
该哪个进程运行?
何时开始?
运行多长时间?
Linux中每个进程都可以指定不同的调度方案(可以自己写)
多任务
非抢占式(cooperative multitasking):除非进程主动让出CPU,否则将一直运行。(很少用)
抢占式(preemtive multitasking):由进程调度器强制开始或暂停(抢占)某一进程的执行。
进程调度
Linux2.5 经典Unix O(1)调度策略,偏向服务器,但对UI交互不友好。平均分配时间片,不能及时响应。
Linux2.6.23 采用CFS完全公平调度算法Completely Fair Scheduler
不再采用绝对时间片。因为有的进程不需要那么多时间。而是按优先级分配时间片的比例,记录每个进程的执行时间,如果有一个进程执行时间不到他应该分配的比例,优先执行(计算原来执行的时间片)
进程类型:
IO密集型 大部分时间用于等待IO
CPU密集型 大部分时间用于闷头计算
(时间片先给CPU密集型,什么时候IO来了,马上给它执行)
进程优先级:
实时进程 > 普通进程(099)19)
普通进程nice值(-20
linux默认调度策略:
对于实时进程:使用SCHED_FIFO
和 SCHED_RR
(Round Robin轮询)
对于普通进程:使用CFS
- 其中等级最高的是FIFO。如果一个进程中一个优先级是99,一个是98。99的是先进先出,那么执行完了才轮到98.这种进程除非自己让出CPU,或者更高级的FIFO和RR抢占它。
- 两个进程优先级一样的进程,使用RR策略。平均分配。
- 只有实时进程主动让出,或执行完毕,普通进程才有机会运行。
(类似急诊和普通号。急诊中,有非常严重的,用FIFO,按严重程度顺序进行,除非前一个主动放弃,可以让出医生资源。不是特别严重的,严重程度相同,采用RR策略。急诊都结束,才进行普通号就诊,CFS策略)