目录
一、 前言
二、 什么是进程?
进程的基本概念
理解进程
三、 查看进程
通过 ps 命令可以查看进程
通过 ls/ proc 命令查看进程
cwd的作用
通过系统调用获取进程标示符
一、 前言
通过上一节的学习,我们了解了操作系统是如何进行管理的,那么同理,对于进程,操作系统是如何进行管理的呢?
很简单,先将进程描述起来,再把进程组织起来!
那么进程又是什么呢? 下面我们就围绕进程进行讲解
二、 什么是进程?
首先我们来看一个问题:
操作系统能不能一次运行多个程序? 答案是肯定的
因为操作系统运行的程序很多,所以操作系统需要将这些运行的程序管理起来。
而我们就将这些正在运行的程序称为进程。(是正在运行的程序叫进程,而并非是程序本身)
- 如何管理这些进程呢? —— 先描述,在组织
那么,操作系统是如何进行描述,以及组织的呢?
- 操作系统会创建一个描述和控制该进程的结构体。这个结构体称之为进程控制块 (PCB,Processing Control Block), 里面包含了该进程几乎所有的属性信息(可以理解为进程属性的集合),同时CPU也可以通过进程控制块也可以找到该进程的代码和数据。
- 在 Linux 中,进程控制块就是 struct task_struct 结构体。
- 描述好所有进程了,还需要将所有进程的 PCB 给组织起来(通过双链表的方式),此时操作系统只需要拿到双链表的头指针,就可以找到所有进程的PCB。
- 这时,操作系统 就把对进程的管理转换为了对数据结构中PCB的管理,即对双链表的增删查改的操作。
假设,有一个可执行程序test,它存储在磁盘上,就是一个普通文件,当我们 ./test 运行该程序时,操作系统会做出以下的处理: 首先在内存中,先为程序申请进程控制块(PCB),之后将程序从磁盘加载到内存中,并将PCB中相应的指针指向程序,至此就为该程序创建了对应的进程。
总结:
- 为什么要存在 PCB呢? —— 因为OS 要对进程进行管理
- 目前对于进程的理解: 进程 = 程序 (代码 + 数据) + 内核申请的与该进程所对应的数据结构(PCB)。
进程的基本概念
- 【课本概念】: 程序的一个执行实例,正在执行的程序等。
- 【内核观点】: 担当分配系统资源(CPU时间,内存)的实体。
当然,我们目前就可以在Windows系统下,直观的看到目前正在执行的所有程序,可以摁下【ctrl + shift + ESC】 打开任务管理器 查看
这也就证实了,在一个操作系统中不仅只能运行一个进程,还可以运行多个进程。
但是,为什么操作系统可以运行多个进程,CPU是如何对待这多个进程的。这个我们以后在做解释。
理解进程
在了解了进程的基本概念后,我们在更加进一步地认识一下进程管理是如何实现的?
还是那句话,先描述进程,构建一个抽象的数据体,再进行组织进程。
描述进程 —— PCB(进程控制块)
我们对于不同事物的区分方式,就是通过各种不同的属性,故而我们将每个进程的各个属性集合到一个数据信息体中,之后对这个数据信息体进行组织,管理,就达成了进程管理的目的,而其中的数据信息体 就是 PCB(进程控制块)
- 课本中的叫法是: PCB(Process Control Block)
- Linux 操作系统下的PCB 是: task_struct
因为Linux 的内核源代码都是用C语言来编写的,所以linux的这 task_struct 实际上就是一个结构体,而这个结构体就是组织了各种各样的属性,去描述一个进程,Linux采取的组织方式是【双向链表】
以下就是task_struct 的所有结构信息:
- 标示符:描述本进程的唯一标示符,用来区别其他进程。
- 状态: 任务状态,退出代码,推出信号等。
- 优先级: 相对于其他进程的有哦县级。
- 程序计数器:程序中即将被执行的下一条指令的地址。
- 内存指针: 包括程序代码和进程相关数据的指针,还有和其他进程共享的内存块的指针
- 上下文数据:进程执行时CPU的寄存器中的数据。
- I/O 状态信息: 包括显示的 I/O请求,分配给进程的 I/O设备和被进程使用的文件列表。
- 记账信息:可能包括处理器时间总和,使用的时钟数总和,时间限制,记账号等。
- 其他信息
三、 查看进程
接下来,我们就来看一看进程到底长什么样吧
测试代码
1 #include <stdio.h>2 #include <unistd.h>3 #include <sys/types.h>4 #include <stdlib.h>5 6 int main()7 {8 while(1)9 {10 printf("I am a process! pid : %d, ppid: %d\n",getpid(),getppid());11 sleep(1);12 } 13 return 0;14 }
通过 ps 命令可以查看进程
通过 ps ajx 命令查看进程
接下来,我们来查看我们当前运行的测试程序的进程
ps ajx | head -1 && ps ajx | grep test
注: ps ajx | head -1 可以显示进程头,也就是进程的标签头
grep test 只筛选 test 进程
那么, 我们查找的总共有三条,中间那条是我们运行的测试进程,那么其他两条是什么呢?
- 第一条是我们系统中自带的test文件,test系统中是有同名文件的,所以我们在命令行,直接输入test 是可以运行的,而这个查找到的就是系统自带的
- 第三条 grep --color=auto test 这个进程,是grep也是一个程序,他要进行过滤,就要运行,运行了就也会产生进程, 这也证明了所有指令本质上都是程序,运行时也要变成进程
那么,我们应该怎么才能过滤掉【grep】命令执行的进程呢, 其实只需在 原命令后面加个 -v grep 把其过滤掉即可 这样就只有我们执行的程序了
通过 ls/ proc 命令查看进程
我们都知道根目录下有很多的路径:
其中,这个proc目录, 它是一个内存文件系统,里面放的是当前系统实时的进程信息。我们进入此目录看看:
这些就是系统中的所有进程,它的文件夹名称就是对应进程的PID值。PID 就是我们上面介绍的task_struct (Linux下的PCB结构体)中的【标示符】,这个PID是描述本进程的唯一标识符,用于区别其他进程。
之后,我们重新运行下,测试程序,进去测试程序的文件夹中看看
这就是进程中的所有信息,我们换一种方式详细查看下
其中,我们先看 其中的【cwd】这文件,可以看到它的文件类型是【l】,这个文件类型表示的 该文件是链接文件类似于Windows下的快捷方式,而cwd 的全称是 current work directory 当前进程的工作目录,所以,这个文件存储的就是这个进程的工作目录, 【exe】文件,指向的就是这进程的exe执行文件。
cwd的作用
那么,cwd的作用是什么呢?
我们可以在程序中在扩展一段代码,用于创建一个文件,然后我们可以观察看这个文件的创建位置。
可以看到,现在当前文件夹是没有文件的,接下来我们编译运行以下看看
运行结束之后,我们可以看到当前文件夹中就出现了 这个新建的文件【log.txt】,而这也就说明一个情况,那就是当程序中创建的文件没有指定存放位置时,文件会默认放在进程中cwd所指向的位置,所以cwd的一个主要作用就是 —— 默认下载路径
通过系统调用获取进程标示符
上面我们有提到过PID进程标示符,是通过ps这个命令来查看的,那么我们能否直接获取这PID呢?
那么,我们能不能直接获取到每个进程的PID呢?
在之前的文章中我们说到过操作系统是不会相信任何人的,所以操作系统只会提供给用户一些封装一些系统调用(库函数),那我们只需要通过这个系统调用即可获取到当前进程的PID值
注: ps 的搜索进程对应的PID的方法,其实更像是遍历操作,只不过是我们在遍历的基础上增加了筛选操作,才使得能够看到所对应的PID,而下面要说的一种方法,是作用于程序中,获得该程序所对应的进程PID与PPID
接下来,我们来讲解一下 获得PID的命令,也就是getpid()函数和getppid函数
我们可以man 2 一下查看下
下面这段命令,可以实时监控当前系统的进程
while : ;do ps ajx | head -1 && ps ajx | grep test | grep -v grep; sleep 1; done;
然后,我们来运行一下命令,看是否真的能检测到相关的进程
但是,我们多运行几遍,就会发现一个特点:
我们发现,每次重新启动程序时,PID 的值时会出现不同的,而PPID 的值是不变的,因为PID是子进程,而PPID是父进程,其实子进程就是父进程建立的,每次杀死的也都是子进程,所以每一次重启程序,都是父进程重新建立一个子进程,所以每次子进程的PID值是不同的,而父进程的PID却不改变。
那么,我们去看看这父进程到底是什么东西
这个bash 就是我们建立的程序的父进程,而这个bash是什么呢?
其实,每次在登陆Xshell的时候,系统都会为我们单独再创建一个Bash进程,即命令行解释的进程,帮我们在显示器中打印出对话框终端
我们在命令行中输入的所有指令都是Bash进程的子进程,Bash进程只负责命令行的解释
,具体执行出问题的时候只会影响它的子进程