【Linux的进程篇章 - 环境变量的理解】

Linux之进程优先级、环境变量以及地址空间的理解

前言:
前篇了解学习了Linux的进程概念、冯诺依曼体系结构,操作系统等基础知识,这篇介绍开始学习LInux的进程更深一层次的理解,优先级、环境变量、地址空间和进程调度等相关内容,更深入地了解这个强大的开源操作系统。
/知识点汇总/

1、进程优先级

1.1、什么是优先级?

本质就是指定一个进程,并获取某种资源(CPU)的先后顺序。

Linux中优先级数字越小,优先级越高。

优先级和权限的区别:
权限决定的是能不能的问题;优先级是已经能的前提条件。只是能了,来获取某种资源的先后顺序。

1.2、为什么要有优先级?

要明白进程访问的资源(CPU)始终是有限的。系统中进程数量大部分情况都是较(硬件等资源)多的(windows的任务管理器)。

1.3、Linux的优先级特点以及查看方式

测试代码:
在这里插入图片描述
查看进程命令:ps -al
查看结果显示
在这里插入图片描述
说明:

UID:执行者身份
PID:进程ID
PPID:父进程ID
PRI:进程优先级
NI:进程优先级的修正数据(nice值)
其中:nice值–>新的优先级 = 优先级 + nice值,达到对于进程优先级动态修改的过程。
nice和renice命令 – 调整优先级方法1
用top命令更改已存在的进程的nice值 – 调整优先级方法2
步骤:
1.top
2.进入top后输入’r’–>输入需修改的进程PID–》输入nice值

调整优先级方法2:
当nice输入100时,观察现象可知:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

nice值并不能被任意调整,它是属于一定范围的数值。–》[-20,19]–>40个数值
(通常情况下,不提倡随意更改nice值)默认情况下,每次调整都是从80开始的。[60,99]

1.4、进程的几个特性

竞争性: 系统进程数目众多,而CPU资源只有少量,甚至1个,所以进程之间是具有竞争属性的。为了高效完成任务,更合理竞争相关资源,便具有了优先级。
独立性: 多进程运行,需要独享各种资源,多进程运行期间互不干扰
并行: 多个进程在多个CPU下分别,同时进行运行,这称之为并行
并发: 多个进程在一个CPU下采用进程切换的方式,在一段时间之内,让多个进程都得以推进,称之为并发。

2、环境变量

2.1、概念

概念:一般是指操作系统中用来指定操作系统运行环境的一些参数。 环境变量通常具有某些特殊的用途,还有在系统中通常具有的全局特性。

2.2、命令行参数

引言:

int main(int argc,char* argv[])
{}

main函数的参数可带可不带。

这里研究可带:那么这些参数的意义又是什么呢?

2.2.1、什么是命令行参数?

测试代码:
在这里插入图片描述
说明

main函数的前两个参数:int argc,char* argv[]
argc表示argv的个数,argv表示的是字符数组,并以’\0’/NULL结尾,命令行字符串(选项)

代码现象

argv[0]->./process —程序的路径+名称
argv[0]->a —和进程匹配的选项
argv[0]->b —和进程匹配的选项
argv[0]->c —和进程匹配的选项
argv[0]->d —和进程匹配的选项

结论:说明命令行参数本质就是字符串。

2.2.2、为什么要有命令行参数?为什么要这么干?

本质是可以交给我们程序的不同选项,用来定制不同功能的程序。通常命令中携带很多的选项。

其次,可以这么理解,windows中关机选项有:关机、重启、睡眠
本质就是一个程序的不同选项,从而跑三个功能。解决资源和提升利用效率。

2.2.3、这个工作过程由谁完成?

是父进程bash利用命令行解释器完成的
测试代码
在这里插入图片描述
发现:
不管父子进程,对于全局变量都能拿到数据。
即:父进程的数据,默认能被子进程看到并访问(注意:并不代表能访问写)。

再次观察父进程ID:
命令行中启动的程序,都会变成进程,其实都是bash的子进程。

命令行参数默认是输入给父进程bash的。所以是父进程bash利用命令行解释器完成的。

2.2.4、命令行参数的意义

小结
这些参数是在命令行中执行程序时传递给程序的值,用于控制程序的运行方式和实现更多功能。命令行参数可以分为可选参数和必选参数,其中可选参数是程序运行时可以省略的,而必选参数则是必须提供的。
通过命令行参数,用户可以定制程序的运行行为,以满足不同的需求。
命令行参数是参数化程序执行的一种常见且简单的方法。它们允许程序在运行时接收外部数据,如文件路径或其他配置信息,并通过主函数的参数将这些数据传递进函数内部。这样,程序就能够根据这些参数执行相应的操作,从而扩展了程序的功能和灵活性。
总之,命令行参数的意义在于提供了一种简单而有效的方式来控制和定制程序的运行行为,使得程序能够适应不同的应用场景和用户需求。

2.3、环境变量

引言:
我们自己写的程序想要执行需要带上:./ + filename.exe(可执行程序路径)?
而像ls/touch/makdir/rm…可带可不带呢?

为什么呢?

因为Linux系统中存在一些全局的设置,表明来告诉命令行解释器,应该去哪些路径下去找对应的可执行程序。

查看指定环境变量命令

echo $PATH

说明:
系统中有很多的设置,在我们登录Liunx系统时,就已经被加载到了bash进程(内存)中了,通常是一些重要的配置文件。
当我们把我们自己写的程序想像ls命令一样不用输入路径时,可以将程序拷贝或添加到PATH环境变量中去。

PATH= (赋值就是覆盖)/home/class/process(path) – 会导致其他指令被覆盖无法再运行其他指令了。不建议
好在此时的环境变量处于内存级,属于在登录Linux时被加载过来的,具有临时属性(内存级),所以重启一下即可恢复。

要想真的添加进环境变量不能任性的赋值,可以使用如下格式:

PATH=$PATH:+path

其次要想永久有效,不需要每次重启后都添加,那么就可以直接到具体的配置文件中添加即可。
具体的配置文件:

在各自账户的home下,有两个特殊的隐藏文件就是配置文件。
1…bash_profile
2…bashrc

如图所示
在这里插入图片描述

小结:环境变量默认是在配置文件里。

2.4、见识更多的环境变量以及操作。

echo $HOME/SHELL/HISTRIZE/PWD…
在这里插入图片描述

自己定义一个环境变量:
环境变量赋值:export HELLO=1233 — 环境变量
export HELLO=12345
hellobit=1233 —不加export时,属于本地变量
在这里插入图片描述
取消环境变量:
unset +环境变量名
在这里插入图片描述

2.5、整体理解环境变量、系统以及程序的结合

a.代码获取环境变量
测试代码
在这里插入图片描述

环境变量默认也是可以被子进程拿到的。
磁盘中包含了环境变量,加载(拷贝)到内存中(全局的),父进程的数据默认能被子进程所访问的(并不能代表访问并修改)。

那么环境变量可是有很多的,bash内部如何组织呢?
由char* env[]的链表管理。(管理 --》先描述再组织)
bash进程启动的时候,默认会给我子进程形成两张表:
argv[]命令行参数表,env[]环境变量表,bash通过各种方式交给子进程。

argv[]命令行参数表 — 用户输入命令行
env[]环境变量表 — 从os配置文件来
export:本质就是把我们的环境变量拿到表里去。
环境变量具有系统级的全局属性,因为环境变量本身会被子进程继承下去。

单个获取环境变量的内容:getenv
在这里插入图片描述

环境变量三种获取
1.extern char** environ
2.通过mian函数参数
3.getenv(‘path’)

2.6、内建命令

像export/echo/…由bash亲自执行的命令属于内建命令。就算把环境变量清空,也正常能跑。

新建的环境变量HELLO=123456(仅仅这样赋值的变量是本地变量),不用export命令导入时,getenv()和 env | grep HELLO 是找不到的。只有echo $HELLO内建命令能找到。
所以需要使用:export HELLO导入才行。可理解为修改为全局。不然仅仅作为本地变量。
unset HELLO释放环境变量

结论:

本地变量只在本bash内部有效,无法被子进程继承下去使用,只有利用export导入全局环境变量,此时才能够被获取到。
要想真的添加进环境变量不能任性的赋值,可以使用如下格式:
PATH=$PATH:+path

3、地址空间

3.1、代码直观现象

1.直接写代码,看现象
在这里插入图片描述
现象
在这里插入图片描述
发现同一个变量,地址相同,父子进程的变量值可不同。但是这个地址一定不是物理地址。而是虚拟地址。
父子进程是具有独立性的。
理解

进程 = 内核数据结构(task_struct) + 代码和数据

3.2、引入最基本的理解

地址空间示意图
在这里插入图片描述

地址空间的本质就是内核中一个结构体对象
子进程会把父进程的很多内核数据全拷贝一份。

1.所以变量值不同,但是地址相同,是由操作系统通过页表把地址空间的虚拟地址与物理内存地址进行匹配或管理,如果对应物理地址已经有了地址,可访问但不代表能访问修改;
2.所以就在物理内存上开辟另一个相同大小的空间,且把数据拷贝过来,再修改为指定的数值。
3.所以物理地址实际是不同的,返回的是虚拟地址,再页表上虚拟地址是相同的,所以地址会显示相同而值不同。
操作系统对页表和物理内存的匹配机制分配操作称为写时拷贝(OS自主完成)。
本质就是按需申请。更好的利用效率。提升容错率。

每一个进程都会有一个虚拟地址空间,都会有一个独立的页表

3.2、细节问题

父子进程具备独立性。如果父进程不写呢?
未来一个全局变量,默认是被父子共享的,代码是共享的(只读)
为什么要独立呢?可不可以,把数据在创建子进程的时候,全部给子进程拷贝一份?

3.2.1、如何理解地址空间?

a.什么是划分区域?
地址空间的本质就是内核的一个struct结构体。
内部有很多属性都是表示,区域的范围。

b、地址空间的理解。

地址空间包括物理空间和虚拟空间两部分。物理空间指的是实际存在的硬件内存,而虚拟空间则是通过内存管理技术(如分页、分段等)创建的,用于扩展程序的可用内存范围。这种扩展是通过将部分物理内存与硬盘上的空间结合使用来实现的,从而给程序提供更大的内存空间。

3.2.2、为什么要有地址空间?

①将无序变成有序,让进程以统一的视角看待物理内存,以及自己运行的各个区域。实际的物理内存中,代码区、数据区、堆区、栈区、共享区、命令行参数和环境变量。
②进程管理和内存管理模块进行解耦。
③拦截非法请求。防止废旧的数据,影响物理内存。
本质就是对物理内存的保护。

3.2.3、如何进一步理解页表和写时拷贝?

进程挂起、字符串常量不可修改。(权限只读)
所以页表具有权限属性。

当时匹配识别到错误:
1.是不是数据不存在物理内存。 – 缺页中断
2.是不是数据需要写时拷贝。 — 发生写时拷贝
3.如果都不是,才进行异常处理。 – error

3.3、写时拷贝

**写时拷贝主要适用于深拷贝,**以提高效率。深拷贝是在对象再开一块空间,将值拷贝过去,再让新对象的指针指向新开辟的空间。
写时拷贝在开始时不会立即申请新空间,而是让新对象的指针先指向原对象的空间。在读取或写入时,再在堆上申请新空间。
这种技术通过引用计数来记录资源使用者的个数,当销毁对象时,引用计数会相应减少,只有当引用计数为1时,才会释放空间。

写时拷贝的一个典型应用是Linux的fork()系统调用。
传统的fork()系统调用会复制所有的资源给新创建的进程,这既简单又低效,因为很多数据其实是可以共享的。
而Linux的fork()使用写时拷贝技术,使得父子进程在开始时共享同一个地址空间,只有在实际需要写入数据时,才会复制地址空间,从而提高了效率。

3.4、如何理解虚拟地址?

在最开始的时候,地址空间在页表里的数据从哪里来的?

程序本身就自带的地址。 – 虚拟地址,高地址向低地址,地址空间(虚拟地址/逻辑地址)。
所以虚拟地址和页表的数据都来自于程序本身的字段。 — 因为已经被编址了。

反汇编指令

objdump -S filename.c

测试代码
virtual.c

再次理解父子进程的return
本质就是对id赋值。
所以fork有两个返回值就明白了,底层发生写时拷贝。

3.5、地址空间小结

地址空间(Address Space)是一个用于描述计算机实体(如外设、文件、服务器或网络计算机)所占用的内存大小的概念。在计算机科学中,地址空间不仅仅指的是物理内存的大小,还包括了虚拟内存的部分。每个计算机设备以及进程都会被分配一个特定的地址空间,这个空间定义了该实体可以访问的内存范围。
总的来说,地址空间是计算机内存管理中的一个核心概念,它决定了计算机实体可以访问的内存范围和方式。通过对地址空间的合理分配和管理,可以确保计算机系统的稳定性和安全性,提高程序的运行效率。

4、内核进程调度队列

4.1、Linux真正是如何调度的呢?

首先已经知道了,nice值并不能让你任意的调整,而是有范围的。([-20,19] – 40个数字)

1.Linux系统中每一个CPU都有一个运行队列。
2.分时操作系统(基本公平)
进程调度是操作系统中的一个核心功能,它负责在多个进程之间分配处理器时间片,以确保这些进程能够并发执行。
进程调度程序可以被视为在可运行态进程之间分配有限的处理器时间资源的内核子系统,是像Linux这样的多任务操作系统的基础。
调度程序通过一系列算法来决定下一个应该运行的进程是哪个,这些算法会考虑多个因素,包括进程的优先级、资源占用情况以及等待时间等。
其主要目标是最大化系统资源的利用率,最小化进程的等待时间,并保证系统的高吞吐量和低延迟。
Linux的进程调度是一个复杂且精细的过程,旨在有效地管理并发执行的进程,确保它们公平且高效地共享系统资源。
Linux采用了多种调度策略和算法,如完全公平调度器(CFS)和实时调度策略(SCHED_FIFO和SCHED_RR),以及主动和被动调度方式,以应对不同场景的需求。
通过这些机制,Linux能够动态地计算进程优先级,并根据优先级和执行历史来决定下一个执行的进程,从而最大化CPU利用率并保证进程的公平执行。

相关推荐

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-04-11 12:28:03       94 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-04-11 12:28:03       101 阅读
  3. 在Django里面运行非项目文件

    2024-04-11 12:28:03       82 阅读
  4. Python语言-面向对象

    2024-04-11 12:28:03       91 阅读

热门阅读

  1. 3.12 Python赋值运算符

    2024-04-11 12:28:03       31 阅读
  2. KISS 原则和 YAGNI原则

    2024-04-11 12:28:03       39 阅读
  3. 「PHP系列」PHP超级全局变量详解

    2024-04-11 12:28:03       28 阅读
  4. 基于Spring Boot的宠物咖啡馆平台的设计与实现

    2024-04-11 12:28:03       37 阅读
  5. 【Linux】tcpdump P1 - 网络过滤选项

    2024-04-11 12:28:03       42 阅读
  6. git修改某个远端服务器的地址的方式以及4种remote

    2024-04-11 12:28:03       35 阅读
  7. 【报错】Not allowed to load local resource:...

    2024-04-11 12:28:03       29 阅读
  8. STL--容器

    2024-04-11 12:28:03       32 阅读
  9. vue动态绑定class的几种方法

    2024-04-11 12:28:03       38 阅读
  10. 养牛场污水处理的方法和运用的工艺

    2024-04-11 12:28:03       36 阅读
  11. SpringBoot-如何设计优秀的后端接口?

    2024-04-11 12:28:03       35 阅读
  12. 软件设计模式之解释器模式

    2024-04-11 12:28:03       34 阅读