Linux进程概念(四)环境地址变量
我们先来思考几个问题
1.为什么函数返回值,会被外部拿到呢?函数里面定义的不都是局部变量吗
2.系统如何得知我们的进程当前执行到哪行代码了?
第一个问题,函数在return a时,会直接编译成mov eax 10,会将a的值放到寄存器当中。所以函数返回值是通过寄存器拿到外部的。
第二个问题 cpu内部有个程序计数器(pc,eip),他会记录当前进程正在执行指令的下一行指令的地址。cpu内寄存器里面保存的都是进程相关的数据。
进程在从cpu上离开的时候,要将自己的上下文数据保存好,甚至带走。保存的目的,未来都是为了恢复。所以进程在切换的时候:
1.保存上下文。
2.恢复上下文。
那么cpu上的临时数据应该保存在哪里比较好。
我们知道可执行程序本质上就是指令,为什么我们自己创建的可执行程序需要./才能够被执行,而系统指令例如ls等可以直接被运行。
基本概念
环境变量(environment variables)一般是指在操作系统中用来指定操作系统运行环境的一些参数 如:我们在编写C/C++代码的时候,在链接的时候,从来不知道我们的所链接的动态静态库在哪里,但是照样可以链接成功,生成可执行程序,原因就是有相关环境变量帮助编译器进行查找。 环境变量通常具有某些特殊用途,还有在系统当中通常具有全局特性
常见环境变量
PATH : 指定命令的搜索路径
HOME : 指定用户的主工作目录(即用户登陆到Linux系统中时,默认的目录)
SHELL : 当前Shell,它的值通常是/bin/bash。
PATH:
我们知道可执行程序本质上就是指令,为什么我们自己创建的可执行程序需要./才能够被执行,而系统指令例如ls等可以直接被运行。
操作系统怎么知道ls在哪里呢,因为操作系统在执行ls命令时候,shell会在PATH一个路径一个路径
下去找,找到对应的ls后会直接执行对应路径下的ls程序。
而mycmd不在系统默认的指令搜索路径下,所以在直接执行mycmd时找不到这个指令,会有“command not found”。所以PATH时linux下的指令搜索路径,输入指令时会在PATH中查找。
./的目的是告诉Linux我们要执行的指令mycmd对应的可执行程序就在当前路径,执行对应的可执行程序的时候不要去PATH环境变量中的路径去找对应指令的可执行程序了,而是去当前路径下去找可执行程序mycmd去运行
如果把我当前所处的路径放到PATH环境变量中,自己写的程序是否就可以不用./而可以直接像指令一样去运行呢?
我们先将当前路径加到PATH中
我们再执行mycmd
发现此时mycmd可以像系统指令一样不需要./就可以直接被运行
查看环境变量方法 echo $环境变量名
环境变量是系统提供的一组name=value形式的变量,不同的环境变量有不同的用户,通常具有全局属性。
env显示所有环境变量
/dev/pts/0字符设备
我们复制ssh渠道,在复制的ssh渠道上输入hello显示在复制的ssh渠道中,那么如果我们想要将其显示在当前原ssh渠道,那么可以使用echo>重定向到原ssh渠道的字符设备上进行显示
HISTSIZE默认保存的历史指令条数
我们使用方向键中“上”,可以不断查看上一次输入的指令,那么在linux中一定是有专门的文件存储,这些指令的默认上限是1000,即linux会默认保存1000条之前在命令行中输入的指令。
OLDPWD保存上一次所处路径
OLDPWD是将上一次所处路径进行保存,因为cd-的作用可以返回上一次路径,所以就一定会将上一次路径进行保存。
getenv通过系统调用获取环境变量
输入要获取的环境变量名,进行传参给getenv即可通过系统调用接口getenv获取PATH环境变量的值。
#include <stdio.h>
#include <stdlib.h>int main()
{printf("PATH:%s\n",getenv("PATH"));return 0;
}
操作系统可以通过如下方式来区分当前用户是root用户还是普通用户,那么也就可以与文件的权限进行结合起来,如果登录用户是普通用户那么受权限限制,如果登录用户是root用户那么不受权限限制。
#include <stdio.h>
#include <string.h>
#include <stdlib.h>int main()
{char who[32];strcpy(who,getenv("USER"));if(strcmp(who,"root")==0){printf("你是root用户,不受权限限制\n");}else{printf("你是普通用户,受权限限制\n");}return 0;
}
命令行参数
命令行参数:是指在命令行界面运行程序时,跟随在程序名称后的额外输出参数,用于控制程序的运行或传递数据
int main(int argc,char* argv[])
{}
我们最常使用的main函数,同样是一个函数,main函数是被Startup()或CRTStartup()进行调用,所以main函数可以传参,可以带有额外参数,其中argc是参数的个数,argv是一个指针数组,我们使用命令行参数时,其实在bash中仅仅是输入了使用空格间隔的字符串,那么linux会将空格间隔出来的字符串的首字符的指针传递给这个argv指针数组,那么就可以使用argv+[]的形式获取这些字符串,那么根据字符串的不同去显示不同的内容或运作不同的操作,这就是linux中指令的选项的原型,例如mycmd -a使用空格分隔开的参数个数就对应是2
main函数可以传参,仅支持命令行参数是为了指令,命令,软件等提供命令行选项服务。
打印命令行参数表
1.对应的我们将argv这个指针数组叫做向量表,即命令行参数表,其中会默认在表的表的最后放入
一个NULL空,即
#include <stdio.h>
#include <string.h>
#include <stdlib.h>int main(int arvc,char* argv[])
{if(arvc!=2){printf("请输入%s -[a|b|c]\n",argv[0]);return 0;}else if(strcmp(argv[1],"-a") == 0){printf("%s[1]->%s ",argv[0],argv[1]);printf("功能1\n");}else if(strcmp(argv[1],"-b") == 0){printf("%s[1]->%s ",argv[0],argv[1]);printf("功能2\n");}else if(strcmp(argv[1],"-c") == 0){printf("%s[1]->%s ",argv[0],argv[1]);printf("功能3\n");}else{printf("default输入\n");}return 0;
}
运行结果:
命令行参数表,其底层如图所示
环境变量为什么具有全局属性
理论:
我们所运行的进程都是子进程,bash本身在启动的时候,会从操作系统的配置文件中读取环境变量信息,子进程会继承父进程交给子进程的环境变量,那么bash是所有进程的父进程,bash创建的子进程有可能在创建子进程,那么此时的进程关系就形成了一颗多叉树的形状,那么对应由于子进程要继承父进程的环境变量,而bash又是所有进程的父进程,那么bash中的环境变量在全部进程中都会有一份,那么此时环境变量就具有全局属性。
如何证明
我们先引出本地变量的概念,即在bash命令行中定义出的变量是本地变量,本地变量只在本bash内部有效,不会被子进程继承,本地变量不同于环境变量。
1.export设置一个新的环境变量,unset清除环境变量
2.本地变量可以通过export将自己变为环境变量
3.当本地变量使用export变成环境变量之后,可以通过unset将环境变量再变回本地变量
4.可以使用set查看环境变量和本地变量
我们在bash命令行中定义一个本地变量MY_VALUE,接着使用env显示环境变量,通过换到将数据传输给grep进行过滤MY_VALUE,结果什么都不显示,即无法在环境中找到MY_VALUE
接下来,我们通过echo $查看MY_VALUE,查看到MY_VALUE对应的值为555
本地变量xxx是不可以被子进程继承的,但export将xxx变为环境变量后,是可以被子进程继承的
内建命令和常规命令
我们思考一个问题,my_108是本地变量,是不会被子进程继承的,那为什么echo可以查看本地变量my_108的值呢?这是因为有两种类型命令,并不是所有命令都是通过fork创建子进程完成任务的。
1.常规命令--通过创建子进程完成
2.内建命令--bash不创建子进程,而是有自己亲自执行,类似于bash调用了自己写的,或者系统提供的函数
模拟内建命令
系统调用接口chdir,可以将当前调用的进程的工作目录更改至指定路径下
那么由于查看进程的工作目录(当前进程的路径即当前进程所在的目录位置)需要找到其PID和进入对应的proc中进程对应对应的PID中的cwd需要一定时间,所以需要在程序运行起来之后进行休眠,即使用系统调用接口sleep
通过第三方变量environ获取环境变量
我们还可以通过第三方变量environ获取环境变量,libc中定义的全局变量environ指向环境变量表,environ没有包含在任何头文件中,所以在使用时,要进行声明
extern char **environ
在使用environ时候,函数原型应该是int main(int argc,char*argv[])的形式
同时还应该声明二级指针environ
那么在使用的时候类似的就要类似于原main中第三个参数env去使用env[]去访问的形式,这里的environ也应该使用environ[]的形式去访问环境变量。