谈一谈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和内存之间还由多级缓存,我们的线程中的资源好多都是共享的,所以缓存命中率高,而进程切换,则需要重新加载缓存。
线程的释放也要比进程快。
但是我们同一进程内的线程异常退出,那么我们整个进程也都退出了,而且线程由于一些数据共享的问题,会带来线程安全。

你可能感兴趣的:(linux,运维,服务器)