96SEO 2026-02-19 20:12 0
variables)一般是指在操作系统中用来指定操作系统运行环境的一些参数

如我们在编写C/C代码的时候在链接的时候从来不知道我们的所链接的动态静态库在哪里但是照样可以链接成功生成可执行程序原因就是有相关环境变量帮助编译器进行查找。
环境变量通常具有某些特殊用途还有在系统当中通常具有全局特性。
:为什么我们平常输入的指令需要直接运行而我们编译好的可执行文件需要./指明在当前路径下
——》因为一般我们输入的命令可执行文件在/usr/bin下查指令的我们./
为分隔符的路径集我们直接输入的命令通常会直接在这几个路径下寻找
./程序名》将我们的程序拷贝到PATH路径中的其中一个搜索路径
——》2在PATH添加一个自己的路径这样OS就会在自己的路径下寻找
…就能添加自己的路径要注意这样的写法是覆盖式的也就是要加上原本环境变量的路径再以为分隔符上需要添加的路径。
或者PATH$PATH:……
—》像这样在命令行环境变量的修改的生命周期随以你的bash进程的结束而重置的意思就是说如果你在以上操作中误改了环境变量也没关系只需重启一下Xshell所有的环境变量都会重置我们的修改只影响当次使用。
——》首先肯定不是从内存中来的。
环境变量的内容最开始都是从系统的配置文件中来的我们登录Xshell—启动一个shell进程—进程会读取跟环境变量有关的配置文件—然后形成自己的环境变量表—之后再执行命令时生成子进程去执行命令就可以把环境变量表传给子进程。
——》这也能解释为什么在疑问2修改环境变量时我们重启shell就能重置环境变量表因为我们根本就没有真正的修改环境变量我们的修改操作都是内存级的没有触碰到配置文件bash进程在启动时只会去读取配置文件的环境变量表
:我们知道进程会记录是谁启动了自己——》你在启动进程的时候系统怎么知道你是谁的并且把你的uid写到进程的PCB结构体里面去
当前环境变量下早就记录了你是谁你在启动进程的时候bash进程会传入环境变量表到子进程根据环境变量表也就知道是谁启动了自己。
environ[i];i){printf(%s\n,environ[i]);}
——》PWD由于这个环境变量的存在我们创建新文件删除文件或者通过./执行命令系统都可以根据PWD找到当前工作路径执行操作。
其实我们的main函数是可以带参数的因为他其实也是被别人调用的一个函数分别是int
{printf(argc:%d\n,argc);for(int
i){printf(argv[%d]:%s\n,i,argv[i]);}
记录命令行参数的个数argv[]命令行参数表里面存储就是命令行参数最后一个成员是null
——》命令行参数其实就是我们在执行命令时包括命令在内的以空格为分割的各个选项argc记录个数argv[]以字符串数组的形式记录命令行参数
——》为什么需要命令行参数同一个程序就可以根据命令行参数中选项的不同表现出不同的功能了我们在shell上的指令都是这样的
其实main函数除了命令行参数的两个参数外还有一个参数(第三个参数)——》char
——》综上我们知道了在程序中有两个重要的表命令行参数表环境变量表。
我们执行一个程序而我们我们执行程序所启动的进程本质上bash的子进程子进程会拷贝父进程的相关变量即使进行了程序替换子进程拿到环境变量表。
——》就是因为所有的进程都是bash的亲子进程而bash已启动时就会获取环境变量——环境变量可以被所有bash之后的进程全部看到所以环境变量具有“全局属性”
就算其中一个子进程修改了环境变量表也不会影响全局的环境变量表子进程修改环境变量表只会影响他自己的子进程。
环境变量的“全局性”是充分运用了进程的继承性质的子进程在开始时与父进程除了数据是独立的之外完全一样。
——》像这样在命令行中直接定义的变量就是本地变量它可以通过echo
——》就是只希望在bash里面使用但是不希望被子进程继承下去的比如说我们的命令行提示符如果是root用户就是#
环境变量一般是指在操作系统中用来指定操作系统运行环境的一些参数它一般具有全局属性。
环境变量表记录这每个环境变量每个程序都会收到一张环境变量表它是一个字符指针数组每个指针指向一个以’\0’结尾的环境字符串这个指针数组以NULL结尾
0;}在以上的代码中我们创建了一个子进程然后让父进程和子进程都打印出全局变量g_val的地址然后发现
——》我们发现输出出来的变量值和地址是一模一样的这很好理解呀因为子进程按照父进程为模版父子并没有对变量进行进行任何修改。
可是将代码稍加改动
//child,子进程肯定先跑完也就是子进程先修改完成之后父进程再读取g_val100;printf(child[%d]:
//parentsleep(3);printf(parent[%d]:
——》父进程与子进程输出地址是一致的但是变量内容不一样这好像不对啊我们知道进程之间具有独立性父进程与子进程的数据是相互独立一份的子进程的g_val进行修改那他们的g_val数据也应该是不一样的但是这二者的地址又相等这是怎么回事难道是不同的数据存到同一份地址上那就更不可能了
1变量内容不一样,所以父子进程输出的变量绝对不是同一个变量2这个地址绝对不是物理地址3在Linux地址下这种地址叫做虚拟地址或称线性地址4我们在用C/C语言所看到的地址全部都是虚拟地址物理地址用户一概看不到由OS统一管理。
——》在之前的学习中我们知道这张图被称为程序地址空间但其实这个说法并不准确实际上应该叫做进程地址空间上面的编制(0x0000000xFFFFFFF)也全都是虚拟的地址。
——》有一个大富翁他不仅100亿的资产而且还有很多的私生子私生子与私生子之间互相不知道对方的存在。
私生子知道自己的老爸是富翁所以要钱是大把大把的要只有一个私生子还好说但是他有数不清的私生子他这资产怎么遭得住所以他给每一个私生子都画了个大饼自己死后会把自己的100亿资产继承给你。
有了这个承诺私生子都不急着要钱了所以向富翁要钱花的时候需要多少才取多少这样富翁就实现了保持和每一个私生子的友好关系又能不被私生子们发现其他私生子的存在。
——》回到话题这里的大富翁就是操作系统100亿是实际的物理内存而每一个私生子就是进程大富翁给他们的大饼就是进程地址空间OS向每一个进程都许诺了它们有4G如上图所示结构的的进程地址空间每一个进程真的有占这么大的内存吗并不是实际上是进程要多少空间才会向OS申请多少空间。
OS让每一个进程都认为自己独占了系统的物理内存大小进程之间彼此不知道不关心对方的存在从而实现一定程度的隔离。
——》饼画多了OS也要对它们进行管理先描述再组织所谓的进程地址空间在上图是他的逻辑结构在内存中本质是一个内核数据结构对象OS描述为mm_struct
我们在上面了解到os描述进程地址空间的结构体叫做mm_struct——》这样的成员构成是做到进程地址空间的区域划分的
一个区域只用两个整形进行管理一个记录着begin的地址一个记录着end的地址是这样的实现区域划分
个地址这些地址的线性的连续的——》这样设置地址的方式叫做内存编址。
类型的变量保存着——》这就是区域划分的变量内容只有一个begin一个end——》我们不需要记录一个区域中除了begin和end之外其他的地址就能知道一个地址值是否在这一个区域内了——》所以空间范围内的地址我们可以随便用甚至不用记录他只用知道一个区域的begin和end就行了。
程序在运行时所用的是虚拟进程地址空间的虚拟地址这个虚拟地址是怎么和物理地址产生关联的怎么通过虚拟地址找到物理地址
进程中维护一个表用来一个一个记录虚拟地址和物理地址的映射关系叫做页表。
上图中管理进程的结构体为task_struct在task_struct中有一个mm_struct的结构体指针成员管理者进程地址空间。
在其中g_val变量在进程地址空间中使用的是虚拟地址0x601054他在实际的物理地址是0x11223344页表就将g_val的虚拟地址和物理地址记录下来让他们一一对应。
从理解的角度页表的结构可以看作一个我们曾经学过的一个数据结构——》map当然真实的结构还要复杂很多由虚拟地址作为key找到对应的物理地址这个value——》很像一张表所以叫页表。
父进程创建子进程后子进程会拷贝父进程PCB中的大部分属性其中当然包括进程地址空间和页表页表的内容及映射关系全部都拷贝一份——》在这个时候父进程的g_val通过页表映射到物理内存的数据和子进程是一样的父进程的g_val和子进程是同一份——》类别到其他的数据比如说代码区父子映射到同一份代码区所以一般来说父子进程代码共享
——》有没有一点奇怪?对呀之前我们说过进程之间具有独立性他们的代码是共享的但是数据是独一份的但是这里的g_val为什么是同一份原因是OS具有写实拷贝机制
——》当不修改变量的时候父子进程的数据确实的同一份但是一旦子进程或者父进程要对数据进行写入修改)那么这个时候就会出发写实拷贝机制子进程对父进程的数据空间进行一次拷贝修改页表中物理地址的部分重新建立映射关系再对数据进行写入。
——》这样的机制由OS自主完成——》默认不分开只有在你需要的时候再分开——》这样好处非常大既节省了内存空间避免资源的浪费又保证了进程之间的独立性
——》最终我们终于能够解释我们一开始举例的现象了为什么同一个地址能看到不同的内容因为这个地址的虚拟地址在底层父子进程使用相同的虚拟地址但是他们的页表不同映射到的物理地址也不相同——》所以同一个虚拟地址在不同的进程通过同一个页表就可以看到不同的内容
——》在汇编底层中变量名其实不存在了转而变成了地址地址变成了变量的唯一标识符对变量进行操作其实也就是对地址的值进行操作。
内核数据结构(task_struct/进程地址空间mm_struct/页表/…)自己的代码和数据代码和数据是独立的从前文中我们也可以得知像进程地址空间mm_struct这样的内核数据结构也是独立的task_struct是独立的——右式都具有独立性左式也就具有相同的性质所以进程就是独立的。
上文提到页表的数据结构可以看作一个map一个虚拟地址对应一个物理地址——但其实没有谈完每一个对应关系还存在标记位用来记录映射关系的相关信息这里谈两个标记位
rwx有一个标记位记录着物理地址的rwx权限如果有对应的权限就可以通过映射关系拿到对应的物理地址。
——》比如代码区的数据我们知道它是只读的读取代码时先拿着虚拟地址通过页表检查权限有r权限就成功拿到物理内存的代码区数据但如果我要修改代码区数据时流程在页表时检查没有w权限虚拟地址无法转化为物理地址这个时候在cpu一般直接报错os直接把进程杀了也就改不了代码区的数据了。
world这样的数据在内存中是只读的不可写入一旦写入cpu通过页表拿不到对应的物理内存就会出错缺页中断os检查原因认为你的程序对只读的区域进行写入是你的代码出了问题所以就直接杀掉进程我们的程序也就直接崩溃了。
——》针对上面的问题C语言就有了constconst是在编译阶段就告诉编译器我这个变量不可修改所以一旦对const修饰的变量进行修改程序就会在编译阶段就给你拦截了。
——》综上所述一个是编译器在编译阶段就拦截了一个是进程运行时cpu报错被OS杀掉了。
isexists这个标记位记录着这个映射关系是否有效说白了就是在页表中填写的这个物理地址是否存在对应的数据。
——》数据是从磁盘加载到内存的有时候你在程序中定义了一个数据但是你程序并没有使用这个数据那么这个数据虽然有在页表中有映射关系但这份数据不一定加载到内存了isexists这个标志位就表示它是否加载。
如果后面的代码中使用了才会从磁盘中加载数据进来。
mm_struct他们是进程管理进程地址空间的结构体在内存中是一个结构体变量是结构体变量就要初始化啊进程地址空间相关的信息比如栈区代码区未初始化变量区…各个区域的大小要设置多少由哪里得到的呢
——》答案是从可执行程序中来的编译器在编译程序形成可执行程序后各个区域的大小信息就已经有了OS只需要访问特定区域读取可执行文件的相关区域就能设置好进程地址空间了。
——》页表的相关信息包括标记位映射关系也是一样的该物理地址是可读可写不是OS规定的是可执行文件设置的
——》从这里我们可以窥见一二操作系统的进程管理与编译器形成的可执行程序并不是相互独立的关系二者是息息相关的。
——》堆区申请空间的本质——》改变区域划分给堆区划分更多的虚拟地址——》再由页表中建立映射关系——》在使用时在申请物理内存。
——》意思就是可以有效解决野指针问题。
什么是野指针我们程序为什么使用了野指针就会崩溃野指针有两种情况第一是指向了未被在页表建立映射的虚拟地址第二是指向了不该被指向区域的地址比如代码区当你通过野指针进行写入时页表会找不到对应的映射条目或者检查标记位发现你没有该虚拟地址的权限虚拟地址到物理地址的转化不成功cpu报错OS就会把你的进程杀掉——》这样就有效避免会出现野指针问题胡乱修改物理内存也就保护了物理内存。
——》OS在内存管理中不需要知道进程在物理内存申请一片空间是为了做什么。
而在进程管理中也不需要知道物理内存申请有没有成功如果没有成功就没有映射关系程序再怎么折腾也只是导致自己的进程崩溃。
——》可执行程序的代码和数据记载到内存时不一定是连续的可能是物理内存的任意位置或者说连续的虚拟地址在经过页表映射时物理地址不一定是连续的
——》进程不需要关心物理内存是否是连续的在进程中有页表使用连续的虚拟进程地址空间。
页表和虚拟进程地址空间将地址从“无序”物理地址变为“有序”虚拟地址。
进程地址空间在内存中本质是一个结构体mm_struct是task_struct结构体管理进程的结构体的一个成员OS只需要把进程task_struct管理好进程地址空间本身就已经管理好了
——》理解全局变量的全局性今天我们就知道在代码中具有全局性的变量不放在栈上而放在初始化数据区
——》而该区域的生命周期的跟随进程的只有进程被销毁进程地址空间才会被销毁该区域的数据才会被销毁全局变量的虚拟地址一直被整个进程看到所以全局变量才具有全局性。
我知道一些人看文章喜欢静静看不评论但是他会点赞这样的人帅气低调有内涵美丽大方很优雅明人不说暗话要你手上的一个点赞
作为专业的SEO优化服务提供商,我们致力于通过科学、系统的搜索引擎优化策略,帮助企业在百度、Google等搜索引擎中获得更高的排名和流量。我们的服务涵盖网站结构优化、内容优化、技术SEO和链接建设等多个维度。
| 服务项目 | 基础套餐 | 标准套餐 | 高级定制 |
|---|---|---|---|
| 关键词优化数量 | 10-20个核心词 | 30-50个核心词+长尾词 | 80-150个全方位覆盖 |
| 内容优化 | 基础页面优化 | 全站内容优化+每月5篇原创 | 个性化内容策略+每月15篇原创 |
| 技术SEO | 基本技术检查 | 全面技术优化+移动适配 | 深度技术重构+性能优化 |
| 外链建设 | 每月5-10条 | 每月20-30条高质量外链 | 每月50+条多渠道外链 |
| 数据报告 | 月度基础报告 | 双周详细报告+分析 | 每周深度报告+策略调整 |
| 效果保障 | 3-6个月见效 | 2-4个月见效 | 1-3个月快速见效 |
我们的SEO优化服务遵循科学严谨的流程,确保每一步都基于数据分析和行业最佳实践:
全面检测网站技术问题、内容质量、竞争对手情况,制定个性化优化方案。
基于用户搜索意图和商业目标,制定全面的关键词矩阵和布局策略。
解决网站技术问题,优化网站结构,提升页面速度和移动端体验。
创作高质量原创内容,优化现有页面,建立内容更新机制。
获取高质量外部链接,建立品牌在线影响力,提升网站权威度。
持续监控排名、流量和转化数据,根据效果调整优化策略。
基于我们服务的客户数据统计,平均优化效果如下:
我们坚信,真正的SEO优化不仅仅是追求排名,而是通过提供优质内容、优化用户体验、建立网站权威,最终实现可持续的业务增长。我们的目标是与客户建立长期合作关系,共同成长。
Demand feedback