进程和线程关系?
进程:进程是操作系统的核心,是执行任务的单元。进程都是有对应的实体,每一个进程被创建,系统会为他分配存储空间等必要资源,然后在内核管理区为该进程创建管理节点,方便控制和控制进程的执行。
线程:线程的操作系统的运算调度的最小单元,是包含在进程之中。
相同点:
1、目的:进程和线程都是用实现多任务并发的技术收端,都可以独立调度
差异性:
进程是资源分配的基本单元,线程是调度的基本单元。进程的个体之间是相互独立,但是线程数据之间是共存的。
实现方式:
进程使用:
fork:父进程的全部资源复制给子进程,
vfork:1、子进程和父进程共享资源。2、父进程和子进程有前后关系:子进程结束之后,父进程才会开始运行。特殊点:一般vfork之后都会使用execv启动一个全新的进程
线程:
pthread_create:创建线程,基于clone的内核接口调用。
数据关系:
数据的独立性:
进程会复制父进程的数据,子进程可以直接进行操作父进程的数据,
子线程和父线程共用数据,父线程不能使用一个变量来执行accept,因为在子线程还没有来的及复制这个变量的时候,这个父线程还不能处理子accept的返回值,这个时候需要线程间通讯,效率有些影响。
但是进程之间需要数据交换就需要进行进程间通讯。
进程继承:
子进程会继承父进程的所有数据也包括了套接字,这个时候针对多任务设计模式设计又有特殊避免遗漏的地方(套接字,子进程关闭了但是父进程还是保留有)
通信方式:
进程间通讯:共享内存、消息队列、信号量、有名管道、无名管道、信号。进程间通讯主要是两种方式:1、内核切换上下文;2、与外设访问。这个两个存在速度和效率的问题。
线程间通讯:进程间通讯、互斥量、自旋锁、条件变量、读写锁、线程信号、全局变量
锁特殊点:
1、,进程的单点故障并不会损毁数据,当然这不一定全是优点。比如,进程崩溃前对信号量加锁,崩溃后重启,然后再次进入运行状态,此时直接进行加锁,可能造成死锁,程序再也无法继续运转。需要确认
控制方式:
1、两者的标识不同:进程有进程的PID,是一个int变量 ;2、线程有线程的PID . 是一个long变量
2、资源的回收控制:当子进程结束要回收时(子进程调用exit()退出或代码执行完),需要通过wait()系统调用来进行,未回收的消亡进程会成为僵尸进程。主要是孤儿进程、僵尸进程。
线程:主动终止需要调用pthread_exit() ,主线程需要调用pthread_join()来回收(前提是该线程没有被detached
父子关系:
进程池和线程池的实现差异:
进程池:
1、首先创建了一批进程,就得管理,也就是你得分开保存进程ID,可以用数组,也可用链表。建议用数组,这样可以实现常数内找到某个线程,而且既然做了进程池,就预先估计好了生产多少进程合适,一般也不会再动态延展。就算要动态延展,也能预估范围,提前做一个足够大的数组。不为别的,就是为了快速响应。本来错进程池的目的也是为了效率。接下来就要让闲置进程冬眠了,可以让他们pause()挂起,也可用信号量挂起,还可以用IPC阻塞,方法很多,分析各自优缺点根据实际情况采用就是了。
2、分配任务,当你有任务的时候就要让他干活了。唤醒了进程,让它从哪儿开始干呢?肯定得用到进程间通信了,比如信号唤醒它,然后让它在预先指定的地方去读取任务,可以用函数指针来实现,要让它干什么,就在约定的地方设置代码段指针。这也只是告诉了它怎么干,还没说干什么(数据条件),再通过共享内存把要处理的数据设置好,这也子进程就知道怎么做了。干完之后再来一次进程间通信然后自己继续冬眠,父进程就知道孩子干完了,收割成果。
3、最后结束时回收子进程,向各进程发送信号唤醒,改变激活状态让其主动结束,然后逐个wait()就可以了。
线程池:
线程池的思想与上述类似,只是它更为轻量级,所以调度起来不用等待额外的资源。要让线程阻塞,用条件变量就是了,需要干活的时候父线程改变条件,子线程就被激活。线程间通信方式就不用赘述了,不用繁琐的通信就能达成,比起进程间效率要高一些。线程干完之后自己再改变条件,这样父线程也就知道该收割成果了。整个程序结束时,逐个改变条件并改变激活状态让子线程结束,最后逐个回收即可。