最全Linux 进程手撕笔记——万字深剖详解_linux的process,2024年最新GitHub上标星13k的《Linux运维面试突击版》

最后的话

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

资料预览

给大家整理的视频资料:

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

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

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以点击这里获取!

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

操作系统如何帮我们?操作系统是通过系统调用的方式对外提供接口服务,但是 Windows 和 Linux 的系统接口一样吗?答案很明显是不一样的,在 Windows 下他会选择 Windows 接口,在 Linux 下他会选择 Linux 接口,但是上层调用的函数确实一样的函数名,哎呀,这不就是多态嘛!底层差异交给库解决,我们只管写就行了,所以经常说的可移植性和跨平台性都是在库里面动的手脚。

进程查看🤔

当我在 mytest.c 中写了一个死循环:

while(1)
{
    printf("This is a process\n");
    sleep(1);
}

(我还是使用 Makefile 自动化构建了一下,但过程是一样的)我们编译运行,此时这个一直打印的程序就是一个进程。我们如何查看进程呢?

ps ajx

ps 命令是查看进程情况的命令(未截完):
在这里插入图片描述

图上的就是当前系统中所有启动的进程,我们再 grep 针对当前可执行文件进行查看:

ps axj | grep ‘exe-name’(exename 为可执行程序名)

在这里插入图片描述

我们自己写的代码,编译为可执行程序,启动之后就是一个进程,那么别人写的呢?启动之后他也是一个进程

进程的 PID 🤔

我们还有一种查看进程的方法就是 proc 目录下查看,在根目录下是有很多路径,很多我们没见过也不知道是干啥的,但是没有关系:

在这里插入图片描述
其中 proc 是内存文件系统,里面就记录了当前系统的实时进程信息,那我们进入 proc 发现里面又是一堆奇奇怪怪的东西:

在这里插入图片描述
这些蓝色数字是啥?我们引入一个新的东西:进程的 PID

每一个进程在系统中都有唯一的标识符,就像一个身份证号一样, PID 全称就是进程 id ,重新启动同一个程序会变成一个新的进程,我们依然可以针对 pid 进行 ls 查看,里面会有他的各种属性,其中我们只需要知道两个重要信息:

在这里插入图片描述
cwd 表示当前进程的工作路径,exe 表示对应可执行程序的磁盘文件。pid 和当前路径这些都是进程的内部属性,一般就会放在进程控制块 PCB 中!

获取进程 PID🤔

想知道自己程序的 pid 直接调用 getpid() 函数即可,头文件: #include<unistd.h>,定义函数为 pid_t getpid(void)

在这里插入图片描述

程序的父进程 ppid 也可以用 getppid 获取,我们多重启几次就会发现一个亮点:重启程序会重新分配进程 pid 可以理解,但是父进程为什么不会变?
在这里插入图片描述
其实父进程就是一个 bash ,几乎我们再命令行上所执行的所有指令(cmd),都是 bash 进程的子进程。

我们结束进程除了用 ctrl + c ,还可以用 kill 命令直接杀掉进程,kill 会牵扯到信号现在不做讲解。

fork🤔

代码创建子进程需要 fork() 函数,他的返回值很有意思就是给父进程返回子进程的 pid 然后给子进程返回 0,失败就返回 -1,就意味着他有两个返回值,怪诶。可为什么是这样的呢?

我们知道一个父亲可以有很多儿子,在这个背景下父进程必须要有标识子进程的方案,就像父亲面对几个儿子喊一声儿子,这时候不知道喊的谁就会全部跑过来,因此 fork 之后就需要给父进程返回子进程 pid。

而子进程最重要的就是知道自己是否被创建成功,因为子进程他只有一个父进程,直接 getppid 就能得到,所以他找父进程的成本非常低,所以就没必要返回喽给个 0 即可。

在这里插入图片描述

那 fork 为什么能返回两次?子进程的控制块的内部数据基本上是从父亲哪里继承下来的,也不是全部,起码 pid 是自己的,子进程的代码也是父进程的,在 fork之后 ,父进程和子进程代码共享但是数据私有!因为不同的返回值,让不同的进程可以执行不同的代码。

如果一个程序调用一个函数,但这个函数准备 return 了是不是意味着核心功能已经完成? 答案是是的,return 就代表着此时子进程已经被创建,而且已经将子进程放进了运行队列,这就又要说一说进程的运行。

那又该如何理解进程被运行?在 Linux 内核中每一个 CPU 都会存在一个叫作运行队列的东西,他将存有代码和数据的结构体 task_struct 以链表形式存进队列里面, head 指针指向运行队列,然后会有一个专门调用代码的调度器来调用其中合适的进程放到 CPU 上去运行,这样就能运行一个进程。

进程状态🤔

在 task_struct 中会包含进程的状态信息,进程的状态是用整数来表示的:

在这里插入图片描述
在这里插入图片描述

运行状态有四种:运行态,终止态,阻塞态,挂起态。

运行态:不是指正在运行的进程,进程只要在运行队列中就叫做运行态,代表他已经准备好随时被调度了。

终止态:不是指该进程已经被释放,而是该进程还在但是永远不会运行,随时等待被释放。

那么问题来了,进程不运行了为什么不立马释放却要维持一个所谓的终止态?那么思考一下,释放是否需要花时间?是的,那有没有可能当前系统他抽不身来释放呢?是可能的。

阻塞态:一个进程使用资源的时候,不仅仅是在 CPU 中申请资源,可能会需要磁盘,网卡,显示器等资源。如果我们申请 CPU 资源,如果暂时无法得到满足,需要进行排队也就是加入运行队列,或者说申请其他慢设备的资源也会需要排队。

以上状态都是 task_struct 在进程排队,以此我们再推导,我们 CPU 处理多个进程可能一秒十几个,但是对于许多速度慢的外设就很恼火了,根据冯诺依曼体系,外设速度慢 CPU 块因此内存在其体系核心地位进行协调。因此,当进程访问某些资源时,该资源暂时没有准备好,或者还在为其他进程提供服务,那么当前进程就需要先从运行队列移除并将进程放入对应设备的描述结构体中的等待队列进行排队。

struct _div //某设备的描述结构体
{
//该设备属性(频率,速度等)
task_struct *wait_queue (等待队列)
}

进程在等待外部资源的时候,该进程的代码就不会被执行,这个状态就被视为进程卡住了,也就是我们所谓的进程阻塞!

挂起态挂起态其实和阻塞态有一点类似,但是更恶心,对于挂起我们首要面对的问题就是内存不足了该怎么办?要明白计算机里面 99.99% 的内存不足都是进程导致的,轻者其他程序无法运行,重者操作系统自己无法工作,所以操作系统对进程的管理又能体现了,他会帮我们进行辗转腾挪。

假设此时等待队列已经爆满,排到了二三十个进程之外了,后面的短时间内是无法被调度的,但是此时他的 PCB 和数据仍然在内存中占着茅坑不拉屎,内存告急时操作系统就会将该进程的代码和数据置换到磁盘上,这样的进程就被称为进程挂起。

所以往往在内存不足时,我们的磁盘就会发生高频率访问。

Linux 进程状态🤔

操作系统它是计算机学科的哲学,单说操作系统的教材,书籍,文章,并没有指明是 Linux 还是 Windows 还是移动端,所以大部分情况他会让结论满足各种平台,所以我们需要站在 Linux 角度去具体理解。

在这里插入图片描述

也就是说一个程序不访问显示器等外设,只访问 CPU 且处于运行状态,那么这个进程就是 R 运行态,一访问外设了即便一个 printf 都会呈现 S 阻塞态,说明他肯定在等待某种资源。而我们这里的 S 状态也叫做浅度睡眠或者可中断睡眠,他随时可以被调度唤醒。

D 状态也是一种阻塞状态,也是去等待某种资源,D 全称是 disk sleep ,Linux 中等待的是资源如果是磁盘资源,那么我们所处的状态就是 D 状态!这个状态有些人一辈子见不到,系统开发倒是很常见,一般服务器压力过大时,大部分闪退不是电脑问题,而是操作系统自动杀掉了进程,但是有些重要进程数据要是丢了该找谁背锅?操作系统还是磁盘?由此就诞生出了连操作系统都无权生杀的 D 状态进程。

要想终止 D 进程,关机是没有用的,唯一的办法就是拔掉电源。

Z 状态,z 即 zombie,僵尸状态,当 Linux 中一个进程退出时,一般不会立刻进入 X 状况(死亡状态,资源可立即回收),而是先进入 Z 状态。为什么呢?

首先要知道为什么进程会被创建?我们进程的创建就是为了完成任务,为了得知完成情况,我们是需要将进程的执行结果告知操作系统或者父进程,所以维护 Z 状态就是为了让父进程和 OS 来读取执行结果。

我们可以自己模拟一个僵尸进程:

   int main()
  {
    pid\_t id = fork();
    if(id==0)
    {
      int num = 5;
      while(num--)
      {
        printf("child has left %d\n",num);
        sleep(1);
      }
      printf("child has down");
      exit(0);
    }
    else
    {
       while(1)
       {
         sleep(1);
       }
    }
  }   

结果是这样的:
在这里插入图片描述
子进程已经终止了。但是父进程任然还在嘎嘎执行,这个状态下的子进程就是所谓的僵尸进程,Z 状态。想想一个父进程创建了很多子进程却不回收,僵尸进程就会造成内存泄漏和资源浪费!

但是反过来,如果子进程还在嘎嘎跑但是父进程已经歇菜了,这样的进程就是孤儿进程,但是孤儿进程中父进程并没有 Z 状态,为什么呢?因为此时父进程的父进程是 bash ,bash 会自己回收他的子进程也就是此时的父进程,其实也不是没有 Z 状态,只是父进程有人回收他从 Z 到 X 的过程就非常快。

如果父进程没有了,还在运行的子进程就是被 1 号进程领养,1 号进程就是操作系统。

T 状态 ,T 就是 stopped 暂停状态,我们可以在得到进程 pid 的情况下,用 kill 命令发送 19 号暂停信号来达到目的,想继续就使用 kill 发送 18 号继续信号即可。

在这里插入图片描述
t 状态 t 和 T 都是暂停但区别在于功能性, t 状态是进程被调试过程中,遇到断点时发生的暂停。

进程优先级🤔

优先级就是进程获取资源的先后顺序,注意区分优先级和权限两个概念,权限谈的是能和不能,优先级是已经有权限了谈的是先还是后。

为什么会有优先级?🎉

排队的本质就是确定优先级,很明显嘛为什么有优先级其实就是为什么要排队,作为一个人类这个问题很简单,排队就是为了解决资源不够,要是 5000 人大食堂有 5000 个窗口那还排个屁的队啊是吧,因为系统里面永远都是进程占大多数但是资源却是少数,那就铁铁的排队呗,这就直接决定了进程管理中会有队列这种结构。

Linux 优先级相关操作🎉

ps 指令即可看到对应的 优先级,如果需要更改进程优先级,需要更改的不是 pri 而是 NI,这个 NI 就是优先级的修正数据。
在这里插入图片描述
我们修改可以直接用 top 指令,进入界面后按 r 进入 return 跳转模式,输入 pid 就可以跳转到需要修改的进程,然后再输入你想设置的优先级,但是一个进程优先级不能轻易被修改会显示报错 ,因为他会破坏优先级平衡,要强制修改就需要超级用户权限

sudo top

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以点击这里获取!

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

sudo top

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以点击这里获取!

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

  • 26
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值