进程地址空间
前言
Vue框架:
Vue驾校-从项目学Vue-1
算法系列博客友链:
神机百炼
进程优先级
相关概念:
-
优先级含义:
CPU资源分配的先后顺序
-
优先级作用:
可以将指定进程运行到指定的CPU上(上下文切换次数少了),以改善多任务环境的linux的系统性能
-
进程竞争性:
系统进程数目众多,CPU计算资源有限
-
进程独立性:
一个进程在运行时,独享各种资源(网络带宽/CPU算力),其他进程不干扰
考虑进程独立性,父子进程有时共享程序和数据,有时子进程需另开辟程序和数据,下面的进程地址空间将会展开说
-
进程切换:
当进程数目太多时,CPU切换进程的成本没有增加
但是在应用层看来,会有卡顿的感觉
-
进程切换的时机:
- 多数情况:PCB的时间片到期
- 少数情况:更高优先级的进程到来
查看优先级:
查询指令:
-
查看PCB信息指令:
ps -l //粗略几个进程的信息,如bash + ps ps -la //详细所有进程的信息
-
查看PCB的优先级:
PRI&NI:
-
PRI:当前优先级,默认为80
值越小则越先执行,范围为[60 , 99]
-
NI:Nice值,对优先级的修正数值,默认为0
有正有负:从-20 ~ 19,共40级,输入-100自动-20,输入100自动19
调整优先级其实是在调整NI值,且每次都是在80的基础上调节
调整优先级:
法一:top调整
-
linux的任务管理器:
top //查看所有进程 top 输入r 输入pid 输入nice值 //调整指定进程的优先级
-
top:查看进程管理器
-
top + r + pid + nice:修改进程优先级
-
尝试改动nice为-20后,再查看PCB信息:
法二:renice
-
renice 命令:
renice -n 进程pid 新的nice值
环境变量
- 引导问题:
为什么我们写的程序要运行需要加./
而Linux自带的命令程序要运行不需要加./?
./mytest.c
//运行我们自己写的程序要带./才能找到
ls
//运行系统指令不需要带./就能找到
llll
//报错:command not found,说明命令行也是经过查找才找到的
-
命令本身也是程序,存储在环境变量中
所有可执行程序在运行前都在环境变量中查询源程序
环境变量中查询不到,再根据给定的路径寻找源程序
相关概念:
-
环境变量的含义:
操作系统启动后,某些内容没有启动,需要后续启动
后续启动前要先找到这些程序,就需要设定一些变量记录这些程序的路径
-
组成:
变量名 + 变量内容(与操作系统环境有关)
-
全局性:
环境变量就是系统级别的全局变量
-
举例:
编译c / c++代码时,链接时我们不知道所链接的动态库和静态库在哪个文件夹下,但是仍然可以找到动静态库,并且编译成功
常见的环境变量:
PATH:
- 含义:常用的文件夹
- 查看PATH:
echo $PATH //查看PATH内容,其实是:隔开的很多路径
/usr/local/jdk8/bin:
/usr/local/jsk8/jre/bin:
/usr/local/bin:
/usr/bin:
/usr/local/sbin:
/usr/sbin:
/home/whb/.local/bin:
/home/whb/bin
- ls命令程序的存储位置:在PATH内
HOME:
- 含义:当前用户所处的工作目录
SHELL:
-
我们并不与Linux内核直接交互,而是通过shell:
输入的命令通过shell命令解释器之后交给kel
-
命令解释器也是一个程序,有很多版本,SHELL变量记录我们采用的版本
-
查看SHELL:
环境变量相关命令:
查看所有环境变量:
- env:
env
查看所有变量:
- Linux下变量包括环境变量 + 本地变量
set //查看环境变量 + 本地变量
查看单一环境变量:
- echo + 环境变量名:
echo $PATH
添加环境变量:
- 法一:复制粘贴
sudo cp -f 文件 PATH中某一路径 //删除刚才向PATH添加的myproc
- 法二:export
export PATH=$PATH:新路径 //$PATH为保持原有路径
删除PATH中文件:
- rm删除
sudo rm /usr/bin/文件名 //删除刚才向PATH添加的文件
-
关机重启:
云服务器的xshell每次重新连接,PATH内容自动复原
最好不要随意向PATH等全局变量中添加自己的程序,容易污染命令池
增删本地变量:
-
本地变量的作用域:
只在本进程内有效,command line内输入后,作用域也就是bash进程内
MYVAL = 100 //添加本地变量 env | grep MYVAL //在环境变量中查询为空 set | grep MYVAL //在所有变量中可以查到100 export MYVAL = 100 //将本地变量导入环境变量 export MYVAL //本地变量也是可以直接使用的,直接一个名字就可以导入环境变量 unset MYVAL //从本地变量中删除MYVAL
显示器终端文件:
- dev/pts:代表当前显示器开的所有窗口
- 向此云服务器其他用户窗口发送信息:
//先查看其他用户窗口号:
who
//再登录root权限
su -
//再给其他用户发送消息
echo "消息" > /dev/pts/终端号
main()参数的环境变量:
-
C/CPP代码中的main()函数也可以接收参数:
agrv 和 envp都是指针数组,最后一个元素都是NULL
-
OS调用加载器 ,加载器调用mainCRTStartup()函数,mainCRTStartup()函数调用main()函数
操作系统一方面通过argv & envp为main()函数提供信息
另一方面通过僵尸进程接收main()函数运行完后return 的 0
argc & argv:
-
含义:
- int argc:控制台输入的命令行参数个数
- char* argv[]:控制台输入的命令行参数
-
举例查看argc & argv:
- 代码:
- 输入输出:
- 代码:
-
内存调用:
-
argv的应用:
- 代码:
- 输出:
-
举例:
ls -a -l //此处的ls是命令,a & l就是命令行参数
envp:
-
含义:程序运行时依赖的环境变量
-
来源:
- argc & argv的输入来源于OS自带的内容 / 控制台命令行输入
- envp的输入来源于OS自带的所有环境变量
-
内存调用:
-
查看envp内容:
- 代码:envp[]最后也是一个NULL
- 输出:其实就是PATH等等环境变量的内容
- 代码:envp[]最后也是一个NULL
系统调用接口获取环境变量:
-
函数:
#include <stdlib.h> char* getenv(const char *name); //通过名称获取环境变量 char* secure_getenv(const char *namr); //通过名称获取环境变量
-
实例:
-
输出:
进程地址空间
- 研究背景:
kernel 2.6.32
32位平台
进程地址空间布局:
-
每个进程创建后,真实内存都给予他一个虚拟内存空间,便于进程创建变量/保存数据/进行计算
-
这块固定大小的虚拟内存空间叫做进程地址空间,其组成布局如下:
-
linux中查看进程地址空间布局:
- 代码:
- 输出:从正文代码地址到栈变量地址到环境变量地址,地址应该是从低到高的
- 代码:
虚拟地址:
- 为了便于进程内部支配变量和数据,进程中所有地址都是虚拟地址,不是内存条上的真实地址
- &变量:取出的是虚拟地址
int a = 10;
printf("%p", &a); //&a是变量a在该进程中的地址,不是在内存条中的
- 页表:记录虚拟地址到真实内存条地址的映射关系
父子进程地址空间:
-
不同进程之间为了保证运行的独立性,一般来说各自的虚拟进程地址空间布局使用情况不同
但是父子进程之间,子进程会直接使用父进程的代码和数据
为了保证父子进程之间仍保持独立性,当子进程对父进程的代码或者数据做出更改后
该数据或者程序会被单独拷贝出来一份供子进程使用,同时子进程的虚拟地址在页表中对应的实际地址发生改变
-
图解父子进程的虚拟地址:
-
为什么子进程不直接拷贝父进程的所有程序和数据?
当子进程只读不写时,还要为子进程单独创立一套数据就属于浪费空间
-
举例说明父子进程存在数据不同时,同一虚拟地址对应页表中真实地址不同:
- 代码:
- 输出:
- 解释:
-
父子进程一开始共享父进程的程序和数据,包括g_val变量
也就是父子进程此时页表的虚拟地址和真实地址对应关系同
-
子进程修改父进程程序中g_val数据后
内存中为子进程新建一个g_val变量,值为200
同时修改子进程页表,将原本g_val虚拟地址对应的真实地址改为新建的变量地址
-
最终父子进程中对于g_val的虚拟地址相同
但是页表中映射的真实地址不同
-
printf(“%p\n”, &g_val);取出来的是进程的虚拟地址
-
- 代码: