【Linux】—— 进程的环境变量

news2024/10/1 1:16:58

序言:

在上期我们已经对进程PCB以及进程状态进行了详细的解释说明。今天,我将带领大家学习的是关于进程的环境变量的问题。


目录

(一)孤儿进程

1、基本介绍

2、代码演示

(二)环境变量

1、基本概念

2、和环境变量相关的命令

3、常见环境变量

1️⃣、测试PATH

2️⃣、测试HOME

3️⃣、shell环境变量

4、环境变量的组织方式

(三)通过系统调用获取或设置环境变量

1、getenv

2、putenv

(四)环境变量通常是具有全局属性的

总结


(一)孤儿进程

首先,在正式学习进程环境变量之前。我们先学习一下孤儿进程的相关知识,这个知识点在上篇博文中漏掉了,在本期中我给大家补上。

1、基本介绍

  • 孤儿进程(Orphan Process)是指在父进程结束后,子进程仍然在继续运行的情况;
  • 当父进程先于子进程退出时,子进程将成为一个孤儿进程;
  • 操作系统会将孤儿进程的父进程设置为init进程(通常是进程ID为1的进程),init进程会接管孤儿进程,并负责回收它们的资源。

孤儿进程的产生是由于父进程提前退出,导致子进程失去了父进程的引用,但子进程本身仍然在运行。

这种情况可能出现在以下几种情况下:

  1. 父进程调用了fork创建子进程后,快速退出,不等待子进程的结束。
  2. 父进程意外终止,例如发生了异常被强制终止

2、代码演示

下面是一个简单的代码演示,展示了如何创建一个孤儿进程:

#include <stdio.h>
#include <unistd.h>

int main() {
    pid_t id = fork();
     if(id == 0)                                                                
     {
          //child
          while(1)
          {
              printf("我是子进程: pid: %d, ppid: %d\n", getpid(), getppid());
              sleep(1);
          }
     }
      else{
          //parent
          int cnt = 10;
          while(1)
          {
             printf("我是父进程: pid: %d, ppid: %d\n", getpid(), getppid());
             sleep(1);
             if(cnt-- <= 0) break;
         }
   }

    return 0;
}
  • 通过管道过滤查看进程相应的状态
while :; do ps ajx | head -1 && ps ajx | grep mytest | grep -v grep; sleep 1;echo"---------------"; done
  •  具体过程如下:

其次,在上诉杀进程我们是使用的【kill】进程号的方式。今天,我在交大家一种方式,那就是使用【killall】。

【分析】

  • 在上述代码中,父进程创建了一个子进程,并分别打印出父进程和子进程的进程ID;
  • 父进程在运行10秒立即退出,而子进程会一直在一直程序中运行;
  • 当父进程退出时,子进程变成了一个孤儿进程,但仍然会继续运行。

 【分析】

  • 孤儿进程的资源回收是由操作系统的init进程负责的;
  • 所以在上述代码中,如果查看进程列表,可以看到子进程的父进程ID为1,表示由init进程接管。

(二)环境变量

1、基本概念

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

2、和环境变量相关的命令

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

3、常见环境变量

  1. PATH : 指定命令的搜索路径
  2. HOME : 指定用户的主工作目录(即用户登陆到Linux系统中时,默认的目录)
  3. SHELL : 当前Shell,它的值通常是/bin/bash。

我们可以通过【env】查看:

1️⃣、测试PATH

在Linux下,需要加上【 ./ 】来运行自己写的程序,而系统自带的程序不需要加这个前缀。

这是因为在Linux系统中,执行命令时会按照环境变量 $PATH 中定义的路径来查找可执行文件。当你直接输入一个命令时,系统会按照 $PATH 中定义的目录逐一搜索,找到匹配的可执行文件并执行。而当前目录(.)并不在 $PATH 中,默认情况下系统不会搜索当前目录。

如果你在当前目录下有一个可执行文件,而没有指定路径或者给出了一个相对路径,系统就无法找到该可执行文件,因此需要显式地使用 ./ 表示当前目录来执行程序。

例如,假设你有一个名为 mytest的可执行文件,在当前目录下执行它时,应该使用 【./mytest】命令。如果当前目录不在 $PATH 中,直接输入 mytest是无法找到和执行该程序的。

  • 因此不难看出上述是一个错误的示范
  • 那么正确的应该怎么做呢,即不仅当前自己指令不需要加【./】,系统中的也不需要加。

首先,我们需要先关掉【xshell】重新启动,当我们重新启动之后,一切都会恢复为默认的。

  • 相比之下,系统自带的程序通常被安装在 $PATH 中定义的某个目录下,因此可以直接通过命令名来执行,而无需加上 ./;
  • 系统自带的程序的路径已经在 $PATH 中设置好,所以可以直接使用命令名来运行这些程序。


【注意】

为了安全考虑,当前目录(.)一般不会包含在 $PATH 中,以避免执行恶意文件。因此,为了执行自己写的程序,需要显式地使用 ./ 前缀来指定当前目录。


2️⃣、测试HOME

在 Linux 中,可以使用 echo 命令结合环境变量 HOME 来测试用户的家目录。

(1)用root和普通用户,分别执行 echo $HOME ,对比差异

💨首先以普通用户输入以下命令并按回车键执行:

echo $HOME
  1. 这条命令会输出环境变量 HOME 的值,即当前用户的家目录。

如果一切正常,你将在终端中看到输出结果为当前用户的家目录的路径。

输出演示:

💨 而当我们使用【root】身份后在执行相应的指令时,会是什么效果呢?

具体如下:

 

【分析】

  1. 当使用 root 用户执行 echo $HOME 命令时,输出的结果是【/root】。这是因为在大多数 Linux 系统中,root 用户的主目录被设置为 /root
  2. 而当使用普通用户执行 echo $HOME 命令时,输出的结果是该普通用户的主目录路径;

【小结】

  1. root 用户的主目录是 /root,而普通用户的主目录位于 /home/username,其中 "username" 是用户的实际用户名;
  2. 这是因为在 Linux 系统中,不同用户有各自的独立主目录,用于存储其个人文件和配置信息。

(2)用root和普通用户,分别执行 执行 cd ~; pwd 对比差异

首先,还在以普通身份执行相应的指令。结果如下:

 其次,以 root 身份执行相应的指令。结果如下:

 

【小结】

~】符号和 【$HOME】环境变量都表示当前用户的主目录路径。它们在大多数情况下是等价的,可以互换使用。


3️⃣、shell环境变量

Shell 环境变量是在操作系统中定义的一组全局变量,可以被不同的进程和程序共享和访问。下面是有关 Shell 环境变量的详细说明:

1、查看环境变量:

  • 在 Shell 中可以使用 env 命令来查看当前的环境变量列表。

  • 输出结果列出当前环境中的所有变量及其对应的值。

2、定义环境变量:

  • 可以使用 export 命令来定义一个环境变量。一旦定义,该变量就能够被当前 Shell 进程以及其子进程所访问。
export myenv="hello"

 紧接着去环境变量中查看:



3、引用环境变量:

  • 使用美元符号 $ 作为前缀来引用环境变量。

 【小结】

  1. Shell 环境变量在系统中起着重要的作用,它们可以为不同的进程提供全局的共享配置信息;
  2. 通过定义和引用环境变量,我们可以方便地进行参数传递、配置管理等相关的工作。


4、环境变量的组织方式

环境变量可以组织为键值对的集合。这种方式通常被用于存储一组相关的配置信息。

  1. 环境变量表是操作系统中存储环境变量的数据结构,它通常是一个键值对的集合;
  2. 在 C 语言中,可以通过 environ getenv() 函数来获取环境变量表。

  • 每个程序都会收到一张环境表,环境表是一个字符指针数组,每个指针指向一个以 ’ \0’ 结尾的环境字符串 

 

💨以下是一个示例,展示如何访问环境变量表中的键值对:

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

extern char** environ;

int main() {
    char** env = environ;

    // 遍历环境变量表并打印每个键值对
    while (*env != NULL) {
        printf("%s\n", *env);
        env++;
    }

    return 0;
}

【分析】

  1. libc中定义的全局变量environ指向环境变量表,environ没有包含在任何头文件中,所以在使用时 要用extern声明。
  2. 在上述示例中,environ 是一个全局变量,它会被操作系统设置为指向环境变量表的指针数组;
  3. 通过迭代访问 environ 数组中的元素,可以逐个打印出环境变量表中的键值对。 

输出显示:

 

【注意】

  • environ 在不同的操作系统或编译器中可能有所不同。在某些系统中,可能需要使用 _environ 或者其他类似的变量名来访问环境变量表。

💨 除了上诉方式可以获取系统的环境变量表之外,还有一种方式也可以做到:

  • 命令行第三个参数
#include <stdio.h>
int main(int argc, char *argv[], char *env[])
{
 int i = 0;
 for(; env[i]; i++){
 printf("%s\n", env[i]);
 }
 return 0;
}

【分析】

  • 以前大家写C语言的时候,碰到的最多的main中只有两个参数,其实不然。main其实可以接收三个参数。

💨 接下来,我具体给大家分析:

  • 这段代码是一个C语言程序的入口函数 main,它接受三个参数:argcargv[]env[]

下面对每个参数进行详细解释:

int main(int argc, char *argv[], char *env[])
{
    // ...
}

 

  • int argc代表命令行参数的数量argc 是一个整数类型的变量,表示在命令行中传递给程序的参数数量(包括程序名称本身)。argc 的值至少为 1。

  • char *argv[]表示命令行参数的字符串数组argv 是一个指针数组,其中每个指针指向一个字符串,该字符串是一个命令行参数的副本。argv[0] 存储的是程序的名称,而 argv[1]argv[2] 等存储的是其他参数值。

  • char *env[]表示程序运行时的环境变量表env 是一个指针数组,其中每个指针指向一个字符串,该字符串是一个环境变量的键值对(形如 "键=值")。通过遍历 env 数组,可以访问和操作环境变量表中的所有环境变量。

通过使用这些参数,我们可以在程序中获取并处理命令行参数以及环境变量,以满足不同的需求。例如,使用 argc argv 可以根据命令行参数的数量和内容采取不同的操作,而使用 env 可以访问和操作环境变量表中的各种环境变量。

小结

总之通过环境变量表,我们可以访问和操作系统中定义的各种环境变量,这对于程序的配置和参数传递非常有用。


 

(三)通过系统调用获取或设置环境变量

  1. putenv 
  2. getenv 

在 Linux 中,可以使用 getenv 函数来获取环境变量的值,但是无法直接通过系统调用设置环境变量。要设置环境变量,可以使用  putenv 系统调用。


1、getenv

在 Linux 中,getenv 函数用于获取环境变量的值。它位于 <cstdlib> 头文件中,并且是 C/C++ 标准库提供的函数。

  • getenv 函数的原型如下:

char* getenv(const char* name);

  • 接下来,我们在 man 手册中查看一下这个函数:

 【分析】

  1. 该函数接受一个 const char* 类型的参数 name,表示要获取的环境变量的名称;
  2. 如果成功找到对应的环境变量,则返回一个指向字符串值的指针;
  3. 如果未找到对应的环境变量,则返回 NULL。

💨 接下来,给大家简单的看以下程序来获取  "USER" 的环境变量:

 【分析】

  1. 这段代码的作用是获取名为 "USER" 的环境变量的值,并将其输出到标准输出;
  2. 如果获取环境变量失败,会输出相应的错误信息。

输出显示:

 

💨 以下是使用 getenv 函数获取环境变量值的示例代码:

 输出显示:

 【分析】

  1. 在这个示例中,我们尝试获取环境变量 HOME 的值。首先,声明一个 const char* 类型的变量 varName,并将其赋值为要获取的环境变量的名称;
  2. 然后,调用 getenv 函数,传入 varName,获取环境变量的值,将结果存储在 char* 类型的变量 varValue 中。
  3. 接下来,我们通过判断 varValue 是否为 NULL 来确定是否成功获取到环境变量的值。如果成功,我们输出环境变量的名称和对应的值;如果未找到对应的环境变量,我们输出未找到的提示。

【注意】

getenv 函数返回的指针指向了一个静态分配的字符串缓冲区,因此不应该尝试修改或释放该指针指向的内存。

接下来,再给大家看一段代码:


 输出展示:

 

【分析】

  1. 该段代码的作用是判断当前执行程序的用户是否为 "zp",并根据判断结果输出相应的信息;
  2. 但是如果当前用户是 "zp",则输出执行完成的提示信息;如果当前用户不是 "zp",则输出非法用户的提示信息,指示无法执行。

2、putenv

  • putev的函数原型如下:

int putenv(char* string);

  • 接下来,我们在 man 手册中查看一下这个函数:

 【分析】

  1. 该系统调用用于设置环境变量的值,类似于 setenv。它接受一个字符串参数 string,格式为 "name=value",用于指定环境变量的名称和值;
  2. putenv 直接接受一个字符串参数,而不需要设置是否覆盖已存在的环境变量。
  3. 调用成功时,putenv 返回 0;调用失败时,返回 -1,并设置 errno 变量以指示错误的原因。

💨  以下是一个使用 putenv 设置环境变量的示例代码:

输出显示:

 

 

 【分析】

  1. 我们首先使用 snprintf 将环境变量的名称和值格式化为一个字符串"name=value",然后将该字符串传递给 putenv

【注意】

  1. putenv 函数接受的参数必须是指向静态分配的字符串或者全局变量的指针,因为它会直接使用传递的指针,而不会对其进行拷贝;
  2. 因此这意味着传递栈上的局部变量给 putenv 是不安全的。

常用getenvputenv函数来访问特定的环境变量。


(四)环境变量通常是具有全局属性的

环境变量通常具有全局属性,可以被子进程继承下去

在上述将shell环境时,我们手动的定义了环境变量 【myenv】,接下来我以这个为例进行代码演示:

#include <stdio.h>
#include <stdlib.h>
int main()
{
 char * env = getenv("myenv");
 if(env){
 printf("%s\n", env);
 }
 return 0;
}

输出显示:

 

 接下来,我们在手动的定义一个环境变量:

 

紧接着我们修改一下源代码,看程序输出结果:

 输出显示:

 

【分析】

  1. 我们可以发现带【export】的环境变量可以显示出来;
  2. 直接查看,发现没有结果,说明该环境变量根本不存在

【小结】

  1. 我们可以发现【hello1】此时依旧可以打出来,只是没有放在环境变量表里面而已。
  2. 对于这种,我们有个专门的称呼叫做 shell本地变量

那么此时我想把它输出来有方法吗?具体如下:

 

实验

下面是一个简单的代码示例,说明子进程继承父进程的环境变量:

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

int main() {
    // 父进程定义一个环境变量
    const char* my_var = "Hello World";
    setenv("MY_VAR", my_var, 1);

    // 创建子进程
    pid_t pid = fork();

    if (pid == -1) {
        // 创建子进程失败
        perror("fork");
        return 1;
    } 
    else if (pid == 0) {
        // 子进程
        const char* child_var = getenv("MY_VAR");
        printf("子进程中的 MY_VAR 值为: %s\n", child_var);
    } 
    else {
        // 父进程
        printf("父进程中的 MY_VAR 值为: %s\n", my_var);
    }

    return 0;
}

输出:

 【分析】

  1. 通过 setenv 函数在父进程中定义了一个名为 MY_VAR 的环境变量,并设置其值为 "Hello World"。然后使用 fork 函数创建了一个子进程。
  2. 在子进程中,使用 getenv 函数获取 MY_VAR 环境变量的值,并打印出来。在父进程中,直接打印父进程中定义的 my_var 环境变量的值。

【小结】

可以看到,子进程成功继承了父进程的环境变量,并且可以在子进程中访问和使用

  1. 这是因为在创建子进程时,操作系统会将父进程的环境变量复制给子进程的环境,使子进程可以直接访问父进程的环境变量,从而实现了继承。
  2. 这种继承机制使得父子进程可以共享环境变量的值,方便了进程间的数据传递和共享配置信息。

总结

以上就是关于进程环境变量的全部知识内容了,感谢大家的观看与支持!!!

 

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

使用git管理matlab代码

matlab使用git管理代码 设置 Git 源代码管理 - MATLAB & Simulink - MathWorks 中国 在 R2020b 之前&#xff0c;必须安装命令行 Git 客户端&#xff0c;才能使用 Git 合并 MATLAB 中的分支。有关详细信息&#xff0c;请参阅安装命令行 Git 客户端。 1.在 Git 中注册二进…

Groovy系列三 Java SpringBoot 整合 Groovy

目录 一、概述 一、在Java中使用Groovy&#xff1a; 二、在Groovy中使用Java&#xff1a; 三、几种范式的不同、优缺点 Java调用Groovy的类和方法&#xff1a; Groovy调用Java的类和方法&#xff1a; 使用GroovyShell执行Groovy脚本&#xff1a; 使用GroovyClassLoader加…

4.0ORBSLAM3之局部建图线程概述

1.简介 局部建图线程是ORBSLAM3的核心线程之一&#xff0c;在初始化SLAM系统时被创建和启动&#xff0c;主要作用是为跟踪线程(跟踪局部地图)以及回环检测线程(回环检测)服务&#xff0c;并进行局部地图优化以及时消除轨迹的累计误差。局部建图线程主要维护一个由共视图Covisi…

JSP网上订餐管理系统用eclipse定制开发mysql数据库BS模式java编程jdbc

一、源码特点 JSP 网上订餐管理系统是一套完善的web设计系统&#xff0c;对理解JSP java SERLVET mvc编程开发语言有帮助&#xff0c;系统具有完整的源代码和数据库&#xff0c;系统主要采用B/S模式开发。开发环境为 TOMCAT7.0,eclipse开发&#xff0c;数据库为Mysql5.0&a…

java压测工具 Jmeter初使用

一. 下载及安装教程 1. 有博主总结的很好&#xff0c;这里直接放传送门&#xff1a; 【Jmeter】win 10 / win 11&#xff1a;Jmeter 下载、安装、汉化、新机迁移、版本更新&#xff08;Jmeter 4 以上版本均适用&#xff09; 2. Jmeter 自定义创建桌面快捷方式 3. JMeter插件…

手写vue-diff算法(三)updateChildren

前文回顾 上一篇提到&#xff0c;新老儿子节点比对可能存在的 3 种情况及对应的处理方法&#xff1a; 情况 1&#xff1a;老的有儿子&#xff0c;新的没有儿子 处理方法&#xff1a;直接将多余的老dom元素删除即可&#xff1b; 情况 2&#xff1a;老的没有儿子&#xff0c;…

基础知识--客户端·服务端·代理

目录 一、客户端 1.什么是客户端 2.客户端分类 二、服务端 1.什么是服务器 2.服务器的作用 3.服务器工作原理 4.服务器的组成 服务器硬件 服务器软件 5.补充 三、代理 1.代理的分类 正向代理 反向代理 两者的区别与联系 2.总结 一、客户端 1.什么是客户端 …

【八股】【C++】(二)函数、类、模板

这里写目录标题 形参与实参的区别函数调用过程指针和引用当函数参数回调函数友元函数重载匹配运算符重载直接初始化与拷贝初始化函数指针C中struct&#xff08;结构&#xff09;和class&#xff08;类&#xff09;的区别C有哪几种构造函数构造函数的执行顺序析构函数的执行顺序…

设计消息模块的业务层Web层

目录 业务层 一、定义Message业务接口 二、定义Message业务实现类 Web层 一、获取分页消息列表 二、根据ID查询消息 三、把未读消息更新成已读消息 四、删除消息 业务层 一、定义Message业务接口 创建 MessageService.java 类 public interface MessageService {pub…

mybatis-plus使用@Delete注解批量删除实战

使用Delete注解批量删除 1、控制器调用 // test // http://localhost:3000/function/test // 删除操作按钮权限 Transactional GetMapping("/test") public JSONObject testBatch() {// Arrays.asList(1, 2, 3)JSONObject result new JSONObject();try {functionM…

Django+vue3权限菜单rabc设计和动态路由

本次是基于Django和vue实现 github源码&#xff1a;nineaiyu/xadmin-server: xadmin-基于Djangovue3的rbac权限管理系统 (github.com) 服务器设计及部分代码 权限控制的话&#xff0c;可以基于Django的permission进行控制&#xff0c;并通过访问api的URL操作 核心代码如下 …

stable-diffusion使用openpose报错

依据教程 &#xff1a; https://post.smzdm.com/p/awz2l2xg/ 使用 stable-diffusion教学之ControlNetlora换脸 报错&#xff1a; urllib.error.URLError: <urlopen error [WinError 10054] 远程主机强迫关闭了一个现有的连接。> File "E:\ai\sd-webui-aki-v4\ext…

【数据挖掘】时间序列教程【五】

(说明:本文接上回:【数据挖掘】时间序列教程【四】_无水先生的博客-CSDN博客) 上面介绍的傅里叶变换的问题在于,无论是正弦/余弦回归模型形式还是复指数形式,它都需要 操作以计算所有傅里叶系数。有n 数据点和有n/2 可以计算傅里叶系数的频率。每个频率系…

Springboot3新特性异常信息ProblemDetail详解

环境&#xff1a;Springboot3.0.5 概述 RFC 7807定义了为HTTP响应中错误的可读详细信息&#xff0c;以避免需要为HTTP API定义新的错误响应格式。HTTP [RFC7230]状态码有时不足以传达关于错误的足够信息。 RFC 7807 定义了简单的JSON[RFC7159]和XML[W3C.REC-XML-20081126]文…

7.MHA高可用配置及故障切换

文章目录 MHA高可用配置及故障切换MHA概念实验配置时间同步与主从复制安装MHA服务SSH免交互认证验证MHA服务是否开启启动服务 故障模拟恢复故障过程 MHA高可用配置及故障切换 MHA概念 MHA&#xff08;MasterHigh Availability&#xff09;是一套优秀的MySQL高可用环境下故障切…

计网实验第四章:IP

问题集1&#xff1a; 1.192.168.31.7 2. 上层协议为ICMP&#xff0c;并且其字段值为1 3. 20字节 56字节 - 20字节 36字节 解释&#xff1a;如图所示 4. 这个报文段没有分段发送 原因如图&#xff1a;按照下图所示 标志位显示没有更多的分段。 5. 同一地址发送的这两个…

1.计算机是如何工作的(下)

文章目录 4.编程语言&#xff08;Program Language&#xff09;4.1程序&#xff08;Program&#xff09;4.2早期编程4.3编程语言发展 5.操作系统&#xff08;Operating System&#xff09;5.1操作系统的定位5.2什么是进程/任务&#xff08;Process/Task&#xff09;5.3进程控制…

[LangChain]简介快速入门

⭐作者介绍&#xff1a;大二本科网络工程专业在读&#xff0c;持续学习Java&#xff0c;努力输出优质文章 ⭐作者主页&#xff1a;逐梦苍穹 ⭐所属专栏&#xff1a;人工智能。 目录 1、简介2、快速入门2.1、LLMs2.2、聊天模型2.3、提示模板2.4、链2.5、代理2.6、内存 1、简介 …

1758_C语言通过预处理的eror输出异常信息

全部学习汇总&#xff1a; GreyZhang/c_basic: little bits of c. (github.com) 这个功能我一直没有使用过&#xff0c;能够想到它完全是因为之前从某些代码中看了一眼。或许&#xff0c;这就是我跟这个小小知识点的缘分。 最近想处理一个可以适配多种情况的程序&#xff0c;…

领域驱动设计(DDD,Domain-Driven Design)

领域驱动设计 前言正文领域驱动设计基本概念什么是领域模型&#xff1f;什么是领域服务&#xff08;Domain Service&#xff09;&#xff1f;什么是领域事件&#xff1f; 秒杀项目中的领域分析一、秒杀活动领域设计秒杀活动领域模型领域服务领域事件 二、秒杀品领域设计领域模型…