Linux入门:环境变量与进程地址空间

一. 环境变量

1. 概念

1️⃣基本概念

环境变量(environment variables)一般是指在操作系统中用来指定操作系统运行环境的一些参数

如:我们在编写C/C++代码的时候,在链接的时候,从来不知道我们的所链接的动态静态库在哪里,但是照样可以链接成功,生成可执行程序,原因就是有相关环境变量帮助编译器进行查找。环境变量通常具有某些特殊用途,还有在系统当中通常具有全局特性。

2️⃣见一见环境变量:通过指令env 可以看到当前环境下的环境变量表
在这里插入图片描述
3️⃣可以用环境变量回答的诸多疑问

疑问1 :🤔为什么我们平常输入的指令需要直接运行,而我们编译好的可执行文件需要+./指明在当前路径下?

——》💡因为一般我们输入的命令可执行文件,在/usr/bin目录下,并且是PATH这个环境变量告诉OS,要在哪一个目录下查指令的!我们+./ 是为了告诉OS,要在当前目录下寻找!
在这里插入图片描述
——》PATH环境变量是一个由 : 为分隔符的路径集,我们直接输入的命令通常会直接在这几个路径下寻找

疑问2 :🤔如果我也想不输入./就能运行我的程序,改怎么做?

——》💡(1) cp /usr/bin ./程序名将我们的程序拷贝到PATH路径中的其中一个搜索路径!
——》💡(2)在PATH添加一个自己的路径!这样OS就会在自己的路径下寻找!
在这里插入图片描述
—》(2)ps: 在指令中直接输入PATH= …就能添加自己的路径,要注意,这样的写法是覆盖式的,也就是要加上原本环境变量的路径,再以:为分隔符+上需要添加的路径。或者PATH=$PATH:…… 这样的意思就是在原有路径的基础上新增路径

—》像这样在命令行环境变量的修改的生命周期随以你的bash进程的结束而重置的,意思就是说,如果你在以上操作中误改了环境变量也没关系,只需重启一下Xshell,所有的环境变量都会重置,我们的修改只影响当次使用

疑问3 :🤔这些环境变量的内容最开始从哪里来的?
——》💡首先,肯定不是从内存中来的。环境变量的内容最开始都是从系统的配置文件中来的我们登录Xshell—>启动一个shell进程—>进程会读取跟环境变量有关的配置文件—>然后形成自己的环境变量表—>之后再执行命令时生成子进程去执行命令,就可以把环境变量表传给子进程。

——》这也能解释,为什么在疑问2修改环境变量时,我们重启shell,就能重置环境变量表,因为我们根本就没有真正的修改环境变量!我们的修改操作都是内存级的,没有触碰到配置文件,bash进程在启动时只会去读取配置文件的环境变量表!

疑问4 :🤔我们知道,进程会记录是谁启动了自己——》你在启动进程的时候,系统怎么知道你是谁的?并且把你的uid写到进程的PCB结构体里面去?

——》环境变量表早就告诉了你答案
在这里插入图片描述
当前环境变量下,早就记录了你是谁,你在启动进程的时候,bash进程会传入环境变量表到子进程,根据环境变量表也就知道是谁启动了自己。

2. 查看环境变量的方法

2.1 在命令行中

env ——》查看当前进程的环境变量表

在这里插入图片描述

echo $PATH ——》查看一个环境变量,以PATH为例

在这里插入图片描述

2.2 在c语言程序中获取

getenv ——》系统级接口函数,获取一个环境变量的字符串

在这里插入图片描述

#include<stdio.h>
#include<stdlib.h>


int main()
{
    char * arr = getenv("PATH");
    printf("%s\n",arr);
}

在这里插入图片描述

**extern char environ ——》第三方变量引入
在这里插入图片描述

#include<stdio.h>

extern char **environ;
int main()
{
    for(int i = 0 ; environ[i];i++)
    {
        printf("%s\n",environ[i]);
    }
}

在这里插入图片描述

3. 常见的环境变量

  • PATH : 指定命令的搜索路径
    在这里插入图片描述
  • HOME : 指定用户的主工作目录(即用户登陆到Linux系统中时,默认的目录

在这里插入图片描述

  • SHELL : 当前Shell,它的值通常是/bin/bash
    在这里插入图片描述
  • PWD:保存当前进程的工作路径
    在这里插入图片描述

——》PWD:由于这个环境变量的存在,我们创建新文件,删除文件,或者通过./执行命令,系统都可以根据PWD找到当前工作路径执行操作。

4. 命令行参数

4.1 main函数的两个参数

😇其实我们的main函数是可以带参数的,因为他其实也是被别人调用的一个函数,分别是int argc ,char * argv[]

#include <stdio.h>
int main(int argc,char * argv[])
{
    printf("argc:%d\n,argc");
    
    for(int i = 0 ; i< argc ; i++)
    {
        printf("argv[%d]:%s\n",i,argv[i]);
    }
}
  • argc : 记录命令行参数的个数
  • argv[]:命令行参数表,里面存储就是命令行参数,最后一个成员是null

以上程序运行的例子:
在这里插入图片描述
在这里插入图片描述
——》命令行参数其实就是我们在执行命令时,包括命令在内的以空格为分割的各个选项,argc记录个数,argv[]以字符串数组的形式记录命令行参数

——》为什么需要命令行参数?同一个程序,就可以根据命令行参数中选项的不同,表现出不同的功能了!我们在shell上的指令都是这样的!

4.2 与环境变量相关的参数

👉 其实main函数除了命令行参数的两个参数外,还有一个参数(第三个参数)——》char * env[] ——》它用于记录传入的环境变量表!

#include <stdio.h>


int main(int argc,char * argv[],char * env[])
{
    for(int i = 0 ; env[i];i++)
    {
        printf(" env[%d] = %s\n",i,env[i]);
    }
}

在以上的程序中,我们尝试打印出char * env 的内容,执行命令,他打印出了当前所有的环境变量
在这里插入图片描述
——》综上,我们知道了,在程序中有两个重要的表:命令行参数表,环境变量表

4.3 理解环境变量的"全局性"

🤔思考:当我们执行程序时,环境变量是怎么一步一步传到我们的程序的?

——》💡我们登录Xshell 👉 启动一个shell进程 👉 进程会读取跟环境变量有关的配置文件 👉 然后形成自己的环境变量表👉 我们执行一个程序,而我们我们执行程序所启动的进程,本质上bash的子进程,子进程会拷贝父进程的相关变量,即使进行了程序替换👉子进程拿到环境变量表

——》就是因为所有的进程都是bash的亲子进程,而bash已启动时就会获取环境变量——>环境变量可以被所有bash之后的进程全部看到!!所以,环境变量具有“全局属性”
在这里插入图片描述
💭就算其中一个子进程修改了环境变量表,也不会影响全局的环境变量表,子进程修改环境变量表,只会影响他自己的子进程
在这里插入图片描述
💭环境变量的“全局性”是充分运用了进程的继承性质的:子进程在开始时与父进程除了数据是独立的之外完全一样

4.4 环境变量 VS 本地变量

🤔什么是本地变量?

在这里插入图片描述
——》像这样在命令行中直接定义的变量,就是本地变量,它可以通过echo $指令查看

💭本地变量与环境变量相比,只会在bash内部有效,不会被继承

🤔什么情况下需要本地变量呢??

——》就是只希望在bash里面使用但是不希望被子进程继承下去的,比如说我们的命令行提示符,如果是root用户就是# 如果是普通用户就是$

5 总结

💭环境变量一般是指在操作系统中用来指定操作系统运行环境的一些参数,它一般具有全局属性。
在这里插入图片描述

💭环境变量表记录这每个环境变量,每个程序都会收到一张环境变量表,它是一个字符指针数组,每个指针指向一个以’\0’结尾的环境字符串,这个指针数组以NULL结尾

  1. echo: 显示某个环境变量值
  2. export: 设置一个新的环境变量
  3. env: 显示所有环境变量
  4. unset: 清除环境变量
  5. set: 显示本地定义的shell变量和环境变量

二. 虚拟进程地址空间

先来看一个现象:

 #include <stdio.h>
 #include <unistd.h>
 #include <stdlib.h>
 
int g_val = 0;
 
int main()
 {
    pid_t id = fork();
    if(id < 0){
        perror("fork");
        return 0;
    }
    else if(id == 0){ //child
     printf("child[%d]: %d : %p\n", getpid(), g_val, &g_val);
    }else{ //parent
        printf("parent[%d]: %d : %p\n", getpid(), g_val, &g_val);
    }
    sleep(1);
    return 0;
 }

在以上的代码中,我们创建了一个子进程,然后让父进程和子进程都打印出全局变量g_val的地址,然后发现:

在这里插入图片描述

——》我们发现,输出出来的变量值和地址是一模一样的,这很好理解呀,因为子进程按照父进程为模版,父子并没有对变量进行进行任何修改。可是将代码稍加改动:

 #include <stdio.h>
 #include <unistd.h>
 #include <stdlib.h>
 
int g_val = 0;
 
int main()
 {
    pid_t id = fork();
    if(id < 0){
        perror("fork");
        return 0;
    }
    else if(id == 0){ //child,子进程肯定先跑完,也就是子进程先修改,完成之后,父进程再读取
        g_val=100;
        printf("child[%d]: %d : %p\n", getpid(), g_val, &g_val);
    }else{ //parent
        sleep(3);
        printf("parent[%d]: %d : %p\n", getpid(), g_val, &g_val);
    }
    sleep(1);
    return 0;
 }

以上代码多了在子进程对g_val对象进行修改,输出现象:

在这里插入图片描述

——》父进程与子进程,输出地址是一致的,但是变量内容不一样!?!!😧这好像不对啊!我们知道,进程之间具有独立性,父进程与子进程的数据是相互独立一份的,子进程的g_val进行修改,那他们的g_val数据也应该是不一样的,但是这二者的地址又相等,这是怎么回事🤔?难道是不同的数据存到同一份地址上😯?那就更不可能了😰!

——》所以在此,可以输出结论👉 :(1)变量内容不一样,所以父子进程输出的变量绝对不是同一个变量(2)这个地址绝对不是物理地址!(3)在Linux地址下,这种地址叫做虚拟地址,或称线性地址(4)我们在用C/C++语言所看到的地址,全部都是虚拟地址!物理地址,用户一概看不到,由OS统一管理。

1.1 什么是进程地址空间(是什么)

1.1.1 理解地址空间

在这里插入图片描述
——》在之前的学习中,我们知道这张图被称为程序地址空间,但其实这个说法并不准确,实际上应该叫做进程地址空间,上面的编制(0x000000&&0xFFFFFFF)也全都是虚拟的地址。

——例子引入:
——》有一个大富翁,他不仅100亿的资产,而且还有很多的私生子,私生子与私生子之间互相不知道对方的存在。私生子知道自己的老爸是富翁,所以要钱是大把大把的要,只有一个私生子还好说,但是他有数不清的私生子,他这资产怎么遭得住?所以,他给每一个私生子都画了个大饼:自己死后,会把自己的100亿资产继承给你。有了这个承诺,私生子都不急着要钱了,所以向富翁要钱花的时候,需要多少,才取多少,这样,富翁就实现了保持和每一个私生子的友好关系,又能不被私生子们发现其他私生子的存在。

——》回到话题,这里的大富翁就是操作系统100亿是实际的物理内存而每一个私生子就是进程,大富翁给他们的大饼就是进程地址空间!OS向每一个进程都许诺了它们有4G(如上图所示结构的)的进程地址空间,每一个进程真的有占这么大的内存吗?并不是!实际上是进程要多少空间,才会向OS申请多少空间。OS让每一个进程都认为自己独占了系统的物理内存大小,进程之间彼此不知道,不关心对方的存在,从而实现一定程度的隔离。

——》饼画多了,OS也要对它们进行管理!先描述,再组织!所谓的进程地址空间,在上图是他的逻辑结构,在内存中,本质是一个内核数据结构对象!OS描述为mm_struct
在这里插入图片描述

1.1.2 理解区域划分

我们在上面了解到,os描述进程地址空间的结构体叫做mm_struct——》🤔这样的成员构成是做到进程地址空间的区域划分的?

💡👇
在这里插入图片描述

一个区域只用两个整形进行管理:一个记录着begin的地址,一个记录着end的地址,是这样的实现区域划分!

1.1.3 理解地址空间上的地址

在这里插入图片描述

从0x0000 0000到0xFFFF FFFF,一共有多少地址?答案是 4GB = 2^10 X 2^10 X 2^10 X 2^2 X 1字节 =2^32 个地址,这些地址的线性的,连续的——》这样设置地址的方式叫做内存编址

其实我们可以观察到,地址本质就是一个数字,可以被unsignal long 类型的变量保存着——》这就是区域划分的变量内容,只有一个begin,一个end——》我们不需要记录一个区域中除了begin和end之外其他的地址,就能知道一个地址值是否在这一个区域内了——》所以空间范围内的地址,我们可以随便用,甚至不用记录他,只用知道一个区域的begin和end就行了。

1.1.4 页表

程序在运行时,所用的是虚拟进程地址空间的虚拟地址,这个虚拟地址是怎么和物理地址产生关联的🤔(怎么通过虚拟地址找到物理地址)?

💡进程中维护一个表,用来一个一个记录虚拟地址和物理地址的映射关系,叫做页表

在这里插入图片描述

上图中,管理进程的结构体为task_struct,在task_struct中,有一个mm_struct的结构体指针成员管理者进程地址空间。在其中,g_val变量在进程地址空间中使用的是虚拟地址0x601054,他在实际的物理地址是0x11223344,页表就将g_val的虚拟地址和物理地址记录下来,让他们一一对应

从理解的角度,页表的结构可以看作一个我们曾经学过的一个数据结构——》map(当然,真实的结构还要复杂很多),由虚拟地址作为key,找到对应的物理地址这个value——》很像一张表,所以叫页表。
在这里插入图片描述

父进程创建子进程后,子进程会拷贝父进程PCB中的大部分属性,其中当然包括进程地址空间和页表!页表的内容及映射关系全部都拷贝一份——》在这个时候,父进程的g_val通过页表映射到物理内存的数据和子进程是一样的!父进程的g_val和子进程是同一份!——》类别到其他的数据,比如说,代码区,父子映射到同一份代码区,所以一般来说,父子进程代码共享!

——》有没有一点奇怪?对呀,之前我们说过进程之间具有独立性,他们的代码是共享的,但是数据是独一份的,但是这里的g_val为什么是同一份?原因是,OS具有写实拷贝机制!
在这里插入图片描述

——》当不修改变量的时候,父子进程的数据确实的同一份,但是,一旦子进程或者父进程要对数据进行写入(修改),那么这个时候就会出发写实拷贝机制:子进程对父进程的数据空间进行一次拷贝,修改页表中物理地址的部分,重新建立映射关系,再对数据进行写入。——》这样的机制由OS自主完成——》默认不分开,只有在你需要的时候再分开——》这样好处非常大!既节省了内存空间,避免资源的浪费,又保证了进程之间的独立性

在这里插入图片描述
——》最终,我们终于能够解释我们一开始举例的现象了!🤔为什么同一个地址,能看到不同的内容因为这个地址的虚拟地址!在底层,父子进程使用相同的虚拟地址,但是他们的页表不同,映射到的物理地址也不相同——》所以同一个虚拟地址,在不同的进程,通过同一个页表,就可以看到不同的内容!!!

1.1.5 补充

1️⃣关于变量和地址:
——》在汇编底层中,变量名其实不存在了,转而变成了地址,地址变成了变量的唯一标识符,对变量进行操作,其实也就是对地址的值进行操作。

2️⃣重新理解进程和进程的独立性
——》究其原因,进程为什么具有独立性?我们知道,进程 = 内核数据结构(task_struct/进程地址空间mm_struct/页表/…)+自己的代码和数据代码和数据是独立的,从前文中我们也可以得知,像进程地址空间mm_struct这样的内核数据结构也是独立的,task_struct是独立的——>右式都具有独立性,左式也就具有相同的性质,所以进程就是独立的。

3️⃣关于页表
上文提到页表的数据结构可以看作一个map,一个虚拟地址对应一个物理地址——>但其实没有谈完,每一个对应关系还存在标记位,用来记录映射关系的相关信息,这里谈两个标记位

  1. rwx有一个标记位记录着物理地址的rwx权限,如果有对应的权限,就可以通过映射关系拿到对应的物理地址

——》比如代码区的数据,我们知道它是只读的,读取代码时先拿着虚拟地址,通过页表检查权限,有r权限,就成功拿到物理内存的代码区数据;但如果我要修改代码区数据时,流程在页表时检查没有w权限,虚拟地址无法转化为物理地址(这个时候在cpu一般直接报错,os直接把进程杀了),也就改不了代码区的数据了

——》以前学习C语言的时候我们知道,像char * str = “hello world”,我们通过 * str = “abc”,程序就会直接崩溃——》为什么?原因就是"hello world"这样的数据在内存中是只读的,不可写入,一旦写入,cpu通过页表拿不到对应的物理内存,就会出错(硬件中断),os检查原因认为,你的程序对只读的区域进行写入,是你的代码出了问题,所以就直接杀掉进程,我们的程序也就直接崩溃了。

——》针对上面的问题,C语言就有了const,const是在编译阶段就告诉编译器,我这个变量不可修改,所以一旦对const修饰的变量进行修改,程序就会在编译阶段就给你拦截了。

提个小问题const char * str = “hello world”char * str = “hello world”如果都对* str进行修改而发送报错,区别在哪

——》综上所述,一个是编译器在编译阶段就拦截了,一个是进程运行时cpu报错被OS杀掉了

  1. isexists这个标记位记录着这个映射关系是否有效,说白了就是在页表中填写的这个物理地址是否存在对应的数据

——》数据是从磁盘加载到内存的,有时候你在程序中定义了一个数据,但是你程序并没有使用这个数据,那么这个数据虽然有在页表中有映射关系,但这份数据不一定加载到内存了isexists这个标志位就表示它是否加载。如果后面的代码中使用了,才会从磁盘中加载数据进来。

——》由这个标志位支持分配加载,挂起等操作

4️⃣关于mm_struct
mm_struct他们是进程管理进程地址空间的结构体,在内存中是一个结构体变量,是结构体变量就要初始化啊!进程地址空间相关的信息(比如栈区,代码区,未初始化变量区…各个区域的大小要设置多少)由哪里得到的呢?

——》答案是从可执行程序中来的!编译器在编译程序形成可执行程序后,各个区域的大小信息就已经有了,OS只需要访问特定区域读取可执行文件的相关区域,就能设置好进程地址空间了。——》页表的相关信息包括标记位,映射关系也是一样的!该物理地址是可读可写不是OS规定的,是可执行文件设置的!

——》从这里我们可以窥见一二:操作系统(的进程管理)与(编译器形成的)可执行程序并不是相互独立的关系,二者是息息相关的

——》堆区申请空间的本质——》改变区域划分,给堆区划分更多的虚拟地址——》再由页表中建立映射关系——》在使用时在申请物理内存。
在这里插入图片描述

1.2 进程地址空间存在的原因(为什么)

  1. 虚拟进程地址空间与页表相互配合,可以有效保护内存

——》意思就是可以有效解决野指针问题。什么是野指针?我们程序为什么使用了野指针就会崩溃?野指针有两种情况,第一是指向了未被在页表建立映射的虚拟地址,第二是指向了不该被指向区域的地址(比如代码区)当你通过野指针进行写入时,页表会找不到对应的映射条目,或者检查标记位发现你没有该虚拟地址的权限,虚拟地址到物理地址的转化不成功,cpu报错,OS就会把你的进程杀掉——》这样就有效避免会出现野指针问题胡乱修改物理内存,也就保护了物理内存。
在这里插入图片描述

  1. 进程管理 和 内存管理 在系统层面解耦合了

——》OS在内存管理中,不需要知道进程在物理内存申请一片空间是为了做什么。而在进程管理中,也不需要知道物理内存申请有没有成功,如果没有成功,就没有映射关系,程序再怎么折腾也只是导致自己的进程崩溃。

  1. 让进程以统一的视角看待物理内存

——》可执行程序的代码和数据记载到内存时,不一定是连续的,可能是物理内存的任意位置,或者说连续的虚拟地址在经过页表映射时,物理地址不一定是连续的:
在这里插入图片描述
——》进程不需要关心物理内存是否是连续的,在进程中有页表,使用连续的虚拟进程地址空间。页表和虚拟进程地址空间将地址从“无序”(物理地址)变为“有序”(虚拟地址)

1.3 OS如何管理进程地址空间(怎么做)

进程地址空间在内存中本质是一个结构体:mm_struct,是task_struct结构体(管理进程的结构体)的一个成员,OS只需要把进程(task_struct)管理好,进程地址空间本身就已经管理好了

——》理解全局变量的全局性:今天我们就知道,在代码中具有全局性的变量,不放在栈上,而放在初始化数据区:
在这里插入图片描述
——》而该区域的生命周期的跟随进程的,只有进程被销毁,进程地址空间才会被销毁,该区域的数据才会被销毁,全局变量的虚拟地址一直被整个进程看到,所以,全局变量才具有全局性。

本文就到这里,感谢你看到这里❤️❤️! 我知道一些人看文章喜欢静静看,不评论🤔,但是他会点赞😍,这样的人,帅气低调有内涵😎,美丽大方很优雅😊,明人不说暗话,要你手上的一个点赞😘!

希望你能从我的文章学到一点点的东西❤️❤️

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/912947.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

2024版最新最全的Kali Linux操作系统安装使用教程(非常详细)超十万字解说Kali零基础入门到精通教程,收藏这一篇就够了

前言 这是向阳给粉丝盆友们整理的网络安全渗透测试入门阶段渗透测试工具Kali全套教程&#xff0c;本文超十万字超长分析&#xff0c;建议大家收藏慢慢学习&#xff01; 喜欢的朋友们&#xff0c;记得给向阳点赞支持和收藏一下&#xff0c;关注我&#xff0c;学习黑客技术 Ka…

【计网】基于TCP协议的Echo Server程序实现与多版本测试

目录 前言&#xff1a; 1、InitServer类的实现 1.1. 创建流式套接字 1.2. bind 绑定一个固定的网络地址和端口号 1.3.listen监听机制 1.4.完整代码 2. 循环接收接口与服务接口 2.1.accept函数讲解 讲个商场拉客的故事方便我们理解&#xff1a; 2.2.服务接口实现 3.服…

Latex公式转换编辑网站

https://editor.codecogs.com/ https://www.latexlive.com/home## https://simpletex.cn/ai/latex_ocr https://webdemo.myscript.com/views/math/index.html# 参考 https://latex.91maths.com/ https://web.baimiaoapp.com/image-to-latex https://blog.csdn.net/qq_45100…

多语言电商系统的多语言设计机制

在全球化电商市场中&#xff0c;跨语言沟通是提升用户体验和扩大市场份额的关键。为了满足不同语言用户的需求&#xff0c;构建一个支持多语言的电商系统已成为企业扩展国际市场的重要步骤。多语言电商系统需要能够根据用户的语言偏好自动显示内容&#xff0c;同时保证翻译的准…

VBA08-if语句

一、单行 If 语句 If x > 10 Then MsgBox "x is greater than 10"二、多行 If...Then...End If 语句 If x > 10 ThenMsgBox "x is greater than 10"y x 5 End If 三、If...Then...Else 语句 If condition Then 当条件为真时执行的代码块stateme…

Python数据可视化seaborn

产品经理在做数据分析时可能需要通过可视化来分析。seaborn官网 1. relplot 散点图 https://seaborn.pydata.org/examples/scatterplot_sizes.html import pandas as pd import seaborn as sns df pd.DataFrame({x: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10],y: [8, 6, 7, 8, 4, 6,…

【数据库系列】postgresql链接详解

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

webpack 执行流程 — 实现 myWebpack

前言 实现 myWebpack 主要是为了更好的理解&#xff0c;webpack 中的工作流程&#xff0c;一切都是最简单的实现&#xff0c;不包含细节内容和边界处理&#xff0c;涉及到 ast 抽象语法树和编译代码部分&#xff0c;最好可以打印出来观察一下&#xff0c;方便后续的理解。 re…

Hadoop生态圈框架部署(五)- Zookeeper完全分布式部署

文章目录 前言一、Zookeeper完全分布式部署&#xff08;手动部署&#xff09;1. 下载Zookeeper2. 上传安装包2. 解压zookeeper安装包3. 配置zookeeper配置文件3.1 创建 zoo.cfg 配置文件3.2 修改 zoo.cfg 配置文件3.3 创建数据持久化目录并创建myid文件 4. 虚拟机hadoop2安装并…

Python小白学习教程从入门到入坑------第二十九课 访问模式(语法进阶)

目录 一、访问模式 1.1 r 1.2 w 1.3 1.3.1 r 1.3.2 w 1.3.3 a 1.4 a 一、访问模式 模式可做操作若文件不存在是否覆盖r只能读报错-r可读可写报错是w只能写创建是w可读可写创建是a只能写创建否&#xff0c;追加写a可读可写创建否&#xff0c;追加写 1.1 r r&…

巡检任务管理系统(源码+文档+部署+讲解)

本文将深入解析“巡检任务管理系统”的项目&#xff0c;探究其架构、功能以及技术栈&#xff0c;并分享获取完整源码的途径。 系统概述 巡检任务管理、巡检抽查、巡检任务随机分派等功能 本项目名称为巡检管理系统&#xff0c;是对巡检工作进行数字化管理的系统。该系统适用…

自动驾驶系列—自动驾驶车辆的姿态与定位:IMU数据在复杂环境中的关键作用

&#x1f31f;&#x1f31f; 欢迎来到我的技术小筑&#xff0c;一个专为技术探索者打造的交流空间。在这里&#xff0c;我们不仅分享代码的智慧&#xff0c;还探讨技术的深度与广度。无论您是资深开发者还是技术新手&#xff0c;这里都有一片属于您的天空。让我们在知识的海洋中…

如何运营Github Org

目录 前言 正文 关于分支保护 特别说明 如何在Windows环境下配置GitHub Desktop GPG签名&#xff1f; 推荐分支保护选择 关于good first issue 如何设置good first issue&#xff1f; 关于Project 尾声 &#x1f52d; Hi,I’m Pleasure1234&#x1f331; I’m currently learni…

odrive代码阅读笔记

电机参数 电流环带宽 atan2 #include "float.h" #define MACRO_MAX(x, y) (((x) > (y)) ? (x) : (y)) #define MACRO_MIN(x, y) (((x) < (y)) ? (x) : (y)) #define f_abs(x) ((x > 0) ? x : -x) // based on https://math.stackexchange.com/a/11050…

笔记本怎么开启TPM2.0_笔记本开启TPM2.0教程(不同笔记本开启tpm2.0方法)

在win11最低要求是提示&#xff0c;电脑必须满足 TPM 2.0&#xff0c;并开需要开启TPM 才能正常安装windows11系统&#xff0c;有很多笔记本的用户问我&#xff0c;笔记本怎么开启tpm功能呢&#xff1f;下面小编就给大家详细介绍一下笔记本开启tpm功能的方法。 如何确认你笔记本…

ModuleNotFoundError: No module named ‘_ssl‘ centos7中的Python报错

报错 ModuleNotFoundError: No module named ‘_ssl’ 解决步骤&#xff1a; 1.下载openssl wget https://www.openssl.org/source/openssl-3.0.7.tar.gz tar -zxvf openssl-3.0.7.tar.gz cd openssl-3.0.72.编译安装 ./config --prefix/usr/local/openssl make make install3…

迁移学习相关基础

迁移学习 目标 将某个领域或任务上学习到的知识或模式应用到不同但相关的领域或问题中。 主要思想 从相关领域中迁移标注数据或者知识结构、完成或改进目标领域或任务的学习效果。 概述 Target data&#xff1a;和你的任务有直接关系的数据&#xff0c;但数据量少&#xff…

ReactPress系列—Next.js 的动态路由使用介绍

ReactPress Github项目地址&#xff1a;https://github.com/fecommunity/reactpress 欢迎提出宝贵的建议&#xff0c;感谢Star。 Next.js 的动态路由使用介绍 Next.js 是一个流行的 React 框架&#xff0c;支持服务端渲染、静态站点生成和动态路由等功能&#xff0c;极大地简化…

一文熟悉新版llama.cpp使用并本地部署LLAMA

0. 简介 最近是快到双十一了再给大家上点干货。去年我们写了一个大模型的系列&#xff0c;经过一年&#xff0c;大模型的发展已经日新月异。这一次我们来看一下使用llama.cpp这个项目&#xff0c;其主要解决的是推理过程中的性能问题。主要有两点优化&#xff1a; llama.cpp …

yolov8涨点系列之轻量化主干网络替换

文章目录 YOLOv8 替换成efficientvit轻量级主干网络的好处计算效率提升模型部署更便捷方便模型移植 模型可扩展性增强便于集成其他模块支持模型压缩技术 主干网络替换1.创建yolov8_efficeintVit.py2.修改task.py(1)引入创建的efficientViT文件(2)修改_predict_once函数(3)修改p…