进程概念完了吗?不,还没有!今天博主就来和大家接着学习后半部分的概念~
今天博主主要讲优先级、环境变量、进程地址空间。尤其是进程地址空间的知识,这是我们学习操作系统由量变到质变的一个重要的小转折!
目录
进程优先级
基本概念
为什么要有优先级?
查看系统进程
在linux或者unix系统中,用ps –l或ps -al命令来查看进程优先级:
[cyq@VM-0-7-centos test]$ ps -al
我们来观察一下:
我们发现会出现几个重要的名词信息:
PRI and NI
PRI vs NI
有了上面概念理解,我们有几个问题:
为什么要有NI值,我们,要想修改PRI直接去修改PRI不就好了吗?为啥还要通过修改NI值来间接修改优先级呢?
nice值为什么是一个相对比较小的范围呢?
用top命令更改已存在进程的nice
我们先写一个死循环的C程序(这样可以得到它的pid来修改nice值):
int main()
{
while(1);
return 0;
}
我们运行程序后,先获取对应进程的pid:
接着我们输入指令top,打开任务管理器:
然后我们再按r(不要按回车):
这时候出现了一个让我们输入进程pid的命令行,我们把对应pid输入进去:
输入对应进程pid按下回车键之后,会出现让我们输入nice值的命令行:
[cyq@VM-0-7-centos test]$ ps -al
注意
其他补充概念
环境变量
基本概念
常见的环境变量
我们先思考一个问题:为什么我们在运行自己的可执行程序时要加上./来指定路径,而执行系统的命令不需要加./呢?
实际上这和环境变量(PATH)有关。
PATH
我们来查看一下,系统中默认的搜索路径:
[cyq@VM-0-7-centos test]$ echo $PATH //注意,这里一定要加$
那我们怎么去把我们自己的可执行程序加到环境变量中?
方法一:把自己的可执行程序路径拷贝到系统文件中
方法二:使用export设置一个新的环境变量,仅在本次登录有效。
步骤一:我们先使用pwd指令把当前工作目录的路径显示出来
步骤二:我们使用export命令把当前工作目录路径拷贝进去
[cyq@VM-0-7-centos test]$ export PATH=$PATH:/home/cyq/practice/test
这时候我们可以查看我们是否导入成功了:
这时候我们就可以不加./来运行一下该目录下的可执行文件:
注意:
注意步骤二:export PATH=$PATH:hello程序所在路径,其中$一定要加,否则这时候环境变量就乱了,我们举个栗子:
HOME
我们来举个栗子:
[cyq@VM-0-7-centos test]$ echo $HOME
比如我们换几个用户身份来观察一下:
SHELL
我们来查看一下:
[cyq@VM-0-7-centos test]$ echo $SHELL
和环境变量相关的命令
上面的命令博主就不演示了,但是老铁们要下去试试来感受一下哦~
环境变量的组织方式
通过代码获取环境变量
命令行第三个参数
我们介绍第三个参数前,也顺便先介绍前两个参数。
我们用代码来演示一下:
int main(int argc, int* argv[])
{
for(int i = 0; i < argc; i++)
{
printf("argv[%d]-> %s\n", i ,argv[i]);
}
return 0;
}
运行一下:
好了,介绍完前两个参数后,我们继续。
代码:
int main(int argc, int* argv[], int* env[])
{
for(int i = 0; env[i]; i++) //env为NULL就结束条件
{
printf("%s\n",env[i]);
}
return 0;
}
运行一下(部分截图):
通过第三方变量environ获取
我们在上面的环境变量的组织方式中可以发现environ指向指针数组的第一个指针。我们通过man手册来了解一下:
[cyq@VM-0-7-centos test]$ man environ
代码演示:
int main()
{
extern char** environ;
for(int i = 0; environ[i]; i++)
{
printf("%s\n", environ[i]);
}
return 0;
}
运行结果(部分截图):
通过系统调用获取或设置环境变量
我们通过man手册来查看一下getenv:
举个栗子:
int main()
{
printf("%s\n",getenv("PATH"));
printf("%s\n",getenv("HOME"));
return 0;
}
运行结果:
环境变量通常是具有全局属性的
补充知识,先做个小实验:
[cyq@VM-0-7-centos test]$ env_string=wmm
[cyq@VM-0-7-centos test]$ echo $env_string
wmm
我们先写一个不fork的程序,看看它的父进程是谁:
代码测试:
int main()
{
printf("i am process pid: %d ppid: %d\n", getpid(),getppid());
return 0;
}
测试结果:
假设一个场景,证明环境变量具有全局属性:我们写一个代码,打印环境变量(我们手动导入的)中的一个字符串。对比一下把这个字符串导入到env前后,程序的运行结果。
测试代码:
int main()
{
printf("my_env_string: %s\n", getenv("my_env_string"));
return 0;
}
我们先在本地变量设置一个字符串:
[cyq@VM-0-7-centos test]$ my_env_string="wmmandcyq"
这时候我们发现my_env_string字符串在本地变量中,环境变量中还没有,这时候,我们运行程序,看一下打印my_env_string的情况:
当我们把my_env_string导入环境变量后再来打印:
总结
补充
了解一下即可:
1、系统的环境变量在~/.bash_profile 系统默认执行的环境变量。
[cyq@VM-0-7-centos test]$ vim ~/.bash_profile
2、我们发现上面的环境变量会调用另一个文件,我们来看一下~/.bashrc下的内容
[cyq@VM-0-7-centos test]$ vim ~/.bashrc
3、/etc/bashrc配置文件,获取各种环境变量信息
[cyq@VM-0-7-centos test]$ vim /etc/bashrc
部分截图:
进程地址空间
一个重要现象
在这里博主讲的内容是32位平台的~
我们先来回顾一下曾经学习的空间分布图:
我们先抛出一个问题,我们曾经写的代码定义的变量打印的地址是物理地址吗??
先不多说,来段代码演示一个现象:
int g_val = 100;
int main()
{
pid_t fd = fork();
if(fd == 0)
{
//child
int count = 5;
while(count)
{
printf("i am child, &g_val = %p, g_val = %d\n", &g_val, g_val);
sleep(1);
count--;
if(count == 2)
{
printf("##########child 修改ing##############\n");
g_val = 200;
printf("##########child 修改done#############\n");
}
}
}
else if(fd > 0)
{
//parent
while(1)
{
printf("i am parent, &g_val = %p, g_val = %d\n", &g_val, g_val);
sleep(1);
}
}
return 0;
}
我们看一下运行结果:
那么,我们打印的地址是什么地址的??答案是虚拟地址!
什么是虚拟地址?
虚拟地址和PCB的关系
struct mm_struct里面描述什么?
进一步理解虚拟地址
我们运行代码,实际上还是要找到物理内存上的实际代码,可是PCB看向的是虚拟地址,那么CPU调度PCB时,怎么能找到对应的物理地址上的代码和数据呢?答案是:页表映射
进程地址空间有什么用?
作用一
老铁们会不会这么想,让PCB直接指向物理内存不就行了吗?为什么还要在中间添加虚拟地址、页表,这样不是好麻烦?
所以有了虚拟地址之后,由有了页表,我们每个段地址通过页表映射时,OS会检查一定的权限,检查该进程做法是否合理。
作用二
作用三
总结
回顾一下
最后我们就能解释最开始的演示现象了,子进程继承了父进程的大部分PCB数据,所以g_val的地址也是一样的,当子进程的g_val值被修改后,在物理内存中发生了写时拷贝!虚拟地址中没有变化,所以最后出现了打印变量的地址一样,但是打印出来变量的值不一样的结果。这个打印的地址就是虚拟地址!
写时拷贝前:
写时拷贝后:
看到这里,支持博主一下吧~