【OS】Linux Process Memory的探究

news2024/12/24 21:29:59

【OS】Linux Process Memory的探究

前言

刷B站刷到了南大OS的课程,不得不说酒吧舞的教育水平真滴高,狠狠的看了一些关于进程地址相关的课程。

进程地址空间

1.导言

先导入两个问题:

  • 如下的程序会输出什么?

    #include<stdio.h>
    
    int main() {
        printf("%p\n", main);
    }
    
  • 如果生成一个随机指针,什么情况会segmentation fault?

    chat *ptr = random();
    *p;
    

要解决上述两个问题,我们可以向进程空间中尝试了解。

那么,在Linux中,一个进程的地址空间是如何映射的?可以使用什么样的手段去查看某个进程的地址分配呢?

2.手动解决第一个问题

实践是检验真理的唯一标准,我们可以通过编译的方式来查看第一个问题的答案。

  • 动态编译

    gcc -o libc_ main.c
    
  • 静态编译

    gcc --static -o static_ main.c
    
  • 写成一个Makefile

    all: clean static libc
    clean:
    	rm -rf static_ libc_
    static:
    	gcc --static -o static_ main.c
    libc:
    	gcc -o libc_ main.c
    

运行两个程序,静态编译的结果每次都是相同的0x401745,而动态编译出来的结果是0x55ff1b6ff149,并且每次除了后3位不变其他都是会变的。

(之所以动态编译的main函数地址会变,是因为gcc编译的选项默认开启了PIE的保护手段,会将代码段的函数地址给随机化)

3.尝试分析结果

上述的实验结果已经看到了,那么为什么会这样呢?同时0x40开头的地址和0x55开头的地址分别是什么?

这些问题的答案我们不妨使用gdb来调试查看

针对静态编译的文件,进行starti的操作,来到起点函数,随后执行vmmap进行查看

我这里可以执行vmmap是因为安装了gdb的插件pwndbg

image-20230424161815295

可以非常鲜明的看到当前的内存状态。vmmap函数的底层是通过读取/proc/[pid]/maps这个文件来进行内存的查看的,我们直接cat这个文件来看看

image-20230424162150342

通过ps来读取static_这个文件的pid,随后进行maps的查看

我们的main函数地址就在0x401000~0x498000这一段代码段中,具有可执行权限。

main函数的位置打个断点,然后continue到main函数的开始部分,查看maps

image-20230424162449563

我们发现0x4c5000这个具有可读可写的段存放的其实是got表,同时0x4c8000开始的一段heap内存在经历_start函数之后被扩容了

随后就是0x7f开头的地址了,那么这里的vvar、vdso分别是什么呢?

(stack我们知道就是执行函数调用栈的一个内存数据结构)

看玩静态编译的程序后,我们再来看看动态编译的程序!

image-20230424163852167

这是在_start函数之前,maps的状态

可以看到比起静态编译,多了4段,都是与ld-linux-x86-64.so.2有关的。

ld是用来连接libc文件的,所以会在_start函数中将libc连接到这个进程中

我们continuemain函数的位置,再来看看maps的状态:

image-20230424164107693

惊奇的发现,libc.so.6被注入了进程,这就意味着我们可以调用库函数了。

但是这里的anon又是什么东西?gpt-3.5给出的解释如下:

在Linux中,anon是一种匿名内存映射,它是一个没有关联文件名的内存段。它通常用于动态分配内存、堆栈和共享内存等用途。

当进程需要使用一些内存时,它可以通过系统调用(如malloc())向内核请求一段内存。如果这个内存区域不需要关联任何文件,那么内核会创建一个anon内存段来为进程提供所需的内存。

anon内存段的创建时机包括:

  1. 进程调用malloc()等动态内存分配函数来分配内存;
  2. 进程调用mmap()函数映射一块匿名内存区域;
  3. 进程使用堆栈时,堆栈也是使用anon内存段分配的。

anon内存段在进程终止或释放内存时会被销毁。

看到上述的回答我自己都有点疑惑,malloc申请出来的内存不是会在[heap]中吗?

别急,gpt-3.5给出的解释如下:

你提到的heap是一个进程的堆空间,它用于存储动态分配的内存。当进程调用malloc()等函数分配内存时,它们通常会在堆空间中分配内存。

anon内存段与堆空间是不同的概念。anon内存段通常是使用mmap()函数映射的一块匿名内存区域,或者是操作系统通过其他方式动态分配的一块内存。anon内存段通常不会被标识为heap,因为它们与堆空间的分配方式不同。

当进程使用malloc()等函数分配内存时,通常会在堆空间中分配内存,但是在某些情况下(例如大块内存分配),malloc()函数可能会选择使用mmap()函数来分配内存。此时,分配的内存将被标识为heap。但是,即使是使用mmap()函数分配的内存也可能不会被标识为heap,而是被标识为anon或其他类型的内存段。

因此,anon内存段不一定是通过malloc()函数分配的,也不一定与堆空间有关。它们通常是使用mmap()函数映射的一块匿名内存区域,或者是操作系统通过其他方式动态分配的一块内存。

也就是说,anon是一种不需要关联文件的内存段。

4.拓展vvar与vdso

咱们看到啥,不懂啥,就去学啥。这里对于vvarvdso的解释是来自gpt3.5的

在 Linux 中,vvar 和 vdso 都是进程虚拟地址空间中的特殊区域。

vvar(variable address)是一段特殊的内核虚拟地址空间,它存储了一些全局变量,这些变量与进程有关,例如进程 ID、线程 ID 等。它通常位于进程虚拟地址空间的高地址区域。

vdso(virtual dynamic shared object)是一种特殊的共享库,它包含了一些用户态应用程序可以使用的系统调用的实现。vdso 不同于一般的共享库,它是直接映射到用户态应用程序的地址空间中,而不需要通过动态链接器进行加载。这种技术可以避免进入内核态的开销,从而提高系统调用的性能。

总的来说,vvar 和 vdso 都是 Linux 内核提供的一种机制,用于提高进程与内核之间的通信效率和性能。

这里对vdso进行一下拓展的介绍。

咱们在C中或多或少调用过这样一个函数time(),获取当前的时间。

但是有没有想过,如果每次调用time这种函数陷入内核态,再回到用户态,开销是否太大了?

所以可以使用vdso的技术,将时间戳等信息共享在vdso中,让用户调用time()的时候,无需陷入内核态就可以获取时间

5.总结

上述使用gdb的操作,让我们了解到了进程中的内存空间是怎样的。

其实除了vmmap这种拓展的gdb指令,使用linux自带的pmap指令,或者直接去cat /proc/[pid]/maps查看进程内存map也是可以的

我们在实践后,可以得到如下的结论:

  • 进程的地址空间是若干个连续的内存段
  • 每个有着不同的权限(rwx)
  • 如果触发了违反段权限的操作,就会sigsegv

管理进程地址空间

1.导言

在上述见识到了一个process的maps中都有什么之后,我们应该有这样的思考?

  • 一个进程的地址空间如何进行管理?
  • Linux中有什么系统调用与进程空间相关?

2.mmap

这里直接给出一种修改进程地址空间的系统调用mmap

要使用mmap,首先得#include<sys/mman.h>,因为在这个头文件中有mmap的定义

void *mmap (void *__addr, size_t __len, int __prot, int __flags, int __fd, __off_t __offset);
int munmap (void *__addr, size_t __len);
int mprotect (void *__addr, size_t __len, int __prot);
  • mmap是获取内存映射
  • munmap是释放内存映射
  • mprotect则是修改内存映射空间的权限

来做一个例子:

#include <unistd.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>

#define GiB *(1024LL * 1024 * 1024)

int main() {
    volatile uint8_t *p = mmap(NULL, 8 GiB, PROT_READ | PROT_WRITE, 0x20 | MAP_PRIVATE, -1, 0);
    printf("mmap: %lx\n", (uintptr_t)p);
    if ((intptr_t)p == -1) {
        perror("cannot map");
        exit(1);
    }
    *(p + 2 GiB) = 1;
    *(p + 4 GiB) = 2;
    *(p + 7 GiB) = 3;
    printf("Read get: %d\n", *(p + 4 GiB));
    printf("Read get: %d\n", *(p + 6 GiB));
    printf("Read get: %d\n", *(p + 7 GiB));
}

如上代码映射了一个8G的空间,可读可写,同时是一个匿名私人空间,不使用fd来记录,使用gdb调试发现开辟的空间在libc的上方,且偏移是固定的

image-20230424223910019

之后对内存空间进行写入,随后读取其中的数据。

看似对8G空间的内存进行读写是非常耗时的,但是由于我们使用的是mmap的系统调用,并且使用的是匿名空间,所以执行的速度非常快

image-20230424224317203

我们这里尝试使用mmap去映射文件到内存中

import mmap, hexdump

with open("/mnt/e/virtualMachine/win7-en.iso", "rb") as f:	# 映射一个iso镜像文件到内存中
    m = mmap.mmap(f.fileno(), prot=mmap.PROT_READ, length=5 << 30)
    hexdump.hexdump(m[-512:])

发现读取的速度非常快,这是因为我们没有读取这个文件中的所有,而仅仅是访问了一段映射的内存。

所以我们可以使用mmap来进行大文件的操作。不管是读取还是拷贝,都是不错的选择。

3./proc/[pid]/mem

linux已经对一个process的内存地址进行了抽象,将其抽象成了一个file(everything is a file)

所以如果我们使用fopen的方式将这个mem文件给打开,随后使用fseek的方式将指针偏移到某个地址,最后fwrite的方式将内存修改,就完成了一次对进程空间的修改。这种写文件的方式不同于ptrace的调用。

这种思想方式甚至出过某个Misc题目,印象还挺深刻,春秋杯的一个Python文件通过open(“/proc/self/mem”)写入内存,由于python的二进制文件没开PIE,所以可以通过修改got表的方式将某个函数hook成system的地址,然后执行execve的调用。

后话

学了Linux这么些时日,发现原来挺多细小的知识点还是没细究…

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/458838.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

axios的使用,axios的get请求、post请求方式、put请求方式

axios (发音&#xff1a;艾克C奥斯)是前端圈最火的、专注于数据请求的库。react/vue官方都推荐使用axios发送ajax请求&#xff0c;是一个基于 promise 的 HTTP 库&#xff0c;可以用在浏览器和 node.js 中。 中文官网地址&#xff1a;http://www.axios-js.com/ 英文官网地址&a…

快速下载VScode并配置Python运行环境【详细教程】

快速下载VScode并配置Python运行环境【详细教程】 博主&#xff1a;命运之光 目录 快速下载VScode并配置Python运行环境【详细教程】前言下载vscode第一步vscode官网下载第二步点击下载![请添加图片描述](https://img-blog.csdnimg.cn/1d76c427314b4ddcbd350e0a7e5449d5.png)第…

数据湖Iceberg-FlinkSQL集成(5)

文章目录 数据湖Iceberg-FlinkSQL集成环境准备**Flink与Iceberg的版本对应关系如下**jar包下载地址jar包上传到Flink lib目录下修改flink-conf.yaml配置 创建和使用Catalog创建语法说明Hive CatalogHadoop Catalog配置sql-client初始化文件 DDL语句创建数据库创建表创建分区表使…

ubuntu22.04安装ROS2

ubuntu22.04安装ROS2 0.前言一、安装ROS21.首先将本地的编码格式修改为utf-82.添加ROS2 GPG key3.安装ROS24.设置环境变量 二、简单测试1.Hello ROS&#xff01;2.ROS Turtle 三、总结 0.前言 最近也没找到什么特别感兴趣的小项目&#xff0c;不过偶然间看见ROS2这个东西&#…

中期国际:安卓MT4怎么下载以及下载后需要注意哪些问题

投资现货黄金&#xff0c;需要使用到现货黄金软件。一个简单易用的现货黄金软件&#xff0c;就像是给厨师一把趁手的菜刀&#xff0c;以后的使用会得心应手&#xff0c;投资更加顺利。对于投资者来说&#xff0c;什么现货黄金软件才算是好的呢?小编这里推荐MT4软件。如今不少投…

c++11 标准模板(STL)(std::priority_queue)(四)

适配一个容器以提供优先级队列 std::priority_queue 定义于头文件 <queue> template< class T, class Container std::vector<T>, class Compare std::less<typename Container::value_type> > class priority_queue; priority_queu…

Mysql 查询同类数据中某一数字最大的所有数据

方法一、将时间进行排序后再分组 该表表名为customer, park_id表示园区id&#xff0c;joined_at表示用户的加入时间&#xff0c;created_at表示用户的创建时间。 需求&#xff1a;查出每个园区中&#xff0c;最早加入园区的第一位用户 select * from (select * from custome…

outlook手动配置保姆级别教学

outlook保姆级教学 hello&#xff0c;各位小伙伴&#xff0c;今天呢讲一下outlook的配置&#xff0c;相信啊再次之前也必然看到过其他博主写的&#xff0c;我呢也是前段时间有需求但是网上总是零零散散的。 我呢配置过qq 和126的邮箱这里呢开始教程. 第一步呢首先点击账户的设…

每日一个小技巧:1招教你wav格式如何转换mp3

wav是一种质量较高的音频格式&#xff0c;但它的文件大小通常比较大。为了更方便地分享和存储音频文件&#xff0c;许多人都会选择将其转换为mp3格式。因为mp3格式能够在保持较高音质的同时&#xff0c;尽量降低文件大小&#xff0c;帮助你节省许多磁盘空间。那你们知道wav格式…

Python每日一练(20230425)

目录 1. 多数元素 &#x1f31f; 2. 二叉树的层序遍历 II &#x1f31f;&#x1f31f; 3. 最接近的三数之和 &#x1f31f;&#x1f31f; &#x1f31f; 每日一练刷题专栏 &#x1f31f; Golang每日一练 专栏 Python每日一练 专栏 C/C每日一练 专栏 Java每日一练 专…

欧几里得算法、扩展欧几里得算法(特解、应用、通解)

文章目录 1. 欧几里得算法&#xff08;也叫辗转相除法&#xff09;1.1 直接上模拟1.2 几何理解1.3 用代数方法证明 g c d ( a , b ) g c d ( b , a % b ) gcd(a, b) gcd(b, a \% b) gcd(a,b)gcd(b,a%b)1.3.1 左推右&#xff1a; g c d ( a , b ) g c d ( b , a % b ) gcd(a…

Vue 3 第十五章:组件五(内置组件-keep-alive)

文章目录 1. keep-alive1.1. 基本用法1.2. 包含/排除1.3. 最大缓存实例数1.4. <keepAlive> 组件的生命周期 1. keep-alive <keep-alive>组件用于缓存动态组件的实例&#xff0c;以便在它们被切换时保持状态。例如&#xff0c;当我们在一个选项卡中切换不同的视图…

Unity Camera -- (2)相机投影设置

在Editor中调整相机 和场景视图中的其他游戏物体一样&#xff0c;相机本身也可以通过使用移动和旋转工具来进行调整。但这种方式比较难用&#xff0c;调整起来又慢又不精确。我们可以使用Move To View功能来快速调整相机所拍摄的画面。 1. 打开Camera_Projection_Scene&#xf…

Java 版企业工程项目管理系统平台(三控:进度组织、质量安全、预算资金成本、二平台:招采、设计管理)

工程项目管理软件&#xff08;工程项目管理系统&#xff09;对建设工程项目管理组织建设、项目策划决策、规划设计、施工建设到竣工交付、总结评估、运维运营&#xff0c;全过程、全方位的对项目进行综合管理 工程项目各模块及其功能点清单 一、系统管理 1、数据字典&#…

7、如何使用接口?

1、基本用法 我们需要定义这样一个函数&#xff0c;参数是一个对象&#xff0c;里面包含两个字段&#xff1a;firstName 和 lastName&#xff0c;也就是英文的名和姓&#xff0c;然后返回一个拼接后的完整名字。来看下函数的定义&#xff1a; // 注&#xff1a;这段代码为纯Ja…

【致敬未来的攻城狮计划】— 连续打卡第十二天:FSP固件库开发按键输入检测控制LED灯闪烁。

系列文章目录 1.连续打卡第一天&#xff1a;提前对CPK_RA2E1是瑞萨RA系列开发板的初体验&#xff0c;了解一下 2.开发环境的选择和调试&#xff08;从零开始&#xff0c;加油&#xff09; 3.欲速则不达&#xff0c;今天是对RA2E1 基础知识的补充学习。 4.e2 studio 使用教程 5.…

关于 OpenShift(OKD) 网络 Service、Routes的一些笔记

写在前面 参加考试&#xff0c;分享一些学习 OpenShift 的笔记博文内容为 OpenShift 网络相关组件 Service、Routes 很浅的一些认识学习环境为 openshift v3 的版本&#xff0c;有些旧这里如果专门学习 openshift &#xff0c;建议学习 v4 版本理解不足小伙伴帮忙指正 傍晚时分…

轻量级服务器nginx:反向代理的具体配置

系列文章目录 例如&#xff1a;第一章 Python 机器学习入门之pandas的使用 反向代理和负载均衡 系列文章目录一 反向代理1.正向代理2.反向代理 二 反向代理的实际部署1.配置tomcat2.配置host&#xff0c;nginx反向代理的配置三 结果展示四 总结 一 反向代理 1.正向代理 我们…

通过docker发布项目

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言例如&#xff1a;docker项目的发布方式 [docker发布的参考链接](https://www.cnblogs.com/emperorking/articles/11244253.html) 一、docker是什么&#xff1f;…

Django框架之自定义管理页面

Django框架Admin站点管理一些默认的显示和功能包括语言都可以自定义设置处理&#xff0c;以贴近我们的实际业务。 属性说明 列表页属性 配置文件myapp/admin.py from django.contrib import admin from .models import Grades, Students# Register your models here.# 注册班…