【Linux】进程概念(二)

1.环境变量

1.1. 基本概念

  • 环境变量(environment variables)一般是指在操作系统中用来指定操作系统运行环境的一些参数
  • 能链接成功生成可执行程序,原因就是有相关环境变量帮助编译器进行查找
  • 环境变量通常具有某些特殊用途,还有在系统当中通常具有全局特性

1.1.1 命令行参数

指令选项实现原理

1.1.2 如何理解环境变量

  • 系统中存在环境变量,来帮助系统找到目标二进制文件
  • 系统中默认搜索路径:环境变量:PATH
  • 所以bash通过PATH来找到环境变量!
  • 指令env:查看环境变量
  • bash会形成两张表(命令行参数表+环境变量表)

1.2. 常见环境变量

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

1.3. 和环境变量相关命令

  • echo XXX:显示某个环境变量值(这里 是 "调取变量值的工具",echo $PATH)
  • export:设置一个新的环境变量
  • env:显示所有环境变量
  • unset:清除环境变量
  • set:显示本地定义的shell变量和环境变量

1.4. 环境变量的组织方式

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

1.5. 相关代码获取环境变量

1) 命令行第三个参数

2) 通过第三方变量environ获取

3) 系统调用获得环境变量:getenv函数

bash 复制代码
#include <stdio.h>
#include <stdlib.h>
int main()
{
 printf("%s\n", getenv("PATH"));
 return 0;
}

1.6. 环境变量通常具有全局属性

环境变量通常具有全局属性,可以被子进程继承下去

2. 程序地址空间

2.1 程序地址空间回顾

观察以下现象

第一段代码:

cs 复制代码
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
int g_val = 0;
int main()
{
 pid_t id = fork();
 if(id < 0){
 perror("fork");
 return 0;
 }
 else if(id == 0){ //child
 printf("child[%d]: %d : %p\n", getpid(), g_val, &g_val);
 }else{ //parent
 printf("parent[%d]: %d : %p\n", getpid(), g_val, &g_val);
 }
 sleep(1);
 return 0;
}
cs 复制代码
//输出:
parent[2995]: 0 : 0x80497d8
child[2996]: 0 : 0x80497d8

因为子进程按照父进程为模版,子进程并没有对变量进行进行任何修改,所以父子地址一样

第二段代码:

cs 复制代码
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
int g_val = 0;
int main()
{
 pid_t id = fork();
 if(id < 0){
 perror("fork");
 return 0;
 }
 else if(id == 0){ //child,⼦进程肯定先跑完,也就是⼦进程先修改,完成之后,⽗进程再
读取 
 g_val=100;
 printf("child[%d]: %d : %p\n", getpid(), g_val, &g_val);
 }else{ //parent
 sleep(3);
 printf("parent[%d]: %d : %p\n", getpid(), g_val, &g_val);
 }
 sleep(1);
 return 0;
}
cs 复制代码
//输出:
child[3046]: 100 : 0x80497e8
parent[3045]: 0 : 0x80497e8

父子进程输出地址是一致的,但是变量内容不一样

则可以得出:

2.2 虚拟地址

  • 父子进程输出变量不一样但地址一样,所以该地址绝对不是物理地址
  • 在Linux地址下,这种地址叫做虚拟地址
  • 在C/C++语言所看到的地址,全部都是虚拟地址
  • 物理地址,用户一概看不到,由OS统一管理

2.3 进程地址空间

同一个变量,地址相同,其实是虚拟地址相同,内容不同其实是被映射到了不同的物理地址

2.4 虚拟内存管理

mm_struct

描述linux下进程的地址空间的所有的信息的结构体是mm_struct (内存描述符)。每个进程只有一个mm_struct结构,在每个进程的task_struct结构中,有一个指向该进程的结构

cs 复制代码
struct task_struct
{
 /*...*/
 struct mm_struct *mm; 
 /*...*/
}

每一个进程都会有自己独立的mm_struct,这样每一个进程都会有自己独立的地址空间才能互不干扰

cs 复制代码
struct mm_struct
{
 /*...*/
 struct vm_area_struct *mmap; /* 指向虚拟区间(VMA)链表 */ 
 struct rb_root mm_rb; /* red_black树 */ 
 unsigned long task_size; /*具有该结构体的进程的虚拟地址空间的⼤⼩*/ 
 /*...*/
 // 代码段、数据段、堆栈段、参数段及环境段的起始和结束地址。 
 unsigned long start_code, end_code, start_data, end_data;
 unsigned long start_brk, brk, start_stack;
 unsigned long arg_start, arg_end, env_start, env_end;
 /*...*/
}

每⼀个进程都会有自己独立的mm_struct,操作系统肯定是要将这么多进程的mm_struct组织起来,虚拟空间的组织方式有两种:

  1. 当虚拟区较少时采取单链表,由mmap指针指向这个链表.
  2. 当虚拟区间多时采取红黑树进行管理,由mm_rb指向这棵树

vm_area_struct

inux内核使用 vm_area_struct 结构来表示一个独立的虚拟内存区域(VMA),由于每个不同质的虚 拟内存区域功能和内部机制都不同,因此一个进程使用多个vm_area_struct结构来分别表示不同类型的虚拟内存区域

cs 复制代码
struct vm_area_struct {
 unsigned long vm_start; //虚存区起始 
 unsigned long vm_end; //虚存区结束 
 struct vm_area_struct *vm_next, *vm_prev; //前后指针 
 struct rb_node vm_rb; //红⿊树中的位置 
 unsigned long rb_subtree_gap;
 struct mm_struct *vm_mm; //所属的 mm_struct 
 pgprot_t vm_page_prot; 
 unsigned long vm_flags; //标志位 
 struct {
 struct rb_node rb;
 unsigned long rb_subtree_last;
 } shared; 
 struct list_head anon_vma_chain;
 struct anon_vma *anon_vma;
 const struct vm_operations_struct *vm_ops; //vma对应的实际操作 
 unsigned long vm_pgoff; //⽂件映射偏移量 
 struct file * vm_file; //映射的⽂件 
 void * vm_private_data; //私有数据 
 atomic_long_t swap_readahead_info;
#ifndef CONFIG_MMU
 struct vm_region *vm_region; /* NOMMU mapping region */
#endif
#ifdef CONFIG_NUMA
 struct mempolicy *vm_policy; /* NUMA policy for the VMA */
#endif
 struct vm_userfaultfd_ctx vm_userfaultfd_ctx;
} __randomize_layout;

这张图就显示了,当堆在添加数据扩展时因为堆是向上增长的,地址空间起始都被存在vm里,就不会发混乱

2.5 为什么要有虚拟地址空间

  1. 将地址从"无序" 变"有序"
  2. 地址转化过程中,对操作或者地址进行合法性判断,进而保护了物理内存中的所有的合法数据,例如:为什么不能修改字符串??因为字符串存储在字符常量区,字符常量区数据被操作系统标记为 "只读",想要查询页表的时候,权限拦截了
  3. 物理内存的分配 和 进程的管理就可以做到没有关系,进程管理模块和内存管理模块就完成了解耦合
相关推荐
小杰帅气18 小时前
进程优先级与切换调度
linux·运维·服务器
方便面不加香菜18 小时前
Linux基本指令(1)
linux
济61718 小时前
linux(第十四期)--Uboot移植(1)-- Ubuntu20.04
linux
奋斗的阿狸_198618 小时前
键盘组合键监听与 xterm 唤醒程序
linux·运维·服务器
小张成长计划..18 小时前
【linux】2:linux权限的概念
linux·运维·服务器
马踏岛国赏樱花18 小时前
Windows与Ubuntu双系统,挂载D/E盘到Ubuntu下时只能读的问题
linux·windows·ubuntu
ben9518chen18 小时前
Linux操作系统基本使用
linux·运维·服务器
一个平凡而乐于分享的小比特18 小时前
CPU上电启动到程序运行全流程详解
linux·uboot·根文件系统·cpu上电到启动
不像程序员的程序媛19 小时前
Linux开机自启动systemd配置
linux·运维·服务器
GREGGXU19 小时前
Could not load the Qt platform plugin “xcb“ in ““ even though it was found.
linux·qt