进程概念(PCB、进程创建、进程状态等)
创始人
2024-05-21 14:37:18
0

进程是一个运行的程序,是所有计算机的基础。这个过程与计算机代码不一样,尽管它们非常相似。程序通常被认为是 “被动的” 实体,而进程则是 “主动的” 实体。硬件状态、RAM、CPU和其它属性都是进程持有的属性。下面我们就来了解更多关于进程的知识。

文章题目:

  • 什么是进程?
  • 进程控制块 - PCB(Process Control Block)
  • 如何查看进程
  • 进程相关概念
  • 获取进程标示符
  • 创建进程 - fork
  • OS进程状态
  • 僵尸进程 - zombie
  • 孤儿进程 - orphan
  • 进程优先级
  • 查看系统进程
  • PRI and NI
  • 更改进程优先级

什么是进程?

进程本质上就是运行的软件、任何进程的执行都必须按照特定的顺序进行。进程是指帮助表示必须在任何系统中实现的基本工作单元的实体。🎯

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

进程的组成:
在这里插入图片描述

🎯在电脑开机开始运行时,就已经有很多进程就绪,当我们打开网页进行浏览或者打开QQ,这些都是进程。在系统中有许多的进程,而在我们运行程序时,都需要将其加载到内存中,面对着众多的进程,操作系统应该如何来进行进程的管理呢?因此操作系统为了更好的管理进程,将每一个进程描述起来,这就是描述进程 - PCB。

进程控制块 - PCB(Process Control Block)

🧩每个进程都有一个进程控制块,进程信息就被放在进程控制块的数据结构中,这是一个由操作系统管理的数据结构。

🧩课文中将其称为 PCB(Process Control Block),而在 Linux 操作系统下的 PCB 是:task_struct。

🧩task_struct 是 Linux 内核的一种数据结构,它会被装载到 RAM(内存) 里且包含着进程的信息。

一个进程的进程控制块(PCB)是这样的:

在这里插入图片描述
每一个进程都有自己对应的 PCB 来进行标识,也被称为进程的上下文。

使用命令 ps aux 可查看当前计算机中存在的进程:
在这里插入图片描述

操作系统对这些进程进行管理时:先描述,再组织。操作系统不是直接对进程进行管理的,而是当一个进程启动时,操作系统先将其进行描述,然后将其描述的进程信息放入进程控制块的数据结构中管理起来,实际上,操作系统对进程的管理是对进程描述信息的管理(即对 PCB 进行管理)。

操作系统将计算机中运行的每一个进程都使用 PCB 进行了描述,然后将这些 PCB 增加到双链表中进行管理起来:
在这里插入图片描述
这样以来,操作系统需要对每一个进程进行相关操作时,只需要在链表中找到其对应的 PCB,就可找到进程对其进程操作了。PCB 和 数据结构 对进程的描述和组织使操作系统更便于管理进程,使得操作系统对进程的控制科学、有效。

关于 PCB:

  • 每个进程的 PCB 都存储在主存储器中。
  • 每个进程只有一个与之相关的 PCB。
  • 所有进程的 PCB 都列在一个链表中。

task_struct 内容分类:

  • 标示符:描述进程的唯一标示符,用来区别其它进程。
  • 状态:任务状态、退出代码、退出信号等。
  • 优先级:相对于其它进程的优先级。
  • 程序计数器:程序中即将被执行的下一条指令的地址。
  • 内存指针:包括程序代码和进程相关数据的指针,还有和其它进行共享的内存块的指针。
  • 上下文数据:进程执行时处理器的寄存器中的数据。
  • I/O状态信息:包括显示的 I/O 请求,分配给进程的 I/O 设备和被进程使用的文件列表。
  • 记账信息:可能包括处理器时间总和,使用的时钟数总和,时间限制,记账号等。
  • other。

如何查看进程

通过 /proc 查看系统文件中的进程信息。首先进入根目录,然后进入名为 proc 的目录,里面包含了许多进程信息,例如下面我们进入1号进程查看其信息。

在这里插入图片描述
使用 ps ajx可查看当前所有的进程,配合 grep 命令使用我们可以查看到我们所需要进程信息
在这里插入图片描述

进程相关概念

竞争性:系统进程数目众多,而 CPU 资源是有限的,所以进程之间是具有竞争属性的。为了高效完成任务,更合理竞争相关资源,便具有了优先级。

独立性:多个进程运行,需要独享各种资源,多进程运行期间互不干扰。

并行:多个进程在多个 CPU 下分别,同时进行运行,这称之为并行。

并发:多个进程在一个 CPU 下采用进程切换的方式,在一段时间内,让多个进程得以推进,称之为并发。

获取进程标示符

进程id(PID) - getpid()
父进程id(PPID) - getppid()

在这里插入图片描述

下面通过一个简单的程序来查看以下进程的 PID 和 PPID。当程序重新运行时,进程的 PID 会变化,但是 PPID 是一样的。通过具体查看,发现 PPID 所对应的进程是 bash。在操作系统执行指令时,为了防止因为程序的崩溃而影响到系统,因此,shell 并不会亲自执行指令,而是派生出子进程去执行指令。所以每个进程的 PPID 都是一样的。

在这里插入图片描述

创建进程 - fork

fork 系统调用用于创建一个新的进程,称为子进程,它与进行 fork() 调用的进程并发运行。在创建了新的进程之后,两个进程都将执行 fork() 系统调用之后的下一条指令,子进程使用和父进程相同的程序计数器、相同的 CPU 寄存器,相同的打开文件。下面就来简单的认识一个 fork() 系统调用。

在这里插入图片描述
fork() 不接受参数,返回一个整数值,下面为 fork() 返回的不同值:

负数:子进程创建失败
0:返回到新创建的子进程
正数:返回给父进程或者调用它的地方,取值为新创建的子进程的进程 ID

如下所示,使用 fork() 创建子进程:
在这里插入图片描述
从运行结果可以看出打印函数执行了两次,且不是同一个进程。而其中一个进程的 PPID 是另一个进程的 PID ,由此可以说明其中一个进程是当前进程(父进程)的子进程。

当前程序的代码和数据是属于父进程的,那 fork 创建出的子进程的数据和代码在哪呢?

在这里插入图片描述
上图所示,fork() 调用之后的代码运行了两次,由此可以看出,fork 完成之后,父子进程共享 fork 之后的代码,而父子进程的数据各自开辟空间,独有一份(采用写时拷贝)。

fork 之后用 if 进行分流

当一个进程创建一个新进程时,有两种可能的执行出口:

  • 父进程继续与子进程并发执行。
  • 父进程一直等待,知道部分或全部子进程终止。

如下演示 fork 之后使用 if 进行分流:

在这里插入图片描述
在上面的代码中,使用 fork() 创建了一个子进程。fork() 在子进程中返回0,在父进程中返回正整数,而父进程中的正整数正是子进程的 PID 。这里有两个输出,因为父进程和子进程是并发运行的。而先执行父进程还是子进程是由操作系统决定的。父进程和子进程执行同一个程序,并不意味着它们完全相同。操作系统为这两个进程分配不同的数据和状态,这些进程的控制流可能不同。

总结:

  • fork 系统调用创建一个新的进程,称为 ”子进程“,该进程与父进程并发运行。
  • 父进程和子进程是通过 fork() 调用的返回值来区分开的。
  • 在语法层面上,fork() 不接受任何的参数,其返回值为 pid_t。
  • 子进程与父进程使用相同的 CPU 寄存器、相同的打开文件和相同的程序计数器。
  • 实际上,我们可以用 fork() 系统调用来创建一些唯一的代码,因为每个子进程都会给出唯一的值。

OS进程状态

正在执行的程序称为进程。在进程执行期间,进程在整个生命周期中会经历不同的状态,这些状态称为进程状态。所有与进程相关的信息都会存储在进程控制块(PCB)中,进程状态也如此。

在这里插入图片描述

Linux 内核源代码对进程状态的定义:

/*
* The task state array is a strange "bitmap" of
* reasons to sleep. Thus "running" is zero, and
* you can test for combinations of others with
* simple bit tests.
*/
static const char * const task_state_array[] = {
"R (running)", /* 0 */
"S (sleeping)", /* 1 */
"D (disk sleep)", /* 2 */
"T (stopped)", /* 4 */
"t (tracing stop)", /* 8 */
"X (dead)", /* 16 */
"Z (zombie)", /* 32 */
};

上述代码描述了进程的许多状态。在不同的操作系统中,进程可能存在不同的状态。进程的状态存储在进程控制块(PCB)中。下面我们来详细了解一个每个进程状态。

运行状态 - R(running)

只要从就绪队列中将 CPU 分配给进程,进程状态就会变成运行态。运行状态并不意味着进程一定在运行,它表明进程要么在运行中,要么在运行队列中。
在这里插入图片描述
如下所示的程序处于运行状态中:

在这里插入图片描述

睡眠状态 - S(sleeping)

意味着该进程正在等待某件事情完成,这里的睡眠状态有时候也可以叫做可中断睡眠(interruptible sleep)。处于此状态的进程随时可以被唤醒和杀掉。

在这里插入图片描述

为什么程序一直在执行,而进程状态确为睡眠状态呢?🎯

在我们使用指令 ps ajx 查看进程状态时,我们查看到的是指令执行时进程所处的状态,而不是随着进程的变化而进行动态变化的状态。CPU 的速度远远大于其它外设的速度,因此 CPU 处理很快,大多数的时间都是在等待其它硬件调配资源,执行程序的时间很短,因此查看到的进程处于 S+ 状态。

处于浅度睡眠的进程,我们可以对其唤醒或者杀掉,例如使用 kill -9 命令将进程杀掉:

在这里插入图片描述

磁盘休眠状态 - D(Disk sleep)

一个进程处于深度睡眠状态,则该进程不会被杀掉。磁盘休眠状态有时候叫做不可中断睡眠状态(uninterruptible sleep),这个状态的进程通常会等待 IO 结束。进程为什么会被置为 uninterruptible sleep 状态呢?当某一进程需要对磁盘进行写入,在对磁盘写入数据期间,该进程就处于深度睡眠状态,不会被操作系统杀掉,该进程需要等待磁盘给出是否写的数据成功的信号。当得到相应的条件时才能响应信号。

停止状态 - T(stopped)

可以通过发送 SIGSTOP 信号开停止进程,这个被暂停的进程可以通过发送 SIGCONT 信号来让进程继续运行。

如下,向进程发送 SIGSTOP 命令,使进程进入暂停状态,发送 SIGCONT 信号进入运行状态:

在这里插入图片描述

在上述程序中,进程开始运行时的状态为 R+ ,而暂停之后继续再次运行时进程状态变为 R。进程状态后面的 + 号表示该进程是一个前台进程,没有加号则表明该进程是一个后台进程。前台进程可以通过 Ctrl + c 或者 kill -9 进行终止。没有 + 号表示该进程是后台进程,只能用 kill -9 指令将其杀死。

死亡状态 - X(dead)

死亡状态只是一个返回状态,不会在任务列表看到这个状态。

僵尸进程 - zombie

僵尸进程也被称为 “死亡进程”。理想情况下,当一个进程执行完成时,他在进程表中的条目应该被删除,但是在僵尸进程情况下,这并不会发生。

🎲僵尸状态(Zombies)是一个比较特殊的状态。当进程退出并且父进程(使用 wait() 系统调用)没有读取到子进程退出的返回信息时就会产生僵尸进程。

🎲僵尸进程会以终止状态保持在进程列表中,并且会一直等待父进程读取退出状态信号。

🎲子进程退出,父进程还在运行,父进程没有读取子进程状态,则子进程进入 Z 状态。

如下我们来演示一下僵尸进程,fork() 创建子进程并在其中打印3次信息后退出,父进程一直循环执行代码不退出。子进程退出之后,父进程依旧运行,因此子进程等待父进程读取自己的退出信息,那么等待时就进入了僵尸状态:

#include 
#include
#includeint main()
{pid_t id = fork();if(id == 0){int count=3;while(count){printf("I am a child process! PID : %d,PPID : %d,count = %d\n",getpid(),getppid(),count--);sleep(1);}printf("child process quit!\n");exit(1);}else if(id>0) {while(1){printf("I am a parent process! PID : %d,PPID : %d\n",getpid(),getppid());sleep(1);}}else {perror("fork false");return 1;}return 0;
}

程序运行后,我们使用监控脚本 while :; do ps axj | head -1 && ps axj | grep process | grep -v grep | grep -v Ssl;echo "---------------------------------";sleep 1;done 来实时查看进程的状态。如下所示,当子进程退出以后,父进程没有退出,子进程就会进入僵尸状态。

在这里插入图片描述

僵尸进程的危害:

  • 操作系统有一个有限大小的进程列表,大量的僵死进程将产生一个完整的进程列表,进程列表慢了意味着操作系统无法在需要时创建新进程。
  • 操作系统中的僵尸进程没有用,因为进程已经死亡,但它的条目占用了内存中的空间。
  • 操作系统中的 PID 是有限的,当所有的 PID 都被僵尸进程耗尽后,就无法创建新的进程了。
  • 一个父进程创建了许多子进程却不回收,就会造成资源的浪费。

僵尸进程清理:

  • 僵尸进程不会被操作系统强制结束。长时间运行的僵尸进程是错误或者资源泄漏的结果。它们在进程表中占用大量的空间,因此,需要避免过多的僵尸进程聚集。
  • 进程变成僵尸进程,它将失去所有内存页、打开的文件句柄、信号量锁等。当进程终止时,操作系统会释放几乎所有系统资源。
  • 可以通过在子进程结束后立即调用等待来防止这种情况,尽快从进程表中清理它。(后续说)

孤儿进程 - orphan

什么是孤儿进程?所谓的孤儿,我们可以用现实生活中的例子来理解。在现实中,孤儿指的是父母的孩子。类似的,在 os 中,正在执行但父进程已经终止的进程称为孤儿进程。

孤儿进程将会获得一个新的父进程。当内核检测到一个进程成为孤儿进程时,会为孤儿进程提供一个新的父进程。在大多数情况下,新的父进程的 PID 是为 1 的 INIT 进程。新的父进程等待孤儿进程完成之后,会要求内核清理孤儿进程的 PCB。这样孤儿进程就被回收掉了。🎯

以下代码模拟孤儿进程,fork 之后,父进程循环打印3次后退出,子进程一直执行,当父进程退出之后,子进程就变成孤儿进程了。将被 PID=1 的进程领养。

#include 
#include
#includeint main()
{pid_t id = fork();if(id == 0){while(1){printf("I am a child process! PID : %d,PPID : %d\n",getpid(),getppid());sleep(1);}}else if(id>0) {int count=3;while(count){printf("I am a parent process! PID : %d,PPID : %d,count = %d\n",getpid(),getppid(),count--);sleep(1);}printf("parent process quit!\n");exit(0);}else {perror("fork false");return 1;}return 0;
}

运行结果如下:当父进程退出之后,子进程的 PPID 变为 1.

在这里插入图片描述
❓太多的孤儿进程对系统有什么危害?

  • 操作系统中的孤儿进程在系统中占用资源。
  • 大量的孤儿进程会使 INIT 进程过载并挂起系统。

进程优先级

在优先调度队列中,每个进程都有一个优先级编号。在 Linux 等系统中,优先级编号的数字越低,则优先级越高。在可用进程中具有较高优先级的进程被给到 CPU。目前存在两种优先级调度算法:抢占式优先级调度、非抢占式优先级调度。

✔️CPU资源分配的先后顺序,就是指进程的优先级(priority)。
✔️优先级高的进程有优先执行的权力。配置进程优先级对多任务环境的 Linux 很有用,可以改善系系统性能。

查看系统进程

在 Linux 或者 Unix 系统中,用 ps -l 命令会输出以下内容:

在这里插入图片描述
以上列出几个重要信息:

  • UID :代表执行者的身份
  • PID :代表这个进程的编号
  • PPID :标记了该进程的父进程,即由那个进程发展衍生而来
  • PRI :代表这个进程可被执行的优先级,其值越小优先级越高
  • NI :该进程的 nice 值

PRI and NI

  • PRI 就是进程的优先级,即被 CPU 执行的先后顺序,PRI 值越小,则进程的优先级越高。
  • NI 就是我们所说的 nice 值,其表示进程可被执行的优先级的修正数值。
  • PRI 值越小越快被执行,加入 nice 值之后,将会使 PRI 变为:PRI(new)= PRI(old)+ nice。
  • 当 nice 值为负值时,那么该程序会将优先级值变小,及其优先级会变高,其越快被执行。
  • 因此,调整进程的优先级,在 Linux 下,是调整进程的 nice 值。
  • nce 值得取值范围是 -20 ~ 19,一个 40 个级别。

更改进程优先级

用 top 命令更改已存在进程的 nice 值:

  • 输入 top 命令
  • 进入 top 后按 “r” -> 输入进程PID -> 输入 nice 值

如下所示,调整一个进程的优先级:

在这里插入图片描述
在 Linux 操作系统中,初始进程的默认 PRI 为80,NI 默认为0。

相关内容

热门资讯

常用商务英语口语   商务英语是以适应职场生活的语言要求为目的,内容涉及到商务活动的方方面面。下面是小编收集的常用商务...
六年级上册英语第一单元练习题   一、根据要求写单词。  1.dry(反义词)__________________  2.writ...
复活节英文怎么说 复活节英文怎么说?复活节的英语翻译是什么?复活节:Easter;"Easter,anniversar...
2008年北京奥运会主题曲 2008年北京奥运会(第29届夏季奥林匹克运动会),2008年8月8日到2008年8月24日在中华人...
英语道歉信 英语道歉信15篇  在日常生活中,道歉信的使用频率越来越高,通过道歉信,我们可以更好地解释事情发生的...
六年级英语专题训练(连词成句... 六年级英语专题训练(连词成句30题)  1. have,playhouse,many,I,toy,i...
上班迟到情况说明英语   每个人都或多或少的迟到过那么几次,因为各种原因,可能生病,可能因为交通堵车,可能是因为天气冷,有...
小学英语教学论文 小学英语教学论文范文  引导语:英语教育一直都是每个家长所器重的,那么有关小学英语教学论文要怎么写呢...
英语口语学习必看的方法技巧 英语口语学习必看的方法技巧如何才能说流利的英语? 说外语时,我们主要应做到四件事:理解、回答、提问、...
四级英语作文选:Birth ... 四级英语作文范文选:Birth controlSince the Chinese Governmen...
金融专业英语面试自我介绍 金融专业英语面试自我介绍3篇  金融专业的学生面试时,面试官要求用英语做自我介绍该怎么说。下面是小编...
我的李老师走了四年级英语日记... 我的李老师走了四年级英语日记带翻译  我上了五个学期的小学却换了六任老师,李老师是带我们班最长的语文...
小学三年级英语日记带翻译捡玉... 小学三年级英语日记带翻译捡玉米  今天,我和妈妈去外婆家,外婆家有刚剥的`玉米棒上带有玉米籽,好大的...
七年级英语优秀教学设计 七年级英语优秀教学设计  作为一位兢兢业业的人民教师,常常要写一份优秀的教学设计,教学设计是把教学原...
我的英语老师作文 我的英语老师作文(通用21篇)  在日常生活或是工作学习中,大家都有写作文的经历,对作文很是熟悉吧,...
英语老师教学经验总结 英语老师教学经验总结(通用19篇)  总结是指社会团体、企业单位和个人对某一阶段的学习、工作或其完成...
初一英语暑假作业答案 初一英语暑假作业答案  英语练习一(基础训练)第一题1.D2.H3.E4.F5.I6.A7.J8.C...
大学生的英语演讲稿 大学生的英语演讲稿范文(精选10篇)  使用正确的写作思路书写演讲稿会更加事半功倍。在现实社会中,越...
VOA美国之音英语学习网址 VOA美国之音英语学习推荐网址 美国之音网站已经成为语言学习最重要的资源站点,在互联网上还有若干网站...
商务英语期末试卷 Part I Term Translation (20%)Section A: Translate ...