环境变量
一、基本概念
- 一般是指在操作系统中用来指定操作系统运行环境的一些参数;
- 环境变量通常具有某些特殊用途,还有在系统当中通常具有全局特性。
二、常见环境变量
- PATH:查看可执行程序的环境变量;
- HOME:指定用户的主工作目录;
- SHELL:保存当前所使用的shell得到的环境变量,它的值通常是/bin/bash;
- LD_LIBRARY_PATH:程序运行时,库文件的搜索路径的环境变量;
- LIBRARY_PATH:程序编译时,库文件的搜索路径和环境变量。
三、常见指令
- echo $[变量名]:显示某个环境变量值。
- 在文件中修改环境变量:
①vim ~/.bash_profile
②source [修改过的环境变量文件] :在文件中修改的设置时永久生效的,需要重新加载已修改过的环境变量; - 在命令行中修改环境变量只在当前bash下有用:
①export [环境变量名] = 环境变量值
②export [环境变量名] = $[环境变量名] : [新增的环境变量]。 - env:显示所有环境变量及其值。
当前用户的环境变量:~/.bashrc / ~/.bash_profile - set:显示本地定义的shell变量和环境变量。
系统下的环境变量:/etc/bashrc - readonly:将环境变量设为只读状态。
- unset [环境变量名]:清除环境变量。
四、组织方式
每个程序都会收到一张环境表,环境表是一个字符指针数组,每个指针指向一个以’\0’结尾的环境字符串
五、通过代码获取环境变量
- 命令行参数
#include <stdio.h>
int main(int argc, char* argv[], char* env[]) //argc为命令行参数的个数
{
int i = 0;
for (; i < argc; i++)
printf("argv[%d] = [%s]\n", i, argv[i]);
for (i = 0; env[i]; i++)
printf("env[%d] = [%s]\n", i, env[i]);
return 0;
}
- 第三方变量environ获取
#include <stdio.h>
#include <unistd.h>
int main()
{
extern char** environ; //libc中定义的全局变量environ指向环境变量表,需要extern声明
printf("environ[0] = [%s], pid = [%d], ppid = [%d]\n", environ[0], getpid(), getppid());
for (int i = 0; environ[i]; i++)
printf("environ[%d] = [%s]\n", i, environ[i]);
return 0;
}
- 系统调用
#include <stdio.h>
#include <stdlib.h>
int main()
{
char* path = getenv("PATH"); //用getenc函数来访问特定的环境变量
printf("path = [%s]\n", path);
return 0;
}
六、环境变量通常具有全局属性,可以被子进程继承
[dev@localhost c]$ vim test.c
#include<stdio.h>
#include<stdlib.h>
int main()
{
char * env = getenv("MYENV");
if(env)
printf("%s\n",env);
return 0;
}
[dev@localhost c]$ gcc test.c -o test
[dev@localhost c]$ ./test //没有结果,证明环境变量不存在
[dev@localhost c]$ export MYENV="helloworld" //导出环境变量
[dev@localhost c]$ ./test
helloworld //导出环境变量后才显示
#include <stdio.h> //MyGetenv
int main(int argc, char* argv[])
{
int i = 0;
for(i = 0; i < argc; i++)
printf("argv[%d]:%s\n", i, argv[i]);
extern char** environ;
for(i = 0; environ[i]; i++)
printf("environ[%d]:%s\n", i, environ[i]);
return 0;
}
程序地址空间
一、进程虚拟地址空间
#include <stdio.h>
#include<stdlib.h>
#include <unistd.h>
int g_Val = 10;
int main()
{
pid_t ret = fork();
if (ret < 0)
return ret;
if (ret > 0) {
sleep(2);
printf("father [%d]-[%d]- g_Val = [%d][%p]\n", getpid(), getppid(), g_Val, &g_Val);
}
else {
g_Val = 100;
printf("child [%d]-[%d]- g_Val = [%d][%p]\n", getpid(), getppid(), g_Val, &g_Val);
}
return 0;
}
child [10574]-[10573]- g_Val = [100][0x601054] //变量值不一样,但地址一样
father [10573]-[4723]- g_Val = [10][0x601054]
释:
①父子进程输出的变量不是同一个变量,但地址是一样的,说明该地址绝不是物理地址。
②在Linux地址下,这种地址叫做虚拟地址,它是人为规定的,不能存储数据;我们在用C/C++语言所看到的地址,都是虚拟地址。
③存储数据依靠介质。即物理内存,而物理地址,用户一概看不到,由OS统一管理,OS必须负责将虚拟地址转化成物理地址。
- 页表: 映射进程虚拟地址空间。
写实拷贝: 数据发生修改,才分配一个物理内存,并且会改变页表当中的映射关系。 eg. 上述代码中,若g_val的变量值不修改为100,那么子进程的页表映射关系不变,仍然指向物理内存中的g_val=10。
二、存储器管理方式
- 分页式: 将虚拟地址分成一页一页的格式,将物理内存分成一块一块的格式。(块大小一般为4096KB)
①页号 = 虚拟地址 / 块大小;
②页内偏移 = 虚拟地址 % 块大小;
③块号:根据页号在页表中的映射查找块号;
④块的起始地址 = 块号 * 块大小;
⑤物理地址 = 块的起始地址 + 页内偏移。 - 分段式: 将虚拟地址映射为物理地址的结构为段表,以段为单位划分,各个段的长度因程序而异。
物理地址 = 段起始地址 + 段内偏移 - 段页式:
①页的起始地址:根据段号在段表中的映射查找页的起始地址;
②页内偏移 = 虚拟地址 % 块大小;;
③块号:根据页号在页表当中的映射查找块号;
④块的起始地址 = 块号 * 块大小;
⑤物理地址 = 块的起始地址 + 页内偏移。
注:
分页式存储数据效率高,分段式效率低;
分段式可通过段表的结构,找到虚拟地址空间当中的一段。
三、进程优先级
- 是为了保证操作系统调度进程时比较合理,可以使用 top 命令查看。
- PR为进程优先级的数值,数值越小代表优先级越高,用户不能直接修改该值来改变进程优先级。
- NI为进程优先级的修正值,用户可以改变该值来影响进程优先级,PR(new)=PR(old)+NI,
top -> r -> PID -> 输入NI值 (范围:-20~19),