第五章_进程 : Unix系统环境变量

函数getenv和putenv等

Unix系统的环境变量

Unix是一个多用户的操作系统,每个用户登录系统之后,都会有一个专用的运行环境。通常每个用户默认的环境都是相同的。这个默认的环境实际上就是一组环境变量的定义,用户可以对自己的运行环境进行定制,其方法就是修改相应的系统环境变量。

环境变量一般用大写加下划线命名(例如,PATH、ORACLE_HOME )。环境变量就相当于一个指针,当我们要查看指针所指向的值的时候需要解引用。同样的,当我们想要查看环境变量里面的值的时候,需要在前面加 $ 引用。

linux的变量分为环境变量和本地变量:
环境变量:是一种全局变量,存在所有的shell中,在登录的时候就有系统定义的环境变量了。linux的环境变量具有继承性,即shell会继承父shell的环境变量。
本地变量:当前shell中的变量,本地变量中包含环境变量。Linux的本地变量的非环境变量不具备继承性。

在Linux下面的变量按照生存周期可分为两类:
永久的:需要修改配置文件,变量永久生效。
临时的:使用export命令声明即可,变量在关闭shell时失效。

与环境变量有关的一些shell

echo $HOME 查看单个环境变量

env 查看所有环境变量

env | grep SH 可以和grep一起使用查看某一类环境变量

set 查看本地定义的环境变量

export HELLO = “hello” 设置一个新的环境变量(临时,重启后消失)

unset HELLO 清除环境变量

Linux系统常用的环境变量

PATH:决定了shell将到哪些目录中寻找命令或程序

HOME:指定用户的主目录(即用户登录到Linux系统时的默认目录)

​ 环境变量是一个变量,会随着用户的不同,它的值也就不同。

SHELL:指当前用户使用的shell类型

PS1:命令基本提示符,对root是 #,对普通用户是 $

存放环境变量的文件

(1)/etc/profile (所有用户)

这个文件是每个用户登录时都会运行的环境变量设置,当用户第一次登陆时该文件被执行,并从/etc/profile.d目录的配置文件中搜索shell的设置。这个文件的作用就是当用户登录的时候用于获取系统的环境变量,只在登录的时候获取一次。

添加 export PATH="/opt/hisi-linux/x86-arm/arm-hisiv300-linux/target/bin:$PATH"
执行 source /etc/profile

需要注意的是,修改完文件后想要马上生效还要运行 source /etc/profile 不然只能在下次重进此用户时生效。

(2)~/.bash_profile (单个用户)

每个用户都可以使用该文件输入自己专用的shell信息,当用户登录时,该文件仅仅执行一次。默认情况下,它设置一些环境变量,执行用户的.bashrc文件。单个用户对此文件的修改只会影响到它以后的每一次登录。

也就是说,在用户目录下的.bash_profile文件中增加变量,仅对当前用户永久生效, 操作同 /etc/profile

(3)/etc/bashrc (所有用户)

在执行完 /etc/profile 内容后,如果用户运行 bash shell 的话,则就执行这个文件,当**每次一个新的bash shell 被打开时,该文件被读取。**所以,如果每打开一个bash都执行某些操作,就可以在这个文件里面设置。

(4)~/.bashrc (单个用户)

该文件只包含专用于你的bash信息,当你登录时以及每次打开新的shell时,该文件就会自动被读取

source

“source a.sh” 与 “./a.sh” 的不同

比如你在一个脚本里 export $KKK=111,如果你用 ./a.sh 执行该脚本,执行完毕后,你运行 echo $KKK,发现没有值,如果你用source来执行,然后再 echo $KKK,就会发现输出 KKK=111。因为通过命令 ./a.sh 来执行 a.sh 脚本,这时,a.sh 脚本是在一个子shell里运行的,所以执行后,结构并没有反应到父 shell 里,但是 source 不同,它就是在本shell中执行的,所以可以看到结果。

环境列表

环境列表就是指环境变量的集合,而每个进程都拥有一张独立的环境列表,来保存和当前进程相关的环境变量信息。
环境列表,采用字符指针数组来存储所有的环境变量。
环境列表采用字符指针数组来存储所有的环境变量,使用全局变量char** environ来记录环境表的首地址,使用NULL 来代表环境表的结束。 所以访问环境表则需要借助environ变量。

//示例一 依次打印环境变量
// #include<stdio.h>
// int main()
// {
//     extern char** environ;
//     char** p = environ;
//     while(*p!=NULL){
//         printf("%s\n",*p);
//         p++;
//     }
//     return 0;
// }

//示例二 功能等同指令echo $SHELL,可自行尝试
#include <stdio.h>
#include <string.h>
 
int main(void)
{
	//声明全局变量
	extern char** environ;
	//给环境表首地址指定替身
	char** p = environ;
	//练习:查找名字为SHELL的环境变量,获取SHELL的值存到buf的字符数组中,然后进行打印
	char buf[20] = {0};
	p = environ;
	while(*p != NULL)
	{
		//比较前6个字符是否相等
		if(!strncmp(*p,"SHELL=",6))
		{
			//跳过环境变量名=
			strcpy(buf,*p+6);
			break;
		}
		//比较下一个
		p++;
	}
	printf("SHELL=%s\n",buf);
	return 0;
}

函数getenv

#include <stdlib.h>
char *getenv (const char *name);

函数功能:
表示根据参数指定的环境变量名去环境表进行查找
返回该环境变量所对应的环境值,查找失败则返回NULL.

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

在这里插入图片描述

函数setenv

#include <stdlib.h>
int setenv (const char *name, const char *value, int overwrite);

第一个参数:环境变量名
第二个参数:环境变量值
第三个参数:是否修改
函数功能:
改变或增加环境变量,如果参数指定的环境变量不存在则增加。如果存在并且 overwrite 是非0,则修改环境名变量值,否则环境变量不变
成功返回 0,失败返回 -1

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

int main()
{
    if(setenv("SHELL","no shell",1)!=0){
        printf("setenv error\n");
        exit(1);
    }
    printf ("修改后的SHELL = %s\n", getenv ("SHELL"));
    return 0;
}

在这里插入图片描述

函数putenv

#include <stdlib.h>
int putenv (char *string);

函数功能:
表示按照参数的内容增加/修改环境变量,其中string的格式为:name=value,如果不存在则添加,存在则删除
成功返回0,失败返回-1

#include <stdio.h>
#include <stdlib.h>
 
int main (void)
{
	char *pc = getenv ("SHELL");
	if (NULL == pc)
		perror ("getenv"), exit (-1);
	printf ("SHELL = %s\n", pc);
 
		
	int res = putenv ("SHELL=abc");
	if (res)
		perror("putenv"),exit(-1);
	printf ("修改后的SHELL = %s\n", getenv ("SHELL"));
 
	putenv("CSDN=666");
	printf("增加的环境值是:%s\n",getenv("CSDN"));//123
	return 0;
}

在这里插入图片描述

函数setenv和putenv的区别

setenv需要分配存储空间
Putenv将传递的参数字符串直接放入环境表
在这里插入图片描述

函数unsetenv

#include <stdlib.h>
int unsetenv (const char *name);

函数功能:
表示根据参数指定的环境变量去环境表中进行删除
成功返回0,失败返回-1

#include <stdio.h>
#include <stdlib.h>
 
int main (void)
{
	putenv("CSDN=666");
	printf("增加的环境值是:%s\n",getenv("CSDN"));//123
 
	unsetenv("CSDN");
	printf("删除的结果是:%s\n",getenv("CSDN"));
	return 0;
}

在这里插入图片描述

函数clearenv

#include <stdlib.h>
int clearenv (void);

函数功能:
表示清空整个环境表
成功返回0,失败返回-1

主函数的原型

main函数原型:

int main (int argc, char * argv[], char * envp[]) {…}

第一个参数:命令行参数的个数

第二个参数:命令行参数的地址信息

第三个参数:环境表的首地址

参数argc表示输入参数的个数(含命令名),如果有命令行参数,argc应不小于1;argv表示传入的参数的字符串,是一个字符串数组,argv[0]表示命令名,argv[1]指向第一个命令行参数;至于第三个参数env,它与全局变量environ相比也没有带来更多益处,所以POSIX.1也规定应使用environ而不使用第三个参数。通常用getenv和putenv函数来存取特定的环境变量,而不是直接使用environ变量。

上述这些函数是如何修改环境变量表的呢?

C语言存储空间布局
在这里插入图片描述1. 正文段(.text)。这是由CPU执行的机器指令部分。通常,正文段是可共享的,所以即使是频繁执行的程序(如文本编辑器、C编译器和shell等)在存储器中也只需有一个副本,另外,正文段常常是只读的,心防止程序由于意外而修改其指令。
2. 初始化数据段(.data)。通常将此段称为数据段,它包含了程序中需明确地赋初值的变量(已经初始化的非零全局变量)。例如,C程序中任何函数之外的声明:
int maxcount = 99;
使此变量以其初值存放在初始化数据段中。
3. 未初始化数据段(.bss)。通常将此段称为bss段,这一名称来源于时期汇编程序的一个操作符,意思是“由符号开始的块“(block started by symbol),在程序开始执行之前,(通常)内核将此段中的数据初始化为0或空指针。存放程序中未初始化的和零值全局变量。函数外的声明:

long sum[1000];

使此变量存放在非初始化数据段中。

text和data段都在可执行文件中,由系统从可执行文件中加载;而bss段不在可执行文件中,由系统初始化。
4. 栈(stack)。按内存地址由高到低方向生长,其最大大小由编译时确定。自动变量以及每次函数调用时所需保存的信息都存放在此段中。每次函数调用时,其返回地址以及调用者的环境信息(如某些机器寄存器的值)都存放在栈中。然后,最近被调用的函数在栈上为其自动和临时变量分配存储空间。通过心这种方式使用栈,C递归函数可以工作。递归函数每次调用自身时,就用一个新的栈帧,因此一次函数调用实例中的变量集不会影响另一次函数调用实例中的变量。
5. 堆(heap)。自由申请的空间,按内存地址由低到高方向生长,其大小由系统内存/虚拟内存上限决定。通常在堆中进行动态存储分配。由于历史上形成的惯例,堆位于未初始化数据段和栈之间。

每个线程都会有自己的栈,但是堆空间是共用的。

size命令报告正文段、数据段和bss段的长度(以字节为单位)。
在这里插入图片描述
其中第4列和第5列是分别民以十进制和十六进制表示的3段总长度。

删除环境字符串:在环境表中找到该指针,然后将所有后续指针都向表首部顺次移动
一个位置。
增加或修改环境字符串
➢ 占用进程地址空间的顶部,无法向高地址方向
扩展
➢ 不能移动在它之下的各栈帧(想象下整个存储
空间都要移动),也不能向低地址方向扩展
➢ 因此环境表空间无法增加

(1) 如果修改一个现存的 name:
(a) 如果新 value的长度少于或等于现存 value的长度,则只要在原字符串所用空间中写入新字符串。
(b) 如果新va l ue的长度大于原长度,则必须调用 ma l l oc为新字符串分配空间,然后将新字符写入该空间中,然后使环境表中针对 n ame的指针指向新分配区。
***(2) 如果要增加一个新的 name,则操作就更加复杂。***首先,调用 malloc为name =value分配空间,然后将该字符串写入此空间中。
(a) 然后,如果这是第一次增加一个新name,则必须调用malloc为新的指针表分配空间。将原来的环境表复制到新分配区,并将指向新 name=value的指针存在该指针表的表尾,然后又将一个空指针存在后。最后使 environ指向新指针表。再看一下图 7-3,如果原来的环境表位于栈顶之上 (这是一种常见情况 ),那么必须将此表移至堆中。但是,此表中的大多数指针仍指向栈顶之上的各 name=value字符串。
(b) 如果这不是第一次增加一个新 name,则可知以前已调用 malloc在堆中为环境表分配了空间,所以只要调用 realloc,以分配比原空间多存放一个指针的空间。然后将该指向新name= value字符串的指针存放在该表表尾,后面跟着一个空指针。

简单来说,就是
修改现存的name,分为长度小于现存value(直接利用所用空间写入)和长度大于现存value (malloc分配新空间,用环境表中name的指针指向)

增加新的name,第一次增加name(打造新的环境表,将name=value存在指针表后,+NULL ,使得原来的环境表指向新的环境表)。不是第一次增加(调用realloc,将新的name=value字符串存放在该表表尾+NULL)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值