【linux】:进程状态(僵尸进程等)以及环境变量(下)

简介: 【linux】:进程状态(僵尸进程等)以及环境变量(下)

二、环境变量



环境变量一般是指操作系统中用来指定操作系统运行环境的一些参数,比如我们在编写c/c++代码的时候,在链接的时候,从来不知道我们的所链接的动态静态库在哪里,但是照样可以链接成功,生成可执行程序,原因就是有相关环境变量帮助编译器进行查找,环境变量通常具有某些特殊用途,还有在系统当中通常具有全局特性。比如我们在linux中写的可执行程序,要想这个程序先运行起来必须在前面就是 .   /   ,  . 是在当前路径,/ 是路径分隔符,而同样为可执行程序的 ls指令等却不需要在前面加上./,难道就因为一个是被纳入系统的程序一个是我们自己写的吗?其实并不是这样,系统的指令之所以不需要在前面加./是因为有环境变量的帮助,这个环境变量会帮我们去搜索系统中的ls命令,而这个环境变量叫PATH,下面我们查看一下这个环境变量:

eef2400857064d3984f83907a6e6973a.png

echo是打印一个字符串,PATH是环境变量,前面加上$符号就是获取环境变量的内容,这里与指针解引用相似。


和环境变量相关的命令:


1.echo:显示某个环境变量值


2.export:设置一个新的环境变量


3.env:显示所有环境变量


4.unset:清除环境变量


5.set:显示本地定义的shell变量和环境变量

fa6597d954d5457a9cbf4f001828b532.png

我们之前讲过linux的指令,which可以查看指令的路径,通过查看我们发现ls指令在usr/bin中,而PATH环境变量是根据冒号一个一个路径进行查找,当找到usr/bin这个路径的时候就不需要我们在使用的时候加./了。那么如何将我们写的可执行程序添加到环境变量中呢?看下图:

db99d84aed4d48f88c3223e9c1526bbf.png

我们写了一个程序用来演示。接下来我们用命令将我们写的这个程序加入到环境变量中


9179e0db8aa74d2196d602781ee889d4.png


可以看到我们成功添加,然后我们试试可以直接运行吗?

c3f9e490a15a4b9593cea23c8f8b8584.png


我们看到是可以运行的并且不用在输入前面的./了

827ead7ebe764ca78c953512cf28bc88.png

这个时候我们的其他指令不能使用该怎么办呢?这时只需要重新登录xshell即可。

那么我们怎么样才能既使用系统的指令又用自己的呢?

69c775a1039445efa5443b330d36f084.png

我们将刚刚的命令修改一下就可以既使用系统的指令又用自己的。当然除了这一种方式我们还可以直接将要添加的可执行程序的目录拷贝到PATH中,而这种方式在linux中相当于软件的安装。

下面我们用env指令查看系统中的环境变量:

d4dbfd76edcf4344be89b8c9c2551257.png


我们可以看到系统中的环境变量很多,当然我们也可以用history指令查看以往我们用过的指令:

10c64bfd8c7d49a7880dd3db7da4d4da.png


为什么从10开始呢,因为history只会保留最新的1000条指令,一旦超过就会删掉原来旧的指令。

下面我们用C语言来获取系统中的环境变量:

1053a65ec3f145e0ac903e548dfecbb6.png

我们再写c/c++的时候从来没有写过main函数的参数,而main函数实际上有3个参数,这三个参数不需要我们手动去写编译器会默认给我们传参,envp这个指针数组中每一个指针都指向一个有效的字符串,而最后一个指针必须以NULL结尾。

437592c9d81447f69511ffb70f3eb002.png


然后我们将代码写完整如下图:

517d42ddc8ed4c258f8552a19429ef7a.png

为什么for循环中envp没有写判断呢?因为我们刚刚说过,envp这个指针数组中最后一个指针指向的一定是NULL,而NULL在for循环中对应为假,所以不需要写判断语句。

91984d5470944dd896117db9c15d9955.png

由于使用main函数的三个参数是c99标准下的,所以我们在编译后面加上c99。0493d41210ec4fc88805e0a966a6dec6.png

然后我们直接运行程序发现这里的环境变量与我们用env命令显示的一致。


总结:环境变量本质就是内存级的一张表,这张表由用户在登录系统的时候,进行给特定用户形成属于自己的环境变量表。环境变量中的每一个都有自己的用途,有的是进行路径查找的,有的是进行身份认证的,有的是进行动态库查找的,有的是用来进行确认当前路径等等,每一个环境变量都有自己特定的应用场景。那么环境变量对应的数据都是从哪来的呢?是从系统的相关配置文件中读取进来的。下面我们验证一下:


我们先用ctrl + ~进入家目录,然后输入指令ls -al查找文件:

e0a14d674ac247ba9d4c4c1b727fb701.png

我们可以看到bash的两个shell脚本,然后我们用vim打开这个脚本:

ae34742d84d447578034c3d119f5a4f0.png

我们用vim进入etc/bashrc,注意在etc目录下bashrc是全局的。

5463d7bebacc4306bfd430955fc36dfb.png

比如我们命令行上的# 或者 $提示符就是这样编写的。  环境变量是通常具有全局属性的,当我们写了一个环境变量val=100,然后这个环境变量就会shell的表中,当我们给这个进程在开一个子进程的时候,shell中的这张表也会交给子进程,这样子进程中也就有了环境变量val=100,下面我们来证明一下:

946aac7ad9fc4dce8ad5b3cdc71bf470.png

我们先自己导入一个环境变量,然后输入env命令查看:

97d7963b289b40c0badbd1e5bb70f892.png


我们看到确实将这个环境变量添加到系统中,下面我们修改一下代码进行演示:

17dd266e3a7b436f9bee421adfb364cc.png


getenv()函数是获取一个环境变量并打印

02d0eb73320c493e99461da6c3318923.png

我们从上图中可以看到成功获取到了我们自己设的环境变量,我们前面讲过当我们运行一个进程的时候这个进程的父进程是bash,刚刚我们的环境变量是保存在系统中也就是说只有bash可以访问,但是现在这个子进程也可以使用就说明了环境变量是全局的,会由父进程传给子进程。

d9aed7534c1c46f2a3093c444c92f572.png

我们定义了一个变量前面没有加export,然后我们在前面加$符号打印其内容发现也能正常打印

721859772cf748c09ecbc161b9eacd02.png

下面我们用getenv获取一下这个环境变量:

a815b9ad3e4d4774a0e5ddc509c16cff.png3886220543ad459d90366fcce00653ea.png

这个时候我们发现用函数获取环境变量获取不到我们刚刚定义的hello1,也就是说不带export定义的环境变量是不可以被getenv()获取到的,那么也就不可以被子进程继承,那么为什么加了export就能被继承呢?因为不加export定义的环境变量是不会被添加到环境变量表中,这样的变量被称为shell的本地变量,这种变量只能在shell内部有效。

b01629f725ca4cdba22c8b2362c99dd0.png 因为hello1已经在shell本地了,所以前面加export可以直接将hello1添加到环境变量表,也就可以正常被子进程继承了。

接下来我们继续解释刚刚main函数的三个参数中的另外两个。

6919d0e5236a4f24b8c0fd25e610f666.png


argv的使用与argc都有一个共同点,就是不需要在判断结束条件。

6e4188fc28384bdb91e521796a593cc6.png

如下图所示:

f97e681055f84c0492bef8c8f721e5be.png


-a -b其实是参数选项,my2.24是可执行。那么这个操作有什么作用呢?

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
//int main()
//{
//  int cnt = 10;
//  while (cnt--)
//  {
//    printf("这里在倒计时:%d\n",cnt);
//  }
//  return 0;
//}
void Usage(const char* name)
{
  printf("\nUsage:%s -[a|b|c]\n",name);
  exit(0);
}
int main(int argc,char* argv[])
{
  if (argc!=2) Usage(argv[0]);
  if (strcmp(argv[1],"-a")==0) printf("打印当前目录下的文件名\n");
  else if(strcmp(argv[1],"-b")==0) printf("打印当前目录下的详细信息\n");
  else if(strcmp(argv[1],"-c")==0) printf("打印当前目录下的文件名(包含隐藏文件)\n");
  else printf("其他功能,待开发\n");
//{
//  for (int i = 0;argv[i];i++)
//  {
//    printf("argv[%d]->%s\n",i,argv[i]);
//  }
  return 0;
}
//int main()
//{
//  printf("myenv:%s\n",getenv("hello1"));
//  return 0;
//}

我们重新写一段代码,然后我们运行起来。


b4680554930146ad93724193fd4aab62.png

启动软件后这个软件告诉我们使用方法是./my2.24 + abc任意一个字符

6ad89f683c6240bdbbe80480f81ad57f.png


这样就能完成一个类似于打印目录的操作,这只是简单的演示实际上可以实现一些有用的东西。


总结



本篇文章相较于上一篇进程的概念多了很多需要实践的东西,比如测试进程的优先级,理解孤儿进程,学会理解环境变量并且可以自己添加环境变量,环境变量的获取,环境变量的修改等。下一篇继续更深入的学习linux的进程,即使进程这部分概念多也希望大家可以多多练习才能更深入的理解进程。

目录
相关文章
|
30天前
|
算法 Linux 调度
深入理解Linux操作系统的进程管理
本文旨在探讨Linux操作系统中的进程管理机制,包括进程的创建、执行、调度和终止等环节。通过对Linux内核中相关模块的分析,揭示其高效的进程管理策略,为开发者提供优化程序性能和资源利用率的参考。
67 1
|
19天前
|
存储 监控 Linux
嵌入式Linux系统编程 — 5.3 times、clock函数获取进程时间
在嵌入式Linux系统编程中,`times`和 `clock`函数是获取进程时间的两个重要工具。`times`函数提供了更详细的进程和子进程时间信息,而 `clock`函数则提供了更简单的处理器时间获取方法。根据具体需求选择合适的函数,可以更有效地进行性能分析和资源管理。通过本文的介绍,希望能帮助您更好地理解和使用这两个函数,提高嵌入式系统编程的效率和效果。
84 13
|
26天前
|
SQL 运维 监控
南大通用GBase 8a MPP Cluster Linux端SQL进程监控工具
南大通用GBase 8a MPP Cluster Linux端SQL进程监控工具
|
1月前
|
运维 监控 Linux
Linux操作系统的守护进程与服务管理深度剖析####
本文作为一篇技术性文章,旨在深入探讨Linux操作系统中守护进程与服务管理的机制、工具及实践策略。不同于传统的摘要概述,本文将以“守护进程的生命周期”为核心线索,串联起Linux服务管理的各个方面,从守护进程的定义与特性出发,逐步深入到Systemd的工作原理、服务单元文件编写、服务状态管理以及故障排查技巧,为读者呈现一幅Linux服务管理的全景图。 ####
|
2月前
|
缓存 算法 Linux
Linux内核的心脏:深入理解进程调度器
本文探讨了Linux操作系统中至关重要的组成部分——进程调度器。通过分析其工作原理、调度算法以及在不同场景下的表现,揭示它是如何高效管理CPU资源,确保系统响应性和公平性的。本文旨在为读者提供一个清晰的视图,了解在多任务环境下,Linux是如何智能地分配处理器时间给各个进程的。
|
2月前
|
网络协议 Linux 虚拟化
如何在 Linux 系统中查看进程的详细信息?
如何在 Linux 系统中查看进程的详细信息?
171 1
|
2月前
|
Linux
如何在 Linux 系统中查看进程占用的内存?
如何在 Linux 系统中查看进程占用的内存?
|
8月前
|
Linux 调度
【Linux】详解进程状态之僵尸进程——孤儿进程
【Linux】详解进程状态之僵尸进程——孤儿进程
144 0
|
8月前
|
Linux Shell 调度
【Linux】进程排队的理解&&进程状态的表述&&僵尸进程和孤儿进程的理解
【Linux】进程排队的理解&&进程状态的表述&&僵尸进程和孤儿进程的理解
103 0
|
8月前
|
Linux 调度
『 Linux 』僵尸进程与孤儿进程
『 Linux 』僵尸进程与孤儿进程