前言

进程这章节的内容是我们学习linux遇到的第一座大山,它的重难点程度不亚于C++中的继承多态。由于它的内容很多,所以也会分多篇文章进行介绍。在学习进程之前,建议同学们先阅读文章对冯诺依曼体系结构和操作系统的理解

一,对PCB的理解

先简单认识一下进程的概念:

(1) 课本概念:程序的一个执行实例,正在执行的程序等
(2) 内核观点:担当分配系统资源(CPU时间,内存)的实体

同学们肯定是理解不了这种概念的。
我们打开windows的任务管理器:

在这里插入图片描述
这些就是当前我电脑上存在的进程。

结论:操作系统中,进程可以同时存在非常多!

既然有这么多进程同时存在,那操作系统怎么管理这些进程呢?
虽然现在我们不知道什么是进程,但是我们可以通过先描述,再组织这一结论得出,操作系统管理进程肯定也是管理进程对应的数据,请看下图:

在这里插入图片描述
难道这些在内存里的代码和数据就是进程吗?
如果是进程,那问题又来了,这个进程被调度多久时间了,代码/数据是从哪开始到哪结束的,有多个进程时,凭什么优先调度我?这些问题都不能在上图中表示出来。

结论:对于操作系统来讲,这个被加载到内存的可执行程序根本就不是进程!它只是进程对应的代码和数据!

OS为了管理已经被加载进来的进程,都要为每个进程创建一个struct结构体(用来描述),使用链表或其他数据结构将它们链接在一起(用来组织)。

在OS学科中,每个进程都会有一个struct PCB,这个PCB中包含了每个进程的所有属性,也叫进程控制块

struct PCB
{
	// id
	// 代码/函数地址
	// 状态(是否被CPU处理)
	// 优先级
	// 内存指针(指向自己的代码和数据)
	struct PCB* next
}

所以上图可修改为:
在这里插入图片描述

结论:
(1) PCB是在操作系统内部管理进程的内核数据结构。
(2) 进程 = PCB + 自己的代码和数据!
(3) 对进程的管理,就变成了对链表的增删查改!

讲到这里可能同学们还是有点迷糊,现在把它们和我们生活中的事物类比理解:

我们把学校类比成内存,OS类比成学校的教务管理系统,把一个学生类比为加载到内存的代码和数据,而学生在教务系统中的属性信息类比为进程中的PCB在OS的内部。
怎么证明你是这个学校的学生呢?因为你的信息在这个学校教务系统中。
所以OS对进程的管理,本质是对PCB的管理,而并非把可执行程序加载进来对可执行程序进行直接管理!

对PCB的再理解:
PCB是操作系统中的概念(linux中有PCB,windws中也有PCB…),而我们学习的linux操作系统是一款具体的操作系统,在linux系统中具体实现的PCB,叫做 task_struct

struct task_struct
{
	// linux进程控制块
}

二,CPU对进程列表的处理

进程被链接在链表中会等待CPU去PCB找数据做处理,那么CPU怎么知道要处理哪些数据呢?这不得不提到进程排队的概念:

把对应的PCB从链表中提取到队列中队,PCB中的数据不会一次性被CPU处理完,它有时被处理,有时在等待被处理,这是一种动态运行的特征,请看下图:

在这里插入图片描述
理解进程动态运行:

只要我们的进程task_struct将来在不同的队列中,进程就可以访问不同的资源。

结论:
(1) 调度运行进程,本质就是让进程控制块task_struct进行排队!
(2) 再给进程下一个定义:进程 = 内核task_struct + 程序的代码和数据!

三,进程标识符:pid

我们先构建一个代码样例:
在这里插入图片描述

让它运行起来:
在这里插入图片描述

1.得到第一个结论:

./xxx 本质就是系统创建进程并运行

在这里插入图片描述

可以得到启示:

我自己写的代码相乘的可执行文件 == 系统命令。在linux系统中大部分执行的操作,本质就是运行进程。

在这里插入图片描述

2.每个进程都有自己的唯一标识符,叫做进程pid

1. 查看系统进程1: ps axj

先来查看当前系统中的进程信息:

使用指令:ps axj

在这里插入图片描述

这样查看的是所有的进程,很难帮助我们学习,现在我们根据上面的样例代码,所有进程中搜索我刚刚写的可执行程序:

使用指令:ps axj | grep myprocess

在这里插入图片描述
将进程信息的第一行打印出来:

使用指令: ps ajx | head -1

在这里插入图片描述

把上面进程中每行多列的含义进行对应起来:

使用指令:ps ajx | head -1 && ps axj | grep process

在这里插入图片描述

3.在Linux下使用指令终止进程
在我们的程序运行时,可以在运行的地方按CTRL+c来结束进程,但是还有一种方法可以结束进程:

使用指令: kill -9 要杀掉的进程id
(注:这里的-9是信号参数,直接使用即可)

2. 查看系统进程2: /proc

在Linux系统的根目录下,有一个动态文件proc,它里面存放着所有进程的信息,之所以叫动态文件是因为它会随着进程的改变而随时更新它的内容!
在这里插入图片描述

查看所有进程文件:

使用指令: ls /proc/

查看特点的进程文件:

使用指令: ls /proc/pid

我们每启动一个进程都会在proc文件目录下创建一个以该进程的pid为名称的文件夹。
在这里插入图片描述

可以发现,在自行创建的进程中有很多我们看不懂的文件,这些文件也不需要掌握,但是有两个文件需要大家注意,一个是cwd一个是exe:

在这里插入图片描述

1. exe指向可执行程序的位置:
进程的pcb中会记录自己对应的可执行文件的路径。
2. cwd代表当前文件:
进程的当前工作路径。

四,系统调用函数:getpid

每次查看进程使用都要使用ps指令,我感觉非常的麻烦,于是这里有一个系统调用函数可以直接返回当前进程的pid,由于操作系统是由C语言编写的,所以可以直接在程序中调用此函数:

使用函数: getpid()

使用man手册查看getpid相关信息:

在这里插入图片描述

使用方式:
在这里插入图片描述
在这里插入图片描述

五,父进程和子进程的概念

在使用ps指令查看进程详情时,除了pid我们可以看见左边还有一个ppid,这是parent pid的意思,也就是父进程的pid,请看下图:
在这里插入图片描述

再来学习一个可以查看父进程id的系统调用函数:
在这里插入图片描述

使用函数: getppid()

使用方式:
在这里插入图片描述
在这里插入图片描述

可以发现,每次运行时,子进程的id都在变化,然而父进程的id一直没变!这是因为在命令行中,父进程一般是命令行解释器: bash

在这里插入图片描述

六,创建子进程–fork函数的使用

Linux中创建进程的方式有两种:

1. 命令行中直接启动可执行程序
2. 通过代码创建进程

启动进程的本质就是创建进程,一般是通过父进程创建子进程,构成一种父子关系而命令行中启动的进程都是由bash为父进程模拟创建子进程的!

使用man指令查看fork函数信息:
在这里插入图片描述

1. 创建一个子进程并观察

写一个代码样例创建子进程观察情况:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

2. fork返回值的理解

请大家自行使用man手册查看forh函数的返回值,这里把它翻译成中文:

在这里插入图片描述

理解下面的代码:
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

首先,fork之后,父子进程都会执行代码的本质是它们都被内存调度了,而当一个函数执行到return时,它的核心工作才算执行完成,于是我们可以想象一下fork函数内部的一些代码信息:

在这里插入图片描述

在这里插入图片描述

3. 进程的独立性

在这里插入图片描述
在这里插入图片描述

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐