谈一谈Linux下的进程和线程

文章目录

进程

什么是进程?

概念上来说,进程是担当OS资源分配的实体。通俗来说,进程是我们OS上一个在运行的程序。

我们的OS上不止有一个进程,当我们的某一个进程像是去磁盘上读文件时,由于磁盘的速度很慢,这是为了提高CPU的利用率,这时就会将该进程挂起,而去执行另一个进程,直到磁盘读写完毕,给OS一个信号,OS从而在去调度原来被挂起的进程。

进程不止有一个,所以就需要我们对进程进行管理,怎么管理:先描述,后组织 ,将进程的属性抽象成结构体,然后将每个进程的结构体通过链表组织起来。其中所谓 的结构体就是我们的PCB---进程控制模块,在Linux中就是我们的task_struct。每一个进程都有其独立的PCB,那么操作系统对进程的管理就变为了实际上对PCB进行管理。PCB放在哪个组织结构里实则对应着的是其不同的进程状态,我们的进程至少具备三种基本状态:运行状态,就绪状态,阻塞状态。其分别对应着运行队列,就绪队列,阻塞队列这样的组织结构。

运行状态:正在运行的进程

就绪状态:随时可以运行,但CPU正在被其他进程所占有

阻塞状态:由于等待输入输出等时间,无法运行,及时给它CUP控制权,也无法运行。

若有大量的阻塞状态,回导致我们的内存利用率不高,所以通常会把阻塞状态的进程的物理内存换入到磁盘中,只留有其内核数据在内存中,当需要再运行的时候,再从磁盘中写回,我们将这种状态成为挂起状态。

task_struct中有什么?

标示符: 描述本进程的唯一标示符,用来区别其他进程。

状态: 任务状态,退出代码,退出信号等。

优先级: 相对于其他进程的优先级。

程序计数器: 程序中即将被执行的下一条指令的地址。

内存指针: 包括程序代码和进程相关数据的指针,还有和其他进程共享的内存块的指针

上下文数据: 进程执行时处理器的寄存器中的数据。

I/O状态信息: 包括显示的I/O请求,分配给进程的I/O设备和被进程使用的文件列表。

记账信息: 可能包括处理器时间总和,使用的时钟数总和,时间限制,记账号等。

其他信息

那我们的进程都要是处于就绪状态,OS该如何决策调度哪一个进程呢?这就是是OS调度器要干的事情啦?我们可以在可以简单的说名以下:每个进程都有优先级,优先级越高,则越是容易被调度,当然这个优先级的计算,是调度器帮我们去评判的,那么我们人为的可以干预吗,是可以的!我们的nice值就可以帮助我们进行优先级的干预。

PRI是进程的优先级,其表示该进程被cpu执行的先后顺序,其值越小,优先级越高。 那么NI是啥呢?可以将其理解为优先级的修正数值。

因此调整nice值就是调整进程的优先级。nice值的取值范围是-19 - 20。

那么如何更改nice值

可以使用top命令进行修改。其使用格式如下: 进入top后按"r"-->输入进程PID-->输入nice值。

调整已存在进程的nice:renice

我们的每个进程都拥有其独立的进程地址空间,以及页表。这样的设计使得我们多进程得以实现,避免了直接对物理内存操作而引起地址冲突,再者,由于虚拟地址到物理地址之间要经过映射,并且我们对进程地址空间进行了分段,因此在映射时,我们自然就可以进行一些检查,检查其访问的地址是否安全,以及该地址所存储数据的属性是否允许我们进行操作等;进程地址空间使得让每一个进程都觉得自己拥有整个内存,它不关心其他的进程,这也是进程独立的体现,并且,由于有局部性原理,我们可以将一些暂时不用的其他进程所占用的内存,换出到磁盘中,从而给当前进程用,提高了内存的使用效率。

我们可以在当前进程中,利用fork函数创建子进程,我们的进程具有独立性,创建的子进程通过拷贝父进程的PCB,再更具自己的情况,稍加更改,就成了自己的PCB结构体,因此在父进程在创建子进程前的一些属性和数据,例如文件描述符表等子进程都是可以看见的。当然子进程也会创建出自己的进程地址空间。只不过为了提高效率,节约内存其采用读时共享,写时拷贝的策略,在读时,和父进程映射同一块物理内存,只有写时,才会重新将数据映射到心的物理内存上。

新创建出来的子进程,若我们不进行等待,就会形成僵尸进程,僵尸进程会占用一定的资源,我们的子进程在退出时,其会保留一些退出信息,当然由于进程并未完全退出,所以其PCB当然也存在在内核中,所以为了减少资源的浪费,我们要对子进程进行回收,回收可以采用父进程阻塞式的等待,父进程进行轮询判断等方式进行回收,还可以通过信号的方式:因为我们的子进程在退出时,会像父进程发送SIGCHILD信号,所以我们若不关心退出信息,可以通过显式的忽略该信号(特例),关心的话可以在自定义信号处理函数中进行等待(循还+非阻塞式等待)。

我们在的进程还可以进行替换,利用execv函数族进行进程替换,其本质就是将该进程的代码和数据进行替换,PCB等结构不变。

进程终止我们可以通过exit函数,信号,直接return等方式进行退出。

线程

什么叫线程?

线程是OS进行调度运行的最小单位,线程运行在进程的内部,是进程实际运行的单位。进程是承担OS资源的实体,那么线程就是承担进程部分资源的一个实体。

在一个进程内部,不止有一个进程,那么我们是不是要对其进行管理,先描述,后组织,将其属性抽象从某种结构体,我们将这种结构体成为TCB。也就是说,OS实际调度的是一个个TCB,当然这是对于一般OS来说。Linux有自己的方案。

Linux认为既然线程的结构和进程类似,那么我就不用去再实现线程的一套结构,而是将一个线程当作一个特殊的进程来看待,这样的优势则是,减少了我们结构的复杂度,从而降低了BUG的产生。

因此,现在对于CPU而言,其不管你是线程还是进程,它所认的就是PCB结构体。

为了能够实现线程,我们就要将原来进程的那一套进行一些更改。这就涉及到了线程的一些特性。我们的同一个进程的多个线程之间是能够共享代码段,数据段,打开的文件等资源的,也就是说其要共享进程地址空间上的一些数据。这当然好办,我们在创建这个特殊的进程时,将其和创建它的进程的地址空间进行共享。

当然除了共享的数据以外,每个线程都是一个执行流,它也应该拥有自己的私有栈,独立的上下文数据,信号屏蔽字,线程ID,调度优先级等。后面的一些数据,由于线程有独立的PCB,因此可以私有化,但是,栈怎么办?用户栈只有一个,要是都放在用户栈里,岂不是太过于混乱。Linux的解决办法是这样的:此时,我们的线程对于上层用户来说,操作是有难度的,所以有第三方库经过对Linux 的线程接口封装为我们提供了使用Linux线程更方便的方法,在使用线程时,程序运行起来后,会将第三方库加载到我们的mmp共享区域,这个库不光为我们解决了使用线程系统调用接口难的问题,还帮我们提供了线程所使用的栈。这个栈在哪呢?就在我们的mmp中,并且我们的线程id就是其栈的起始地址,而我们ps -aL中显式的LWP则是内核中标记线程的id。它与我们用户所看到的线程id是一一对应的。

所以Linux下的进程<=其他OS下的进程。OS下的进程我们称为轻量级进程。

进程与线程比较

进程承担OS资源分配的实体,而线程是CPU的基本调度单位,线程在进程中。

线程之间大多数资源是共享的,所以线程间通信方便,而进程是独立的,所以进程间通信难度较大。

线程能够减少并发执行的时间和空间开销-----体现在:

线程的创建更加简单。

线程进行切换时效率更快。因为我们在进行切换时,不仅仅是将PCB换下去,保存上下文,我们的计算机为了更快的效率,在CPU和内存之间还由多级缓存,我们的线程中的资源好多都是共享的,所以缓存命中率高,而进程切换,则需要重新加载缓存。

线程的释放也要比进程快。

但是我们同一进程内的线程异常退出,那么我们整个进程也都退出了,而且线程由于一些数据共享的问题,会带来线程安全。

相关推荐
A小辣椒11 小时前
TShark:Wireshark CLI 功能
linux
A小辣椒15 小时前
TShark:基础知识
linux
AlfredZhao17 小时前
OCI 明明分配了 200G 系统盘,为什么 df 只看到 30G?
linux·oci
AlfredZhao1 天前
vi 删除指定范围的行,不用再反复按 dd
linux·vi
用户9718356334662 天前
银河麒麟 KY10 申威(SW64) 安装 nginx-1.16.1-2.p01.ky10.sw_64.rpm 详细步骤
linux
猪脚踏浪2 天前
linux 拷贝文件或目录到指定的位置
linux
大树882 天前
金刚石散热越强,管路越先见顶
大数据·运维·服务器·人工智能·ai
摇滚侠2 天前
Linux CentOS7 rpm 安装 MySQL 5.7
linux·运维·mysql
霸道流氓气质2 天前
领域驱动设计(DDD)在 Spring Boot 微服务中的实践指南
运维·spring boot·微服务
bush42 天前
嵌入式linux学习记录十四、术语
linux·嵌入式