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

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.dtcms.com/a/274351.html

相关文章:

  • 掌握Spring声明式事务传播机制:AOP与ThreadLocal的协同工作
  • 破解异构日志清洗五大难题,全面提升运维数据可观测性
  • 用FunctionCall实现文件解析(一):环境准备与基础知识
  • uniapp语音播报天气预报微信小程序
  • 秒杀系统该怎么设计?
  • uniapp-在windows上IOS真机运行(含开发证书申请流程)
  • 在Spring Boot 开发中 Bean 的声明和依赖注入最佳的组合方式是什么?
  • Spring Boot集成Redis:从配置到实战的完整指南
  • Adobe Acrobat DC JavaScript 基础到应用
  • python的卷烟营销数据统计分析系统
  • 重学前端003 --- CSS 颜色
  • 汽车级MCU选型新方向:eVTOL垂桨控制监控芯片的替代选型技术分析
  • 实现在线预览pdf功能,后台下载PDF
  • PDF 转图助手 PDF2JPG 绿色版:免安装直接用,急处理文件的救急小天使
  • 电力分析仪的“双语对话”:CCLinkIE与Modbus TCP的无缝连接
  • 【Unity游戏存档系统】
  • 爬虫练习1
  • 【环境配置】KAG - Windows 安装部署
  • 7.11文件和异常
  • kafka kraft模式升级metadata.version
  • JVM--监控和故障处理工具
  • Oracle 高可用性与安全性
  • SpringCloud【OpenFeign】
  • 数据治理(管理)能力评估——解读2024数据治理与数据管理能力成熟度评估模型【附全文阅读】
  • 10款主流报销管理平台对比及推荐
  • Linux操作系统之进程间通信:命名管道
  • Linux编程练习题1:打印图形
  • python学习DataFrame数据结构
  • 制作一款打飞机游戏79:道具拾取系统
  • c++设计模式:简单工厂模式