【Linux内核】Linux内核 - 进程管理

发布时间 2023-04-04 22:44:56作者: -zx-

Linux进程管理包括进程调度,中断处理,信号,进程优先级,进程切换,进程状态,进程内存等等。

什么是进程

进程是在处理器上执行的一个实例,进程可使用任意资源以便Linux内核可以处理完成它的任务。在Linux上运行的所有进程都是通过task_struct结构来管理的,被称为进程描述符。一个进程描述符包含单个进程在运行期间的所有必要的信息,比如进程表示,进程的属性,构建进程的资源等等。

image

进程的生命周期

进程有自己的生命周期,比如创建、执行、终止、删除等,进程典型的生命周期:

img

创建进程

当一个进程创建一个新进程的时候,父进程发出一个fork()系统调用,然后父进程得到一个新创建的子进程的进程描述符,并设置一个新的进程ID。复制父进程的进程描述符的值给子进程。此时两个进程的地址共享相同的地址空间。

详见:https://www.cnblogs.com/Wangzx000/p/16973746.html

执行进程

exec系统调用将新的程序复制到子进程的地址空间。因为创建后两个进程共享共同的地址空间,所以新程序写数据时会导致页错误。这时内核会给子进程分配定的物理页。这种延迟操作被称为Copy On Write。此操作可以避免不必要的开销,因为复制着整个地址空间是非常慢和低效的操作,会占用处理器大量事件和资源。

终止进程

当程序执行完成的时候,通过一个exit()系统调用终止子进程。exit()系统调用释放进程的大部分数据结构并发送一个终止信号给父进程,此时的子进程被称为僵尸进程(aombie process)。

删除进程

子进程终止后不会完全的移除,知道父进程通过wait()系统调用得知子进程已终止。只要子进程的终止通知发送到父进程,父进程就会移除所有子进程的数据结构,并释放进程描述符。

线程

线程是在进程中产生的一个执行单元,其在同一进程中与其他线程并行运行。它们可以共享相同的资源,比如内存、地址空间、打开的文件、等等。它们也可以访问同一组应用程序的数据。线程被称为轻量级进程的原因是因为它们共享资源,所以它们中的每个线程不能同时改变它们共享的资源。

从性能的角度看,线程的创建要比进程的创建开销更小,因为创建线程不需要复制资源。在内核层面处理它们使用相似的方式。

img

在Linux环境下线程的操作使用pthread库进行操作。

详见:https://www.cnblogs.com/Wangzx000/p/16929584.html

进程优先级和nice等级

cpu管理进程是按照时间片的方式来划分CPU的资源。CPU可以把1s划分为若干份,轮询时间片。通过设置进程的优先级,让某些重要的进程优先并长时间获取CPU资源来运行。进程的优先级分为静态优先级和动态优先级。

  • 静态优先级:-20到19共40个优先级。nice值越高,优先级越低;nice值越低,优先级越高
  • 动态优先级:1-99

只有root用户才能设置负nice级别,或者降低现有进程的nice,来提高优先级。普通用户只能设置正nice级别,也就是只能降低优先级不能提高优先级。

上下文切换

在处理器执行期间,运行进程的信息被存储在处理器的寄存器和高速缓冲cache中,执行的进程被加载到寄存器的数据集被称为上下文(context)。在切换过程中,先存储运行进程的上下文,然后将下一个要运行的进程的上下文恢复到寄存器。进程描述符和内核模式堆栈区域用于存储上下文。这个切换的过程被称为上下文切换(context switching)。过多的上下文切换,会导致性能下降,因为处理器每次都要刷新寄存器和高速缓存(cache),以便释放给新的进程。

中断处理

中断处理是优先级最高的任务之一。中断通常由I/O设备产生,比如网卡,键盘等等。中断处理是Linux内核通知事件,比如键盘输入、以太网帧到达等等。并要尽可能快地执行中断处理,因为有些设备需要快速响应。当一个中断信号到达内核的时候,内核必须从当前执行的进程切换到一个新的进程,以处理这个中断。这就意味着中断会导致上下文切换。大量的中断会导致性能下降。

在Linux实现中,有两个类型的中断。硬中断和软中断;硬中断是由硬件设备产生的,需要快速响应。软中断被用来处理可以推迟的任务比如TCP/IP操作,SCSI协议操作等等。可以在/proc/interrupts下看到硬件中断的相关信息。

进程状态

每个进程都有自己的状态,进程有以下几种状态:

  • TASK_RUNNING:进程正在CPU上运行,或者在运行队列中等待运行
  • TASK_STOPPED:进程由于某些信号(SIGINT,SIGSTOP)被暂停。进程在等待一个恢复信号比如SIGCONT
  • TASK_INTERRUPTIBLE:在这个状态下,进程暂停,并等待某个条件得到满足。
  • TASK_UNINTERRUPTIBLE:类似于TASK_INTERRUPTIBLE,当一个进程处在TASK_INTERRUPTIBLE状态时,它是能够被中断的。在TASK_INTERRUPTIBLE状态下给进程发送一个不执行任何操作的信号。进程的典型例子是一个进程在等待磁盘I/O操作
  • TASK_ZOMBIE:一个进程通过exit()系统调用退出之后,它的父进程应该知道它已终止。在TASK_ZOMBIE状态下,一个进程在等待通知它的父进程释放所有的数据结构

僵尸进程:

当一个进程接收到一个终止信号时,在它结束之前一般需要一些时间结束所有的任务(比如关闭打开的文件)。通常在很短的时间内,这个进程是一个僵尸进程。有时候一个僵尸进程不能终止自己,在这种情况下显示为Z(僵尸)状态。

使用kill命令是不能啥事这样的一个进程的,因为它已经被认定为死亡,如果想摆脱一个僵尸进程可以通过杀死父进程,这样僵尸就会随之消失。但是如果僵尸进程的父进程是init,那么就需要重启系统来摆脱僵尸进程。

进程的内存段

Linux内核对每个进程采用的是动态内存分配机制。进程的内存分配结构如下图:

img

进程的内存区域由以下段组成:

  • 文本段(代码段):存储可执行代码,权限为只读(r)
  • 数据段:数据段由3个区域组成:
    • 数据:这个区域存储初始化数据,比如静态变量
    • BSS:这个区域存储零初始化数据。数据被初始化为0
    • 堆:这个区域,malloc()会根据需求动态分配内存,堆向较高的地址增长。
  • 栈段:这个区域是局部变量、函数参数、返回的存储函数的存放区域。栈向低地址增长