进程VS线程的理解

2020-08-29  本文已影响0人  M_天河

进程,在一定的环境下,把静态的程序代码运行起来,通过使用不同的资源,来完成一定的任务。比如说,进程的环境包括环境变量,进程所掌控的资源,有中央处理器,有内存,打开的文件,映射的网络端口等等。
线程作为进程的一部分,扮演的角色就是怎么利用中央处理器去运行代码。这其中牵扯到的最重要资源的是中央处理器和其中的寄存器,和线程的栈(stack)。这里想强调的是,线程关注的是中央处理器的运行,而不是内存等资源的管理。

相同点:

无论是进程还是线程,对于程序员而言,都是用来实现多任务并发的技术手段。二者都可以独立调度,因此在多任务环境下,功能上并无差异。并且二者都具有各自的实体,是系统独立管理的对象个体。所以在系统层面,都可以通过技术手段实现二者的控制。而且二者所具有的状态都非常相似。而且,在多任务程序中,子进程(子线程)的调度一般与父进程(父线程)平等竞争。

其实在Linux内核2.4版以前,线程的实现和管理方式就是完全按照进程方式实现的。在2.6版内核以后才有了单独的线程实现。

不同点:

“进程是资源分配的基本单位,线程是调度的基本单位。”这是教材上的经典描述,这也确实是二者的显著区别,线程是最小的调度单位,进程当然也可以被调度,分配资源时的对象必须是进程,所以要想运行一个任务,需要获取资源,至少要有进程。

简而言之,进程的个体间是完全独立的,而线程间是彼此依存的。多进程环境中,任何一个进程的终止,不会影响到其他进程。而多线程环境中,父线程终止,全部子线程被迫终止(没有了资源)。而任何一个子线程终止一般不会影响其他线程,除非子线程执行了exit()系统调用。任何一个子线程执行exit(),全部线程同时灭亡。

从实现的角度来看:

开发者大多从实际使用中来对比二者的区别,进程是实现fork系统的调用,pid_t fork(void);而线程则是实现clone系统的调用,int clone(int (*fn)(void *),void *child_stack,int flags,void *arg, ...);其中,frok()是将所有资源复制给了子进程,而clone则是一小部分必要的资源,clone函数可以通过参数控制要复制的对象。
实际中,编写多进程程序时采用fork创建子进程实体。而创建线程时并不采用clone系统调用,而是采用线程库函数,因此在多线程程序中看到的是pthread_create而非clone。

vfork()也是一个系统调用,用来创建一个新的进程。它创建的进程并不复制父进程的资源空间,而是共享,也就说实际上vfork实现的是一个接近线程的实体,只是以进程方式来管理它。并且,vfork()的子进程与父进程的运行时间是确定的:子进程“结束”后父进程才运行。请读者注意“结束”二字。并非子进程完成退出之意,而是子进程返回时。一般采用vfork()的子进程,都会紧接着执行execv启动一个全新的进程,该进程的进程空间与父进程完全独立不相干,所以不需要复制父进程资源空间。此时,execv返回时父进程就认为子进程“结束”了,自己开始运行。实际上子进程继续在一个完全独立的空间运行着。

实体间(进程间,线程间,进线程间)通信方式的不同

进程间的通信方式有这样几种:

A.共享内存 B.消息队列 C.信号量 D.有名管道 E.无名管道 F.信号 G.文件 H.socket

线程间的通信方式上述进程间的方式都可沿用,且还有自己独特的几种:

A.互斥量 B.自旋锁 C.条件变量 D.读写锁 E.线程信号 G.全局变量

值得注意的是,线程间通信用的信号不能采用进程间的信号,因为信号是基于进程为单位的,而线程是共属于同一进程空间的。故而要采用线程信号。

综上,进程间通信手段有8种。线程间通信手段有13种。

控制方式的不同

进程与线程的身份标示ID管理方式不一样,进程的ID为pid_t类型,实际为一个int型的变量,在全系统中,进程ID是唯一标识,对于进程的管理都是通过PID来实现的。每创建一个进程,内核去中就会创建一个结构体来存储该进程的全部信息;
每一个存储进程信息的节点也都保存着自己的PID。需要管理该进程时就通过这个ID来实现(比如发送信号)。当子进程结束要回收时(子进程调用exit()退出或代码执行完),需要通过wait()系统调用来进行,未回收的消亡进程会成为僵尸进程,其进程实体已经不复存在,但会虚占PID资源,因此回收是有必要的。

线程的ID是一个long型变量,它的范围大得多,管理方式也不一样。线程ID一般在本进程空间内作用就可以了,当然系统在管理线程时也需要记录其信息。其方式是,在内核创建一个内核态线程与之对应,也就是说每一个用户创建的线程都有一个内核态线程对应。但这种对应关系不是一对一,而是多对一的关系,也就是一个内核态线程可以对应着多个用户级线程。
对于线程而言,若要主动终止需要调用pthread_exit() ,主线程需要调用pthread_join()来回收(前提是该线程没有被detached,相关概念请查阅线程的“分离属性”)。像线发送线程信号也是通过线程ID实现的。

参考:https://my.oschina.net/cnyinlinux/blog/422207

上一篇 下一篇

猜你喜欢

热点阅读