当前位置: 首页 > news >正文

进程Linux

1.冯诺依曼体系

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

关于冯诺依曼,必须强调几点:


1.这里的存储器指的是内存

2.不考虑缓存情况,这里的CPU能且只能对内存进行读写,不能访问外设(输入或输出设备)

3.外设(输入或输出设备)要输入或者输出数据,也只能写入内存或者从内存中读取。

一句话,所有设备都只能直接和内存打交道。

冯诺依曼就是一个为了性价比而产生的体系

外设备和cpu的运行时间不是一个量级,所以就以一个中间(内存)为中间载体

2.操作系统

操作系统包括两部分:

1.内核(进程管理,内存管理,文件管理,驱动管理)

2.其他程序(例如函数库,shell程序等等)

设计OS的目的:总的来说,操作系统就是软件层面和硬件层面交互的中间商

对下,与硬件交互,管理所有的软硬件资源

对上,为用户程序(应用程序)提供一个良好的执行环境

3.进程

3.1.进程的概念:

进程=PCB+自己的代码和数据

PCB里面有指向自己代码和数据的指针

基本概念:

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

描述进程——PCB:

进程信息被放在⼀个叫做进程控制块的数据结构中,可以理解为进程属性的集合。
课本上称之为PCB(process control block),Linux操作系统下的PCB是: task_struct

task_struct包含的数据:

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

3.2.在Linux对进程的操作

1.查看所有的进程: cd /proc

2.代码内部调用查看id

getpid:获取本身的进程(哪个进程调用这个函数,就返回哪个进程的id)

getppid:获取父亲的进程

3.根据执行的文件,查看指定的进程

1.查看所有的进程: ps axj  或者是 top

top:

ps axj:

2.查看正在运行的指定进程

假设此时我设计一个程序是一直在进行运行:

根据PID和程序名都是可以查到指定程序的状态

此时我们三种方式都是可以的:

3.在本身进程中创建一个子进程:fock()

fock():创建的子进程调用的是和父进程相同的一段代码,

fork()函数创建子进程后,子进程会从fork()调用后的下一行代码开始执行

fork():创建失败返回-1,子进程调用返回0,父进程调用返回子进程的PID

fork()函数内部的大致执行逻辑:

3.3.进程的状态

3.3.1.运行&&阻塞&&挂起

进程状态的变化,表现之一,就是要在不同的队列中进行流动。本质都是数据结构的增删查改!

每个进程的task_struct都指向自己的代码数据:

运行:如果此时是运行状态那么所有的运行状态的task_struct就形成一个运行状态的双向链表

阻塞:等待某种设备或者资源就绪键盘,显示器,中网卡,磁盘,摄像头,话筒..(阻塞状态那么所有的运行状态的task_struct就形成一个阻塞状态的双向链表)

挂起:此时因为cpu的内存严重不足,只能将一些进程暂时挂起,释放其内存(挂起状态那么所有的运行状态的task_struct就形成一个挂起状态的双向链表)

3.3.2.不同状态的双向链表

这么多状态的链表的形成:

其实是Node节点内部再有个head结果,不同状态的链表的形成,也只需要靠head进行连接即可

此时虽然我们只有每一个Node节点中的head的地址,但是我们可以靠偏移量来找到PCB的位置,从而访问到Node节点中其他变量的数值

cpu在对这些状态的调度,对其head链表进行增删查改即可

3.3.3.Linux中的进程状态

R运行状态(running):并不意味着进程一定在运行中,它表明进程要么是在运行中要么在运行队列里。


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


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


T停止状态(stopped):可以通过发送 SIGSTOP 信号给进程来停止(T)进程。这个被暂停的进程可以通过发送 SIGCONT 信号让进程继续运行。

X死亡状态(dead):这个状态只是一个返回状态,你不会在任务列表里看到这个状态。

状态的查看指令:

每个字母代表的含义:

查看进程状态:

R运行状态(running):

下面的代码就是一直都在进行运行,所以是一个运行的状态

S睡眠状态(sleeping):

因为此时代码中有一个向屏幕输出的操作,所以此时的cpu就会对输出操作进行等待,所以此时大部分是睡眠状态

因为此时代码中有一个等待键盘输入的操作,所以此时的cpu就会对输入操作进行等待,所以此时是睡眠状态

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

S对应的是浅睡眠,D对应的就是一个深度睡眠,S的进程在睡眠的状态下是可以被杀死的,

但是D多了一个特权,就是程序在休眠的状态下是不可以被杀死,内存不可以被cpu回收

t 追踪状态(tracing stop)

此时我们个运行文件加上调试信息,并打上断点,运行的时候,就会在断点处停下

T停止状态(stopped):

此时我们的代码一直在向屏幕打印数据,但是此时我们 CTRL+z:由我们人自己进行暂停,在另一个终端显示的就是T状态

3.3.3.1.僵尸状态

在Linux中,所有的进程都是一个进程的子进程,我们创建子进程就是为了完成某种事情,但是子进程完成之后,父进程需要查看子进程完成的情况如何,所以子进程结束后不可以将自己的PCB

信息给释放,要等待父进程结束,此时子进程在等待父进程的这段区间就是被称为僵尸状态

僵死进程会以终⽌状态保持在进程表中,并且会⼀直在等待⽗进程读取退出状态代码。
只要⼦进程退出,⽗进程还在运⾏,但⽗进程没有读取⼦进程状态,⼦进程进⼊Z状态

终端的观察情况:

3.3.3.2.孤儿进程

父子进程关系中,如果父进程先退出,子进程要被1号进程领养,这个被领养的进程(子进程),叫做孤儿进程

3.4.进程优先级

首先我们要先理解下面四个概念:

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

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

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

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

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

总的来说就是谁先运行谁后运行

查看系统进程:

UID:代表执行者的身份
PID:代表这个进程的代号
PPID:代表这个进程是由哪个进程发展衍生而来的,亦即父进程的代号

PRI:代表这个进程可被执行的优先级,其值越小越早被执行(默认值为80)

N:代表这个进程的nice值

真实的的进程优先级=PRI(默认)+N;

改变进程的优先级:

1. 进入top 查看优先级

2.输入 r 要更改的进程PRI 回车

3.输入要改的 nice值即可

4.对更改的NI的文件进行查询的时候,就发现PRI变成了 95(默认是80) NI变成了15

优先级极值的问题:

nice范围:【-20,19】;

默认:80;

所以Linux优先级的取值范围是[60,99]

优先级设立不合理,会导致优先级低的进程,长时间得不到CPU资源,进而导致:进程饥饿

所以设置这种范围

3.5.进程的切换

进程的切换从下面三部分进行理解;

1.cpu的运行机制:

2.宏观上进程的切换

3.Linux真实调度算法:O(1)调度算法

3.5.1.cpu的运行机制

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

问题:假设一个死循环进程运行,是否会一直占有cpu,导致后面的进程无法运行

不会,因为有一个时间片的东西,过来时间片,进程就会停止,这就是并发

3.5.2.宏观上进程的切换

现在我们有了cpu运行进程的时候是采用时间片的方式运行,在理解一下寄存器的工作原理

寄存器:寄存器是cpu内部的临时空间,存储的是进程的临时数据

当进程超出了规定的时间片后,进程就会将寄存器中的临时数据取出,到下次的进程再次运行的时候,就可以找到对应的数据,后面的进程直接将自己的数据在寄存器进行覆盖即可

当前进程要把自己的进程硬件上下文数据,保存起来,保存到哪里了呢


保存到进程的task struct 里面==》》TSS:任务状态段

3.5.3.Linux真实调度算法:O(1)调度算法

1.queue[140]

此时优先级分成下面两种:

实时优先级:就是加入实时优先级中的进程是必须完成(0~99(不关心))


普通优先级:100~139(我们都是普通的优先级,想想nice值的取值范围,可与之对应!)

 x(PRI-NI)-60+(140-40):就是此时普通优先级储存的位置

其实这个queue就相当于一个hash表,先根据优先级确定存储的位置,每一个位置相当于这个位置优先级的头结点,过了时间片就将其舍弃,遵行先进先出的原则

2.bitmap[5]

bitmap的左右就是为了标记对应的queue数组是否有进程,如果没有bitmap数组,cpu运行的时候只能遍历queue数组一遍,但是此时引入了bitmap数组,我们只要O(1)的时间复杂度,就可以确定进程的位置

一共有32*5个二进制位,但是queue只有140位,所以最后的20位不要了舍弃

3.nr_active:表示queue数组中进程的个数

4.*active和*expired

elem的数组大小为2,第一个是活跃进程,一个是过期进程,他们的作用又是什么?

机制:

其实cpu运行进程只运行活跃进程中的进程,当这个进程过了设置的时间片,就把这个进程从活跃进程中删除,根据对应的优先级插入到过期进程中(这就是为什么   真实的优先级=PRI+NI 的原因),当活跃进程中的进程运行完后,将活跃进程和过期进程,进行交换,再次运行活跃进程即可

4.环境变量

基本概念:

环境变量(environment variables)一般是指在操作系统中用来指定操作系统运行环境的一些参数如:我们在编写C/C++代码的时候,在链接的时候,从来不知道我们的所链接的动态静态库在哪里,但是照样可以链接成功,生成可执行程序,原因就是有相关环境变量帮助编译器进行查找。

环境变量通常具有某些特殊用途,还有在系统当中通常具有全局特性

常见的环境变量:

PATH:指定命令的搜索路径

HOME:指定用户的主工作目录(即用户登陆到Linux系统中时,默认的目录)

SHELL:当前Shell,它的值通常是/bin/bash。

查看指定的环境变量:

测试PATH:

假设此时我的main.c文件

此时运行它有两种方式:

方式一:./main:指定路径

方式二:将main文件的路径加入到  PATH环境变量中,直接运行main,系统自动在环境变量中的路径下找到相对应的文件,并运行(这就是cd pwd,直接输入命令就可以运行的原因)

和环境变量相关的命令:

echo:显示某个环境变量值

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

env:显示所有环境变量
unset: 清除环境变量
set: 显示本地定义的shell变量和环境变量

环境变量的组织方式:

每个程序都会收到⼀张环境表,环境表是⼀个字符指针数组,每个指针指向⼀个以’\0’结尾的环境
字符串
通过代码获取环境变量:
1.命令行的三个参数
  • argc:表示命令行参数的数量,是一个整数。当程序从命令行启动时,argc的值会根据传入的参数个数确定,至少为 1(表示程序名本身)。
  • argv:是一个字符指针数组,用于存储命令行参数。argv[0]通常是程序的名称,后续元素argv[1]argv[2]等依次是传入的命令行参数。
  • env:是一个字符指针数组,存储了系统的环境变量。每个元素都是一个以key=value形式表示的环境变量字符串。
结果:
2.通过第三方变量:
libc中定义的全局变量environ指向环境变量表,environ没有包含在任何头⽂件中,所以在使⽤时 要⽤
extern声明。
结果:
3.getevn:获取指定的环境变量的内容
理解环境变量的特性:
1.环境变量通常是具有全局属性的 ,环境变量会继承给子进程,但是本地变量不会被继承
像下面的i变量就不会被子进程继承,只能被bash内部使用

2.我们的环境变量都是继承于bash进程,bash中的变量分成环境变量和本地变量

5.程序地址空间

5.1.虚拟地址空间

观察一个现象:

子进程肯定先跑完,也就是子进程先修改,完成之后,父进程再读取

结果:

我们发现,⽗⼦进程,输出地址是⼀致的,但是变量内容不⼀样!能得出如下结论:
变量内容不一样,所以父子进程输出的变量绝对不是同一个变量

但地址值是一样的,说明,该地址绝对不是物理地址!

在Linux地址下,这种地址叫做 虚拟地址
我们在用C/C++语言所看到的地址,全部都是虚拟地址!物理地址,用户一概看不到,由OS统一管理OS必须负责将 虚拟地址 转化成 物理地址 。

结论:用户是拿不到真正的物理地址,我们拿到的是cpu给你的虚拟地址,如何要访问物理内存的话,cpu会管理一个页表,相当一个map一样,可以通过虚拟地址找到真正的地址

5.2.虚拟地址空间的划分

描述linux下进程的地址空间的所有的信息的结构体是 mm_struct (内存描述符)。每个进程只有
一个mm_struct结构,在每个进程的task_struct结构中,有⼀个指向该进程的结构。

mm_struct里面存储的都是每一个区域的start位置和end位置

操作系统组织这些进程的mm_struct的方式有下面两种:
linux内核使用 vm_area_struct 结构来表示一个独立的虚拟内存区域(VMA),由于每个不同质的虚拟内存区域功能和内部机制都不同,因此一个进程使用多个m_area_struct结构来分别表示不同类型的虚拟内存区域。上面提到的两种组织方式使用的就是vm_area_struct结构来连接各个VMA,方便进程快速访问。
struct vm_area_struct {unsigned long vm_start;        // 虚存区起始unsigned long vm_end;          // 虚存区结束struct vm_area_struct *vm_next, *vm_prev;  // 前后指针struct rb_node vm_rb;          // 红黑树中的位置unsigned long rb_subtree_gap;struct mm_struct *vm_mm;       // 所属的mm_structpgprot_t vm_page_prot;unsigned long vm_flags;        // 标志位struct {struct rb_node rb;unsigned long rb_subtree_last;} shared;struct list_head anon_vma_chain;struct anon_vma *anon_vma;const struct vm_operations_struct *vm_ops;  // vma对应的实际操作unsigned long vm_pgoff;        // 文件映射偏移量struct file *vm_file;          // 映射的文件void *vm_private_data;         // 私有数据atomic_long_t swap_readahead_info;
#ifndef CONFIG_MMUstruct vm_region *vm_region;   /* NOMMU mapping region */
#endif
#ifdef CONFIG_NUMAstruct mempolicy *vm_policy;   /* NUMA policy for the VMA */
#endifstruct vm_userfaultfd_ctx vm_userfaultfd_ctx;
} __randomize_layout;


1. 当虚拟区较少时采取单链表,由mmap指针指向这个链表;
2. 当虚拟区间多时采取红⿊树进⾏管理,由mm_rb指向这棵树。

创建虚拟地址空间的意义:

1.此时代码和数据在物理空间上就可以随便存储,只有我们的虚拟空间是连续的就可以(无序==》》有序)

2.在地址转换的过程中,可以对你的地址的合法性和操作做出判断,从而保护了物理空间,

页表其实后面还有一列来标明,用户对这块地址的使用权限

3.让进程管理和内存管理进行一定程度的解耦合

像进行进行进程的调度的时候,我只需要改变虚拟地址即可,内存和磁盘进行内存管理的时候,也只是改变物理空间而已

http://www.lryc.cn/news/613188.html

相关文章:

  • TSMaster-C小程序使用
  • 深度学习之opencv篇
  • change和watch
  • GPT-5 将在周五凌晨1点正式发布,王炸模型将免费使用??
  • 16.Home-懒加载指令优化
  • [C++20]协程:语义、调度与异步 | Reactor 模式
  • 在 Linux 系统上安装 Docker 的步骤如下(以 Ubuntu/Debian为例)
  • 深度学习(1):pytorch
  • Android-Kotlin基础(Jetpack②-Data Binding)
  • 内存杀手机器:TensorFlow Lite + Spring Boot移动端模型服务深度优化方案
  • Bosco-and-Mancuso Filter for CFA Image Denoising
  • python函数--python010
  • Java NIO 核心原理与秋招高频面试题解析
  • MySQL 极简安装挑战:跨平台高效部署指南
  • 大数据中需要知道的监控页面端口号都有哪些
  • 【unity知识】unity使用AABB(轴对齐包围盒)和OBB(定向包围盒)优化碰撞检测
  • 单词的划分(动态规划)
  • OpenCV 图像处理基础操作指南(一)
  • 非化学冷却塔水处理解决方案:绿色工业时代的革新引擎
  • Android视图状态以及重绘
  • 如何将服务器中的Docker镜像批量导出?
  • uat是什么
  • SIP - Centos 7 搭建freeswitch服务器
  • Linux第一阶段练习
  • Microsoft Office PowerPoint 制作简单的游戏素材
  • Sklearn 机器学习 数据降维PCA 自己实现PCA降维算法
  • 智能升级革命:Deepoc具身模型开发板如何让传统除草机器人拥有“认知大脑”
  • 【智能协同云图库】第六期:基于 百度API 和 Jsoup 爬虫实现以图搜图
  • RabbitMQ面试精讲 Day 15:RabbitMQ故障转移与数据恢复
  • 【数据结构】排序(sort) -- 交换排序(冒泡快排)