Linux进程概念——其二

这篇具有很好参考价值的文章主要介绍了Linux进程概念——其二。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

目录

环境变量

基本概念

常见环境变量

查看环境变量方法

测试PATH[重点]

测试HOME

和环境变量相关的命令

环境变量的组织方式

通过代码获取环境变量

通过系统调用获取或设置环境变量

环境变量通常是具有全局属性的[重点]

程序地址空间

研究背景

程序地址空间回顾

程序地址空间新认识

进程地址空间

程序——>进程

g_val的地址相同值不同解释

fork为什么两个返回值

虚拟空间存在的意义


环境变量

基本概念

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

常见环境变量

  • PATH : 指定命令的搜索路径 [重点]
  • Linux进程概念——其二
  • HOME : 指定用户的主工作目录(即用户登陆到Linux系统中时,默认的目录)[重点] 

Linux进程概念——其二

  • SHELL : 当前Shell,它的值通常是/bin/bash

Linux进程概念——其二

  • HOSTNAME:当前主机的机器名

Linux进程概念——其二

  •  HISTSIZE:系统一次允许记录使用过的命令的最多条数

Linux进程概念——其二

查看环境变量方法

  • echo $NAME    //NAME:你的环境变量名称[重点]

比如查看PATH环境变量,使用以上命令:

Linux进程概念——其二

测试PATH[重点]

  • 创建myprocess.c文件
    #include <stdio.h>
    int main()
    {
         printf("hello world!\n");
         return 0;
    }
    
  • 对比./myprocess执行和之间myprocess执行

发现后者报错:

Linux进程概念——其二

  • 为什么有些指令可以直接执行,不需要带路径,而我们的二进制程序需要带路径才能执行?

因为其他的指令的二进制文件路径被添加到了PATH环境变量中,而我们的没有

  • 将我们的程序所在路径加入环境变量PATH当中, export PATH=$PATH:hello程序所在路径

Linux进程概念——其二

  • 还有什么方法可以不用带路径,直接就可以运行呢?

直接将我们的文件拷贝到指令集的二进制文件目录(/usr/bin)下

测试HOME

  • 用root和普通用户,分别执行 echo $HOME,对比差异

Linux进程概念——其二

Linux进程概念——其二

  • 执行 cd ~; pwd,对应 ~ 和 HOME 的关系

Linux进程概念——其二

 cd ~即访问的是HOME的变量值

和环境变量相关的命令

  • echo: 显示某个环境变量值[重点]

比如我们想查看PATH环境变量,如下图:

Linux进程概念——其二

  • export: 设置一个新的环境变量[重点]

Linux进程概念——其二

 通过上述实验发现确实可以,当如果想要把我们自己的路径添加到PATH中时,如下图:

Linux进程概念——其二

发现我们是修改了PATH环境变量,并不是添加,此时PATH中环境变量全没了

由于没有修改系统中的配置文件只命令行上修改是临时性的,只要重新登录终端就恢复了,如下图:

Linux进程概念——其二 增加环境变量的正确操作应该如下图:

Linux进程概念——其二

我们发现在原来的基础增加了新的PATH环境变量,而我们使用的命令是export PATH=$PATH:环境变量值这个命令的意思是$PATH是把PATH中的所有环境变量提取出来放到PATH中,:后面是新增加一个环境变量

  • env: 显示所有环境变量[重点]
  • unset: 清除环境变量

清除环境变量方式如下图:

Linux进程概念——其二

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

使用set | less命令能看到这些全是环境变量和shell本地变量

Linux进程概念——其二

Linux进程概念——其二

可以看到我们定义的本地shell变量和环境变量都可以查看到 

环境变量的组织方式

Linux进程概念——其二

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

通过代码获取环境变量

  • 命令行第三个参数[重点]
#include <stdio.h>
int main(int argc, char *argv[], char *env[])
{
     for(int i = 0; env[i]; i++){
         printf("%s\n", env[i]);
     }
     return 0;
}

main函数的第三个参数接受的就是环境变量,打印env即打印的是环境变量

  • 通过第三方变量environ获取[重点]
#include <stdio.h>
int main(int argc, char *argv[])
{
     extern char **environ;
     for(int i = 0; environ[i]; i++){
         printf("%s\n", environ[i]);
     }
     return 0;
}

libc中定义的全局变量environ指向环境变量表,environ没有包含在任何头文件中,所以在使用时要用extern声明。

通过系统调用获取或设置环境变量

  • putenv
  • getenv
#include <stdio.h>
#include <stdlib.h>
int main()
{
 printf("%s\n", getenv("PATH"));
 return 0;
}

常用getenv和putenv函数来访问特定的环境变量。

例如使用getenv获取PATH环境变量:
Linux进程概念——其二

环境变量通常是具有全局属性的[重点]

  • 环境变量通常具有全局属性,可以被子进程继承下去
#include <stdio.h>
#include <stdlib.h>
int main()
{
     char * env = getenv("MYENV");
     if(env){
         printf("%s\n", env);
     }
     return 0;
}

首先定义一个bash本地变量MYENV,发现没有结果,说明该环境变量根本不存在,导出环境变量 export MYENV="hello world",再次运行程序后会发现就会有hello world。

解释:MYENV被定义且没有使用export时,此时MYENV是bash本地变量,我们使用getenv时返回的是null,而使用export导出MYENV后就返回的是hello world,说明MYENV变成了环境变量。

由于我们能访问到本来是在bash中被定义的本地变量MYENV,而MYENV变成了环境变量后我们作为bash的子进程的程序也能访问到MYENV,说明了MYENV具有了全局属性,可以被子进程继承下去

额外的:既然子进程无法访问作为父进程bash中的本地变量,但是却可以使用echo输出bash中的本地变量。虽然Linux大部分命令都是通过子进程的方式执行的,但是还有一部分命令不通过子进程的方式执行,而是由bash自己执行(调用自己对应的函数来完成特定的功能,我们把这种命令叫做自建命令)

程序地址空间

研究背景

  • kernel 2.6.32
  • 32位平台

程序地址空间回顾

学习C语言的时候,接触过如下空间布局图:

Linux进程概念——其二

 使用下面的代码验证一下:

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

int ug_val;
int g_val = 100;

int main(int argc, char* argv[], char* env[])
{
    printf("main addr:%p\n", main);
    printf("init addr:%p\n", &g_val);
    printf("uninit addr:%p\n", &ug_val);
  
    char* m1 = (char*)malloc(100);
    char* m2 = (char*)malloc(100);
    char* m3 = (char*)malloc(100);
    char* m4 = (char*)malloc(100);
    static int n = 100;//全局数据区
 
    printf("static addr:%p\n", &n);                                                                                                                     
 
    printf("heap addr:%p\n", m1);
    printf("heap addr:%p\n", m2);
    printf("heap addr:%p\n", m3);
    printf("heap addr:%p\n", m4);

    printf("stack addr:%p\n", &m1);
    printf("stack addr:%p\n", &m2);
    printf("stack addr:%p\n", &m3);
    printf("stack addr:%p\n", &m4);
 
    for(int i = 0; i < argc; ++i)
        printf("argv addr:%p\n", argv[i]);
    for(int i = 0; env[i]; ++i)
        printf("env addr:%p\n", env[i]);
    return 0;
 }

然后运行该程序:

Linux进程概念——其二

根据输出结果仔细对比上面的图发现确实如此,栈是向下增长的,堆是向上增长的,相对而生

程序地址空间新认识

根据程序地址空间,认识地址空间的存在,有如下代码:

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
 
int g_val = 100;

int main()
{
    pid_t id = fork();
    if(id == 0)
    {
         int flag = 0;
         while(1)
         {
             printf("I am son: pid:%d, ppid:%d, g_val=%d, g_val's address:%p\n", getpid(), getppid(), g_val, &g_val);
             flag++;
             sleep(1);
             if(flag == 5)
             {
                 g_val = 200;
                 printf("warning:the number has been changed!\n");
             }
        }
   }
   else
   {
     while(1) 
     {
         printf("I am father: pid:%d, ppid:%d, g_val=%d, g_val's address:%p\n", getpid(), getppid(), g_val, &g_val);                                     
         sleep(1);
     }
   }
   return 0;
 }

运行该程序,在flag等于5之前:

Linux进程概念——其二

父进程和子进程的g_val值一样,地址也一样,这里很好理解,因为子进程是以父进程为模板创建的。

当flag等于5以后,出现了一个无法理解的现象:

Linux进程概念——其二 父进程和子进程中的g_val地址一样,但是值却不一样! 能得出如下结论:

  • 变量内容不一样,所以父子进程输出的变量绝对不是同一个变量!
  • 地址值是一样的,说明该地址绝对不是物理地址!
  • 在Linux地址下,这种地址叫做虚拟地址(也叫逻辑地址或者线性地址)
  • 我们在用C/C++语言所看到的地址,全部都是虚拟地址!物理地址,用户一概看不到,由操作系统统一管理!

操作系统必须负责将虚拟地址转化成物理地址 。

所以说“程序地址空间”的说法是不准确的,准确的应该说成进程地址空间

进程地址空间

每一个进程都会有一个自己的进程地址空间。

地址空间:操作系统通过软件的方式,给进程提供一个软件视角,认为自己会独占系统的所有资源(主要体现在内存上)。

操作系统通过先描述再组织的方式管理进程地址空间,也就是说进程地址空间其实是内核的一个数据结构(struct mm_struct)

Linux进程概念——其二

Linux进程概念——其二我们发现在内核源码确实存在struct mm_struct,操作系统将虚拟地址分为了很多区域,如栈区,堆区等,每个区域都有一个start和end来划分区域,只不过是使用链表的方式划分的,而最后会经过页表的方式映射到物理内存上

程序——>进程

当一个程序写完并编译后未被加载,程序内部有无地址?

有,从链接中可以体现出来,链接会将我们的程序和库文件产生联系,而产生联系的方式就是需要使用地址

当一个程序写完并编译后未被加载,程序内部有无区域?

有,使用readelf -S命令查看编译好的二进制文件:

Linux进程概念——其二

发现文件中确实有address(地址)和offset(偏移量) 

我们所熟知的是只有当程序加载到内存中才有地址,但是这里并未加载到内存中怎么会有地址?

说明编译好文件中的地址并非物理地址,而是虚拟地址,当加载到内存中的时候经过相关转化的处理即可,所以在编译程序的时候就已经编址好了,等加载到内存中经过页表的处理映射到物理内存就行了

注:虚拟地址空间不仅操作系统会考虑,编译器也会考虑!

g_val的地址相同值不同解释

这里就能解释上面的父进程和子进程中的g_val地址一样,但是值却不一样,如下图(只划分出了数据,未划分出代码的存储,和数据存储的情况一致):

当数据未被修改时,由于子进程是以父进程为模板创建的,因此两个进程虚拟地址一致,并且映射到了同一个物理地址上,也就是flag=5之前的现象

Linux进程概念——其二

但是,当我们进行写入时,无论是父进程还是子进程,由于进程具有独立性,进程之间互不影响,因此就会发生写时拷贝,操作系统重新给子进程开辟新的空间并将g_val的值拷贝到开辟的新空间中,两个进程不在使用同一块物理空间,如下图:

写时拷贝:父进程/子进程尝试修改对应的数据时,操作系统会给修改的一方重新开辟一块空间并将原始数据拷贝到新空间当中

Linux进程概念——其二

通过页表,将父子进程的数据就可以通过写时拷贝的方式进行分离,做到父子进程具有独立性!

所以就出现了地址一样但却值不一样的现象,本质上就是虚拟地址一样但是物理地址不一样

fork为什么两个返回值

使用fork时我们知道它有两个返回值,而我们还是使用同一个变量(pid_t id)去接收的,怎么做到的?

pid_t id是属于父进程栈空间定义的变量,fork内部的return被执行两次,而return是通过寄存器将返回值写入到接收返回值的变量中

当id=fork()时,先返回的进程就要先发送写时拷贝,所以同一个变量有不同的返回值,本质上就虚拟地址一致但是物理地址不一样

虚拟空间存在的意义

为什么存在虚拟地址?

  • 保护内存

直接让进程访问物理内存是不安全的,虚拟地址的存在让访问内存添加了一层软硬件层,能对转化过程进行审核,拦截非法访问。以及防止越界后进程之间互相影响或者访问到操作系统的进程导致进程崩溃

  • Linux内存管理

按需提供物理内存,并非一次性就给申请的全部空间,使用到多少空间给多少空间,通过地址空间进行功能模块的解耦,提高Linux操作系统的运行效率

  • 让进程或者程序可以以统一的视角看待内存

方便以统一的方式来编译和加载所有的可执行程序,简化进程本身的设计与实现文章来源地址https://www.toymoban.com/news/detail-430686.html

到了这里,关于Linux进程概念——其二的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【Linux】进程优先级 && 进程切换 && 环境变量

    目录 一、进程优先级  1、优先级概念  2、优先级特点  3、修改Linux下的优先级 二、进程切换  1、进程特性  2、进程切换 三、环境变量  1、基本概念  2、常见环境变量  3、查看环境变量方法  4、PATH环境变量  5、和环境变量相关的命令  6、环境变量的组织方式  7、通过

    2024年02月11日
    浏览(47)
  • Linux之进程(三)(环境变量)

    目录 一、基本概念 二、环境变量 1、PATH 2、HOME 3、SHELL  三、环境变量参数 四、argc和argv 环境变量(environment variables)一般是指在操作系统中用来指定操作系统运行环境的一些参数。如:临时文件夹位置和系统文件夹位置等。环境变量通常具有某些特殊用途,还有在系统当中通

    2024年02月04日
    浏览(39)
  • 【Linux】进程状态|优先级|进程切换|环境变量

    💕 运行队列: 进程是如何在CPU上运行的:CPU在内核上维护了一个运行队列,进行进程的管理。让进程进入队列,本质就是将该进程的task_struct 结构体对象放入运行队列之中。这个队列在内存中,由操作系统自己维护。 💕 运行状态: 运行状态 进程PCB在运行队列里就是运行

    2024年02月02日
    浏览(37)
  • Linux中的进程、fork、进程状态、环境变量

            进程信息被放在一个叫做进程控制块的数据结构中,可以理解为进程属性的集合。课本上称之为PCB(process control block),Linux操作系统下的PCB是: task_struct 在Linux中描述进程的结构体叫做task_struct。task_struct是Linux内核的一种数据结构,它会被装载到RAM(内存)里并且包

    2024年02月10日
    浏览(43)
  • 【Linux】进程优先级 + 环境变量

    在学完进程状态之后,本章我们将来学习一下环境变量,还有进程优先级等🙋🙋🙋…… 本文实验系统: CentOS 7.6 ~ 优先级是权限吗? 在之前我们讲解过什么是权限:👉 传送门 我们这里讲的优先级并不是权限,那么优先级到底是什么呢? 如果我们说权限是能不能的问题的

    2024年02月05日
    浏览(45)
  • 【Linux】进程周边005之环境变量

      👀 樊梓慕: 个人主页  🎥 个人专栏: 《C语言》 《数据结构》 《蓝桥杯试题》 《LeetCode刷题笔记》 《实训项目》 《C++》 《Linux》 🌝 每一个不曾起舞的日子,都是对生命的辜负 目录 前言 1.环境变量是什么? 1.1查看环境变量的方法 1.2常见的环境变量 1.2.1PATH 1.2.2USER

    2024年02月04日
    浏览(56)
  • 【Linux】探索Linux进程优先级 | 环境变量 |本地变量 | 内建命令

    最近,我发现了一个超级强大的人工智能学习网站。它以通俗易懂的方式呈现复杂的概念,而且内容风趣幽默。我觉得它对大家可能会有所帮助,所以我在此分享。点击这里跳转到网站。 🎉博客主页:小智_x0___0x_ 🎉欢迎关注:👍点赞🙌收藏✍️留言 🎉系列专栏:Linux入门

    2024年02月04日
    浏览(68)
  • Linux下进程的特点与环境变量

    目录 进程的特点 进程特点的介绍 进程时如何实现并发性的 进程间如何切换 概念铺设 PC指针 上下文 环境变量 PATH 修改PATH HOME SHELL env 命令行参数 什么是命令行参数? 打印命令行参数 通过函数获得环境变量 getenv 命令行参数 env 修改环境变量 环境变量总结 本地变量 内建命令

    2024年02月13日
    浏览(16)
  • 【Linux】环境变量和进程优先级

    目录 环境变量 什么是环境变量 系统结构 系统接口 深度解析 命令行参数 进程优先级 优先级查看 优先级修改 进程间的概念 🍮平时在使用  Linux  的时候,总会使用  ls  、 pwd  这类的命令,我们也都知道这些命令也是  C  语言写的,但是为什么我们自己写的可执行程序需

    2023年04月27日
    浏览(51)
  • [Linux 进程(三)] 进程优先级,进程间切换,main函数参数,环境变量

    排队的本质就是确认优先级。 优先级是什么?它也是PCB中的一个整型字段 , 数值越小,优先级越高 。是得到某种资源的先后顺序。 Linux进程的优先级数值范围:60~99。 Linux中默认进程的优先级都是80。 为什么要有优先级 ?本质是资源不足。 谈到优先级,就不得不说我们以

    2024年01月21日
    浏览(46)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包