
🎬 个人主页:HABuo
📖 个人专栏:《C++系列》 《Linux系列》《数据结构》《C语言系列》《Python系列》《YOLO系列》
⛰️ 如果再也不能见到你,祝你早安,午安,晚安

目录
[📖3.1 进程标识符:pid](#📖3.1 进程标识符:pid)
[📖3.2 在Linux下使用指令终止进程](#📖3.2 在Linux下使用指令终止进程)
[📖3.3 系统调用函数:getpid](#📖3.3 系统调用函数:getpid)
[📖3.4 子进程与父进程](#📖3.4 子进程与父进程)
前言 :
上篇博客我们了解了冯诺依曼结构和计算机组成体系,本篇博客我们就来了解我们虽熟知但不理解的进程以及操作系统要管理进程所产生的PCB。
本章重点:
本章会重点讲解进程的概念以及操作系统是如何管理进程的 ,PCB的概念以及Linux下的PCB实体,最后讲解进程对应的标识符:pid和ppid的概念以及查看方式
如果你还没了解学习上篇博客计算机体系结构-->冯诺依曼结构&操作系统理解
请你一定先去看看这篇博客,上篇博客所介绍的知识点,是我们以后学习Linux操作系统的基石!
📚一、进程概念
我们常听说,只要一个在磁盘的程序文件加载到内存就成为了进程。
?是我知道了,什么意思?从来没有人给我们解释一番,你要是问老师,是不是大概率得到的结论就是这是定义。知识是无法建立在想象的基础之上,你说的那么抽象啥叫程序文件加载到内存就是进程,如果就仅仅是加载,为啥在磁盘的就是程序文件,到了内存就是进程?有啥区别?
相信你会有疑问,我也是,因此你才会打开这篇博客,所以让我们一起来学习它!
先说结论:进程 = 内核数据结构(struct task_struct)+ 进程对应的磁盘程序数据
什么意思?你怎么也先给我上结论,能不能直接点,行别着急,马上来。
📚二、进程控制块(PCB)
在上篇博客冯诺依曼系统中讲到,管理的本质不是真正的管理实体本身,而是管理它的数据,虽然现在我们不知道什么是进程,但是我们可以通过先描述,再组织这一结论得出,操作系统想要管理进程肯定也是管理进程对应的数据,并且要管理它,肯定先要描述。请看下图:

操作系统将可执行程序的各种数据提取出来放到内存,由于同时会存在多个程序文件,所以操作系统要管理它们,有必要把它们的属性信息提取出来(具体理解例子可以看上篇博客),而这些信息就可以放到一个结构体当中,因此对于这些程序文件的管理,就变成了对这些结构体对象进行管理,管理可以使用链表等一些数据结构进行管理。虽然我们并不知道这些结构体都有什么数据,但是可以假设或者猜想一下:
cpp
struct task_struct
{
//id
//代码/函数地址
//状态(是否被CPU处理)
//优先级
struct data* next
}
概念来了:
这个结构体就被称为PCB(ProcessControlBlock)``也叫进程控制块
所以我们可以回答上面疑问了:进程是什么?
进程就是磁盘中加载到内存之后的程序文件 + 操作系统要管理这些成勋文件所随之产生的内核数据结构PCB(ProcessControlBlock,也即是struct task_struct)。因此操作系统(os)是怎样管理进程的呢?os对进程的管理被建模成为了对链表中结构体的增删查改!
📚三、见见进程
📖3.1 进程标识符:pid
每一个进程都有自己对应的pid,查看当前所有进程的信息:
使用指令**:
ps ajx**

这样查看的是所有的进程,很难帮助我们学习,我们可以使用管道命令来帮助我们进行过滤,现在写一个死循环程序并运行让它一直处于进程运行的状态:
cpp
#include<stdio.h>
#include<unistd.h>
int main()
{
while(1)//死循环
{
printf("我现在是一个进程了\n");
sleep(1);//休眠一秒
}
return 0;
}
现在让程序运行起来,再去查看进程:
在所有进程中搜索我刚刚写的可执行程序:
使用指令**:
ps ajx | grep mytest**
将进程信息的第一行打印出来:
使用指令**:
ps ajx | head -1**

查看进程
可以看见当前mytest程序的进程pid是80162,会出现两个mytest的原因是,grep指令本身也要形成mytest文件,所以其实第二个红字mytest是grep的!
📖3.2 在Linux下使用指令终止进程
在我们的程序运行时,可以在运行的地方按CTRL+c来结束进程,但是总有一些顽固分子,因此还有一种方法可以结束进程:
使用指令:
kill -9 要杀掉的进程id
注:这里的-9是信号参数,直接使用即可

注意你别杀成该进程的父进程了即16193(ppid),那是解释器的,杀了就直接退出了
📖3.3 系统调用函数:getpid
每次查看进程使用都要使用ps指令我感觉非常的麻烦,于是这里有一个系统调用的接口函数可以直接返回当前进程的pid,由于操作系统是由C语言编写的,所以可以直接在程序中调用此函数:
使用函数**:
getpid()**
man手册查看getpid相关信息:

cpp
#include<stdio.h>
#include<unistd.h>
int main()
{
int pid = getpid();
int ppid = getppid();
while(1)
{
printf("我是一个进程,我的id是: %d,我的父进程id是: %d\n", pid, ppid);
sleep(1);
}
return 0;
}

📖3.4 子进程与父进程
上面我们看到了一个进程既有pid又有ppid,并看到相应的接口函数,获取这两个值。那么什么叫做子进程什么又叫做父进程呢?我们还是以上述的例子来讲解。

可以发现,每次运行时,子进程的id都在变化然而父进程的id一直没变!这是因为在命令行中,父进程一般是命令行解释器**:
bash**
然后再来看一个神奇的现象:
cpp
#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>
int main()
{
printf("我是一个进程,我的id是: %d,我的父进程id是: %d\n", getpid(), getppid());
pid_t id = fork();
if(id > 0){
while(1){
printf("我是一个父进程,我的id是: %d\n", getpid());
sleep(1);
}
}
else if(id == 0){
while(1){
printf("我是一个子进程,我的id是: %d\n", getpid());
sleep(2);
}
}
else
printf("创建子进程失败\n");
return 0;
}

发现了一件细思极恐的事情,同一个if语句竟然,if和else同时执行,这在学习C语言当中是从未遇到过的,unbelievable!这就是fork子进程的魅力,也是我们超越语言层级,学习编程的魅力,在此我们先了解一下,至于fork更详尽的内容,我将在接下来的博客当中更加详细的介绍!这里先简单说明:fork之后bash解释器就成为了爷爷,而当前进程又衍生出了一个子进程,那么这段代码即成为了当前进程和子进程共有,因此它们将共同执行这段代码,fork的返回值中,父进程(即当前进程)返回的是子进程(创建进程)的id,子进程(创建进程)返回的是0,创建失败返回值是-1;
📚四、总结
本篇博客我们进一步加深了解了进程,并通过命令行窗口见了进程,小结一下:一句话:进程 = 内核数据结构 + 磁盘加载到内存的程序数据,内核数据结构就是操作系统用来管理进程所创建的结构体对象,通过相应的数据结构来对这些对象进行管理,从而实现对进程的管理!
对于所使用的指令,不必刻意记住,练习个一遍两遍也就熟悉了!
