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

序言:

在上期我们已经对进程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)                                                                {//childwhile(1){printf("我是子进程: pid: %d, ppid: %d\n", getpid(), getppid());sleep(1);}}else{//parentint 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.hqwc.cn/news/4617.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

RedmiBook Pro 15S AMD Ryzen 7 5800H电脑 Hackintosh 黑苹果efi引导文件

原文来源于黑果魏叔官网&#xff0c;转载需注明出处。&#xff08;下载请直接百度黑果魏叔&#xff09; 硬件配置 硬件型号驱动情况 主板RedmiBook Pro 15S 2021 处理器AMD Ryzen™ 7 5800H已驱动 内存16 GB 3200 MHz DDR4已驱动 硬盘Samsung 970EVO 512GB已驱动 显卡HD …

SpringMVC (一) 什么是SpringMVC

一、回顾MVC 1.1、什么是MVC MVC是模型(Model)、视图(View)、控制器(Controller)的简写&#xff0c;是一种软件设计规范。是将业务逻辑、数据、显示分离的方法来组织代码。MVC主要作用是降低了视图与业务逻辑间的双向偶合。MVC不是一种设计模式&#xff0c;MVC是一种架构模式。…

红色通信史(二):半部电台起家

上一期&#xff0c;我给大家介绍了“四一二”反革命政变后&#xff0c;我党在上海开通了第一部秘密电台的过程。 秘密电台的开通&#xff0c;标志着我党通信事业正式起步。然而&#xff0c;没过多久&#xff0c;顾顺章叛变&#xff0c;给上海党组织带来了极大的破坏。于是&…

论文笔记--Goat: Fine-tuned LLaMA Outperforms GPT-4 on Arithmetic Tasks

论文笔记--Goat: Fine-tuned LLaMA Outperforms GPT-4 on Arithmetic Tasks 1. 文章简介2. 文章概括3 文章重点技术3.1 LLM的选择3.2 算数任务的可学习性(learnability)3.3 大模型的加减乘除 4. 数值实验结果5. 文章亮点6. 原文传送门7. References 1. 文章简介 标题&#xff…

Java基础---为什么不能用浮点数表示金额

目录 缘由 十进制转二进制 不是所有数都能用二进制表示 IEEE 754 避免精度丢失 缘由 因为不是所有的小数都能用二进制表示&#xff0c;所以&#xff0c;为了解决这个问题&#xff0c;IEEE提出了一种使用近似值表示小数的方式&#xff0c;并且引入了精度的概念这就是我们所…

Day6——Web安全基础

网络安全学习笔记Day6 Web安全基础 一.Web简介什么是Web&#xff1f;什么是因特网&#xff1f;互联网&#xff0c;因特网&#xff0c;万维网的关系万维网构想的诞生http协议URL 二.Web发展史Web1.0Web2.01.0与2.0的区别Web1.0的安全漏洞Web2.0的安全漏洞 三.杂项门户网站静态页…

tomcat接入skywalking

tomcat接入skywalking 一、说明二、步骤2.1 准备java-agent包2.2 tomcat部署2.2.1 下载2.2.2 tomcat修改catalina.sh文件2.2.3 tomcat修改启动端口2.2.4 启动tomcat 三、验证四、问题排查4.1 tomcat的启动日志 一、说明 服务器中已经运行着skywalking&#xff0c;准备在同一台…

Spring Boot实战:拦截器和监听器的应用指南

当使用Spring Boot时&#xff0c;我们可以通过拦截器&#xff08;Interceptor&#xff09;和监听器&#xff08;Listener&#xff09;来实现对请求和响应的处理。拦截器和监听器提供了一种可插拔的机制&#xff0c;用于在请求处理过程中进行自定义操作&#xff0c;例如记录日志…

智慧文旅VR全景展示,深度VR沉浸式体验

导语&#xff1a; 智慧文旅VR全景展示为我们带来了一种独特的旅行体验&#xff0c;让我们可以穿越时空、身临其境地感受历史、艺术和自然的魅力。 在这个数字化时代&#xff0c;智慧文旅VR全景展示成为了旅游界的新宠&#xff0c;它让我们能够以一种前所未有的方式探索世界&am…

Django之ORM

一、Django模型层之ORM介绍 使用Django框架开发web应用的过程中&#xff0c;不可避免地会涉及到数据的管理操作&#xff08;增、删、改、查&#xff09;&#xff0c;而一旦谈到数据的管理操作&#xff0c;就需要用到数据库管理软件&#xff0c;例如mysql、oracle、Microsoft S…

梁宁:VisionPro、GPT、Web3三件套齐备,元宇宙开启

本文内容整理自图灵社区对谈栏目直播&#xff0c;主题为 ChatGPT 真需求&#xff0c;从产品的第一性原理解析。 上篇内容回顾&#xff1a;梁宁&#xff1a;为什么中国没有像 ChatGPT 和 Vision Pro 这样的创新产品&#xff1f; 梁宁&#xff0c;产品战略专家&#xff0c;曾任湖…

【Linux】MySQL 主从复制与读写分离

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 MySQL 主从复制与读写分离 MySQL 主从复制与读写分离1、什么是读写分离2、为什么要读写分离3、什么时候要读写分离4、主从复制与读写分离5、mysql支持的复制类型6、主从复制的…