目录
2.2.有什么方法可以不用带路径,直接就可以运行自己的程序呢?
1.命令行参数
1.1.概念:
命令行参数是指在执行一个程序或命令时,通过命令行输入的附加信息和选项。
我们为什么会有不同的指令对应的不同的功能,就是因为命令行参数的存在,命令行参数就是Linux指令选项的基础!
注意我们的main()函数也是有参数的,且有三个参数,也就是命令行参数。
我们首先来研究main函数的前两个参数:一个整数类型的参数argc
和一个字符指针数组类型的参数argv
。其中,argc
表示命令行参数的个数,同时也表示argv数组中元素的个数,而argv
是一个指向参数值的指针数组,每个指针指向一个命令行参数的字符串
注意在argv数组存储的时候,默认第一个参数就是程序的名称。最后一个参数是NULL
字符串会被bash解析成一对多,放在指针数组里面,这些都是操作系统自己完成的!
如何获取环境?利用命令行传参,main函数第二个参数,会把所有环境参数都会被解析成一对多进行输出
所以我们可以通过不同的选项,让我们的同一个程序执行它内部不同的功能
1.2.利用命令行参数打造计算器:
cpp
int main(int argc, char* argv[], char* env[])
{
// ./myprocess -add 1 2 有4个参数,所以argc == 4
if (argc != 4)
{
printf("Usage:\n\t%s -[add|sub|mul|div] x y\n\n", argv[0]);
return 1;
}
int x = atoi(argv[2]);
int y = atoi(argv[3]);
if (strcmp("-add", argv[1]) == 0)
{
printf("%d+%d=%d\n", x, y, x + y);
}
else if (strcmp("-sub", argv[1]) == 0)
{
printf("%d-%d=%d\n", x, y, x - y);
}
else if (strcmp("-mul", argv[1]) == 0)
{
printf("%d*%d=%d\n", x, y, x * y);
}
else
{
printf("unknown!\n");
}
return 0;
}
2.环境变量
2.1.环境变量是什么?
是由系统提供的全局变量,每一个环境变量都有它的系统和用途
这个概念很明显有点抽象,接下来给大家举几个例子,就能理解了。
我们知道,当我们运行自己的程序是都需要加上 ./ 而我们用 ls mv等指令却不需要加上 ./ 这是为什么呢?
这就跟我们其中的一个环境变量有关了,名叫PATH。在运行程序,系统会去用PATH中存的默认路径,通过路径去查找我们要执行的程序。ls mv等系统指令都是默认存放在这些路径当中,但是你自己写的程序默认路径不在环境变量中的路径,找不到就需要说明在当前目录 ./就是表明在当前目录!
查看环境变量:使用指令 echo $[环境变量]
以:分隔,都是一个一个子路径
2.2.有什么方法可以不用带路径,直接就可以运行自己的程序呢?
法一:
直接将我们的程序拷贝到PATH默认路径下的任意一个。这一操作相当于将自己的软件安装到系统当中了。这种方法并不推荐,会污染环境!
法二:
我们可以将当前路径添加到环境变量PATH当中,当成系统的子路径,这样也可以不用./ 可以直接执行!
指令: PATH=$PATH:想要添加的路径
注意环境变量具有全局属性,全局变量会被所有的子进程包括孙子进程进行继承!
2.3.通过代码如何获取环境变量
main()函数第三个参数
cpp
#include <stdio.h>
int main(int argc, char *argv[], char *env[])
{
int i = 0;
for(; env[i]; i++){
printf("%s\n", env[i]);
}
return 0;
}
通过第三方变量environ获取
cpp
#include <stdio.h>
int main(int argc, char *argv[])
{
extern char **environ;
int i = 0;
for(; environ[i]; i++){
printf("%s\n", environ[i]);
}
return 0;
}
3.进程地址空间:
每一个进程都有自己的地址空间
3.1奇怪的现象:
父进程和子进程的值不同,但是为什么父进程和子进程的地址是一样的!?
3.2.用进程空间解释该现象
上图我们用红色框住的地址,绝对不是物理地址!不然地址不可能会相同的,这个地址叫做虚拟地址(线性地址)!
我们在用C/C++语言所看到的地址,全部都是虚拟地址!物理地址,用户一概看不到,由OS统一管理,下面这张图对应的都是虚拟地址,同样也是我们的地址空间!
父进程和子进程地址都不变,但是经过页表映射关系访问了不同的内存,所以打印出来的值是不一样的
上面的图就足以说明问题:
同一个变量,地址相同,其实是虚拟地址相同,内容不同其实是被映射到了不同的物理地址!
3.3.地址空间的原理
地址空间相当于就是操作系统给每一个进程画的大饼,所以并不会直接在物理内存中开辟空间,而是会用虚拟地址,当这个进程真正需要利用内存的时候,才会通过页表将虚拟内存映射到物理内存当中。
那么操作系统要不要对地址空间进行管理?当然是需要的,我们要先描述,再组织,进程地址空间是数据结构,具体到进程中,就是特定数据结构的对象!
区域划分的本质就是区域内的各个地址都可以使用,进行充分利用!
进程的地址空间一定有整数描述的各个区域,跟三八线用整数描述是一个道理。
在虚拟地址申请的内存空间不会立刻给你在屋里内存中申请,而是会等待你真正需要内存空间的时候才会在物理空间中开辟,地址空间相当于是一个支票,等到你需要的时候才会给你真正的申请空间
3.4.为什么要有地址空间+页表?
- 将物理内存从无序变有序,让进程以统一的视角,看待内存
- 将进程管理和内存管理进行解耦合
- 地址空间+页表是保护内存安全的重要手段
我们的地址空间,不具备对我们的代码和数据的保存能力!是在物理内存中存放的。
页表的最大作用就是将地址空间上的地址(虚拟/线性)转化到物理内存当中!
我们当用malloc申请空间时,我们其实并没有在地址空间中申请,而是在地址空间上开辟空间,我们经过页表+物理内存映射,但是我们当前并没有建立映射关系,就会出现缺页中断!
缺页中断会暂停程序执行,将控制权交给操作系统内核。 内核会检查缺失的页面是否在磁盘上,并进行必要的页面置换。
3.5.问题:malloc/new申请内存相关问题:
1、申请的内存,你会直接使用吗
不一定
2、申请内存,本质是在哪里申请?
在进程的虚拟空间中申请。
注意操作系统一定要为效率和资源使用率负责,保证内存的使用率,不会空转,提升new或者malloc的速度