【Linux从入门到放弃】Linux操作系统环境变量一文详解

🧑‍💻作者: @情话0.0
📝专栏:《Linux从入门到放弃》
👦个人简介:一名双非编程菜鸟,在这里分享自己的编程学习笔记,欢迎大家的指正与点赞,谢谢!

在这里插入图片描述


前言

  提到环境变量,大家应该都不会陌生,不管学习Java还是Phython都需要去安装环境变量,尤其是我自己在做深度学习时配置环境变量简直了,那叫一个惨,真的是莫名其妙不知道是哪里出的错,总之就是很难!那么今天的目的就是解释一下什么是环境变量。


一、环境变量是什么?

1.1 概念

  环境变量是在操作系统当中由系统在开机之后帮我们维护的一些系统运行时的动态参数。

1.2 从Linux角度认识环境变量

  环境变量因为所针对的场景不同,应用的软件不同而会有不同的种类。

  1. 我们在Linux中写的代码在编译之后形成的可执行程序为什么运行的时候要加上./呢?
  2. 可执行程序它是工具吗?是指令吗?比如我们可以执行指令file /user/bin/ls其实发现我们平时用的指令本质就是拿C写的可执行程序。当然我们自己写的可执行程序也可以通过这样的方式来查看。

那么这两种有什么区别呢? 答案是没有任何区别,只不过那些指令被纳入到了Linux基本指令的范畴中了。

  1. 我们可以从这样的一个角度出发,为什么系统的指令(可执行程序)不用加./,而运行我自己写的非得加上./呢?反过来说,我也想让我的程序不用加上./就可以运行该怎么做呢?

我们自己运行程序的方式其实是采用相对路径的方式来完成的,当然绝对路径的方式也是可以的。而之所以默认的指令可以不加./是因为在系统中存在一个环境变量,可以通过这个环境变量帮助我们在系统中特定路径下去搜索对应的命令。而这个环境变量是PATH,可以通过echo $PATH查看变量内容。

//我自己机器对应的环境变量
/usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/home/zhuxu/.local/bin:/home/zhuxu/bin

系统的指令在执行时会在这个环境变量中一个个查找,找到了就直接执行,而我们对应的可执行程序对应的路径肯定不在里面,因此在执行时就得加上路径。
在这里插入图片描述
通过上图可以看到ls命令在/user/bin目录下,而这个就在PATH环境变量中(包含多个路径,以冒号作为分隔符),但是自己的可执行程序并不在其中。那我们将这个路径添加到环境变量中应该就可以直接执行了吧。
在这里插入图片描述
可以通过上面这条指令给环境变量中添加那条路径,再查看环境变量明显发现多了一条路径,而这就是我们自己写的可执行程序对应的路径。这样就可以不用加./而直接运行程序了。

  1. 除了上面的做法,那我们还可以直接将可执行程序添加到环境变量中的系统默认路径下——而这种方式就相当于Linux下的软件安装。当然不要了的话可以直接删除——卸载。

1.3 查看环境变量

env //很简单,这个命令就可以查看所有的环境变量

在这里插入图片描述
常见环境变量:

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

1.4 获取环境变量

  我们一般写main函数时都是不写参数的,但是人家其实是有参数的,而且还有三个参数,分别是:int argc、char *argv[]、char *envp[],前两个大家应该还挺熟悉,第三个参数被称为环境变量表。
  这个环境变量表是一个指针数组,每一个位置存放着地址数据,基本上指向一个个字符串,最后必须以NULL结尾。

1.4.1 通过命令行第三个参数

#include <stdio.h>
int main(int argc,char *argv[],char *envp[])
{
	int i=0;
	for(;envp[i];++i)
	{
		printf("envp[%d]:%s\n",i,envp[i]);
	}
	return 0;
}

1.4.2 通过第三方变量environ

environ是一个二级指针,和命令行的第三个参数是一样的,都指向那张环境变量表。
libc中定义的全局变量environ指向环境变量表,environ没有包含在任何头文件中,所以在使用时 要用extern声明。

#include <stdio.h>
#include <unistd.h> 
int main()
{
  extern char **environ;
  int i=0;
  for(;environ[i];++i)
  {
    printf("environ[%d]:%s\n",i,environ[i]);
  }
  return 0;
}

到这里我们就能明白在进程内部本身就有环境变量。然后呢?有什么用呢?我们是可以拿到一张环境变量表,难道我们要用某个环境变量的时候还得从这个表一个个去查找吗?那岂不是太挫了。

1.4.3 通过函数获取

//通过环境变量命来获取对应的环境变量
char *getenv(const char *name);
#include <stdio.h>
#include <stdlib.h>

int main()
{
	char *path=getenv("PATH");
	if(path==NULL) perror("getenv");
	else printf("PATH:%s\n",path);
	return 0;
}
  1. 当然可以写一个获取当前路径的方法,那最终出来的效果是和系统本身指令pwd是一样的。
  2. 如果你想写一个只允许你自己执行的进程,那么可以获取USER这个环境变量,只要它的用户和你设置的用户名不同那就不能够执行。

1.5 环境变量概念再总结

  1. 环境变量本质就是一张内存级的一张表,这张表由用户在登陆系统的时候,进行给特定用户形成属于自己的环境变量表。
  2. 环境变量中的每一个都有自己的用途:有的是进行身份认证的,有的是进行确认当前路径的,有的是进行路径查找的,而有的是进行动态查找的等等。
  3. 每一个环境变量都有自己特定的应用场景。
  4. 每一个元素都是K-V类型的,不同名字对应不同的内容。

1.6 环境变量的数据从哪来?

  它是一张内存级的表,那么内存中的数据在没有加载到内存之前是从哪来的呢?都是从系统的相关配置文件中读取进来的
  在家目录下,有这么两个文件: .bash_profile.bashrc,它们本质都是 shell 脚本,每次重新登陆的时候,系统都会重新读取配置文件,将配置文件中的脚本一执行从而形成对应的环境变量。
  我们在执行命令时都是bash去执行的,除了这个,它也支持命令行式的定义变量export myval=100,这就相当于在bash内部定义了myval和100的字符串。
  每当我们登陆了系统之后,操作系统就会创建shell进程,它的功能是读取命令和命令行的。当我们在命令行中设置了刚才那个变量为100,那么shell就会将该命令以字符串的形式读入其中,并且在shell内部会维护一张环境变量表,然后通过一个指针指向那个字符串,也就相当于把这个命令导进了环境变量中使之成为环境变量中的一员。要知道,所有的命令都是shell的子进程,所以当执行命令的时候shell就会将自己的表传给它的子进程。

这也间接说明了环境变量具有全局属性,它是可以被相关子进程继承下去的。

  那怎么证明呢?

  1. 先将该命令导入环境变量中:export hello="world"
  2. 查看环境变量是否导入成功。
    在这里插入图片描述
  3. 编写代码通过函数调用的方式看是否能获取到刚设置的环境变量。
int main()
{
  char *path=getenv("hello");
  if(path==NULL)
    perror("getenv");
  else printf("%s\n",path);
  return 0;
}

注:
命令行式的导入环境变量不加export的话同样也是可以通过命令行的方法将设置的对应变量打印出来,但是函数调用的方法是不行的,为什么呢?原因就是这种方法定义出来的变量属于本地变量,只在shell内部有效,不能被子进程继承。

二、main函数的参数列表

2.1 参数介绍

int main(int argc,char *argv[])
argv:一张表,指针数组,
argc:指针数组元素的个数。
int main(int argc,char *argv[])
{
	int i=0;
	for(;i<argc;++i)
	{
		printf("argv[%d]->%s\n",i,argv[i]);
	}
	return 0;
}

在这里插入图片描述

  1. 当我们在命令行中这样输入,在shell看来他就是一个字符串,那就以空格为分隔符拆成一个个子串,./myproc为可执行程序,后面的一个个子串为参数选项。而那张参数表分别指向这一个个子串,最后NULL结尾。
  2. 那谁制作的这张表呢?当然是bash,也就是在执行命令的时候就将这张表做好了,而我们写的这个进程(子进程)使用这张表。
  3. bash(父进程)创建出来了一个个参数,然后子进程使用。

2.2 命令行参数的含义

可以根据命令行的参数选项不同而表现出不同的功能。

看下面的代码:

void Usage(const char* name)
{
  	printf("\nUsage: %s -[a|b|c]\n\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");
}

代码功能:实现了一个必须按照正确命令行参数规范输入而执行打印效果的demo。
当命令行参数个数为两个时才进行打印,若不是,就会进入Usage函数,这个函数功能就是告诉你应该怎样去正确的输入命令行。

在这里插入图片描述

从上图可以看出,当没有按照规范输入就会给出应该输入的规范模板,当正确输入时就会根据参数选项的不同而表现出不同的功能。这也就和ls命令和不同的选项而表现出不同的功能输出。

三、进程优先级

3.1 概念

  优先级指的是CPU资源分配的先后顺序
  当然优先级高的进程首先获取到CPU资源并执行,就可以把那些不重要的进程安排在其他位置,让优先级高的进程先运行,这样可以大大改善系统的整体性能。

3.2 为什么有优先级呢?

  举个简单例子,在学校里,每到中午的时候我们都要去食堂买饭,往往伴随着排队买饭的现象,原因就是卖饭窗口少,学生人数多。而优先级的出现就在于CPU资源有限。
  正是因为CPU资源的有限,导致了众多进程需要竞争获取资源,必定会有先后顺序,进而会有优先级的出现让进程使用CPU资源有一定的先后顺序。

3.3 查看系统进程优先级 (ps -l)

在这里插入图片描述

PRI :代表这个进程可被执行的优先级,其值越小越早被执行
NI :该进程的nice值,修正数据

  每个进程都有自己的PRI,而这个数字则代表着自己的执行优先级,也就是该程序被CPU执行的先后顺序,此值越小进程的优先级别越高,NI表示修正数据,这个东西是Linux操作系统独有的,在其它操作系统中是没有的。
  当然,一个进程最终的优先级不只是仅仅看PRI的大小,还有需要看Ni的值。PRI(new)=PRI(old)+NI
  NI值为负值的时候,那么该程序将会优先级数值将变小,即其优先级会变高,则其越快被执行。所以,在Linux下调整进程优先级就是调整进程NI值,NI其取值范围是-20至19,一共40个级别。

3.4 更改进程优先级

用top命令更改已存在进程的nice:top

进入top后按“r”–>输入进程PID–>输入nice值

当然除了这种还有其它方式,具体是那种在这里不多介绍,毕竟这个点不是很重要。有兴趣可以自己了解一下。


总结

此篇博客主要是对操作系统中的环境变量以及参数列表,进程优先级做了陈述,重点还是放在环境变量的理解上。

  • 24
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 25
    评论
### 回答1: Linux内核是一种开源的操作系统内核,是Linux操作系统的核心组成部分。它提供了操作系统与硬件之间的抽象层,负责管理系统的资源、调度任务、提供驱动程序等功能。 Linux内核采用分层的架构,包括硬件抽象层、系统调用层、进程管理层、文件系统层和网络层等。硬件抽象层负责将不同硬件设备的接口统一起来,使得上层的软件可以方便地与硬件进行通信。系统调用层提供了一组API供用户进程调用,如文件操作、网络通信等。进程管理层负责进程的创建、销毁以及调度等任务。文件系统层负责文件的管理和存储。网络层负责网络协议的实现和网络通信。 Linux内核的工作原理可以简单概括为以下几个关键步骤。首先,当一台计算机启动时,BIOS会加载内核映像到内存中,并执行启动代码。然后,内核初始化各种数据结构、驱动程序和关键服务。接下来,内核创建一个初始的用户空间进程,称为init进程。init进程是所有其他进程的祖先进程。在此之后,内核根据调度算法来决定哪个进程可以使用CPU,并依次执行。同时,内核会提供一个中断机制,以便处理硬件事件的优先级。 内核还提供了许多系统调用供用户进程调用,以实现对各种功能的访问。当用户进程需要操作文件、创建进程或进行网络通信时,会通过系统调用将请求传递给内核,由内核代用户进程执行相应的操作。内核通过调度算法来分配CPU时间片,并通过虚拟内存管理来管理内存资源的分配和回收。 总而言之,Linux内核是一个高度可配置和模块化的操作系统内核,通过分层架构和系统调用机制实现了对硬件的抽象和对用户进程的管理。了解Linux内核的架构和工作原理,有助于深入理解Linux操作系统以及开发和调试相关应用程序。 ### 回答2: Linux是一种开源的操作系统内核,其设计目标是为了在不同的计算机硬件平台上提供高效的、稳定的和安全的操作系统服务。 Linux内核的架构可以分为三个主要部分:进程管理、内存管理和文件系统管理。 在进程管理方面,Linux内核使用了多任务处理技术,可以同时运行多个进程。每个进程都有独立的地址空间和资源,通过调度算法可以合理分配CPU时间片,优化系统的响应速度和资源利用率。 在内存管理方面,Linux内核使用了虚拟内存技术,将物理内存和逻辑内存进行了映射,使得每个进程都有独立的地址空间。当物理内存不足时,Linux内核会通过页面置换算法将暂时不使用的页写入磁盘交换空间,以释放物理内存供其他进程使用。 在文件系统管理方面,Linux内核支持多种文件系统,包括传统的ext3和ext4文件系统,以及现代的Btrfs和XFS文件系统。它负责文件的读写操作,以及文件的权限控制和磁盘空间的管理。 Linux内核的工作原理可以简单概括为以下几个步骤:首先,启动引导程序将内核加载到内存中,并进行初始化。然后,内核分配一部分内存作为内核空间,用于存放内核代码和数据结构。接着,内核根据系统的硬件配置进行设备的初始化和驱动程序的加载。之后,内核根据系统的启动参数和配置文件进行一系列的初始化工作,包括启动系统服务和加载用户程序。最后,内核进入主循环,不断地处理中断、调度进程、管理内存和文件系统,以提供稳定的操作系统服务。 总之,Linux内核是一个复杂而高效的软件系统,它通过进程管理、内存管理和文件系统管理等功能,实现了操作系统的基本功能。了解Linux内核的架构和工作原理,有助于我们更好地理解和使用这个优秀的开源操作系统。 ### 回答3: Linux内核是一个开放源代码的操作系统内核,由一个核心程序和一组通用的系统工具组成。它是Linux操作系统的核心,负责处理硬件设备、管理系统资源、实现进程管理、文件系统和网络功能等。 Linux内核的架构可以分为两个层次:用户空间和内核空间。用户空间包括用户应用程序,如图形界面、终端程序等,它们通过系统调用接口与内核进行通信。内核空间包括内核核心的数据结构和程序,用于管理和控制硬件资源。 Linux内核的工作原理可以概括为以下几个方面: 1. 进程管理:内核负责创建、调度和终止进程。它使用进程描述符(task_struct)来跟踪进程的状态和资源使用情况,并根据调度算法分配CPU时间片给不同的进程。 2. 内存管理:内核负责管理系统的物理内存和虚拟内存。物理内存管理包括内存分配和释放,虚拟内存管理包括页面置换和页面回写等策略,以优化内存的使用效率。 3. 文件系统:内核提供文件系统接口,管理文件和目录的创建、读写和删除等操作。它通过虚拟文件系统层(VFS)将不同的文件系统统一管理,如ext4、NTFS等。 4. 设备驱动:内核提供了访问硬件设备的接口,通过设备驱动程序与硬件交互。不同的硬件设备需要不同的驱动程序,如网卡、显卡、声卡等。 5. 网络功能:内核提供TCP/IP协议栈和网络设备驱动程序,用于实现网络通信功能。它提供网络连接的建立、数据传输和断开等功能,支持各种网络协议,如HTTP、FTP、SSH等。 总的来说,Linux内核是一个非常复杂且功能强大的软件,它负责管理计算机的各种资源和提供操作系统的各种功能。通过深入理解其架构和工作原理,我们可以更好地理解和使用Linux操作系统

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 25
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

情话0.0

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值