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

Linux 进程管理核心机制

目录

  • Linux 进程管理核心机制
    • 一、命令行参数与环境变量
      • 1.1 命令行参数
      • 1.2 环境变量
      • 1.5 子进程访问机制
        • 直接访问
        • 传参访问
      • 1.6 Bash 如何存储环境变量
      • 1.7总结
    • 二、进程地址空间
      • 2.1 概念
      • 2.2虚拟地址
      • 2.3 地址空间的优点
      • 2.4 硬件协同机制
    • 三、Linux 2.6 进程调度
      • 3.1运行队列架构
      • 位图索引机制
      • 双队列轮转机制

Linux 进程管理核心机制

一、命令行参数与环境变量

1.1 命令行参数

本质:用户传递给程序的选项,用于定制程序功能,命令本质也是可执行程序,命令中携带的选项就是命令行参数

传递者:由父进程(通常是 bash 命令行解释器)传递

作用:通过不同参数组合实现程序功能的动态配置

1.2 环境变量

在linux系统中,存在一些全局的设置,用某些变量保存,就是环境变量。

举例:PATH环境变量,告诉命令行解释器(bash),应该去哪些路径下去寻找可执行程序

他们是如何加载呢?

Linux系统中存在很多的配置,在登录LInux系统的时候,已经被加载到bash进程中(加载到内存),如果不小心删了,可以重新登录来恢复(从磁盘中重新加载),因为这个是内存级别的环境变量。最开始的环境变量不是在内存中,而是在磁盘文件中。

相关操作

env:查看所有环境变量

echo &XXX ;打印环境变量内容

export XXXXXX = xxxxx;导入环境变量,即自己定义环境变量,但是实在内存里(bash进程)中修改,并非配置文件,关机后消失

unset name;取消环境变量

环境变量其他例子

HOME:保存家目录的环境变量

PWD:当前路径,该环境变量是变化的

SHELL:命令行解释器保存路径

HISTSIZE:记录命令的默认数量,

HOSTNAME:主机名

本地变量

没有导入到环境变量里,但是在bash中实际存在,叫做本地变量

本地变量无法被子进程继承

1.5 子进程访问机制

直接访问

父进程的数据,默认可以被子进程看到并且访问

Linux系统中的环境变量是保存在配置文件里的,在磁盘上,机器开机的时候,会启动bash进程,这之后会将包含了环境变量的配置文件加载到bash进程中,我们自己运行程序或者指令程序,本质都是bash进程的子进程,因此都可以看到并且访问环境变量

传参访问

main函数支持传参

int main(int argc, char* argv[], char* env[])

  • argc:命令行参数个数

  • argv[]:命令行参数指针数组

  • env[]:环境变量指针数组(以 NULL 结尾)

1.6 Bash 如何存储环境变量

存储方式:

char *env[];

即一个指针数组,数组中每个元素指向一个字符串,这个字符串就是刚开始加载进来的环境变量。该数组必须以空指针NULL结尾

char *env[] = {"PATH=/usr/bin","HOME=/home/user",/* ... */ NULL  // 结束标志
};

1.7总结

bash进程启动时,默认会生成两张表。

1.argv[],命令行参数表。这张表从命令行获取,即用户输入

2.env[], 环境变量表。这张表从os的配置文件获得

这两张表bash通过各种方式交给子进程

二、进程地址空间

2.1 概念

每个进程都有自己独立的地址空间

地址空间里所用地址都是虚拟地址,由页表进行地址映射,从而找到物理地址

地址空间本质是一个内核数据结构对象

请添加图片描述
地址空间本质是内核的一个struct结构体!内部很多的属性都是表示start,end的范围

在这里插入图片描述

源码中的struct mm_struct就是地址空间的结构体。其中start_code就是代码段的开始,end_code就是代码段的结束,类似的还有数据段开始和结束start_data,end_data等。

2.2虚拟地址

虚拟地址指的是程序地址空间中的地址,相对应的也有物理地址,指的是在内存中实际存放的地址,他们之间由页表进行映射,在访问某一数据时,程序只知道其虚拟地址,操作系统会帮他找到物理地址。创建子进程时,子进程会继承父进程的代码和数据,也包括页表,两个进程刚开始的代码和数据都在同一片空间。

我们看一段代码

#include<stdio.h>
#include<unistd.h>
int main()
{int val = 10;pid_t id = fork();if(id == 0){int cnt = 0;while(1){cnt++;if(cnt == 5){val = 55;printf("val change-> 55;\n");}printf("I am child process, id = %d , pid = %d ,&val = %p,val = %d\n",getpid(),getppid(),&val,val);sleep(1);}}else{while(1){printf("I am parent process, id = %d , pid = %d , &val = %p,val = %d\n",getpid(),getppid(),&val,val);sleep(1);}}return 0;
}

请添加图片描述

对于图片里的结果,父进程和子进程对于一个全局变量val,因为子进程继承了父进程的页表,所以对于某一数据来说,他们的虚拟地址和物理地址都相同。

如果是读取数据,会读取同一片物理空间的数据,不会进行父子进程数据分离

如果要写入数据,为了保持进程运行独立性,操作系统会为子进程中要修改的数据重新开辟物理空间,并且修改子进程的页表,让修改的数据与父进程的数据不在同一物理空间,虚拟地址相同,这种特点叫做写时拷贝

对于代码这一类的只读数据,他们不会被修改,因此共享一块物理空间,不发生写时拷贝,对于要修改的数据再去开空间,这样做即节省空间,又不破坏进程独立性原则。

如果对于父进程的整块地址空间进行拷贝,即浪费时间又浪费空间,写时拷贝是一种按需申请。

2.3 地址空间的优点

1.将无序变成有序,让进程以统一的视角看待物理内存以及自己运行的各个区域

2.进程管理模块和内存管理模块解耦

3.拦截非法请求,对物理内存进行保护

2.4 硬件协同机制

cpu内有相应的寄存器和硬件,来保证进程在访问内存时能快速的将虚拟地址转为物理地址,其中有CR3(存储进程页表指针的寄存器)和MMU(地址管理单元),他们两个配合起来就可以快速进行地址转换。

页表中除了虚拟地址和物理地址的映射关系之外,还存在其他字段,比如是否在内存中的标识位,以及该地址的权限(rwx)。

在父子进程运行中,子进程继承父进程的页表时,操作系统会将可能被修改的数据的权限设为r,在进程修改数据时因为没有w权限而识别到错误,然后进行进一步处理。

操作系统在访问内存时识别到错误:

1.是不是数据不在内存中 ,是的话发生缺页中断

2.是不是数据需要发生写时拷贝,是的话进行缺页访问

3.其他错误进行异常处理


三、Linux 2.6 进程调度

3.1运行队列架构

请添加图片描述

在LInux操作系统中每一个cpu都有一个运行队列

queue中共有140个队列(task_struct * queue[140]),其中前100个我们不使用,只用后40个,对应40个优先级,优先级相同的进程在同一个子队列里。

进程优先级的值默认为80,用nice值修正,nice值的范围是[-20,19],因此进程优先级的范围就是[60,99],加上40就可以映射到queue队列里下标[100,139]的范围。

位图索引机制

long bitmap[5];  // 160位位图(实际使用140位)

对于bitmap中前140位,若某一位为1,则改位置对应下标的运行队列不为空,即有进程。

位图操作

  1. 检查 bitmap[0] 是否为0 → 跳过前32子队列队列,每次检查32位,以此类推

  2. 非零 bitmap[i] → 按位比较,找到对应子队列去执行

    这种遍历方式时间复杂度约为O(1),也称作进程调度里的O(1)算法

双队列轮转机制

       struct proi_array_t{int nr_active;long bitmap[5];task_struct * queue[140];}struct proi_array_t array[2];//2个队列,一个活跃队列,一个过期队列 task_struct *active;    // 活跃队列(只出不进),直接指向活跃队列的queuetask_struct *expired;   // 过期队列(只进不出),直接指向过期队列的queue
  1. 调度器从 active 指向的队列选取进程执行,使用O(1)算法
  2. 新唤醒/新建进程加入 expired指向的队列
  3. 当 active指向的队列空时:swap(active, expired)
http://www.xdnf.cn/news/15238.html

相关文章:

  • 【mac】快捷键使用指南
  • 【AI大模型】RAG系统组件:向量数据库(ChromaDB)
  • 飞算 JavaAI 体验:重塑 Java 开发的智能新范式
  • LeetCode--43.字符串相乘
  • JVM故障处理与类加载全解析
  • ✨ FastMCP 实战进阶:构建可远程访问的 MCP 工具服务与客户端(Python 深度解析)
  • Elasticsearch混合搜索深度解析(上):问题发现与源码探索
  • 【flutter】flutter网易云信令 + im + 声网rtm从0实现通话视频文字聊天的踩坑
  • 影石(insta360)GO3拇指相机格式化后的恢复方法
  • OpenCV 与深度学习:从图像分类到目标检测技术
  • 如何安装和配置Autoptimize插件以提高WordPress网站访问速度
  • 飞算JavaAI:重塑Java开发的“人机协同“新模式
  • 免费应用分发平台的安全漏洞和防护机制是什么?
  • Jenkins 自动触发执行的配置
  • 飞算JavaAI:重构Java开发的“人机协同”新范式
  • JavaScript VMP (Virtual Machine Protection) 分析与调试
  • 创建显示心电图的组件
  • 前端学习4:小白入门注册表单的制作(包括详细思考CSS、JS实现过程)
  • uniapp语音播报天气预报微信小程序
  • 格密码--数学基础--02基变换、幺模矩阵与 Hermite 标准形
  • 从UI设计到数字孪生实战应用:构建智慧金融的风险评估与预警平台
  • 使用 SSH 连接 GitHub
  • 飞算 JavaAI 深度体验:开启 Java 开发智能化新纪元
  • 速学 RocketMQ
  • 基于定制开发开源AI智能名片与S2B2C商城小程序的旅游日志创新应用研究
  • FPGA实现SDI转LVDS视频发送,基于GTX+OSERDES2原语架构,提供2套工程源码和技术支持
  • Maui劝退:用windows直接真机调试iOS,无须和Mac配对
  • leetcode:518. 零钱兑换 II[完全背包]
  • Python 类型注解实战:`Optional` 与安全数据处理的艺术
  • 静态路由综合实验