【Linux内核源码剖析】进程原理及系统调用

前言

操作系统作为硬件的使用层,提供使用硬件资源的能力,进程作为操作系统使用层,提供使用操作系统抽象出的资源层的能力。

进程:是指计算机中已运行的程序。 进程本身不是基本的运行单位,而是线程的容器。程序本身只是指令、数据及其组织形式的描述,进程才是程序(那些指令和数据)的真正运行实例。

一、什么是进程

1. 进程的四要素

  1. 有一段程序代其执行;
  2. 有进程专用的系统堆栈空间;
  3. 在内核有task_struct数据结构;
  4. 进程有独立的存储空间,拥有专有的用户空间;

如果具备前面三条而缺少第四条,那就称为“线程”;
如果完全没有用户空间,就称为“内核线程”;
如果共享用户空间,则就称为“用户线程”。

2. 进程的生命周期

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

进程之间的状态关系:

运行:该进程此刻正在执行。

等待:进程能够运行,但没有得到许可,因为CPU分配给另一个进程。调度器可以在下一次任务切换时选择该进程。

睡眠:进程正在睡眠无法运行,因为它在等待一个外部事件。调度器无法在下一次任务切换时选择该进程。

二、task_struct数据结构分析

Linux内核涉及进程和程序的所有算法都围绕一个名为task_struct的数据结构建立,该结构定义在include/linux/sched.h中。
这是系统中主要的一个结构。在阐述调度器的实现之前,了解一下Linux管理进程的方式是很有必要的。

如下图,在include/linux目录下找到sched.h头文件

在这里插入图片描述

在这里插入图片描述

在1379行找到task_struct的定义

在这里插入图片描述

1. 对task_struct的注释

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

三、进程优先级

并非所有进程都具有相同的重要性。除了大多数我们所熟悉的进程优先级之外,进程还有不同的关键度类别,以满足不同需求。首先进行比较粗糙的划分,进程可以分为实时进程非实时进程(普通进程)。

实时进程优先级(0-99)都比普通 进程的优先级(100-139)高。当系统中有实时进程运行时,普通进程几乎无法分到赶时间片(只能分到5%的CPU时间)。

在这里插入图片描述

四、进程系统调用

讨论fork和exec系列系统调用的实现。通常这些调用不是由应用程序直接发出的,而是通过一个中间层调用,即负责与内核通信的C标准库。从用户状态切换到核心态的方法,依不同的体系结构而各有不同。

在这里插入图片描述

1. 进程复制

传统的UNIX中用于复制进程的系统调用是fork。但它并不是Linux为此实现的唯一调用,实际上Linux实现了3个。

(1) fork是重量级调用,因为它建立了父进程的一个完整副本,然后作为子进程执行。为减少与该调用相关的工作量,Linux使用了写时复制(copy-on-write)技术。

(2) vfork类似于fork,但并不创建父进程数据的副本。相反,父子进程之间共享数据。这节省了大量CPU时间(如果一个进程操纵共享数据,则另一个会自动注意到)。

(3) clone产生线程,可以对父子进程之间的共享、复制进行精确控制。

1.1写时复制

内核使用了写时复制(Copy-On-Write,COW)技术,以防止在fork执行时将父进程的所有数据复制到子进程。在调用fork时,内核通常对父进程的每个内存页,都为子进程创建一个相同的副本。

如图,父子进程共享物理内存:

在这里插入图片描述

问题:如果主进程修改其中页z的数据,会发生什么?

此时就会发生父子进程在内存分离。

在这里插入图片描述

只有在不得不复制数据内容时,才去复制数据内容,这就是写时复制的核心思想。
可以看到,因为修改页z导致子进程不得不去复制原页z来保证父子进程互不干扰。

内核置为新生成的子进程创建虚拟空间结构,他们来复制于父进程的虚拟结构,但是不为这些段分配物理内存,他们共享父进程的物理空间,当父进程中有更改相应段的行为发生时,再为子进程相应段分配物理空间。

1.2 执行系统调用

fork、vfork和clone系统调用的入口点分别是sys_fork、sys_vfork和sys_clone函数。其定义依赖于具体的体系结构,因为在用户空间和内核空间之间传递参数的方法因体系结构而异。

1.3 do_fork实现

所有3个fork机制最终都调用kernel/fork.c中的do_fork(一个体系结构无关的函数),其代码流程如图所示:

在这里插入图片描述

其参数为:一组标记位,栈顶指针,栈的大小,父进程和子进程的指针

在这里插入图片描述

2. 内核线程

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

  1. 周期性地将修改的内存页与页来源块设备同步(例如,使用mmap的文件映射)。
  2. 如果内存页很少使用,则写入交换区。
  3. 管理延时动作(deferred action)。
  4. 实现文件系统的事务日志。

3. 退出进程

进程必须用exit系统调用终止。 这使得内核有机会将该进程使用的资源释放回系统。
见kernel/exit.c------>do_exit。
简而言之,该函数的实现就是将各个引用计数器减1,如果引用计数器归0而没有进程再使用对应的结构,那么将相应的内存区域返还给内存管理模块。

在这里插入图片描述

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

_索伦

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值