Linux内核进程原理

2022-11-14 16:25:08 浏览数 (1)

进程的概念

进程是指计算机中已运行的程序。进程本身不是基本的运行单位,而是线程的容器。程序本身只是指令、数据及组织形式的描述,而进程才是程序真正的运行实体。在Linux内核中,进程又称为任务(task),进程的虚拟地址空间可以分为用户虚拟地址空间和内核虚拟地址空间,所有进程共享内核虚拟地址空间,又各自拥有独立的用户虚拟地址空间。

进程拥有两种特殊的形式:没有用户虚拟地址空间的进程叫内核线程(守护进程便属于内核线程),共享用户虚拟地址空间的进程叫用户线程,共享同一个用户虚拟地址空间的所有用户线程称为一个线程组。

C语言标准库进程和Linux的内核进程称呼有所区别,对应关系如下:

C标准库 Linux内核

包括多个线程的进程 线程组

只有一个线程的进程 任务或进程

线程 共享用户虚拟地址空间的进程

可通过 ps 命令查看进程信息(瞬时的进程状态信息,如果想要实时监控,使用 top 命令):

USER:指明进程所属者名称 PID:进程ID %CPU:占用CPU的百分比 %MEM:占用物理内存的百分比

VSZ:占用了多少虚拟内存 STAT:进程状态

进程生命周期

Linux操作系统属于多任务操作系统,系统中每个进程能够分时复用CPU时间片,通过有效的进程调度策略实现多任务并发/并行执行。而进程在被CPU调度运行,等待CPU资源分配以及等待外部事件时会属于不同的状态:

创建态:创建新进程 就绪态:进程获取可以运作所有资源及准备相关条件

执行态:进程正在使用CPU 阻塞态:进程因等待某些资源而被跳出CPU

终止态:进程消亡

系统会将所有的进程保存在一个进程表中,无论其运行、睡眠、等待。但睡眠进程会特别标注出来,调度器会知道它们无法立即运行。

状态转换状态转换

Linux内核提供了一组宏值来表示进程的状态:

TASK_RUNNING(可运行状态或就绪状态);Linux并没有严格区分运行态或就绪态,统一为TASK_RUNNING,表示可运行状态。

TASK_INTERRUPTIBLE(可中断睡眠状态);进程被阻塞,来等待某些条件的发生,一旦条件满足资源就位,那么该进程可直接进入TASK_RUNNING状态,只要调度器选中该进程就可恢复执行,因此这种状态又称为浅睡眠。

TASK_UNINTERRUPTIBLE(不可中断状态);进程在睡眠等待时不被信号等干扰,ps命令下显示状态为D,此类进程只能由内核亲自唤醒,因此该状态又称为深度睡眠。

_TASK_STOPPED(终止状态);表示进程特意停止运行,例如调试器暂停。

EXIT_ZOMBIE(僵尸状态);子进程退出时父进程应使用 wait 或 waitpid

task_struct数据结构

进程控制块PCB的内核实现:task_struct结构体:

task_structtask_struct

下面介绍一些核心成员:

state:进程状态

stack:指向内核栈的指针

pid:全局的进程号 tgid:全局的线程组标识符

pid_links:

real_parent和parent:

个人理解:real_parent指向真实的父进程(调用fork的那个),如果父进程终止了,子进程被孤儿院收养(init进程),此时parent指向init,但大多情况下这两个成员的值是相同的

group_leader:

指向线程组的组长

comm:

其他的一些成员:

进程优先级

进程可以分为实时进程和非实时进程(普通进程),实时进程中的硬实时进程又称为限期进程。限期进程是指必须在一定时间内要完成的进程。其余的非限期进程的实时进程也是需要在一定时间内完成,但不是那么急需。

Linux不支持硬实时处理,至少在主流的内核中不支持。

限期进程优先级大于实时进程,实时进程优先级大于普通进程

限期进程优先级为-1;

实时进程优先级为1-99(值越大优先级越高);

普通进程优先级为100-139(数值越小优先级越高,可通过修改nice的值改变普通进程的优先级,优先级=120 nice)

系统调用

当运行应用程序时,调用fork,vfork,clone等函数就是系统调用

5.0之后的版本直接调用do_fork()

内核线程

内核线程是直接由内核本身启动的线程,内核线程实际上是将内核函数委托给独立的进程,与系统中其他进程“并行执行”(实际上也并行于内核自身的执行)。内核线程经常称之为守护进程。它们用于:

1.周期性地将修改的内存页与页来源块设备同步(例如使用mmap的文件映射)

2.如果内存页很少使用,则写入交换区

3.管理延时动作

4.实现文件系统的事务日志

内核线程是独立运行在内核空间的进程,与普通用户进程区别在于内核线程没有独立的进程地址空间。task_struct结构体的指针成员 mm 设置为NULL。

退出进程

退出进程的方式有两种:1.调用exit()或者从主函数返回。2.接收到终止信号或者异常时被终止。

代码语言:javascript复制
SYSCALL_DEFINE1(exit, int, error_code)
{
 do_exit((error_code&0xff)<<8);
}

0 人点赞