1.进程优先级
概念:cpu资源分配的先后顺序,就是指进程的优先权(priority)
在linux系统中,用ps –l命令则会类似输出以下几个内容:
其中PRI就是指该进程优先级,NI代表该进程的nice值
新PRi = 旧PIR + nice
nice的取值范围是-20 到 19共40个级别。PRI值越小,代表优先级越高。在Linux下,可以通过调整nice值来调整优先级。
我们可以通过top指令更改进程的nice值,方法如下:
- top
- 进入top后按“r”–>输入进程PID–>输入nice值
其他概念:
- 竞争性: 系统进程数目众多,而CPU资源只有少量,甚至1个,所以进程之间是具有竞争属性的。为了高效完成任务,更合理竞争相关资源,便具有了优先级
- 独立性: 多进程运行,需要独享各种资源,多进程运行期间互不干扰
- 并行: 多个进程在多个CPU下分别同时进行运行,这称之为并行
- 并发: 多个进程在一个CPU下采用进程切换的方式,在一段时间之内,让多个进程都得以推进,称之为并发
2.环境变量
概念:环境变量(environment variables)一般是指在操作系统中用来指定操作系统运行环境的一些参数。我们在编写C/C++代码的时候,在链接的时候,从来不知道我们的所链接的动态静态库在哪里,但是照样可以链接成功,生成可执行程序,原因就是有相关环境变量帮助编译器进行查找。
常见的环境变量:PATH,HOME,SHELL
PATH:指定命令的搜索路径
ls 这条命令其实也是一个可执行程序,myproc也是一个可执行程序,我们如果要运行myproc,必须要加"./" , 这是因为我们在执行一个程序必须要先找到他,可是为什么ls不需要加“./”就能找到他呢呢,这就是环境变量PATH起的作用。他帮助操作系统找到了ls的位置。
通过echo $PATH 可以查看PATH有哪些路径,当我们ls回车的时候,操作系统会在上面每一个:分隔的路径下寻找ls可执行程序。
如果我们想运行myproc像ls一样不加“./”就能运行,那我们可以把myproc,拷贝到PATH上面的路径下即可。或者,我们可以把myproc所在目录添加到PATH。具体操作如下
HOME:保存当前用户所处的工作目录
SHELL:命令行解释器,echo $SHELL 可以查看我们当前系统使用的是哪一款命令行解释器
与环境变量相关的命令:
-
echo: 显示某个环境变量值
-
export: 设置一个新的环境变量
-
env: 显示所有环境变量
以上都是系统设置的环境变量
-
unset: 清除环境变量
-
set: 显示本地定义的shell变量和环境变量
我们可以自己定义本地变量,但是env是不显示本地变量的,需要set才能显示出来
本地变量只在本进程(bash)有效
我们可以通过export导入一个环境变量,这样env就可以显示了
那么如果我们想取消刚刚导入的那个环境变量MYVAL怎么办呢,unset MYVAL即可
3.main()函数的三个参数
我们先说前两个参数,argv[]是一个指针数组,这个数组的char*指针的个数为argc。
当我们这样运行这段代码,我们可以发现argc = 1;再看下面
这是什么意思呢?
这里的 -a , -b, -c叫做命令行参数,如同ls命令后面可以加 -a, -l;
char* envp[]存放的是环境变量信息;结构如下
那我们如何打印环境变量呢?
当我们运行这段代码就可以得到环境变量;
下面这段也可以
也可以通过getenv()获取
环境变量是一个系统级别的环境变量,bash之下所有进程都可以获取
4.进程地址空间
当我们在学习c语言的时候,一定对下面的这张图非常熟悉。
接下来我们先写一段代码对上图进行一下验证
#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
#include <string.h>
#include <string.h>
int g_unval;//未初始化全局数据
int g_val = 100;//已初始化全局数据
int main(int argc, char* argv[], char* envp[])
{
printf("code addr: %p\n", main);//函数名就是代码起始地址
char* str = "hello world";//"hello world"在字符常量区
printf("read only addr: %p\n", str);
printf("init addr: %p\n", &g_val);
printf("uninit addr: %p\n", &g_unval);
//堆区
int* p1 = malloc(10);
int* p2 = malloc(12);
printf("heap addr: %p\n", p1);
printf("heap addr: %p\n", p2);
//栈区
printf("stack addr: %p\n", &p2);
printf("stack addr: %p\n", &p1);
//这里我们再测试一下命令行参数的地址
for(int i = 0; i < argc; i++)
{
printf("args addr: %p\n", argv[i]);//ls -a -l
}
int i = 0;
while(envp[i])
{
printf("envp addr: %p\n", envp[i]);
i++;
}
return 0;
}
结果如下,与上图吻合
下面我们再来看这样一段代码
#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
4#include <string.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
int g_val = 100;
int main()
{
pid_t id = fork();
if(id == 0)
{
//子进程
g_val = 200;
printf("child: pid: %d, ppid: %d, g_val: %d, g_val addr: %p\n", getpid(), getppid(), g_val, &g_val);
}
else
{
//父进程
sleep(2);
printf("father: pid: %d, ppid: %d, g_val: %d, g_val: %p\n", getpid(), getppid(), g_val, &g_val);
}
return 0
}
运行结果如下
我们再子进程里面修改了g_val的值
我们可以看到的是,对于全局变量g_val的地址,父子进程是一样的,可是g_val的值却是不同的。这说明这个地址绝对不是真是的物理地址,而是虚拟地址。我们再语言层面上看到的地址都是虚拟地址。父子进程的g_val值不同,也是我们所希望看到的,因为这保证了进程间的独立性,互不影响
至于为什么发生这样的结果,看了下面这张图,你就明白了
进程地址空间在内核中是一个数据结构类型,含有描述进程的地址空间变量,Linux下进程地址空间用一个结构体来描述——mm_struct。在32位操作系统下面,我们可以将mm_struct看成一把有刻度的尺子,从0x00000000到0xFFFFFFFF。这一串叫做虚拟地址。无论真正的内存有多大,mm_struct都把他分成从0x00000000到0xFFFFFFFF这么多份。比如说你要把一个长度等分成10份,那么如果总长为10米,每一份就是1米,如果总长为100米,那么每份的长度就是10米
-
什么叫做创建进程?
task_struct,mm_struct,页表。(之后会继续完善)
-
为什么要有地址空间?
从此以后不会有任何系统级别的越界问题存在了。
1.进程虚拟地址空间+页表,本质功能之一:保护内存!
2.每个进程都认为看到的是相同的空间范围(构成+顺序)
3.每个进程都认为自己在独占内存,更好地完成进程独立性以及合理的使用空间,将进程调度和内存管理进行解耦。
Cj-1656488144536)]
-
什么叫做创建进程?
task_struct,mm_struct,页表。(之后会继续完善)
-
为什么要有地址空间?
从此以后不会有任何系统级别的越界问题存在了。
1.进程虚拟地址空间+页表,本质功能之一:保护内存!
2.每个进程都认为看到的是相同的空间范围(构成+顺序)
3.每个进程都认为自己在独占内存,更好地完成进程独立性以及合理的使用空间,将进程调度和内存管理进行解耦。