ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Linux:进程的命令行参数环境变量和虚拟空间地址的初识

Linux:进程的命令行参数环境变量和虚拟空间地址的初识 一、前言上篇文章讲了进程是大概是怎么跑起来的,也就是进程是怎么调度的,接下来的知识是能和以前与语言相关的知识进行相关的理解与认识二、命令行参数2.1基本概念简单来说就是我们输入的指令会被特定的参数接收从而做出不同的操作2.2跟语言的关系其实我们编写的c/c等语言的mian函数其实是可以写参数的-int main(int argc,char* argv[]),第一个是命令的个数,第二个是指针数组,存放每一个命令,我们来尝试接收一下参数:如图可以看出我们的指针数组第一个数就是运行的程序那我在前面再加aaa bbb ccc:也就是说我们在终端输入的字符串会以空格作为分隔符拆分后存入argc和argv数组。argc记录参数总数量argv是存储各个参数字符串的指针数组。反过来思考我们平时敲的系统命令例如ls -l本质也是一个 C 语言编译出来的可执行二进制程序。-ls对应argv[0]代表程序本身-l对应argv[1]是传入的命令参数。程序内部读取argv数组根据不同的参数做分支逻辑拿到‑l就执行长格式显示文件拿到‑a就显示隐藏文件。命令行参数就是用来给用户态程序传递配置让同一个程序实现多种不同功能。还有指针数组的最后一个下标是存’\0的,也就是NULL所以上面的遍历查找也可以把iargc换成argv[i];三、环境变量3.1基本概念• 环境变量(environmentvariables)⼀般是指在操作系统中⽤来指定操作系统运⾏环境的⼀些参数• 如我们在编写C/C代码的时候在链接的时候从来不知道我们的所链接的动态静态库在哪⾥但是照样可以链接成功⽣成可执⾏程序原因就是有相关环境变量帮助编译器进⾏查找。• 环境变量通常具有某些特殊⽤途还有在系统当中通常具有全局特性简单来说就是像语言一样没有类型只有变量名加内容3.2由问题引环境变量一、难道你没发现你要./xxx.c才能编译对应的二进制文件,我不是明明程序在当前目录下为什么不能直接xxx.c运行?为什么ls pwd等这种指令就能在任意路径下执行?因为ls pwd等系统指令的二进制文件的路径被环境变量PATH记录下来了,也就是说一个二进制文件的执行它会去环境变量PATH的内容下在它记录的路径下一个个寻找,通过上面那个操作就能看见PATH环境变量的内同是什么, :冒号前的就是一个个路径,以冒号作为路径的分隔符,你的二进制文件如果你没指明路径就默认会去PATH这里的路径下找,也就是说我如果把我当前程序的路径加到环境变量里就能不带路径就能直接运行。操作:PATH$PATH:程序的当前路径(你可以先用pwd显示当前路径然后直接复制),加进去就可以使你比如写的code.c直接运行在任意路径下都能随用随到!其实还有很多环境变量,但PATH是最重要的环境变量输入env就能查看所有的环境变量比如图上由pwd环境变量,这也是为什么你执行pwd指令能找到当前路径,它早已在环境变量里保存起来了,总结:常⻅环境变量:• PATH:指定命令的搜索路径• HOME:指定⽤⼾的主⼯作⽬录(即⽤⼾登陆到Linux系统中时,默认的⽬录)• SHELL:当前Shell,它的值通常是/bin/bash。环境变量相关的命令:echo:显⽰某个环境变量值(echo $环境变量)export: 设置⼀个新的环境变量 (export xxxxyyyyyy)env: 显⽰所有环境变量unset: 清除环境变量set: 显⽰本地定义的shell变量和环境变量3.3环境变量的组织⽅式通过代码获取环境变量跟命令行参数一样也在main函数中传参int main(int argc.char* argv[],char* env[])当然window系统和Linux系统所打印出来的环境变量表肯定是有所差异的你们可以自行对比一下也可以通过第三⽅变量environ获取libc中定义的全局变量environ指向环境变量表,environ没有包含在任何头⽂件中,所以在使⽤时要⽤extern声明。通过系统调⽤获取环境变量提供的getenv,可以用man查看需要的头文件和返回值你先可以通过env查看当前Linux有什么环境变量然后再验证代码是否能看到的和env看到的是否一样,比如下面是查看当前用户是谁,找不到环境变量就会返回NULL3.4环境变量具有全局性回答这个问题之前我们先要了解环境变量表是从哪里来的,我export或unset清除环境变量会这么样?它是从配置文件里面来的,我们登入用户之后 ls -la就能看到隐藏文件中有:并且每个用户有独立的配置文件,只要进入某个用户就会自动的先创建bash进程并且把配置文件给bash进程,比如我加句话到.bashrc中如果重新连接云服务器后出现了我写的话那就证明是对的并且bash是进程是加载到内存当中的你改的环境变量其实本质是改bash的环境变量表,当你退出再重进登入后又会重新再重建一个bash进程,然后把Linux的配置文件给用户再给bash进程,所以是不要紧的那为什么环境变量是全局性的呢?比如上面这个进程本质就是bash的子进程,它为什么能获得环境变量呢?本质不就是子进程能继承父进程的环境变量!!!你在代码里fork()后的子进程也会能得到环境变量表,更本质的原理还要再看下面要讲的虚拟地址空间本地变量也就是不加export直接定义的变量就叫做本地变量,子进程查不到的,只有bash进程能看到,set就能查看本地的所有变量四、虚拟空间地址它其实又叫进程空间地址,在讲虚拟空间地址之前,我们学C/c等语言都讲过下面这张图本质是为了把抽象的代码转化为能看到的实体,也就是你写的一些变量的分布在物理空间中的排布就是上面这张图那我想说,我们的这些栈\堆等空间是在物理内存中的吗?不是的它们不是在内存中,为什么下面再引出4.1通过代码引出下面这段代码就是我们前面的探究父子进程是写的,我们来重新看一下现象我们看到g_val修改了之后子进程确实是100,但为什么父进程还是0呢?按照我们之前说的不是父子共享代码,父进程是100呀那就证明:• 变量内容不⼀样,所以⽗⼦进程输出的变量绝对不是同⼀个变量• 但地址值是⼀样的说明该地址绝对不是物理地址•在Linux地址下这种地址叫做虚拟地址• 我们在⽤C/C语⾔所看到的地址全部都是虚拟地址物理地址⽤⼾⼀概看不到由OS统⼀管理OS必须负责将虚拟地址转化成物理地址。下面是真实地流程图:其本质就是,我们知道进程是具有独立性的,但是独立性也是相对的,因为你可能会在父进程或子进程运行时修改代码呀整个流程是这样的:当比如当创建子进程时,会发生拷贝它们的页表映射值本来是指向同一块物理内存中存放g_val的地址中,但一旦值的变化比如子进程使g_val变化就会再在物理内存你中开辟一块新的地址存放g_val然后再通过规则映射到页表中,实际可能更复杂这里是给出一个大概轮廓所以也就能明白为什么int pidfork();就能获取不同的id值了对页表的简单认识为什么代码是只读的是怎么实现的其实就是通过页表的标志位中有权限写入rwx这几个权限,它给代码只设置了r的权限所以是只读.并且常量字符串为什么不能修改也是在页表中字符串的权限是r如果修改了就会比对权限不相等就会报错还有很多关于页表的知识后面会补充以我们现在理解可能会很困难下面是Linux32位系统的虚拟地址区域分布4.2虚拟地址的实际结构上面的图也标注了,我们知道虚拟地址不管是什么,它一定是要被管理起来的,所以它的本质就是个结构体,是在task_struct里的一个指针,它是struct mm_struct,所以虚拟地址也没什么可怕的就是一个结构体管理我们区域划分的数据罢了那虚拟空间是怎么划分区域的?通过一个个整型变量来划分的,比如栈区域,是有一个开始的整型变量xxx_start划分栈区域的开始,xxx_end来划分栈区域的结束位置,其他的区域也是同样的道理,就有点像我们小时候喜欢和同桌划分三八线一样,我们活动的区域是有范围的那堆空间是不连续的那是怎么找到的?其实里面的结构可以分的更细致点:linux内核使⽤vm_area_struct结构来表⽰⼀个独⽴的虚拟内存区域(VMA)由于每个不同质的虚拟内存区域功能和内部机制都不同因此⼀个进程使⽤多个vm_area_struct结构来分别表⽰不同类型的虚拟内存区域。上⾯提到的两种组织⽅式使⽤的就是vm_area_struct结构来连接各个VMA⽅便进程快速访问。也就是一个个小小的bm_area_strcut来连接某个区域的某个堆上的申请资源然后再通过vm_prev和vm_next来连接各个部分,并且可以说是只有堆才有多个vm_area_struct因为只有它是可能不连续的4.3为什么要有虚拟地址控制进程的行为,拦截非法行为,保护物理内存将内存空间无序变有序让进程解耦如果每个进程都可以访问任意的内存空间这也就意味着任意⼀个进程都能够去读写系统相关内存区域如果是⼀个⽊马病毒那么他就能随意的修改内存空间让设备直接瘫痪其次可能对应代码数据的地址会不确定因为一般是多个进程一起进行运行的,当你运行完一个进程后可能会找不到对应的地址因为写入物理内存的代码是无序的,有了页表映射就能找到对应的内存地址又因为有地址空间的存在和⻚表的映射的存在我们的物理内存中可以对未来的数据进⾏任意位置的加载物理内存的分配和进程的管理就可以做到没有关系。进程管理模块和内存管理模块就完全解耦了
返回列表