Linux----进程概念(下)

news/2024/6/26 18:50:31

进程概念(下)

    • 7. 环境变量
      • ①概念
      • ②Linux常见的环境变量
        • 1.PATH
        • 2.HOME
        • 3.SHELL
        • 4.HISTSIZE
      • ③其他
        • 1.环境变量的函数
        • 2.本地变量和export
        • 3.set和env
        • 4.内建命令
        • 5.环境变量组织方式以及如何获取环境变量
    • 8. 命令行参数
    • 9. 初识进程地址空间 (重点,概览)
      • ①引入虚拟内存
      • ②虚拟内存
      • ③总结

7. 环境变量

①概念

环境变量(environment variables)

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

在windows中也有环境变量,快捷键win+r可以直接打开的都是在path里添加好绝对路径

在这里插入图片描述


②Linux常见的环境变量

注意:程序,命令,指令,可执行程序… … 都是一个概念

1.PATH

指定命令的搜索路径
在这里插入图片描述
当我们运行我们已编译好的可执行程序时有一个./表示当前路径
如果我们不带./系统会报错command not find
但系统命令ls cd 等就可以,这是因为系统在PATH(辅助系统进行指令查找)的帮助下能够找到
有两种方案

  1. 将自己的可执行程序添加到系统路径下
    命令:cp 程序 /usr/bin/
  2. 将当前的路劲添加到PATH
    用指令:PATH=$PATH:路径
    在这里插入图片描述

注意:环境变量是在用户登录时从配置文件加载的,所以方法2在重启后不会保留
环境变量的信息在bashrc里面
在这里插入图片描述

2.HOME

指定用户的主工作目录(即用户登陆到Linux系统中时,默认的目录)这里是引用
在这里插入图片描述

3.SHELL

当前Shell,它的值通常是/bin/bash常见shell还有sh,ksh,csh等这里是引用

4.HISTSIZE

可以保存历史命令的最多条数
在这里插入图片描述

③其他

1.环境变量的函数

头文件<stdlib.h>

函数功能
char *getenv(const char *name)获取环境变量
int setenv(const char *name, const char *value, int overwrite)设置环境变量

2.本地变量和export

本地变量:只能在当前shell命令行解释器内被访问,不能被子进程继承
环境变量:具有“全局属性”,可以被子进程继承
export 本地变量将本地变量导为环境变量

3.set和env

env只能显示环境变量
set可以显示环境变量和本地变量
在这里插入图片描述

4.内建命令

内建命令:shell程序内部的一个函数
echo export
shell执行的命令的是内建命令时就直接调用内建命令的方法(这也是为什么echo export可以使用本地变量),不是时会再去调用fork()创建子进程子进程不可继承本地变量

5.环境变量组织方式以及如何获取环境变量

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


获取方式

  1. libc中定义的全局变量environ指向环境变量表,environ没有包含在任何头文件中,所以在使用时 要用extern声明
    例如:
int main(int argc, char *argv[])
{
	extern char **environ;
	for(int i=0; environ[i]; i++)
		printf("%s\n", environ[i]);
	return 0;
}
  1. 通过命令行参数(详见下面

8. 命令行参数

main函数有三个参数:int argc, char *argv[], char *env[]
Linux中的指令有那么多选项也是通过命令行参数来实现的

  1. int argc和char *argv[]:argv[]指针数组元素个数是argc

有如下代码

int main(int argc,char *argv[])
{
	for(int i=0;i<argc;i++)
	        printf("%s\n",argv[i]);
}

在这里插入图片描述

  1. 第一个元素是 ./test
  2. 第二三四个是接下来的dd ff gg
  3. 最后一个元素是NULL

  1. char *env[],接收环境变量
int main(int argc, char *argv[], char *env[])
{
	for(int i=0; env[i]; i++)
		printf("%s\n", env[i]);
	return 0;
}

9. 初识进程地址空间 (重点,概览)

在C和C++学习的时候我们看到有关内存的操作时会联想到下面这张图
在这里插入图片描述
下面有一段代码用来验证各个区域的划分

printf("code addr         :%p\n",main);
const char *p="hello world";
printf("p(read_only)      :%p\n",p);
printf("global(g_val      :%p\n",&g_val);
printf("global(g_unval)   :%p\n",&g_unval);
//stack
int a=0;
int b=0;
static int c=0;
int d=0;
printf("stack addr(&a)    :%p\n",&a);
printf("stack addr(&b)    :%p\n",&b);
printf("stack addr(static &c)    :%p\n",&c);
printf("stack addr(&d)    :%p\n",&d);
//heap
char*q1=(char*)malloc(10);
char*q2=(char*)malloc(10);
char*q3=(char*)malloc(10);
char*q4=(char*)malloc(10);
printf("heap addr(q1)      :%p\n",q1);
printf("heap addr(q2)      :%p\n",q2);
printf("heap addr(q3)      :%p\n",q3);
printf("heap addr(q4)      :%p\n",q4);

printf("args addr(args[0]):%p\n",argv[0]);
printf("args addr(args[argc-1]):%p\n",argv[argc-1]);
printf("env addr(env[0])  :%p\n",env[0]);

在这里插入图片描述

①引入虚拟内存

下面有一段代码

printf("At begin g_val is: %d\n",g_val);
pid_t id=fork();
if(id==0)
{//child
	int count =0;
	while(1)
	{
	    printf("child pid: %d, ppid: %d, g_val=%d, [&g_val=%p]\n",
	    						getpid(),getppid(),g_val,&g_val);
	    sleep(1);
	    count++;
	    if(count==5)
   		g_val=100;
	}
}
else if(id>0)
{//parent
	while(1)
	{
	    printf("parent pid: %d, ppid: %d, g_val=%d, [&g_val=%p]\n",
	    						getpid(),getppid(),g_val,&g_val);
	    sleep(1);
	}
}
else
	//TODO

这里是引用
可以看到:父子进程的g_val值不一样(代码共享,数据各自私有一份:写时拷贝)
但为什么它们有相同的地址[0x601048]呢?

  1. 变量内容不一样,所以父子进程输出的变量绝对不是同一个变量
  2. 但地址值是一样的,说明,该地址绝对不是物理地址!
  3. 在Linux地址下,这种地址叫做 虚拟地址
  4. 我们在用C/C++语言所看到的地址,全部都是虚拟地址!物理地址,用户一概看不到,由OS统一管理

②虚拟内存

<冯诺依曼>规定: 程序运行的时候,代码和数据一定在物理内存上
而将虚拟地址转化为物理地址的操作由OS完成

在32位下, 操作系统默认会给给个进程构成一个地址空间的概念(0000…00——FFFF…FF) 4GB的空间

在Linux中描述进程的是task_struct,而描述进程空间是用的一个结构体mm_struct
下面是Linux2.6.39的源码
在这里插入图片描述
形象表现如下图在这里插入图片描述
而完成虚拟内存到物理内存的映射的工作由页表完成
页表:是一种特殊的数据结构,放在系统空间的页表区,存放逻辑页与物理页帧的对应关系。 每一个进程都拥有一个自己的页表,PCB表中有指针指向页表
在这里插入图片描述
有了这种映射关系,虚拟内存完全可以完全一样


那么为什么不直接访问内存呢

  1. 如果直接进程访问物理内存,那么看到的地址就是物理地址,使用指针造成越界(越界不一定报错,关键字:金丝雀保护机制)
  2. 进程的独立性,无法保证
  3. 因为物理内存暴露,其中就有可能有恶意程序直接通过物理地址,进行内存数据的篡改,也可以读取

虚拟地址到物理地址的-一个转化,由OS来完成的,同时也可以帮系统进行合法性检测
每个页表项会有许可位来控制对一个虚拟页面内容的访问,如果一条指令违反了许可条件,CPU就会触发保护机制,Linux Shell一般将这种异常报告为段错误(segmentation fault)
例子C语言中对字符常量的修改
在这里插入图片描述


为了会进行回收等操作,内存需要知道某个进程的退出内存,所以内存管理模块和进程管理模块是强耦合的,
管理只需要知道那些内存区域(page)是无效的,哪些是有效的将内管管理 所以和进程管理进行解耦
底层类似智能指针,每次申请空间就会给一个count变量++,回收一次count就–当count为0就进行解耦

为什么进程地址空间是按照区域划分的
在磁盘中的可执行程序,本身是按文件的方式组织成一个个的区域
在这里插入图片描述
链接过程更方便
由于程序在物理空间内的存放位置完全是根据当前内存状态存放的,为了能让进程(PCB)找到 ,进程地址空间也进行了区域划分,通过页表将所有的数据整合起来,使在地址空间看到的和在磁盘看到的是同一种物理排序
有了地址空间,我们就可以在确定的位置,执行代码的入口,完成运行
物理内存是以页为单位的,一页为4kb,而程序中的每一个块被分为若干个4kb,一个4kb叫做页帧


回到最开始的问题:为什么父子进程的g_val地址一样

  1. 当创建子进程的时候,子进程的task_struct,mm_struct ,页表等都会以父进程为模板创建,(默认继承父进程的大部分属性)
  2. 父子进程共享一份代码,控制语句if else 控制父子进程执行的代码块
  3. 父子进程的进程地址空间里的虚拟地址&g_val一定一样,当子进程想对g_val进行写的时候,操作系统发现进程只有读权限,于是操作系统在物理内存开辟一个新的和g_val一样的空间,将g_val拷贝进去,同时让子进程的页表映射到这块空间,同时具有写权限
  4. 在上层看来虚拟地址是一样的,只是映射到的物理地址变化了,本质上进行了写时拷贝

③总结

进程和程序的区别:(宏观)
进程包括:task_struct, mm_struct 页表代码和数据在这里插入图片描述


四个问题:

  1. 进程地址空间究竟是什么

地址空间本质是进程看待内存的方式,抽象出来的一一个概念,内核struct mm. struct. 这样的每个进程,都认为自己独占系统内存资源
区域划分本质:将线性地址空间划分成为一个七个的区域[start, end]
虚拟地址本质:在[start, end]之 间的各个地址叫做虚拟地址

  1. 为什么要存在地址空间?

保护物理内存,不收到任何进程内的地址的直接访问,方便进行合法性校验
将内存管理和进程管理进行解耦
让每个进程,以同样的方式来看待代码和数据

  1. 验证地址空间的基本排布

(待补充)

  1. 地址空间vs物理内存之间的关系

虚拟地址:和物理地址之间是通过页表完成的映射关系


http://www.niftyadmin.cn/n/4608930.html

相关文章

C++初阶---array forward_list 模板进阶

array forward_list 模板进阶1&#xff09;arrary2&#xff09;forward_list3&#xff09;模板的特化1.函数模板特化2.类模板特化①全特化②偏特化4&#xff09;模板分离编译①分离编译②函数模板分离编译③类模板其他问题5&#xff09;模板总结1&#xff09;arrary 静态数组 注…

Expo大作战(二十三)--expo中expo kit 高级属性(没干货)

简要&#xff1a;本系列文章讲会对expo进行全面的介绍&#xff0c;本人从2017年6月份接触expo以来&#xff0c;对expo的研究断断续续&#xff0c;一路走来将近10个月&#xff0c;废话不多说&#xff0c;接下来你看到内容&#xff0c;讲全部来与官网 我猜去全部机翻个人修改补充…

bzoj1036: [ZJOI2008]树的统计Count link-cut-tree版

题目传送门 这 算是link-cut-tree裸题啊 不过以前好像没有写过单点修改.............. #include<cstdio> #include<cstring> #include<algorithm> #define LL long long using namespace std; const int M50007; int read(){int ans0,f1,cgetchar();while(c&…

【吴恩达】prompt engineering(原则 迭代 文本概括 推断、订餐机器人)

简介 Introduction 基础的LLM训练的模型&#xff0c;问法国的首都什么&#xff0c;可能会将答案预测为“法国最大的城市是什么&#xff0c;法国的人口是多少”许多 LLMs 的研究和实践的动力正在指令调整的 LLMs 上。指令调整的 LLMs 已经被训练来遵循指令。因此&#xff0c;如…

Linux----进程控制(上)

Linux----进程控制&#xff08;上&#xff09;1&#xff09;进程创建fork()① fork()返回值为什么有两个&#xff08;返回两次&#xff09;&#xff1f;② fork()常见使用场景③ fork()调用失败的原因2&#xff09;进程终止进程退出的情况分类进程退出方法①exit()②_exit()在操…

14.Nginx防盗链Nginx访问控制Nginx解析php相关配置Nginx代理

[toc] 一、Nginx防盗链&#xff1a; 1. 打开配置文件&#xff1a; 增加如下配置文件&#xff1a; [rootxavi ~]# cd /usr/local/nginx/conf/vhost/ [rootxavi vhost]# vim test.com.conf} # location ~ .*\.(gif|jpg|jpeg|png|bmp|swf)$# {# expires 7d;# …

安装戴尔OMSA

设置变量versionumcat /etc/redhat-release | awk {print $3} | awk -F . {print $1}versionnamecat /etc/redhat-releaseif [ $versionum 5 ];then echo $versionname" Tikanga" > /etc/redhat-release echo "OM-SrvAdmin-Dell-Web-LX-7.4.0-866.RHE…

Linux----进程控制(下)

Linux----进程控制&#xff08;下&#xff09;3&#xff09;进程等待wait()waitpid()①status②option(阻塞与非阻塞)4&#xff09;进程程序替换替换函数①execl系列&#xff08;l&#xff1a;list&#xff09;②execv系列&#xff08;v&#xff1a;vector&#xff09;场景3&am…