一文带你轻松入门Linux中的『进程』-1

简介: 一文带你轻松入门Linux中的『进程』

一、进程的基本概念

本文开始,我们就正式地来讲讲 Linux 中的【进程】

首先读者要知道的是,什么是进程

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

对于课本中的观点大家可能会觉得难以理解,为何正在执行的程序就是一个进程呢。我们可以在Windows下按[Ctrl + Alt + Delete]打开任务管理器查看一下

可以看到左上角的这个【进程】标志,代表呢我们下面所运行的程序都是一个进程

image.png👉 这也就表明了在一个操作系统中不仅只能运行一个进程,还可以运行多个进程

但是呢,进程不仅仅可以像上面这样去理解。我们来思考一个问题:==程序是文件吗?==

  • 相信有学习过 Linux权限概念 的读者肯定很清楚,文件是存放在磁盘中的,磁盘呢则是属于外设。这一块我们在 冯诺依曼体系结构 有讲得很清楚,对于CPU来说,它是只会和内存打交到的,所以磁盘中的数据需要先加载到内存中才可以被执行
  • 那么,当可执行文件被加载到内存中时,该程序就成为了一个【进程】

二、理解进程

在了解了进程的基本概念后,我们来更加进一步地认识一下进程到底是个什么东西

1、描述进程 - PCB

  • 我们在 操作系统概念理解 中有谈到过管理的本质为【先描述,再组织】,那操作系统要将这些进程管理起来,就需要 先去描述一个进程,那该怎么去描述呢?

首先读者要清楚我们人是怎么辨别一个事物的:没错,就是通过其各种属性

image.png

  • 那么在 Linux 中,我们使用一个结构体去描述一个进程,因为 Linux 的内核源代码都是使用C语言来写的,而 结构体 则是C语言里面的一些知识,不懂的同学可以先去了解一下。并且呢这个结构体还有名字的,它叫做【进程控制块
  • 课本中的叫法呢是:PCB(Process Control Block)
  • Linux操作系统下的PCB是:task_struct
  • 这个结构体呢就是组织了各种各样的属性,才可以去很好地描述一个进程

以下就是这个task_struct的所有结构信息

  • 标示符: 描述本进程的唯一标示符,用来区别其他进程。
  • 状态: 任务状态,退出代码,退出信号等。
  • 优先级: 相对于其他进程的优先级。
  • 程序计数器: 程序中即将被执行的下一条指令的地址。
  • 内存指针: 包括程序代码和进程相关数据的指针,还有和其他进程共享的内存块的指针
  • 上下文数据: 进程执行时处理器的寄存器中的数据[休学例子,要加图CPU,寄存器]。
  • I/O状态信息: 包括显示的I/O请求,分配给进程的I/O设备和被进程使用的文件列表。
  • 记账信息: 可能包括处理器时间总和,使用的时钟数总和,时间限制,记账号等。
  • 其他信息
  • 转换为代码形式的话就可以是下面这样


struct PCB{
  进程的编号
    进程的状态
    进程的优先级
    ...
    相关的指针信息
};

既然知道了如何去描述一个PCB结构体,我们就要来知道操作系统对一个进程总共会做哪些事情

  1. 为该进程创建对应的PCB对象
  2. 将该进程的代码和数据加载到内存中
  • 所以,很多教科书在介绍进程的时候只会说它在计算机内部是一个PCB对象,其实对于一个进程来说:应该是由操作系统为其创建出来的 PCB对象 + 其数据代码 组成的

image.png

但是呢光这么说读者可能还不是很明白,这里举一个例子说明一下

  • 高考呢也结束了,各大高校正在准备给即将入学的大一新生准备发放录取通知书,作为读者的你思考一下,在你刚高考完的那个时候是否还待在家中过暑假呢🍉,虽然你已经是某一所高校的学生,但是呢人还不在学校🏫,待在家里而已

image.png

  • 不过这个时候呢,你高中时候的档案已经被迁移到了大学中,即你的各种信息和数据。此时你还不算是这所学校真正的学生,只有当你在9月份开学的时候,你已经是属于某个班级中的某个学生时,即算是一个完整的对象,此时你才是算真正属于这个学校中的人:boy:

最后我们可以总结一点:

👉 【进程】 = 内核PCB数据结构对象(描述你这个进程所有的属性值) + 你自己的代码和数据(在磁盘当中所形成的可执行程序)

2、组织进程

上面谈到了操作系统如何利用PCB去描述一个相关的进程,接下去我们来说说如何去组织进程

  • 大学的校园我们都知道,比中学的大多了。但是呢在学校里每个学生他的人身是自由的,不可能每个人拿着一个牌子,上面自己写上个人的基本信息,这是不现实的。比方说学院的辅导员想要找一个学生的话,一定是通过在学籍系统中找到这个学生的所在行记录,才可以对其进行相关的操作,读者可以认为这个信息记录即为我们在上面所讲到的【PCB对象
  • 因此操作系统如果要去管理一个进程的话只需要找到某个==进程的PCB对象==即可,通过这个PCB对象就可以找到相对应的代码和数据

image.png

  • 那这个PCB结构体中就一定存在一个东西叫做结构体指针,里面保存了某个内容的地址,方便我们很快地知道其对应的信息


struct PCB{
  进程的编号
    进程的状态
    进程的优先级
    ...
    相关的指针信息
    struct PCB* next;
};

这样可能还是抽象了一点,我们再举个形象点的案例

  • 读者可能是在校大学生,亦或者是已经工作的人。但是无论是谁都要去投递简历找工作。如果读者有当过面试官的话一定可以知道每天公司都会收到成千上百份的简历,因为面试官无法看到投递者这个人,所以其只能通过简历来识别这个人,那么读者就可以认为每一份简历就相当于是每个进程所专属的【PCB对象
  • 所以面试官对于面试者的管理就是对==简历的管理==。让进程去某个队列里排队,不是让代码去排队,而是进程的PCB结构体对象去排队

image.png

我在一开始的时候就讲到过,在内存中不仅仅是跑着一个进程,而是有可能会存在多个进程。那对于这多个进程要如何去起到关联呢?

  • 这一点其实我们在讲操作系统的时候也有讲到过,我们学习编程语言,像C/C++、Java,可以帮助我们去很好地描述一个抽象的事物;我们学习数据结构与算法,可以帮助我们更好地去组织相同的对象数据
  • 那么在Linux内核中,最基本的组织进程的是用task_struct的方式,采用【双向链表】组织的

image.png

💬 那有了上面的这个理论,操作系统在对于进程的管理就转换成了对于链表的增删查改

3、查看进程

明白了操作系统如何去描述并组织进程,接下去我们就切身地来看一看进程长什么样吧

下面呢是我们要进行测试的代码:


1 #include <stdio.h>
  2 #include <unistd.h>
  3 
  4 int main(void)
  5 {
  6     while(1)
  7     {
  8         printf("我是一个进程了...\n");
  9         sleep(1);                                                                 
 10     }
 11     return 0;
 12 }

① 通过ps命令查看进程


ps ajx | head -1 && ps ajx | grep proc

解析:

  1. ps ajx —— 查看当前系统中所有进程
  2. head -1 —— 获取第一行
  3. grep proc —— 过滤只带【proc】的进程

image.png💬 那有同学可能会问:为什么在过滤进程的时候会有grep --color=auto proc这个东西呢?

  • grep在进行过滤的时候自己也要变成一个进程,也可以看到他们使用grep命令的时候也带【proc】关键字的,所以在过滤的时候把自己也过滤出来了。这也侧面证明了所有指令在运行的时候都是进程

image.png

  • 但如果我们不想看到这个也是有办法的,那就是在【grep】命令后面加个-v grep把其过滤掉即可


ps ajx | head -1 && ps ajx | grep proc | grep -v grep

image.png

② 通过top命令查看进程


top

image.png③ 通过ls命令查看进程目录


ls /proc/PID -l
  • 除了以上两种方法之外,我们还有一种方法就是查看 Linux 下的这个【proc】目录

image.png

  • 但是呢,上面这些呢是全部的进程,若我们只是想要查看某个进程的话就要根据其PID值去进行对应的查找。这个PID呢就是我们在上面在介绍task_struct这个Linux下的PCB结构体的时候所讲到的【标识符】这个东西,它是 ==描述本进程的唯一标示符,用来区别其他进程==

image.png

  • 但是这么看不够清晰,我们以列表的形式来进行查看。这里我们主要关注两个:一个是cwd目录,另一个则是exe

image.png首先我们来看到的是这个【exe】,很明显它是一个可执行文件,那就是我们在当前目录下的myprocess这个可执行文件

image.png接下去的话就是这个【cwd】了,其意思为current work directory当前进程的工作目录

image.png

还记得我们在上面说到==所有指令在运行的时候都是进程==

  • 那我们若是在一个目录中执行下面这句指令的话所会发生的事就是在当前目录下创建一个名为log.txt的文件,但是我们有指明在哪里创建吗?并没有🙅‍


touch log.txt

再说一点,还记得我们在 C语言文件操作 中有讲到过我们以写的方式去打开一个不存在文件的时候,其就会在当前路径下创建一个同名的文件

  • 我们在myproc.c这个源文件中加入以下这句代码


fopen("log.txt", "w");
  • 可以看到,当我们在执行了这个可执行程序后,当前的目录中就多出了一个log.txt的文件,即当这个进程被调用的时候,便会自动在其当前工作目录创建中这个文件

image.png👉 所以,当我们在执行一个进程的时候,其会默认把这个 cwd 粘贴到这个进程前面,即cwd / myproc


这里再补充一点,既然可以查看进程的话,那是否可以杀掉进程呢?

  • 那当然是可以的,首先第一种方法就是使用我们的热键Ctrl + C

image.png

  • 第二种杀进程的办法就是根据这个进程的PID向这个进程发送【9】号信号(信号的内容会在之后的Linux进程信号介绍


kill -9 22146
  • 可以看到,当我们执行这句命令的时候,这个进程就会杀掉了,其状态变为了Killed

image.png

4、通过系统调用获取进程标示符

上面我们有讲到了这个PID进程标示符,是通过ps这个命令来查看的,那我们能否直接获取这个PID

  • 在上面我们使用ps ajx查看到了当前进程所对应的 PID,但是呢这相当于是遍历操作,如果我没有加grep proc的话出来的进程数就会很多了
  • 那现在我们所要做到的就是对一个单独的进程去获取其 PID,此时我们能想到的就是通过库函数来实现。在之前的文章中我们又说到过对于操作系统而言它是不会相信任何人的,所以会提供给用户一些系统调用(库函数),那我们只需要通过这个系统调用即可获取到当前进程的 PID 值

image.png

  • 那首先呢,我们先要去查询一下这个getpid()怎么使用,那还是使用到我们的老朋友man


man getpid
  • 进去之后看到,有两个库函数,那如果要使用这两个库函数的话就需要引入对应的头文件

image.png

下面我给出一段命令,它可以实时监控当前系统的进程


while :;do ps ajx | head -1 && ps ajx | grep proc | grep -v grep; echo "------------------------------------------------------------"; sleep 1; done;
  • 然后就让我们来观察一下其是否真的可以获取到当前进程的PID,首先运行上面的这段指令,我们看到了当前系统中并不存在有关proc的进程,但是呢在我们把myprocess这个可执行程序运行起来的时候,右侧就突然就多出了一条进程的相关信息
  • 然后一核对相关的PID值就发现确实是当前运行起来的这个进程

  • 但是呢当我在将当前这个进程给结束之后再去把它起起来的时候,就发现当前这个进程的PID值发生了变化

💬 其实的话,这个现象是很正常的,每次重新启动进程其 PID 值是会出现不同的情况

  • 举个很简单的例子来说吧,小王在高考结束完后上了一所不是很理想的大学🏫,在开学前两天时学习为其分配了对应的学号。但是呢小王却并不满意自己所待的这个学校,所以就退了学继续参加高考,在又一次的高考结束后他还是被原来的这所学校给录取。但是呢我们可以知道,即使你进了一个学校两次,但是学号却不一定是一样的
  • 这也就是为什么一个进程在启动两次后会出现不同PID值的原因

刚才我们在通过【man】手册查看getpid()这个函数的时候,还看到了getppid()这个函数,它是获取当前进程的父进程的 PID

  • 这个PPID呢就在PID的左边

image.png

  • 下面是改进的测试代码


printf("I am a process, my id is: %d, parent is: %d\n", getpid(), getppid());
  • 马上来看一下是否真的可以获取到

  • 接下去我们再来观察一下现象:通过3次结束子进程,我们观察到了子进程确实每次都会发生变化,但是呢对于父进程而言却不会发生任何的变化,这是为什么呢?

  • 我们可以先去查看一下这个父进程到底是什么鬼...(((m -__-)m


ps ajx | head -1 && ps ajx | grep 29576
  • 然后我们就可以发现这个父进程原来是bash,它可以执行我们所输入的命令

image.png原因解析:

① 每次在登录XShell的时候,系统会为我们单独再创建一个Bash进程,即命令行解释的进程,帮我们在显示器中打印出对话框终端


[pjl@VM-4-12-centos lesson10]$    -- 父进程【PPID】

② 我们在命令行中输入的所有指令都是Bash进程的子进程,Bash进程只负责命令行的解释,具体执行出问题的时候只会影响它的子进程


ls /proc/12864  -- 子进程【PID】

上面这样解释可能还是比较抽象,一样来举个例子

  • 还记得下面这张图吗,我们在讲解 Shell运行原理 的时候曾经说到过王婆是一位资本家,她为了不损坏自己的名声呢,在别人找她说媒的时候会派遣一些实习生去,即使实习生出了问题她的名誉也不会受到影响
  • 这就可以对照到父进程与多个子进程,可以有多个子进程在这个父进程上面运行,即使某一个子进程突然出问题终止了,还有其他子进程在运行,此时父进程也是有保障的,所以子进程每次都会发生变化,但是父进程永远都不会变化

image.png

目录
打赏
0
0
0
0
122
分享
相关文章
|
1月前
|
Linux进程控制(详细讲解)
进程等待是系统通过调用特定的接口(如waitwaitpid)来实现的。来进行对子进程状态检测与回收的功能。
44 0
Linux2.6内核进程调度队列
本篇文章是Linux进程系列中的最后一篇文章,本来是想放在上一篇文章的结尾的,但是想了想还是单独写一篇文章吧,虽然说这部分内容是比较难的,所有一般来说是简单的提及带过的,但是为了让大家对进程有更深的理解与认识,还是看了一些别人的文章,然后学习了学习,然后对此做了总结,尽可能详细的介绍明白。最后推荐一篇文章Linux的进程优先级 NI 和 PR - 简书。
38 0
|
1月前
|
Linux进程概念-详细版(二)
在Linux进程概念-详细版(一)中我们解释了什么是进程,以及进程的各种状态,已经对进程有了一定的认识,那么这篇文章将会继续补全上篇文章剩余没有说到的,进程优先级,环境变量,程序地址空间,进程地址空间,以及调度队列。
34 0
Linux进程概念-详细版(一)
子进程与父进程代码共享,其子进程直接用父进程的代码,其自己本身无代码,所以子进程无法改动代码,平时所说的修改是修改的数据。为什么要创建子进程:为了让其父子进程执行不同的代码块。子进程的数据相对于父进程是会进行写时拷贝(COW)。
40 0
获取和理解Linux进程以及其PID的基础知识。
总的来说,理解Linux进程及其PID需要我们明白,进程就如同汽车,负责执行任务,而PID则是独特的车牌号,为我们提供了管理的便利。知道这个,我们就可以更好地理解和操作Linux系统,甚至通过对进程的有效管理,让系统运行得更加顺畅。
76 16
|
2月前
|
对于Linux的进程概念以及进程状态的理解和解析
现在,我们已经了解了Linux进程的基础知识和进程状态的理解了。这就像我们理解了城市中行人的行走和行为模式!希望这个形象的例子能帮助我们更好地理解这个重要的概念,并在实际应用中发挥作用。
72 20
Linux内核中的线程和进程实现详解
了解进程和线程如何工作,可以帮助我们更好地编写程序,充分利用多核CPU,实现并行计算,提高系统的响应速度和计算效能。记住,适当平衡进程和线程的使用,既要拥有独立空间的'兄弟',也需要在'家庭'中分享和并行的成员。对于这个世界,现在,你应该有一个全新的认识。
184 67
【YashanDB 知识库】如何避免 yasdb 进程被 Linux OOM Killer 杀掉
本文来自YashanDB官网,探讨Linux系统中OOM Killer对数据库服务器的影响及解决方法。当内存接近耗尽时,OOM Killer会杀死占用最多内存的进程,这可能导致数据库主进程被误杀。为避免此问题,可采取两种方法:一是在OS层面关闭OOM Killer,通过修改`/etc/sysctl.conf`文件并重启生效;二是豁免数据库进程,由数据库实例用户借助`sudo`权限调整`oom_score_adj`值。这些措施有助于保护数据库进程免受系统内存管理机制的影响。
【Linux】进程概念和进程状态
本文详细介绍了Linux系统中进程的核心概念与管理机制。从进程的定义出发,阐述了其作为操作系统资源管理的基本单位的重要性,并深入解析了task_struct结构体的内容及其在进程管理中的作用。同时,文章讲解了进程的基本操作(如获取PID、查看进程信息等)、父进程与子进程的关系(重点分析fork函数)、以及进程的三种主要状态(运行、阻塞、挂起)。此外,还探讨了Linux特有的进程状态表示和孤儿进程的处理方式。通过学习这些内容,读者可以更好地理解Linux进程的运行原理并优化系统性能。
149 4
|
4月前
|
Linux 进程前台后台切换与作业控制
进程前台/后台切换及作业控制简介: 在 Shell 中,启动的程序默认为前台进程,会占用终端直到执行完毕。例如,执行 `./shella.sh` 时,终端会被占用。为避免不便,可将命令放到后台运行,如 `./shella.sh &`,此时终端命令行立即返回,可继续输入其他命令。 常用作业控制命令: - `fg %1`:将后台作业切换到前台。 - `Ctrl + Z`:暂停前台作业并放到后台。 - `bg %1`:让暂停的后台作业继续执行。 - `kill %1`:终止后台作业。 优先级调整:
212 5
AI助理

你好,我是AI助理

可以解答问题、推荐解决方案等

登录插画

登录以查看您的控制台资源

管理云资源
状态一览
快捷访问