Linux知识点 -- 进程概念(二)

news2024/11/22 12:06:56

Linux知识点 – 进程概念(二)

文章目录

  • Linux知识点 -- 进程概念(二)
  • 一、进程优先级
    • 1.概念
    • 2.进程中的优先级信息
    • 3.更改进程优先级
    • 4.进程切换
  • 二、环境变量
    • 1.概念
    • 2.常见的环境变量
    • 3.环境变量相关命令
    • 4.通过代码获取环境变量
    • 5.环境变量的全局属性
    • 6.定义普通变量
    • 7.命令行参数
  • 三、进程地址空间
    • 1.验证进程地址空间
    • 2.计算机访问地址的历史
    • 3.对进程地址空间的理解
    • 4.进程地址空间的扩展内容


一、进程优先级

1.概念

为什么要有优先级:就是因为CPU资源有限,进程太多,需要通过某种方式竞争资源;

  • Linux具体优先级做法
    优先级 = 老的优先级 + nice值

2.进程中的优先级信息

在这里插入图片描述
使用ps -al指令可查看进程的优先级信息;

  • PRI是进程的原优先级,此值越小,进程的优先级别越高;
  • NI是nice值,表示进程可被执行的优先级修正指数,取值范围是-20至19,一共40个级别;
  • PRI(NEW) = PRI(OLD) + NI

3.更改进程优先级

更改进程的优先级就是更改nice值,在top命令中可以更改进程的优先级:

  • 进入top后按"r" -> 输入进程PID -> 输入nice值
    在这里插入图片描述
    在这里插入图片描述
    如果系统不允许将进程优先级调高,可以使用sudo top命令;
    在这里插入图片描述
    在这里插入图片描述

  • 注:每次设置优先级都要从进程最开始的优先级开始设置,老的优先级基本都是80,也就是PRI(old)都是固定的;

  • 每个进程在运行的时候都有时间片的概念,时间片一到,就让出cpu资源;
    操作系统还支持抢占,更高优先级的进程会抢占低优先级的进程的cpu资源,还可以出让cpu资源

4.进程切换

  • 如果进程A正在被运行,CPU内的寄存器里面,一定保存的是A的临时数据,这些临时数据叫做进程A的上下文
  • CPU的寄存器只有一份,但是上下文可以有多份,分别对应不同的进程;
  • 当进程A暂时被切换下来的时候,需要进程A顺便带走自己的上下文,带走暂时保存是为了下次回来的时候能够恢复上次的运行状态,继续按照之前的逻辑向前运行,就如同没有中断过一样;

二、环境变量

1.概念

  • 环境变量:一般是指在操作系统中用来指定操作系统运行环境的一些参数,通常在系统中具有全局特性;

  • 系统中的bash命令也是存在文件中的,为什么执行bash命令时不用带路径,而执行我们自己的可执行程序时需要带路径:
    这是因为bash命令所在的路径在环境变量路径的搜索当中,而自己的程序不在;

  • 执行自己的程序不带路径的方法:
    (1)将自己的程序拷贝到系统命令文件中(不推荐);
    (2)将程序的路径复制到环境变量维护的路径中;
    在这里插入图片描述
    在这里插入图片描述
    加入环境变量后,自己的程序就不用带路径了;

2.常见的环境变量

  • PATH:指定命令的搜索路径;

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

  • SEHLL:当前shell,它的值通常是/bin/bash;

  • 查看环境变量的方法:
    echo $NAME //NAME:你的环境变量名称

3.环境变量相关命令

  • echo:显示某个环境变量值;
echo $NAME  //NAME:你的环境变量名称
  • export:设置一个新的环境变量;
set PATH=$PATH:/home/lmx/linux/lesson0729_process/myproc  //冒号后跟想要设置的路径
  • env:显示所有环境变量;
    -

  • unset:清除环境变量;

  • set:显示本地定义的shell变量和环境变量;

4.通过代码获取环境变量

  • 方法一:通过命令行第三个参数获取
    main函数可以有三个参数:
    在这里插入图片描述
    其中env是环境变量参数,是char*类型的指针数组,是每个进程在启动时,启动该进程的进程传递给main函数的环境变量信息;
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
  • 方法二:通过第三方变量environ获取
    在这里插入图片描述
  • 方法三:调用getenv函数(常用)
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

5.环境变量的全局属性

  • 子进程的环境变量是继承父进程的
    export:向当前bash中导出环境变量
    在这里插入图片描述
    这个环境变量属于bash的上下文
    通过程序拿到"class_105"的环境变量:
    在这里插入图片描述
    在这里插入图片描述
    由于该进程的父进程是bash,环境变量也是继承于bash,因此也可以拿到class_105的环境变量;
    因此子进程的环境变量都是从父进程继承来的,环境变量具有全局属性;

6.定义普通变量

  • 定义bash中的普通局部变量
    在这里插入图片描述
    我们在命令行将xo定义为12358,可以通过echo打印出来,但是无法通过env查到,因为xo并没有定义为环境变量,而只是bash中的普通局部变量;
    在这里插入图片描述
    也可以通过set打印出来;
    在这里插入图片描述
    这里的cnt也是局部变量;

7.命令行参数

main函数的前两个参数argc和argv是系统给程序传的命令行参数,argc是参数个数,argv是参数列表
在这里插入图片描述
在这里插入图片描述
可以通过这两个参数得到命令后面的参数个数和类型;

  • 命令行参数的应用
    在这里插入图片描述
    在这里插入图片描述
    命令行参数最大的意义在于可以让同一个程序通过选项的方式选择使用同一个程序的不同子功能,-a -l这些命令都是通过命令行参数实现的;
    当我们在命令行做命令调用时,命令行参数是由父进程bash先拿到,再给子进程;

三、进程地址空间

1.验证进程地址空间

#include<stdio.h>                                                               
#include <unistd.h>

int g_val = 10;

int main()
{
    pid_t id = fork();
    if(id == 0)
    {
        //child
        int cnt = 0;
        while(1)
        {
            printf("I am child, pid: %d, ppid: %d, g_val: %d, &g_val: %p\n", getpid(), getppid(), g_val, &g_val);
            sleep(1);
            cnt++;
            if(cnt == 5)
            {
                g_val = 200;
                printf("child change g_val 100 -> 200 success\n");
            }
        }
    }
    else 
    {
        //father
        while(1)
        { 
            printf("I am father, pid: %d, ppid: %d, g_val: %d, &g_val: %p\n", getpid(), getppid(), g_val, &g_val);
            sleep(1);    
        }    
    }    
    
    return 0;    
}    

上面这段代码在Linux系统下运行结果为:
在这里插入图片描述

  • 从结果中我们可以看出:在父子进程中,g_val的值和地址一开始都是相等的,在子进程改变了 g_val的值之后,父子进程中g_val的值不相等了,但是他们的地址还是一样的;
  • 父子进程即便定义全局变量,依然不会是同一个变量,且父子进程的变量地址一样,但是值不一样;
  • 这里的地址不是物理内存的地址,几乎所有的语言,如果有地址的概念,几乎都不是物理地址,而是虚拟地址;
    在这里插入图片描述
    上图是之前学过的程序的空间分布图,下面用代码来验证一下:
#include <stdio.h>    
#include <stdlib.h>    
    
int g_unval;//未初始化全局变量    
int g_val = 100;//已初始化全局变量    
    
int main(int argc, char* argv[], char* env[])    
{    
    printf("code addr: %p\n", main);//打印main函数的地址 - 代码区    
    printf("init global addr: %p\n", &g_val);//已初始化数据区    
    printf("uninit global addr: %p\n", &g_unval);//未初始化数据区    
    
    char* heap_mem = (char*)malloc(10);    
    printf("heap addr: %p\n", heap_mem);//堆区地址    
    printf("stack addr: %p\n", &heap_mem);//栈区地址    
    
    int i = 0;    
    
    for(i = 0; i < argc; i++)    
    {    
        printf("argv[%d]: %p\n", i, argv[i]);    
    }    
    
    for(i = 0; env[i]; i++)                                                     
    {    
        printf("env[%d]: %p\n", i, env[i]);    
    }    
    
    return 0;    
} 

该代码在Linux系统下的运行结果为:
在这里插入图片描述
可以看出,验证结果满足进程地址空间的分布;

  • 注:
    在这里插入图片描述
    堆区向高地址增长,栈区向低地址增长,堆栈相对而生;

  • static修饰变量,变量被开辟到了全局数据区,本质是将局部变量转化为全局变量;

  • 字符常量
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    字符常量和代码是在同一个区的,因为正文代码区上面有一个小分区是字符常量区,属于正文代码区,都是只读的;

  • 在32位系统下,一个进程的地址空间,取值范围是0x00000000 ~ 0xFFFFFFFF,共4GB内存,其中[0, 3GB]是用户空间,[3GB, 4GB]是内核空间;

  • 以上结论,默认只在Linux系统下有效

2.计算机访问地址的历史

  • 计算机刚开始是直接使用物理地址来进行内存管理的,因为内存本身是能够随时被读写的,但是直接对物理内存进行操作,如果遇到了野指针,可能会导致非法访问,进而导致内存中的代码或数据被改写,因此直接访问物理内存是不安全的;

  • 现代计算机的内存管理是使用虚拟地址的方式,具体形式为:进程直接访问的的地址空间是一段虚拟地址空间,所有的进程的地址都是0x00000000 ~ 0xFFFFFFFF,通过虚拟地址来访问物理地址,中间需要一个映射关系,通过这个映射关系就能将虚拟地址转换为相应的物理地址,进而能够访问物理内存;对于野指针,映射关系对于非法地址是禁止映射的,很好的解决了野指针非法访问物理内存的问题;

3.对进程地址空间的理解

(1)进程地址空间实际上是一种内核数据结构

  • 在这种数据结构中完成对进程内各个区域的划分,所谓的区域划分,本质是在一个范围里定义出start和end;
    在这里插入图片描述
  • 进程地址空间在Linux系统中的结构名为mm_struct,每个进程的PCB里面都有一个指针指向自己的mm_struct;
  • mm_struct里面包含了对进程的各个区域的划分,而这些区域的起始地址都是虚拟地址,存储虚拟地址和物理地址之间的映射关系的结构叫做页表
  • 地址空间和页表是每个进程都私有一份的,只要保证每一个进程的页表,映射的是物理内存的不同区域,就能做到进程之间不会互相干扰,保证进程的独立性

在这里插入图片描述

(2)父子进程的创建
在这里插入图片描述

  • 子进程的创建以父进程为模板,各种变量的创建以及地址的划分都是按照模板来,所以两者的虚拟地址是一样的,当父进程或子进程做修改时,就会发生写时拷贝
  • 当我们修改子进程数据时,操作系统会将需要修改的、被父进程也共享的区域为子进程在内存中拷贝一份,然后再修改子进程虚拟地址到物理地址之间的映射关系,因此子进程修改只是修改自己的地址空间对应的内存;

(3)fork函数执行后为什么会有两个返回值

  • 因为fork函数内部实现在return之前就已经创建好了子进程,在return是就是父子进程一块运行了,return的本质就是对id进行写入,return语句被父子进程都执行了,发生了写时拷贝,所以父子进程各自其实在物理内存中已经有了属于自己的变量空间,也就是各自在内存中都管理着一个id变量,只不过在用户层用同一个变量(虚拟地址)来标识了;

4.进程地址空间的扩展内容

(1)当我们的程序编译完、形成可执行程序,但是还没有被加载到内存中时,我们的程序内部有地址吗?

  • 是有地址的,可执行程序在编译的时候,内部已经有地址了;
  • 地址空间不仅是操作系统内部要遵守的,其实编译器也是要遵守的;即编译器在编译代码的时候,就已经给我们形成了进程的各个区域,代码区、数据区…,并且采用和Linux内核中一样的编址方式,给每一个变量,每一行代码都进行了编址,所以程序在编译的时候,每一个字段在已经具有了一个地址;
  • 程序内部的地址,依旧用的是编译器编译好的虚拟地址;当程序加载到内存的时候,每行代码、每个变量便有了一个物理地址,进程地址空间就可以知道每个数据段的大小了;
  • mm_struct存储的是每个数据段的起始地址和终止地址,存的是虚拟地址,放在页表左侧,页表右侧是其对应的物理地址,CPU通过页表可以读到指令;
  • CPU读到的指令内部也有地址,这个地址是虚拟地址,CPU拿到虚拟地址后,再通过页表找到物理地址进行操作;
    例如CPU读到了func函数的跳转地址,这个地址是虚拟地址,CPU通过页表找到该虚拟地址对应的物理地址,然后读取func函数的代码;

(2)创建进程时,一定是操作系统先为该进程创建PCB,然后为该进程创建对应的地址空间mm_struct,并在PCB内用指针指向对应的地址空间对象,地址空间内部可以帮我们找到页表和操作系统对应的逻辑,帮我们自动进行虚拟地址到物理地址之间的转化;

(3)为什么要有进程地址空间?

  • 凡是非法的访问或者映射,OS都会识别到,并且终止该进程,所有的进程崩溃,就是进程退出;虚拟地址有效的防止了用户对内存的非法访问,有效的保护了物理内存
    因为地址空间和页表是OS创建并维护的,意味着想要使用地址空间和页表进行映射,也一定在OS的监管之下来进行访问,这样便保护了物理内存中所有的合法数据,包括各个进程,以及内核的相关有效数据;
    如果一个进程去修改常量字符串,那就是非法访问代码区,OS会杀掉该进程;
    在这里插入图片描述

  • 因为有地址空间的存在,有页表的映射的存在,在物理内存中,我们可以对未来的数据进行任意位置的加载,物理内存的分配进程的管理可以做到没有关系;内存管理模块进程管理模块就完成了解耦合
    我们在语言层面上申请空间(malloc、new),本质是在虚拟地址空间上申请,计算机会使用延迟分配的策略来提高整机的效率:上层申请空间是在地址空间上申请的,物理内存不会分配空间,在用户真正对物理地址空间进行访问的时候,才执行相关的管理算法,进行申请内存、构建页表映射关系等,然后再让用户进行内存的访问;这一步是由操作系统自动完成的,用户,包括进程都是完全没有感知的;

  • 在物理内存中理论上可以在任意位置加载,但是并不意味着物理内存中的数据和代码就是乱序的,因为有页表的存在,它可以将地址空间上的虚拟地址和物理地址进行映射,在进程的视角,所有的内存分布都是有序的;
    地址空间 + 页表可以将内存分布有序化 ,同样也可以实现进程的独立性因为有地址空间的存在,每一个进程都认为自己拥有4GB空间(32位),并且各个区域都是有序的,进而可以通过页表映射到不同区域,来实现进程的独立性
    每一个进程不知道,也不需要知道其他进程的存在;

  • 进程 = task_struct + mm_struct + 页表 + 分配的内存

(4)进程的挂起

  • 加载的本质就是创建进程,那么是不是非得把进程所需的所有代码和数据立马加载到内存中,并创建内核数据结构,建立映射关系?
    不是的,在最极端的情况下,甚至只有内核数据结构被创建出来了,这就叫做进程的新建状态;理论上,OS可以实现对程序的分批加载和换出,甚至,某个进程短时间内不会再被执行了,比如进入了阻塞状态,那么OS就会将该进程的数据和代码换出,清理内存空间,等到该进程需要执行的时候,再将数据和代码换入,这个状态就叫做挂起

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

JDK相关知识

查看是否安装了jdk java -version 将输出当前系统上默认 Java 环境的版本信息&#xff0c;其中包括 JDK 的位数&#xff08;64 位或 32 位&#xff09;。如果输出中包括 “64-Bit” 或 “x64” &#xff0c;则表示你的 JDK 是 64 位的&#xff1b;否则&#xff0c;它就是 32 位…

PackML 学习笔记

PackML 的官网 上如此描述PackML--- PackML是由OMAC开发并由ISA采用的自动化标准TR88.00.02&#xff0c;可以更轻松地传输和检索一致的机器数据。PackML的主要目标是鼓励整个工厂车间的通用“外观和感觉”&#xff0c;并支持和鼓励行业创新。 优点 这里的重点是通用“外观和感…

MySQL树状结构表查询通解

文章目录 前言一、数据准备二、代码实现三、案例使用1. 建立数据表实体类2. mapper文件3. 使用 四、总结 前言 ​ 最近做了一个中医药方面的项目&#xff0c;该项目分为游戏端和服务端。笔者负责的是服务端的开发。在服务端的业务中包含两部分&#xff1a;系统信息管理模块、游…

这是我见过对redis最直白的讲解,看完我哭了......

我是Redis 你好&#xff0c;我是Redis&#xff0c;一个叫Antirez的男人把我带到了这个世界上。 说起我的诞生&#xff0c;跟关系数据库MySQL还挺有渊源的。 在我还没来到这个世界上的时候&#xff0c;MySQL过的很辛苦&#xff0c;互联网发展的越来越快&#xff0c;它容纳的数…

0801向量及其线性运算-向量代数与空间解析几何

1 向量的概念 既有大小又有方向的量叫做向量&#xff08;或矢量&#xff09;。 表示 用有向线段表示向量&#xff1a; 向量大小&#xff1a;有向线段的长度向量方向&#xff1a;有向线段的方向 示例如下图1-1所示&#xff1a; 记法&#xff1a;字母上面加箭头 单字母&#x…

9.前后端的身份认证

1 web开发模式 开发模式有两种&#xff0c;一种是服务端直接返回html字符串&#xff0c;另一种是前后端分类 服务端直接返回html字符串是这样的 这样做的优缺点如下 优点 加载页面快&#xff0c;这样会让设备的能耗减小&#xff0c;对于移动端来讲更省电利于搜索引擎获取数…

北京筑龙吴英礼:ChatGPT在采购与招标中的应用

近日&#xff0c;由中国招标投标协会举办的“人工智能对招标采购数字化发展的机遇与挑战交流座谈会”在北京召开。北京筑龙CTO吴英礼受邀出席&#xff0c;围绕"ChatGPT在招标投标中的应用"进行探讨&#xff0c;从ChatGPT背后的的技术、ChatGPT助力招标投标行业数字化…

【C++】-8- string〔常见接口函数使用〕

文章目录 概览标准库中的string类「string类&#xff08;了解&#xff09;」﹝编码﹞ 「string类的常用函数接口」﹝1.string类对象的常见构造﹞﹝2.string类对象的修改操作﹞‹ c_str ›‹ npos ›‹ string结构 › ﹝3.string类对象的容量操作﹞‹ clear ›‹ reserve ›‹ r…

Pulsar 负载均衡与transaction_coordinator_assign

背景与现状 TC加载到哪个broker上取决于transaction_coordinator_assign-partition-${TC ID}分区加载到哪个broker上。 默认transaction_coordinator_assign有16个分区&#xff0c;因此默认有16个TC&#xff0c;我们需要根据集群机器/broker数目来设置合理的TC个数。 为了保证…

如何使用递归 Grep 命令在目录中搜索?

在 Linux 系统中&#xff0c;grep 是一个强大的文本搜索工具&#xff0c;可以用于在文件中查找指定的文本模式。当需要在目录及其子目录中搜索特定的文本时&#xff0c;可以使用递归 grep 命令来快速定位目标文件。本文将详细介绍如何使用递归 grep 命令来搜索目录中的文件。 递…

如何搭建第一个SpringBoot+MyBatis项目

&#x1f648;作者简介&#xff1a;练习时长两年半的Java up主 &#x1f649;个人主页&#xff1a;程序员老茶 &#x1f64a; ps:点赞&#x1f44d;是免费的&#xff0c;却可以让写博客的作者开兴好久好久&#x1f60e; &#x1f4da;系列专栏&#xff1a;Java全栈&#xff0c;…

ChatGPT已能模仿任何写作风格,让你的自媒体快速起号

我认识的一两个技术大佬目前失业在家&#xff0c;压力不小。对于现在的就业市场来说&#xff0c;再找工作&#xff0c;高不成低不就。他们的薪资&#xff0c;一般企业无法承受&#xff0c;大厂岗位又在缩减。今年真正感受到了寒冬。 对于我们还有饭吃的程序员&#xff0c;现在不…

【Linux网络服务】Apache网页优化

Apache网页优化 一、网页压缩1.1网页压缩步骤 二、网页缓存三、隐藏版本信息五、Apache防盗链 一、网页压缩 在企业中&#xff0c;部署Apache后只采用默认的配置参数&#xff0c;会引发网站很多问题&#xff0c;换言之默认配置是针对以前较低的服务器配置的&#xff0c;以前的…

如何通过自学成为一名白帽黑客(网安工程师)

从事网络安全这么多年&#xff0c;总是会被问到很多奇奇怪怪的问题&#xff1a; 「叔叔&#xff0c;我Steam账号被盗了&#xff0c;能帮忙找回吗&#xff1f;我给你发红包」 「我的手机被监控了&#xff0c;生活和工作受到了严重影响&#xff0c;该怎么解决&#xff1f;」 「…

学会这两件事,让你在人生路上走得更远

人生&#xff0c;就是一场不断前行&#xff0c;没有退路的旅行&#xff0c;也是一场不断醒悟的过程。 看透&#xff0c;然后醒悟&#xff1b;放下&#xff0c;然后幸福。 有些事&#xff0c;看淡就好&#xff1b;有些人&#xff0c;看穿就行。 不管世事如何艰难&#xff0c;只要…

Compose也能开发iOS了,快来体验~

前言 在之前&#xff0c;我们已经体验了Compose for Desktop 与 Compose for Web&#xff0c;目前Compose for iOS 已经有尚未开放的实验性API&#xff0c;乐观估计今年年底将会发布Compose for iOS。同时Kotlin也表示将在2023年发布KMM的稳定版本。 届时Compose-jb KMM 将实…

腾讯云4核8G服务器12M带宽支持多少人访问?

腾讯云轻量4核8G12M服务器配置446元一年&#xff0c;518元12个月&#xff0c;腾讯云轻量应用服务器具有100%CPU性能&#xff0c;系统盘为180GB SSD盘&#xff0c;12M带宽下载速度1536KB/秒&#xff0c;月流量2000GB&#xff0c;折合每天66.6GB流量&#xff0c;超出月流量包的流…

解锁接口关联测试新技能!HttpRunner教你如何轻松搞定。

目录 前言&#xff1a; 一、安装HttpRunner 二、编写测试用例 三、运行测试用例 四、实现接口关联测试 五、总结 前言&#xff1a; 在接口自动化测试中&#xff0c;一个常见的场景就是需要对多个接口进行关联测试&#xff0c;例如登录后获取token&#xff0c;再利用token…

如何自学黑客?零基础自学黑客需要多久?

问题一&#xff1a;黑客如何学起&#xff1f; 必须从学习者的角度来看&#xff0c;如果你是一个已经学过编程&#xff0c;通晓几门语言的人那么这个答案就会和一个从没有接触过的计算机&#xff0c;甚至连什么叫高级语言还不知道的人有所区别的对待。 这就像是登珠穆朗玛峰一…

ARM实验5-流水灯仿真实验

一、实验名称&#xff1a;流水灯仿真实验 二、实验目的&#xff1a; 掌握ARM处理器的输入输出接口。掌握通过MDK提供的仿真功能&#xff0c;实现系统的仿真运行。通过该编程实验&#xff0c;进一步巩固和强化学生ARM汇编编程的能&#xff0c;ARM应用程序框架&#xff0c;培养…