进程?线程?到底共享了什么私有了什么

进程是具有一定独立功能的程序关于某个数据集合上的一次运行活动,进程是系统进行资源分配和调度的一个独立单位

线程是进程的一个实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位。线程自己基本上不拥有系

资源,只拥有一点在运行中必不可少的资源(如程序计数器,一组寄存器和),但是它可与同属一个进程的其他的线程共享进程所拥有

全部资源。

一个线程可以创建和撤销另一个线程;同一个进程中的多个线程之间可以并发执行。

进程在执行过程中拥有独立的内存单元,而该进程的多个线程共享内存,从而极大地提高了程序的运行效率。 

每个独立的线程有一个程序运行的入口、顺序执行序列和程序的出口。但是线程不能够独立执行,必须依存在应用程序中,由应用

程序提供多个线程执行控制。 

从逻辑角度来看,多线程的意义在于一个应用程序中,有多个执行部分可以同时执行。但操作系统并没有将多个线程看做多个

独立的应用,来实现进程的调度和管理以及资源分配。这就是进程和线程的重要区别。

一个进程中的所有线程共享该进程的地址空间,但它们有各自独立的(/私有的)栈(stack),Windows线程的缺省堆栈大小为1M。

堆(heap)的分配与栈有所不同,一般是一个进程有一个C运行时堆,这个堆为本进程中所有线程共享,windows进程还有所谓进程默认

堆,用户也可以创建自己的堆。 用操作系统术语,线程切换的时候实际上切换的是一个可以称之为线程控制块的结构,里面保

存所有将来用于恢复线程环境必须的信息,包括所有必须保存的寄存器集,线程的状态等。


线程共享的环境包括:

  1.进程代码段 

  2.进程的公有数据(利用这些共享的数据,线程很容易的实现相互之间的通讯) 

  3.进程打开的文件描述符、信号的处理器、进程的当前目录和进程用户ID与进程组ID。

线程独立的资源包括:

1.线程ID

每个线程都有自己的线程ID,这个ID在本进程中是唯一的。进程用此来标识线程。

2.寄存器组的值

由于线程间是并发运行的,每个线程有自己不同的运行线索,当从一个线程切换到另一个线程上 时,必须将原有的线程的寄存

器集合的状态保存,以便将来该线程在被重新切换到时能得以恢复。

3.线程的堆栈

堆栈是保证线程独立运行所必须的。线程函数可以调用函数,而被调用函数中又是可以层层嵌套的,所以线程必须拥有自己的

函数堆栈, 使得函数调用可以正常执行,不受其他线程的影响。

4.错误返回码

由于同一个进程中有很多个线程在同时运行,可能某个线程进行系统调用后设置了errno值,而在该 线程还没有处理这个错误,

另外一个线程就在此时被调度器投入运行,这样错误值就有可能被修改。所以,不同的线程应该拥有自己的错误返回码变量。

5.线程的信号屏蔽码

由于每个线程所感兴趣的信号不同,所以线程的信号屏蔽码应该由线程自己管理。但所有的线程都 共享同样的信号处理器。

6.线程的优先级

由于线程需要像进程那样能够被调度,那么就必须要有可供调度使用的参数,这个参数就是线程的优先级。


细化一下线程和进程的区别:

①进程是系统进行资源分配和调度的一个独立单位。线程是进程的一个实体,是CPU调度和分派的基本单位。线程自己基本上不

拥有系统资源,但是它可与同属一个进程的其他的线程共享进程所拥有的全部资源。由于线程比进程更小,基本上不拥有系统资源,

线程上下文切换比进程上下文切换要快得多,故对它的调度所付出的开销就会小得多,从而显著提高系统资源的利用率和吞吐量。

②一个程序至少有一个进程,一个进程至少有一个线程。进程在执行过程中拥有独立的内存单元地址空间,而多个线程共享内存,

从而极大地提高了程序的运行效率。

③进程间通信IPC需要特别的方法,线程间可以直接读写进程数据段(如全局变量)来进行通信。


进程调度开销较大,创建多个进程也降低了系统的响应时间,WEB服务器需要同时响应多个用户的请求,故引入了线程,

进一步提高系统的并发性。

每个线程有自己的堆栈和程序计数器为其执行上下文。

进程的实现只能由操作系统内核来实现,而不存在用户态实现的情况。但是对于线程就不同了,线程的管理者可以是用户也

可以是操作系统本身,线程是进程内部的东西,当然存在由进程直接管理线程的可能性。因此线程的实现就应该分为内核态线程实

用户态线程实现

//下面说一下这两种实现,但是目前暂时没有用到这些知识,所以先转载一些博文,正确性有待考证,供以后查阅资料使用

内核态线程实现:

       线程是进程的不同执行序列,也就是说线程是独立运行的基本单位,也是CPU调度的基本单位。

那么操作系统是如何实现管理线程的呢?

       首先操作系统向管理进程一样,应该保持维护线程的所有资源,将线程控制块存放在操作系统的内核空间中。那么此时操作系统

就同时掌管进程控制块和线程控制块。


操作系统管理线程的好处是:

1.用户编程简单;

2.如果一个线程执行阻塞操作,操作系统可以从容的调度另外一个线程的执行。


内核线程的实现缺点是:

1.效率低,因为线程在内核态实现,每次线程切换都需要陷入到内核,由操作系统来调度,而有用户态切换到内核态是要话费很多

时间的,另外内核态实现会占用内核稀有的资源,因为操作系统要维护线程列表,操作系统所占内核空间一旦装载后就无法动态改

变,并且线程的数量远远大于进程的数量,随着线程数的增加内核将耗尽;

2.内核态的实现需要修改操作系统,这个是谁都不想要做的事情;


那么用户态是如何实现管理线程的呢?

用户态管理线程就是用户自己做线程的切换,自己管理线程的信息,操作系统无需知道线程的存在。在用户态下进行线程的

管理需要用户创建一个调度线程。一个线程在执行完一段时间后主动把资源释放给其他线程使用,而在内核台下则无需如此,因为

操作系统可通过周期性的时钟中断把控制权夺过来,在用户态实现情况下,执行系统的调度器也是线程,没有能力夺取控制权。


用户态实现有什么优点?

 首先是灵活,因为操作系统不用知道线程的存在,所以任何操作系统上都能应用;其次,线程切换快,因为切换在用户态进行,

无需陷入带内核态;再次,不用修改操作系统实现容易。


用户态实现的缺点呢? 

首先编程起来很诡异,由于在用户台下各个进程间需要相互合作才能正常运转。那么在编程时必须考虑什么情况下让出CPU

让其他的线程运行,而让出时机的选择对线程的效率和可靠性有很大影响,这个并不容易做到; 

其次,用户态线程实现无法完全达到线程提出所要达到的目的:进程级多道编程;,如果在执行过程中一个线程受阻,它将无

法将控制权交出来,这样整个进程都无法推进。操作系统随即把CPU控制权交给另外一个进程。这样,一个线程受阻造成整个进程受

阻,我们期望的通过线程对进程实施分身的计划就失败了。这是用户态线程致命的缺点。

调度器激活:线程阻塞后,CPU控制权交给了操作系统,要激活受阻进程的线程,唯一的办法就是让操作系统在进程切换

时先不切换,而是通知受阻的进程执行系统(即调用执行系统),并问其是否还有别的线程可以执行。如果有,将CPU控制权交给

该受阻进程的执行系统线程,从而调度另一个可以执行的线程到CPU上。一个进程挂起后,操作系统并不立即切换到别的进程上,而

是给该进程二次机会,让其继续执行。如果该进程只有一个线程,或者其所有线程都已经阻塞,则控制权将再次返回给操作系统。

而现在,操作系统就会切换到其他线程了。


现在操作系统的线程实现模型:

鉴于用户态与内核态都存在缺陷,现代操作将两者结合起来。用户态的执行负责进程内部线程在非阻塞时的切换;内核态的操作系

统负责阻塞线程的切换,即我们同时实现内核态和用户态线程管理。每个内核态线程可以服务一个或者更多个用户态线程。


线程从用户态切换到内核态:

什么情况下会造成线程从用户态到内核态的切换呢?

首先,如果在程序运行过程中发生中断或者异常,系统将自动切换到内核态来运行中断或异常处理机制。

此外,程序进行系统调用也会从用户态切换到内核态。


 


### 进程线程私有资源的区别 #### 1. **进程私有资源** 进程是一个独立的执行环境,拥有自己的地址空间和其他系统资源。以下是进程的主要私有资源: - **地址空间** 每个进程都有其独立的虚拟地址空间,包括代码段、数据段、堆和栈等部分。这意味着一个进程无法直接访问另一个进程的内存区域[^5]。 - **文件描述符表** 文件描述符表记录了进程打开的所有文件及其权限信息。尽管可以通过某些机制(如`dup()`或`fork()`)共享文件描述符,但在默认情况下,每个进程都维护着属于自身的文件描述符表[^3]。 - **全局变量和静态变量** 虽然同一进程下的线程可以共享这些变量的内容,但对于不同进程而言,它们各自拥有一份副本[^4]。 - **信号处理程序** 不同进程之间具有各自的信号处理器设置,即使父子进程通过继承关系存在相似之处,但本质上仍是相互隔离的状态[^2]。 --- #### 2. **线程私有资源** 在同一进程中运行的多个线程共享大部分资源,但也有一些特定的数据结构仅归属于单一线程所有: - **栈 (Stack)** 每个线程都有自己单独的一块栈区用于存储局部变量、函数调用记录以及其他临时数据。这是为了防止各线程之间的干扰[^2]。 - **程序计数器 (Program Counter/PC)** 记录当前线程正在执行哪条机器指令的位置,在多任务环境中保证各个线程能够按照预定逻辑交替推进工作流程[^3]。 - **寄存器集合 (Registers Set)** 包含通用目的寄存器、浮点单元寄存器等内容,反映了该时刻某具体线程内部计算进展状况[^4]。 - **线程本地存储 (Thread Local Storage, TLS)** 提供了一种方法使得开发者可以在不影响其他线程的前提下定义仅供某个特殊线程使用的对象实例或者配置参数[^1]。 --- ### 总结对比表格 | 类型 | 私有资源 | |--------------|---------------------------------------------------------------------------------------------| | **进程** | 地址空间、文件描述符表、全局变量和静态变量、信号处理程序 | | **线程** | 栈、程序计数器、寄存器集合、线程本地存储 | 以上即为两者主要区别所在,值得注意的是由于现代计算机体系架构设计原因导致创建新进程成本较高而新建线程相对便宜很多因此当需要实现高并发场景应用时常优先考虑采用后者方式构建解决方案[^5]。 ```python import threading # 定义一个简单的线程类展示线程私有属性 class MyThread(threading.Thread): def __init__(self, name): super().__init__() self.name = name def run(self): import os print(f"Thread {self.name} has its own stack and PC.") t1 = MyThread("T1") t2 = MyThread("T2") t1.start() t2.start() t1.join() t2.join() ```
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值