进程的初步认识

目录

一、硬件方面介绍

1.冯诺依曼体系结构

2.存储分级

二、软件 方面

1.操作系统是一款进行管理的软件,它可以管理硬件也可以管理软件

2.操作系统如何管理?

三、进程 

1.概念

总结

四、linux中对进程的管理 

1.task_ struct内容分类

2.查看进程

 3.通过系统调用获取进程标示符

4.知识补充

5.一些信号

6.通过系统调用创建进程-fork

1.对于fork返回值有两个的解释

2.如何做到一个函数返回两次? 

3.一个变量如何储存两个值?

4.父子进程谁先运行呢?

 bash

7.进程状态 

1.运行

2.阻塞

3.挂起

8.linux下状态 

 R运行状态(running):

S睡眠状态(sleeping):

知识补充 

D磁盘休眠状态(Disk sleep)

T停止状态(stopped):

X死亡状态(dead): 

Z僵死状态(Zombies)

孤儿进程

9.进程内的访问

10.进程优先级

1.优先级是什么?

2.为什么要有优先级

3.linux中的进程优先级

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

5.优先级的工作原理

如何判断当前运行的那个队列是否为空 


一、硬件方面介绍

1.冯诺依曼体系结构

abcde都是独立的个体,所以各个单元必须要用“线”连接起来,称为总线,为图中红色

1.系统总线   连接运算器和控制器

2.io总线   连接存储器和输入输出设备

一个程序要运行必须先加载到内存,是因为冯诺依曼体系就是这样子规定的

2.存储分级

二、软件 方面

1.操作系统是一款进行管理的软件,它可以管理硬件也可以管理软件

笼统的理解,操作系统包括:
内核(进程管理,内存管理,文件管理,驱动管理)
其他程序(例如函数库,shell程序等等)

                                                                总体纵览图

操作系统存在的意义是通过管理好底层的软硬件资源,为用户提供一个良好的执行环境 

操作系统里面会有各种数据,但是操作系统不相信任何用户

因此为了保护自身数据的安全,也为了能够给用户提供服务,操作系统以接口的方式给用户提供调用的入口,来获取操作系统内部的数据。

这些接口是操作系统提供的,用c语言实现的,自己内部的函数调用---------系统调用(即上图的系统调用接口)

库函数(lib)vs系统调用

库函数和系统调用是上下层,调用与被调用的关系

所有访问操作系统的行为都只能通过系统调用完成,任何库函数只要试图访问操作系统或者硬件(软硬件资源),都需要经过系统调用

2.操作系统如何管理?

先描述后组织

管理者决策者    操作系统     类比校长

执行者               驱动程序             辅导员

被管理者            软硬件资源         学生

我们要管理学生,先得对学生的信息进行描述,例如学院  姓名  班级  专业等等

每个学生都可以转换成一个结构体

我们再将这些结构体进行组织,例如使用双向链表将每一个结构体进行连接

那么我们对学生的管理就转换成对链表的增删查改了

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

三、进程 

1.概念

一个已经加载到内存中的程序,被称为进程,也被称为任务

可以理解成,正在运行的程序,叫做进程

而一个操作系统不仅仅只能运行一个进程,可以同时运行多个进程,因此我们也必须将进程管理起来,如何管理呢?先描述再组织。

进程 = 内核PCB数据结构对象+ 我们自己的代码和数据

                     这个数据结构对象

                     描述这个进程的所有属性值

任何一个程序在加载到内存的时候,形成真正的进程时,操作系统要先创建描述进程的结构体对象----PCB  (process ctrl block 进程控制块),PCB就是一个进程属性的集合

这个集合就是一个struct结构体,里面包含例如:进程编号(PID)  进程的状态 优先级等等,根据进程的PCB类型,为该进程创建对应的PCB对象

我们只需要对PCB进行管理就可以管理这个进程,PCB中含有进程的各个属性,因此也顺理成章地会去记录代码和数据的位置,记录下指针。

(操作系统也是软件,所以开机时也会加载到内存中) 

在操作系统之中,对进程进行管理,就变成了对单链表进行增删查改

总结

计算机管理硬件
1. 描述起来,用struct结构体
2. 组织起来,用链表或其他高效的数据结构 

四、linux中对进程的管理 

Linux操作系统下的PCB是: task_struct

所以

在Linux中描述进程的结构体叫做task_struct。
task_struct是Linux内核的一种数据结构,它会被装载到RAM(内存)里并且包含着进程的信息

1.task_ struct内容分类


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

9.其它信息

2.查看进程

1.ps ajx(我们通常会搭配grep来使用)

COMMAND系统运行这个进程时是调用什么指令

2. ls  /proc

这个文件夹内的信息实际上是操作系统将内存中的进程进行可视化(因此这个文件夹里面的信息是动态变化的)

这些蓝色的数字就是相应进程的PID 

(同一个程序进行多次运行时,所产生的PID也会不同)

进入目录,里面存储的是该进程的一些属性,我们这里简单看一下cwd和exe

cwd (current work dir)后面的内容指的是:当前的工作目录

例如我们touch  文件名  来创建一个文件,当touch载入内存变成进程的时候会记录它启动时的工作目录,所以创建的文件在没有指定路径的时候,默认就会在当前路径下创建

exe  后的内容指的是:这个进程对应的文件是这个路径下的这个文件,即指针信息可视化

 3.通过系统调用获取进程标示符


进程id(PID)   使用getpid()
父进程id(PPID)使用getppid()

进程的pid也是它的属性,所以pid也在task_struct中存放

ppid在同一个终端下启动一般不会改变,所有子进程的父进程都是bash

4.知识补充

1.在底行模式下,使用!man也可以直接查询手册

2.多条不同指令可以在同一行进行输入,可以使用&&进行分隔,从左到右一次执行

5.一些信号

kill -9  PID 杀死对应pid的进程

6.通过系统调用创建进程-fork

fork可以创建子进程,即系统里多了一个进程,那么这个进程也需要有自己的task_struct和数据以及代码

因为子进程没有自己的代码所以一般而言,fork之后的代码,父子共享

1.对于fork返回值有两个的解释

  我们为什么要创建子进程呢?是因为我们想让父子进程做不同的事情,所以需要让父和子执行不同的代码块,为了实现这个功能,fork就具有了不同的返回值,对父进程返回子进程的pid(因为字进程查询父进程pid成本低,但是父进程无法准确查询子进程),对子进程返回0,如果失败就返回-1,所以我们可以使用if else来对代码进行分流

2.如何做到一个函数返回两次? 

我们可以猜测到fork函数内的大概功能

pid_t fork(void)

{

1.创建子进程pcb

2.填充子进程pcb相应内容

3.让父子进程共享同样的代码

......

此时由于父子进程都有独立的pcb了,他们都可以被cpu调度运行了

return ;

}

我们可以看到,return位于函数的最后,而此时函数的功能已经被实现,因此return的代码也被共享了

3.一个变量如何储存两个值?

首先我们需要知道进程之间是相互独立的

因为数据可能被修改所以我们不能让父子进程共享同一份数据(可以共享代码是因为运行时代码已经不会被修改了,不会影响进程间的独立性)

所以为了解决这个问题,字进程会进行写时拷贝,当子进程修改父进程的数据时,会重新开辟一块空间,因此当return 返回的值被写入父进程的数据时候,父进程的数据直接被修改,而系统会给子进程开辟一块空间用于储存另一个返回值

4.父子进程谁先运行呢?

由调度器决定,是不确定的

调度器会相对公平地调度

 bash

bash,即命令行解释器,它通过fork创建子进程,执行相应的指令,而它本身继续接收我们的指令,这就是fork创建的父子进程的实际应用

7.进程状态 

操作系统学科上的进程状态一般分为,运行,阻塞,挂起 

1.运行

操作系统会将已经准备好运行的进程放入运行队列中,操作系统根据顺序去调度这些进程,处于这个状态的进程就为运行状态:R 

一个进程并不是放上去一直到执行结束的,每一个进程都有一个叫时间片的概念,在一个时间段内所有的进程代码都会被执行,即并发执行

大量地把进程放到cpu上和拿下来的动作被称为进程切换

2.阻塞

操作系统会像管理进程一样管理硬件,每一个硬件属性的结构体中会存在一个等待队列, 如果进程想要读取某个硬件的数据,那么它就会被排在该硬件的等待队列中,这时候进程就处于阻塞状态,一直等待到硬件已经准备好了,然后这个进程就会被排到运行队列中

等待特定设备的进程,我们称该进程处于阻塞态

3.挂起

当操作系统内部的内存不够了,那么在阻塞状态的进程,它的代码和数据就会被换出到磁盘中(swap盘),等到程序准备好运行了,它的代码就又会被换入到内存中,代码和数据被换出状态下的程序就处于挂起状态

8.linux下状态 

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 */
};

 R运行状态(running):

并不意味着进程一定在运行中,它表明进程要么是在运行中要么在运行队列里。


S睡眠状态(sleeping):

意味着进程在等待事件完成(这里的睡眠有时候也叫做可中断睡眠
(interruptible sleep))

由于cpu的速度很快,所以一个进程有很大一部分时间是在等待i/o设备就绪,我们查询的时候基本上是处于S状态,但是当我们执行一个空语句,即例如

while(1)

此时程序不进行输入输出,它就一直处于运行状态了

所以S状态可以对应阻塞状态

知识补充 

如果程序处于  例如S+ R+的状态那么说明该程序在前台运行,这时候我们就不能继续在命令行解释器输入指令了,我们可以

./test.exe  &

这样程序就会转为到后台运行,状态后的加号也会消失,这种进程只能使用kill来杀死

D磁盘休眠状态(Disk sleep)

有时候也叫不可中断睡眠状态(uninterruptible sleep),在这个状态的进程通常会等待IO的结束。

在等待期间这个进程不能被杀死,对应的也是阻塞状态

T停止状态(stopped):

可以通过发送 SIGSTOP 信号给进程来停止(T)进程。这个被暂停的进程可

以通过发送 SIGCONT 信号让进程继续运行。

kill -19 暂停进程,暂停之后进程状态就会变成T

kill -18继续进程  进程继续后会转为后台运行

T状态有自己的应用场景,可能是要等待资源也可能是单纯被其它进程控制了

例如我们使用gdb调试进程时,运行某个程序并且在某个位置打上断点,进程在该代码处停止的状态就处于t状态,(t和T区别不是很大,暂时可以理解为同一种状态)

X死亡状态(dead): 

这个状态只是一个返回状态,你不会在任务列表里看到这个状态

Z僵死状态(Zombies)

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

如果处于Z状态的程序没有被读取,僵尸进程会一直占用内存,但不会继续运行,会造成内存泄露

孤儿进程

当一个子进程的父进程被终止(在代码中使用exit(0)),但是子进程继续运行,那么子进程的父进程会被更改为1进程,即操作系统 ,因为如果子进程不被托管,在子进程运行结束后就不会被回收了

(如果将父进程直接ctrl+c终止,那么在父进程被回收的一瞬间,子进程在将父进程更改为操作系统后,子进程也会被回收)

9.进程内的访问

某个进程,它的pcb信息可以储存多种用于访问不同数据结构的指针,也就是说一个进程可以被存放在链表中也可以同时被存放在多叉树等等中。

下面我们简单讲一下进程如何相互访问,下是三点前提信息

1.这是一个双链表的结构体,我们现在有一个struct node* 类型的start变量,储存了第一个进程的一个地址

2.这个进程指向的是link

3.link节点内是指向 前后进程的指针

首先我们假设地址0是一个task_struct*类型的一个指针

(task_struct*)0

我们让它指向它的结构体内的link

(task_struct*)0->link

我们再将其取地址,得到的就是link与结构体的地址(即结构体最开始的那个变量的地址)之间的地址差,因为结构体地址为0。

&(task_struct*)0->link

因为我们现在已经有了一个结构体的link的地址,因此我们只需要将它的地址减去上面所算出的地址差,就找到了结构体的地址。(要使用int强转,转换成一个单纯数值的运算,而不是地址间的运算)

(int)start-(int)&(task_struct*)0->link

我们就得到了结构体的地址的数值,将其强转成task_struct*类型,即可访问该进程的其它内容了

(task_struct*)(int)start-(int)&(task_struct*)0->link

10.进程优先级

1.优先级是什么?

优先级是决定某个进程被访问顺序的一项属性。是一个[60,99]之间的一个数值

2.为什么要有优先级

因为资源是有限的,进程是多个的,因此进程之间具有竞争性,操作系统必须保证大家良性竞争,确认优先级

如果进程长时间得不到cpu资源,该代码得不到推进,进程就会面临饥饿问题。

3.linux中的进程优先级

我们使用ps-l指令可以看到

其中

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

PRI即进程的优先级,或者通俗点说就是程序被CPU执行的先后顺序,此值越小,进程的优先级别越高
NI就是我们所要说的nice值了,其表示进程可被执行的优先级的修正数值,进程nice值会影响到进
程的优先级变化

程序一般的PRI值为80
当我们对程序进行修正,加入nice值后,将会使得PRI变为:PRI(new)=PRI(old)+nice
这样,当nice值为负值的时候,那么该程序将会优先级值将变小,即其优先级会变高,则其越快被执行
nice其取值范围是-20至19,一共40个级别

我们每次修改进程的优先级,PRI(old)都为80,当nice的值太大或者太小时,nice值会被限制在-20和19。

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

top
进入top后按“r”–>输入进程PID–>输入nice值 

(只有root才能更改优先级)

nice和renice也可以更改

5.优先级的工作原理

 

在运行队列中会存在两个指针数组,他们指向对应的对应的结构体,其中下标[100,139]对应[60.99] 的PRI的值,根据PRI值,我们将相应的task_struct链接上去,相同PRI值遵循先来后到的原则,类似于哈希表的处理办法

运行队列中有两个相同的数组,这里为了区分我们将其命名为,waiting和running,即当前run和当前wait,当running中的进程依次开始运行时,新进入运行队列的进程会被插入到waiting中,当running中的进程都运行完毕,他们两个的职能就互换,这样能够避免先进入但是PRI较大的进程一直处在队列的末尾得不到运行。

为了实现上述功能,两个数组的指针会不断被run和wait交换,这两个二级指针用于寻找当前run和当前wait的数组

如何判断当前运行的那个队列是否为空 

我们可以创建数组

char bits[5],里面有40个比特位,对应四十级优先级,每个比特位上的1与0表示该优先级所对应的进程队列里面是否有进程,当bits为0时,说明当前run的数组里面指向的进程都已经被运行完毕。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/370533.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

解决Linux环境Qt报“cannot find -lgl“问题

今天,在Ubuntu 18.04.6环境下,安装Qt5.14.2之后,运行一个QWidget工程,发现Qt报"cannot find -lgl"错误。     出现这种现象的原因:Qt的Path路径没有配置,缺少libqt4-dev依赖包和一些必要的组件…

Redis基础教程(九):redis有序集合

💝💝💝首先,欢迎各位来到我的博客,很高兴能够在这里和您见面!希望您在这里不仅可以有所收获,同时也能感受到一份轻松欢乐的氛围,祝你生活愉快! 💝&#x1f49…

从“钓”到“管”:EasyCVR一体化视频解决方案助力水域安全管理

一、背景 随着城市化进程的加快,越来越多的市民热衷于钓鱼活动。钓鱼活动在带来乐趣的同时,也伴随着一定的安全隐患。尤其是在一些危险水域,也经常出现垂钓者的身影,非法垂钓,这给城市管理带来了不小的阻力。传统的人…

如何处理 PostgreSQL 中由于表连接顺序不当导致的性能问题?

文章目录 一、理解表连接和连接顺序二、识别由于表连接顺序不当导致的性能问题三、影响表连接顺序的因素四、解决方案手动调整连接顺序创建合适的索引分析数据分布和优化查询逻辑 五、示例分析手动调整连接顺序创建索引优化查询逻辑 六、总结 在 PostgreSQL 中,表连…

【Docker安装】OpenEuler系统下部署Docker环境

【Docker安装】OpenEuler系统下部署Docker环境 前言一、本次实践介绍1.1 本次实践规划1.2 本次实践简介二、检查本地环境2.1 检查操作系统版本2.2 检查内核版本2.3 检查yum仓库三、卸载Docker四、部署Docker环境4.1 配置yum仓库4.2 检查可用yum仓库4.3 安装Docker4.4 检查Docke…

绝区贰--及时优化降低 LLM 成本和延迟

前言 大型语言模型 (LLM) 为各行各业带来了变革性功能,让用户能够利用尖端的自然语言处理技术处理各种应用。然而,这些强大的 AI 系统的便利性是有代价的 — 确实如此。随着 LLM 变得越来越普及,其计算成本和延迟可能会迅速增加,…

Linux配置固定ip地址

虚拟机的Linux操作系统,其IP地址是通过DHCP服务获取的 DHCP:动态获取IP地址,即每次重启设备后都会获取一次,可能导致IP地址频繁变更。 一般系统默认的ip地址设置都是自动获取,故每次系统重启后ip地址都可能会不一样&a…

Redis的使用(二)redis的命令总结

1.概述 这一小节,我们主要来研究一下redis的五大类型的基本使用,数据类型如下: redis我们接下来看一看这八种类型的基本使用。我们可以在redis的官网查询这些命令:Commands | Docs,同时我们也可以用help 数据类型查看命令的帮助文档。 2. 常…

FastAPI教程——部署

部署 部署FastAPI应用程序相对容易。 部署是什么意思 部署应用程序意味着执行必要的步骤以使其可供用户使用。 对于Web API来说,通常涉及将上传到云服务器中,搭配一个性能和稳定性都不错的服务器程序,以便你的用户可以高效地访问你的应用…

Ubuntu安装Docker

一,Docker简介 Docker 是一个开源的应用容器引擎,让开发者可以打包他们的应用以及依赖包到一个可移植的容器中,然后发布到任何流行的Linux机器上,也可以实现虚拟化,容器是完全使用沙箱机制,相互之间不会有…

论文回顾 | CVPR 2021 | How to Calibrate Your Event Camera | 基于图像重建的事件相机校准新方法

论文速览 | CVPR 2021 | How to Calibrate Your Event Camera | 基于图像重建的事件相机校准新方法 1 引言 在计算机视觉和机器人领域,相机校准一直是一个基础而又重要的问题。传统的相机校准方法主要依赖于从已知校准图案中提取角点,然后通过优化算法求解相机的内参和外参。这…

DDR3(三)

目录 1 预取1.1 什么是预取1.2 预取有哪些好处1.3 结构框图1.4 总结 2 突发2.1 什么是突发2.2 突发与预取 本文讲解DDR中常见的两个术语:预取和突发,对这两个概念理解的关键在于地址线的低位是否参与译码,具体内容请继续往下看。 1 预取 1.1…

创新配置,秒级采集,火爆短视频评论抓取

快速采集评论数据的好处 快速采集评论数据是在当今数字信息时代的市场趋势分析和用户反馈分析中至关重要的环节。通过准确获取并分析大量用户评论,您将能够更好地了解消费者的需求、情感和偏好。集蜂云采集平台提供了一种简单配置的方法,使您能够快速采…

离线安装arm架构Firefox

离线安装Firefox浏览器及其插件在ARM架构的设备上(如树莓派、部分Android设备或其他采用ARM处理器的Linux系统)可能需要一些特殊步骤,因为默认情况下,大多数浏览器和插件都是为x86架构设计的。对于ARM架构,你需要找到特…

Leetcode 338. 比特位计数

给你一个整数 n &#xff0c;对于 0 < i < n 中的每个 i &#xff0c;计算其二进制表示中 1 的个数 &#xff0c;返回一个长度为 n 1 的数组 ans 作为答案。 示例 1&#xff1a; 输入&#xff1a;n 2 输出&#xff1a;[0,1,1] 解释&#xff1a; 0 --> 0 1 --> 1…

C#/WPF 自制截图工具

在日常使用电脑办公时&#xff0c;我们经常遇到需要截图然后保存图片&#xff0c;我们往往需要借助安装截图工具才能实现&#xff0c;现在我们通过C#自制截图工具&#xff0c;也能够轻松进行截图。 我们可以通过C#调用WindousAPI来实现截图&#xff0c;实例代码如下&#xff1a…

uni-app x 跨平台开发框架

目录 uni-app x 是什么 和Flutter对比 uts语言 uvue渲染引擎 组合式API的写法 选项式API写法 页面生命周期 API pages.json全局配置文件 总结 uni-app x 是什么 uni-app x&#xff0c;是下一代 uni-app&#xff0c;是一个跨平台应用开发引擎。 uni-app x 是一个庞…

计算机网络-IP组播基础

一、概述 在前面的学习交换机和路由协议&#xff0c;二层通信是数据链路层间通信&#xff0c;在同一个广播域间通过源MAC地址和目的MAC地址进行通信&#xff0c;当两台主机第一次通信由于不清楚目的MAC地址需要进行广播泛洪&#xff0c;目的主机回复自身MAC地址&#xff0c;然后…

Linux多进程和多线程(三)进程间通讯-信号处理方式和自定义处理函数

进程间通信之信号 信号信号的种类 信号在操作系统中的定义如下: 信号的处理流程在 Linux 中对信号的处理⽅式 自定义信号处理函数 信号的发送 kill() 函数:raise() 函数: 示例 : 创建⼀个⼦进程&#xff0c;⼦进程通过信号暂停&#xff0c;⽗进程发送 终⽌信号等待信号 pause()…

【鸿蒙学习笔记】@Link装饰器:父子双向同步

官方文档&#xff1a;Link装饰器&#xff1a;父子双向同步 目录标题 [Q&A] Link装饰器作用 [Q&A] Link装饰器特点样例&#xff1a;简单类型样例&#xff1a;数组类型样例&#xff1a;Map类型样例&#xff1a;Set类型样例&#xff1a;联合类型 [Q&A] Link装饰器作用…