【Linux练习生】两万字带你看透进程,Linux运维开发面试题及答案

深度睡眠状态-D(不可中断的睡眠状态)

我们来形象举个例子:假设当前内存某一进程需要往磁盘写入数据,磁盘处理完数据再返回给此进程,中间这个过程是需要时间的,此时这个进程处于休眠状态,当前如果系统内存资源严重不足,操作系统就会闲置出来内存资源,此等待进程就可能被杀掉,但是当磁盘处理数据完成后需要返回数据给该进程,但是此时该进程已经被杀掉了,那么这些数据就可能丢失,所以就体现了深度睡眠状态的作用:

一个进程处于深度睡眠状态(disk sleep),表示该进程不会被杀掉,即便是操作系统也不行,只有该进程自动唤醒才可以恢复。该状态有时候也叫不可中断睡眠状态(uninterruptible sleep),处于这个状态的进程通常会等待IO的结束。

暂停状态-T(或跟踪状态)
  • 向进程发送一个SIGSTOP信号,它就会因响应该信号而进入暂停状态(stopped)(除非该进程本身处于TASK_UNINTERRUPTIBLE状态而不响应信号)-

  • 进程发送一个SIGCONT信号,可以让其从TASK_STOPPED状态恢复到TASK_RUNNING状态

下面写一个小程序来观察一下:

在这里插入图片描述

我们可以看到每隔一秒打印一次 hello world!

使用kill命令可以列出当前系统所支持的信号集:

c [zjl@ubuntu18 1.13]$ kill -l

在这里插入图片描述

我们可以看到暂停状态恢复暂停状态到TASK_RUNNING状态的序号分别为1918

此时我们对这个进程发送SIGSTOP信号(序号为19),该进程就进入到了暂停状态:

在这里插入图片描述

我们再对该进程发送SIGCONT信号(序号为18),该进程就会继续运行了:

[zjl@ubuntu18 1.13]$ kill -18 22774

僵尸状态-Z

当一个进程将要退出的时候,在系统层面,该进程曾经申请的资源并不是立即被释放,而是要暂时存储一段时间,以供操作系统或是其父进程进行读取,如果退出信息一直未被读取,则相关数据是不会被释放掉的,一个进程若是正在等待其退出信息被读取,那么我们称该进程处于僵尸状态(zombie)。

  • 僵尸状态的存在是必要的,因为进程被创建的目的就是完成某项任务,那么当任务完成的时候,调用方(操作系统或是其父进程)是应该知道任务的完成情况的,所以必须存在僵尸状态,使得调用方得知任务的完成情况,以便进行相应的后续操作。

进程退出的信息(例如退出码),是暂时被保存在其进程控制块当中的,在Linux操作系统中也就是保存在该进程的task_struct当中。

死亡状态-X

死亡状态只是一个返回状态,当一个进程的退出信息被读取后,该进程所申请的资源就会立即被释放,该进程也就不存在了,所以你不会在任务列表当中看到死亡状态(dead)。

8.僵尸进程

对于一般进程来说:正常情况下–

子进程由父进程创建,子进程再创建新的进程。父子进程是一个异步过程,父进程永远无法预测子进程的结束,所以,当子进程结束后,它的父进程会调用wait()或waitpid()取得子进程的终止状态,回收掉子进程的资源

子进程退出了,但是父进程没有用wait或waitpid去获取子进程的状态信息,那么子进程的进程描述符仍然保存在系统中,这种进程称为僵死进程

结合前面说到的,一个进程若是正在等待其退出信息被读取,那么我们称该进程处于僵尸状态。而处于僵尸状态的进程,我们就称之为僵尸进程


举个例子:

对于以下代码,fork函数创建的子进程在打印5次信息后会退出,而父进程会一直打印信息。也就是说,子进程退出了,父进程还在运行,但父进程没有读取子进程的退出信息,那么此时子进程就进入了僵尸状态

补充:任何一个子进程(init除外)在exit()之后,并非马上就消失掉,而是留下一个称为僵尸进程的数据结构,等待父进程去处理。如果父进程在子进程exit()之后,没有及时处理,出现僵尸进程,并可以用ps命令去查看,它的状态是“Z”。

#include <stdio.h>

#include <stdlib.h>

#include <unistd.h>

int main()

{

printf(“I am running…\n”);

pid_t id = fork();

if(id == 0){ //child

int count = 5;

while(count){

printf(“I am child…PID:%d, PPID:%d, count:%d\n”, getpid(), getppid(), count);

sleep(1);

count–;

}

printf(“child quit…\n”);

exit(1);

}

else if(id > 0){ //father

while(1){

printf(“I am father…PID:%d, PPID:%d\n”, getpid(), getppid());

sleep(1);

}

}

else{ //fork error

}

return 0;

}

运行该代码后,我们可以通过监控脚本,每隔一秒对该进程的信息进行检测:

[zjl@ubuntu18 1.13]$ while :; do ps axj | head -1 && ps axj | grep jincheng | grep -v grep;echo “######################”;sleep 1;done

在这里插入图片描述


僵尸进程危害
  • 僵尸进程的退出状态必须一直维持下去,因为它要告诉其父进程相应的退出信息。可是父进程一直不读取,那么子进程也就一直处于僵尸状态。

  • 僵尸进程的退出信息被保存在task_struct(PCB)中,僵尸状态一直不退出,那么PCB就一直需要进行维护。

  • 若是一个父进程创建了很多子进程,但都不进行回收,那么就会造成资源浪费,因为数据结构对象本身就要占用内存。

  • 僵尸进程申请的资源无法进行回收,那么僵尸进程越多,实际可用的资源就越少,也就是说,僵尸进程会导致内存泄漏。

9.孤儿进程

父进程结束了,而它的一个或多个子进程还在运行,那么这些子进程就成为孤儿进程(father died)。子进程的资源由init进程(进程号PID = 1)回收。

通俗来讲:孤儿进程是没有父进程的进程,为避免孤儿进程退出时无法释放所占用的资源而变为僵尸进程),进程号为 1 的 init 进程将会接受这些孤儿进程,这一过程也被称为“收养”。init 进程就好像是一个孤儿院,专门负责处理孤儿进程的善后工作。每当出现一个孤儿进程的时候,内核就把孤 儿进程的父进程设置为 init ,而 init 进程会循环地 wait() 它的已经退出的子进程。这样,当一个孤儿进程凄凉地结束了其生命周期的时候,init 进程就会代表党和政府出面处理它的一切善后工作。因此孤儿进程并不会有什么危害。


举例说明:

对于以下代码,fork函数创建的子进程会一直打印信息,而父进程在打印5次信息后会退出,此时该子进程就变成了孤儿进程

#include <stdio.h>

#include <stdlib.h>

#include <unistd.h>

int main()

{

printf(“I am running…\n”);

pid_t id = fork();

if(id == 0){ //child

int count = 5;

while(1){

printf(“I am child…PID:%d, PPID:%d\n”, getpid(), getppid(), count);

sleep(1);

}

}

else if(id > 0){ //father

int count = 5;

while(count){

printf(“I am father…PID:%d, PPID:%d, count:%d\n”, getpid(), getppid(), count);

sleep(1);

count–;

}

printf(“father quit…\n”);

exit(0);

}

else{ //fork error

}

return 0;

}

在这里插入图片描述

通过运行结果看:在父进程未退出时,子进程的PPID就是父进程的PID,而当父进程退出后,子进程的PPID就变成了1,即子进程被1号进程领养了。

10.进程优先级

什么是(进程)优先级?

优先级实际上就是获取某种资源的先后顺序,而进程优先级实际上就是进程获取CPU资源分配的先后顺序,就是指进程的优先权(priority),优先权高的进程有优先执行的权力。

进程优先级存在的原因?

优先级存在的主要原因就是资源是有限的,而存在进程优先级的主要原因就是CPU资源是有限的,一个CPU一次只能跑一个进程,而进程是可以有多个的,所以需要存在进程优先级,来确定进程获取CPU资源的先后顺序。

查看系统进程

在Linux或者Unix操作系统中,用ps -l命令会类似输出以下几个内容:

[zjl@ubuntu18 1.14]$ ps -l

在这里插入图片描述

列出的信息当中有几个重要的信息:

UID:代表执行者的身份。(Linux中,标识一个用户,并不是通过用户名标识,而是UID,因为计算机比较擅长处理数据)

PID:代表这个进程的代号。

PPID:代表这个进程是由哪个进程发展衍生而来的,亦即父进程的代号。

PRI:代表这个进程可被执行的优先级,其值越小越早被执行。

NI:代表这个进程的nice值。

PRI与NI
  • PRI代表进程的优先级(priority),通俗点说就是进程被CPU执行的先后顺序,该值越小进程的优先级别越高。

  • NI代表的是nice值,其表示进程可被执行的优先级的修正数值。

PRI值越小越快被执行,当加入nice值后,将会使得PRI变为:PRI(new) = PRI(old) + NI。

若NI值为负值,那么该进程的PRI将变小,即其优先级会变高。 调整进程优先级,在Linux下,就是调整进程的nice值。

NI的取值范围是-20至19,一共40个级别

在Linux操作系统当中,PRI(old)默认为80,即PRI = 80 + NI

查看进程优先级信息

讲之前首先我们先创建一个名字为proc的进程,并运行

在这里插入图片描述

当我们创建一个进程后,我们可以使用ps -al命令查看该进程优先级的信息。

  • 在Linux操作系统中,初始进程一般优先级PRI默认为80,NI默认为0

在这里插入图片描述

通过top命令更改进程的nice值

top命令能够动态实时的显示系统当中进程的资源占用情况以及总信息。

在这里插入图片描述

特别注意:更改nice值,最好要在top命令前加上sudo提升权限

使用sudo top命令后按“r”键,会要求你输入待调整nice值的进程的PID

在这里插入图片描述

输入进程PID并回车后,会要求你输入调整后的nice值:

在这里插入图片描述

输入13:

输入nice值后按“q”即可退出,如果我们这里输入的nice值为13,那么此时我们再用ps-al命令查看进程的优先级信息,即可发现进程的NI变成了13,PRI变成了93(80+NI) 在这里插入图片描述

PRI(old)默认为80(为什么)

接下来,同样的方法,在此基础上我们再修改一次:

1.sudo top

2.输入 "r"键

3.输入进程PID

4.输入nice值

这次我们把nice值设为10,进程上次修改完的PRI为93,根据:

PRI(new) = PRI(old) + NI

这次的PRI按理应该变为了103,但是从下图我们得知了此时的PRI是90,这也就是说每次修改前,PRI都会默认变为80,即不会继承上次修改完的PRI值。

在这里插入图片描述

PRI(old)默认为80是系统规定的,有什么好处呢?

1.有一个基准值,方便调整

若没有这个基准值,我们每次修改进程优先级时都需要查看当前PRI值。

2.设计上,实现比较合理

我们知道系统规定:NI(nice值)的取值范围是-20至19,同时限定PRI值上下限分别为99和60(是否如此下面还会验证),如果不默认PRI每次为80,那么基于NI(nice值)的取值范围在数次修改nice值后可能会超出PRI值的上下限。这本身体现了优先级的可控性,一定程度上保证了"公平性"。

验证PRI上下限

我们再修改两次PRI值,nice值分别设为 100和 -100

在这里插入图片描述

在这里插入图片描述

我们可以看到,当nice 值设定超出它的取值范围时,会自动默认nice值为它的上下限值(即-20或19),相应的,PRI值会根据nice值显示为60或99,即PRI值永远在[60,99]这个区间内。

通过renice命令更改进程的nice值

使用renice命令,后面跟上更改后的nice值和进程的PID即可

在这里插入图片描述

注意: 若是想使用renice命令将NI值调为负值,也需要使用sudo命令提升权限

简单理解其他重要概念
  • 竞争性: 系统进程数目众多,而CPU资源只有少量,甚至1个,所以进程之间是具有竞争属性的。为了高效完成任务,更合理竞争相关资源,便有了优先级。

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

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

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

11.环境变量

基本概念

环境变量(environment variables)一般是指在操作系统中用来指定操作系统运行环境的一些参数。环境变量通常具有某些特殊用途,并且在系统当中通常具有全局特性。

例如,我们编写的C/C++代码,在各个目标文件进行链接的时候,从来不知道我们所链接的动静态库在哪里,但是照样可以链接成功,生成可执行程序,原因就是有相关环境变量帮助编译器进行查,或者平时我们学习时装一些虚拟机时也需要配一些环境变量。

Windows系统也有相应的环境变量:

在这里插入图片描述

查看环境变量的方法

我们可以通过echo命令(显示某个环境变量的值)来查看环境变量:

echo $NAME //NAME为待查看的环境变量名称

例如,查看环境变量PATH:

[cl@VM-0-15-centos ENV]$ echo$PATH

常见环境变量
1.PATH( 指定命令的搜索路径)

客观来说,其实平时我们理解的程序,命令,指令,可执行程序等都是一个概念

那么有一个奇怪的现象,为什么我们自己写的可执行程序程序(以下面proc为例)执行时要在前面加./,而我们平时学习到的ls ll top 类似这些指令不需要呢,自己写的可执行程序程序前面不加./则会显示找不到(command not found)

在这里插入图片描述

其实,要执行一个可执行程序必须要先找到它在哪里,既然不带./就可以执行ls ll top等命令,说明系统能够通过ls ll top名称找到它的位置,而系统是无法找到我们自己的可执行程序的,所以我们必须带上./,以此告诉系统该可执行程序位于当前目录下

而系统就是通过环境变量PATH来找到ls命令的,查看环境变量PATH我们可以看到如下内容:

在这里插入图片描述

环境变量PATH当中有多条路径(绝对路径),这些路径由冒号隔开,当你使用ls命令时,系统就会查看环境变量PATH,然后默认从左到右依次在各个路径当中进行查找。而ls命令实际就位于PATH当中的某一个路径下,所以就算ls命令不带路径执行,系统也是能够找到的。

在这里插入图片描述

那可不可以让我们自己的可执行程序也不用带路径就可以执行呢?下面给出两种方式:

方式一:将可执行程序拷贝到环境变量PATH的某一路径下

既然在未指定路径的情况下系统会根据环境变量PATH当中的路径进行查找,那我们就可以将我们的可执行程序拷贝到PATH的某一路径下,此后我们的可执行程序不带路径系统也可以找到了。

[zjl@ubuntu18 1.14]$ sudo cp proc /usr/bin

在这里插入图片描述

方式二:将可执行程序所在的目录导入到环境变量PATH当中 将可执行程序所在的目录导入到环境变量PATH当中,这样一来,没有指定路径时系统就会来到该目录下进行查找了。

export:设置一个新的环境变量

[zjl@ubuntu18 1.14]$ export PATH=$PATH:/home/zjl/1.14

在这里插入图片描述

特别提示:导入时必须写成 PATH=$PATH:/home/zjl/1.14,如果直接写成PATH=:/home/zjl/1.14,那么PATH环境变量原有路径就会消失,只剩下/home/zjl/1.14,这样会导致很多命令直接不能使用。不过好在只要你重启系统重新登录,PATH环境变量就会恢复原有路径!

在这里插入图片描述

2.HOME【指定用户的主工作目录(即用户登录到Linux系统中的默认所处目录)】

不同用户在运行系统登录时都有自己的主工作目录(家目录),环境变量HOME当中即保存的该用户的主工作目录。

root用户:

在这里插入图片描述

普通用户:

在这里插入图片描述

3.SHELL( 当前Shell,它的值通常是/bin/bash)

我们在Linux操作系统当中所敲的各种命令,实际上需要由命令行解释器(例如bash、sh)进行解释,我们可以通过查看环境变量SHELL来知道自己当前所用的命令行解释器的种类。

在这里插入图片描述

和环境变量相关的命令
  1. echo: 显示某个环境变量值

  2. export: 设置一个新的环境变量

  3. env: 显示所有环境变量

  4. unset: 清除环境变量

  5. set: 显示本地定义的变量和环境变量

在这里插入图片描述

理解:

env: 显示所有环境变量,

set: 显示本地定义的变量和环境变量

在这里插入图片描述

我们定义本地变量bitt=1234,通过set可以查看,通过env却不可以,把bitt通过export设置成环境变量后,再次通过env则可以看到bitt=1234

在这里插入图片描述

获取环境变量
1.通过代码获取环境变量

1.1使用main函数的第三个参数来获取环境变量

我们平时使用main函数时,在main函数参数这一栏常常省略不写,或者填上(void)。实际上main函数是具有参数的,并且它的参数有很大作用。

具体关于main函数的三个参数作用可以参考某博主的【浅谈】main函数的三个参数,或者自行了解。

现在我们来说说main函数的第三个参数。

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Linux运维工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Linux运维全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
img
img
img
img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Linux运维知识点,真正体系化!

由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新

如果你觉得这些内容对你有帮助,可以添加VX:vip1024b (备注Linux运维获取)
img

最后的话

最近很多小伙伴找我要Linux学习资料,于是我翻箱倒柜,整理了一些优质资源,涵盖视频、电子书、PPT等共享给大家!

资料预览

给大家整理的视频资料:

给大家整理的电子书资料:

如果本文对你有帮助,欢迎点赞、收藏、转发给朋友,让我有持续创作的动力!

一个人可以走的很快,但一群人才能走的更远。如果你从事以下工作或对以下感兴趣,欢迎戳这里加入程序员的圈子,让我们一起学习成长!

AI人工智能、Android移动开发、AIGC大模型、C C#、Go语言、Java、Linux运维、云计算、MySQL、PMP、网络安全、Python爬虫、UE5、UI设计、Unity3D、Web前端开发、产品经理、车载开发、大数据、鸿蒙、计算机网络、嵌入式物联网、软件测试、数据结构与算法、音视频开发、Flutter、IOS开发、PHP开发、.NET、安卓逆向、云计算

以上Linux运维知识点,真正体系化!**

由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新

如果你觉得这些内容对你有帮助,可以添加VX:vip1024b (备注Linux运维获取)
[外链图片转存中…(img-xnOkwVz5-1712380688464)]

最后的话

最近很多小伙伴找我要Linux学习资料,于是我翻箱倒柜,整理了一些优质资源,涵盖视频、电子书、PPT等共享给大家!

资料预览

给大家整理的视频资料:

[外链图片转存中…(img-LqPNlztv-1712380688464)]

给大家整理的电子书资料:

[外链图片转存中…(img-thrPDLEW-1712380688464)]

如果本文对你有帮助,欢迎点赞、收藏、转发给朋友,让我有持续创作的动力!

一个人可以走的很快,但一群人才能走的更远。如果你从事以下工作或对以下感兴趣,欢迎戳这里加入程序员的圈子,让我们一起学习成长!

AI人工智能、Android移动开发、AIGC大模型、C C#、Go语言、Java、Linux运维、云计算、MySQL、PMP、网络安全、Python爬虫、UE5、UI设计、Unity3D、Web前端开发、产品经理、车载开发、大数据、鸿蒙、计算机网络、嵌入式物联网、软件测试、数据结构与算法、音视频开发、Flutter、IOS开发、PHP开发、.NET、安卓逆向、云计算

  • 27
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值