Linux进程(万字解析)

一.冯诺依曼体系结构

我们常见的计算机,如笔记本。我们不常见的计算机,如服务器,大部分都遵守冯诺依曼体系。

在这里插入图片描述

截至目前,我们所认识的计算机,都是有一个个的硬件组件组成:

1.输入单元:包括键盘, 鼠标,扫描仪, 写板等 。
2.中央处理器(CPU):含有运算器和控制器等 。
3.输出单元:显示器,打印机等。

关于冯诺依曼,必须强调几点:
1.这里的存储器指的是内存。
2.不考虑缓存情况,这里的CPU能且只能对内存进行读写,不能访问外设(输入或输出设备)。
3.外设(输入或输出设备)要输入或者输出数据,也只能写入内存或者从内存中读取。
4. 一句话,所有设备都只能直接和内存打交道。

二.操作系统

在这里插入图片描述

1.管理的概念

在这里插入图片描述

在这里插入图片描述

总结:操作系统通过驱动程序来获取数据进而实现对软硬件的管理。

但如果操作系统管理所有数据的话,很显然数据量太大了,但由于所有的硬件都有公共属性,所以可以将它们组合成对象,最终操作系统就可以从对数据的管理变成对数据结构的管理了。我们把这种方式称为先描述再组织。

结论:在操作系统中,管理任何对象,都可以转化成对某种数据结构的增删查改。

操作系统就是一款搞管理的软件。

2.系统调用和库函数概念

根据上文可以知道显示器是硬件,那么我们平常写c语言里的printf将数据打印到显示器上是怎么实现的呢?毫无疑问是必须通过操作系统的。库函数与系统调用一定是上下层的关系。

在这里插入图片描述

三.进程

简单理解:一个已经加载到内存里的程序叫做进程(任务)。进程=PCB+自己的代码和数据。

1.先描述

一个操作系统可以,不仅仅只能运行一个进程,可以同时运行多个进程。就意味着操作系统必须将进程管理起来,那么它是如何管理的呢?

核心思路先描述再组织。任何一个进程,在加载到内存成为真正的进程的时候,操作系统都要先形成描述进程(属性)的结构体对象–PCB(进程控制块)。本质上PCB就是进程属性的集合。而操作系统是用c语言写的,所以PCB是用struct封装的。

在这里插入图片描述

在加载进程时,不仅要把你自己的代码和数据加载到内存里,还要把PCB也加载进去。而这两者相结合才能成为一个进程。操作系统对进程进行管理就可以直接管理PCB。

2.再组织

对多个进程进行管理

在这里插入图片描述

PCB是一个struct对象,里面可能存放着进程编号,进程状态,优先级,其他PCB指针等属性,而操作系统对多进程管理就可以通过PCB指针,形成链式结构,像管理链表一样进行增删查改,这就是再组织。

四.Linux里的PCB

1.概念

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

在这里插入图片描述

task_struct内容分类

在这里插入图片描述

2.理解当前路径

接下来简单写一个程序

在这里插入图片描述

接着在运行后新开一个标签页来查看进程(ps:如果下面进程PID有变化是正常现象,因为每启动一次程序,PID大概率会改变)

在这里插入图片描述

可以使用ps指令或者查看proc目录来查看进程。

在这里插入图片描述

在这里插入图片描述

这里面的蓝色字体全部都是目录,每一个数字就是当前进程的PID。可以看到myprocess的PID是26117,接着进入这个进程。

进入进程

在这里插入图片描述

而圈着的就是当前路径。一般我们创建一个文件只写文件名,它会默认将文件放在当前路径下。因为进程的PCB里包含了当前路径的信息,而在打开或者创建文件时,默认会将当前路径拼接在文件前面。

3.PID

上文提到PID是进程的唯一标识符,那么PID有什么作用呢?

1.kill指令

可以使用kill-9将指定进程强行停止。

在这里插入图片描述

2.获取自己的PID

PID存放于task_struct里,而task_struct是Linux里的一种数据结构,而操作系统并不信任用户,不能跳过操作系统直接访问task_struct,必须通过系统调用接口。而这个调用接口是getpid。

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

在这里插入图片描述

在这里插入图片描述

PPID是父进程

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

当运行一个进程时,bash会将该进程变成自己的子进程,但即使子进程终止也不会影响bash进程。

4.初识fork函数

在这里插入图片描述

测试

在这里插入图片描述

在这里插入图片描述

我们可以发现fork之后的代码被执行了两次。我们再次阅读手册,可以看到fork的返回值是paid类型,它的作用是创建一个新的进程。如果调用成功,它返回的一个子进程的PID给父进程返回0给子进程。那么为什么它有俩个返回值呢?接着改进代码再测试。

在这里插入图片描述

在这里插入图片描述

结果是不断打印父进程和子进程,说明再同一份代码里,id>0和id=0是同时成立的,也说明有两个死循环在同时运行。简单理解,在执行了fork指令后,该进程产生了一个分支,父进程是它自己,子进程是分支。

在这里插入图片描述

分析

在这里插入图片描述

第一个问题。返回不同的返回值是为了进行区分,让不同的执行流执行不同的代码块。一般而言,fork之后的代码父子共享(参考上文被执行了两次的代码)。一个父进程可以有多个子进程,为了能够识别子进程,所以给父进程返回子进程的PID用来标识子进程的唯一性。而一个子进程只能有一个父进程,故不需要进行标识。

第三个问题。fork创建了一个子进程,而进程=内核数据结构+代码和数据。所以首先要创建自己的task_struct,而它的创建是以父进程作为模板,再对部分属性进行修改。子进程创建时没有代码和数据,只能访问父进程的代码和数据,这就是为什么fork之后父子进程代码共享。而为什么要创建子进程呢?是为了让父子进程执行不同的代码块从而进行协同,所以在fork函数设计上具有了不同的返回值。

第二个问题。首先fork是一个系统调用函数,在执行它时会进入到操作系统内创建子进程。而在创建子进程时,会进行创建子进程PID,填充子进程内容等操作,将这一系列操作完成后会进行返回。关键是在进行返回时子进程已经创建完毕而代码又是父子共享,所以会有两次返回。

在这里插入图片描述

第四个问题。一个概念,在任何平台进程在运行时具有独立性。而又由于数据能够被修改(代码不能修改),所以不能让两个进程共享同一份数据。但由于子进程没有任何数据,所以它有需要将父进程数据进行拷贝。但子进程并不一定会使用父进程里的数据,如果全部拷贝势必会造成资源浪费,所以操作系统做了一些修改。在子进程刚创建时代码和数据都是共享,当子进程需要修改数据时,操作系统额外开辟一块空间,在新空间里写入,这种技术被称为父子进程间在数据层面上的写时拷贝。所以在进行return写入的时候,子进程进行了写时拷贝,这样就保证了返回值不同。

总结

fork创建了一个子进程,而子进程在创建之初与父进程共享代码和数据。由于父和子是两个进程会执行两次return,在两次返回时对PID发生写时拷贝,让父子进程的PID变成不同的值,所以后续我们就可以通过对不同的PID进行分流,让它们执行不同的代码块。

回到前文的bash进程,bash是通过fork函数创建子进程来运行。

五.进程状态

1.一般意义上的

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

以下主要介绍运行,阻塞,挂起三个状态。

1.运行

Linux内部可以同时有多个进程,每个进程通过双链表进行链接,这样链接起来的队列叫做运行队列。所以只要我们能够找到头节点,就能够调用所有进程。那么毫无疑问每个cpu里需要有一个头节点,它会维护一个runqueue(运行队列)的结构体,队列里包含了很多属性,最重要的就是head和tail。这样如果我们需要运行一个进程,直接将它拿到头部就可以了。

而多个进程毫无疑问会强占CPU资源,这时就会有一个调度器(一种函数)得到所有进程的参数之后就可以更好的进行资源分配了。

运行态

凡是处于运行队列里的进程都被称作运行态(R态)。

在这里插入图片描述

一个进程只要放到CPU上去那是否意味着必须跑完才会被CPU放下呢?

当然不是。每个进程里都有一个叫做时间片的概念。比如时间片是10ms,那么该进程最多在CPU里运行10ms,然后放到队列尾部。最终呈现的效果就是,在一个时间段里,每一个进程都会被调用。对于这种情况,又被叫做并发执行。在这段时间里必然存在着大量将进程拿去和放下的操作,这种情况叫做进程切换。

2.阻塞

每个CPU都连接着各种各样的外设,不论是什么硬件,操作系统都会先描述再组织。

在这里插入图片描述

接下来有一个进程要从键盘里读取数据,当前该进程等待键盘输入数据,但我却一直不按下键盘。那么这个进程就绝对不能放到运行队列里,因为想要访问的软硬件资源没有就绪。这个进程就会被链接到键盘资源里,如果又有一个进程需要从键盘读取资源,那么这个进程又会链接到上个进程后面。这个链接起来的队列就是waitqueue(等待队列)。如果键盘输入了数据,那么该进程就会自动进入运行队列了。

在这里插入图片描述
阻塞态

总结:把这种等待某种特定设备的进程称为该进程的阻塞状态。每一个设备都有等待队列。

3.挂起

阻塞挂起态

如果操作系统内存资源严重不足了,那么它会保证正常运行的情况下,省出内存资源。而阻塞队列在等待过程中里面是没有数据的,那么此时操作系统就有可能将等待的进程的代码交还到外设里,只保留PCD,这个过程就叫做内存数据的换出;而当该进程的资源就绪后,CPU又会将代码拷到运行队列里,这个该过程叫做换入。而在这个过程里,代码和数据并没有在内存中,这个状态就叫做挂起状态。

2.Linux内核里的状态

在这里插入图片描述

一个进程的状态与它的代码和数据无关。

Linux内核代码定义的状态

在这里插入图片描述

1.运行态

R态就是运行态,一个例子

在这里插入图片描述

在这里插入图片描述

有个很奇怪的现象,明明程序在运行,但它显示的却不是R态而生S态。接下来把打印都注释掉。

在这里插入图片描述

在这里插入图片描述

这时这个进程就变成R态了,是怎么回事呢?这是因为CPU的速度很快,所以我们在进行printf打印的时候,CPU大部分时间都是在等待外设资源,所以我们看到的是等待态。这里的R+代表的是当前进程在前台运行。

2.阻塞态

S和D态都是阻塞态
在这里插入图片描述

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

S态被称为浅度睡眠,可以被直接唤醒。D态在Linux里被称为深度睡眠,处于不可被唤醒状态,它也不可被操作系统所响应。

3.暂停态

T和t状态就是暂停态

在这里插入图片描述

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

在这里插入图片描述

可以使用Kill-19暂停进程,kill-18重新运行。之前的kill-9是强制结束进程。

在这里插入图片描述

4.僵尸状态

Z状态

1.僵死状态(Zombies)是一个比较特殊的状态。当进程退出并且父进程(使用wait()系统调用,后面讲)
没有读取到子进程退出的返回代码时就会产生僵死(尸)进程
2.僵死进程会以终止状态保持在进程表中,并且会一直在等待父进程读取退出状态代码。
3.所以,只要子进程退出,父进程还在运行,但父进程没有读取子进程状态,子进程进入Z状态

exit

在这里插入图片描述

这个函数的作用就是让一个正常运行的进程直接终止,具体参数现在知识储备不足,之后会更新。

一个例子

在这里插入图片描述

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

在第5秒的时候,有一个进程从S态变到了Z态。并且可以看到后面有个defunct(失效的)单词。对于这种已经失效但在等待父进程回收的状态就叫做僵尸状态。

进程一般退出的时候,如果父进程没有主动回收子进程信息,那么子进程会一直让自己处于Z状态,进程的相关资源尤其是task_struct不能被释放。这样就会造成内存泄漏问题。

在这里插入图片描述

5.孤儿进程

这次让父进程先退出看看会发生什么

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

我们发现过了一会,父进程消失了,子进程还在。并且该子进程的PPID还变成了1。这里的1是什么呢?

在这里插入图片描述

其实就是systemd,也就是操作系统本身。所以可以得出,如果父进程先退出,那么子进程的父进程会被改为1号进程(操作系统)。对于这种进程我们称为孤儿进程,该进程被操作系统领养。

为什么要被领养呢?因为孤儿进程也需要被释放。

六.进程优先级

1.概念

是什么

优先级与权限的区别。权限决定的是能不能,而优先级是谁先谁后的问题。

为什么

因为资源是有限的,进程是有多个的,注定了进程之间有竞争—竞争性。
操作系统必须保证大家进行良性竞争,就必须确认优先级。如果进程长时间得不到CPU资源,该进程代码长时间无法执行–进程的饥饿问题

基本概念

1.cpu资源分配的先后顺序,就是指进程的优先权(priority)。
2.优先权高的进程有优先执行权利。配置进程优先权对多任务环境的linux很有用,可以改善系统性能(谨慎)。
3.还可以把进程运行到指定的CPU上,这样一来,把不重要的进程安排到某个CPU,可以大大改善系统整 体性能。

2.查看进程优先级

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

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

优先级可以调整。那么我们是否可以任意更改nice值,大大的提高优先级呢?技术上是可以实现的,但Linux的调度器设计者并不想让用户过多的参与优先级的调整,所以nice值只能在【-20,19】之间。优先级默认是从80开始。

在这里插入图片描述

3.top调整优先级

在这里插入图片描述

在这里插入图片描述
进入root用户,再输入top

在这里插入图片描述

按r,输入要调整进程的PID

在这里插入图片描述

输入调整的值

在这里插入图片描述

更改成功

在这里插入图片描述

还有两个不常用的方法:
1.通过系统调用接口去调整进程的优先级(sched_get_priority_max);
2.使用renice指令去调整进程优先级.
这些方法都能在man手册里找到详细的使用规则。

在这里插入图片描述

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

咸蛋挞

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值