【OS】Linux Process Memory的探究

【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我们知道就是执行函数调用栈的一个内存数据结构)

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

### Linux 下使用 Qt 监控和检查程序内存使用情况 为了在 Linux 环境下通过 Qt 查看应用程序的内存占用情况,可以采用多种方法和技术来实现这一目标。以下是几种常见且有效的方法: #### 方法一:集成 Valgrind 进行内存检测 Valgrind 是一款强大的工具集,其中包含了多个用于调试和优化的应用程序。特别是其 Memcheck 工具能够帮助开发者识别并修复各种类型的内存错误。 当希望利用 Valgrind 来监测由 Qt 编写的 C++ 应用程序时,可以通过命令行启动该应用,并指定要使用的 Valgrind 模块。例如,在终端输入如下指令即可开启一次带有详细报告的日志记录会话[^2]: ```bash valgrind --tool=memcheck --leak-check=yes ./your_qt_application ``` 这将会执行 `your_qt_application` 并生成一份关于潜在内存泄漏和其他异常行为的诊断日志文件。 #### 方法二:借助 QProcess 实现自动化测试脚本 如果想要更进一步地将上述过程融入到图形界面当中,则可考虑运用 Qt 提供的 `QProcess` 类创建子进程对象,从而允许主程序调用外部命令(如 valgrind),并将输出重定向至 GUI 组件内显示给用户查看。 下面是一个简单的例子展示了怎样构建这样一个功能模块: ```cpp #include <QApplication> #include <QWidget> #include <QTextEdit> #include <QPushButton> #include <QVBoxLayout> #include <QProcess> class MemoryMonitor : public QWidget { Q_OBJECT public: explicit MemoryMonitor(QWidget *parent = nullptr); private slots: void startMonitoring(); signals: private: QTextEdit* logOutput; QPushButton* btnStart; }; MemoryMonitor::MemoryMonitor(QWidget *parent) : QWidget(parent), logOutput(new QTextEdit(this)), btnStart(new QPushButton("Start Monitoring", this)) { QVBoxLayout* layout = new QVBoxLayout(this); layout->addWidget(logOutput); layout->addWidget(btnStart); connect(btnStart, &QPushButton::clicked, this, &MemoryMonitor::startMonitoring); } void MemoryMonitor::startMonitoring() { QProcess process; QString program = "valgrind"; QStringList arguments; arguments << "--tool=memcheck" << "--leak-check=yes" << "./path_to_your_app"; process.start(program, arguments); if (!process.waitForStarted()) { logOutput->append("Failed to start monitoring."); return; } while (process.canReadLine()) { QByteArray line = process.readLine(); logOutput->append(QString(line)); } } ``` 此代码片段定义了一个名为 `MemoryMonitor` 的窗口部件类,它内部包含有一个按钮用来触发监控流程以及一个多行编辑框负责呈现来自 Valgrind 的反馈信息。每当点击“开始监视”按键之后便会异步发起一个新的 shell 命令序列去获取最新的资源消耗统计数据[^3]。 #### 方法三:直接读取 `/proc/[pid]/status` 文件 对于那些不需要深入探究具体哪部分代码存在问题的情况来说,还可以采取更为简便的方式来粗略估计整个进程中所占有的物理 RAM 数量——即定期轮询位于 procfs 虚拟文件系统下的特定路径中的状态描述符文档。这些文本格式的数据源提供了丰富的统计指标可供解析提取有用的内容。 这里给出一段示范性的 Python 版本伪代码说明如何完成这项工作: ```python import os from time import sleep def get_memory_usage(pid=None): pid = str(os.getpid()) if not pid else str(pid) with open(f"/proc/{pid}/status") as f: lines = f.readlines() mem_info = {} for l in lines: key_value_pair = l.split(":") if len(key_value_pair) != 2 or 'Vm' not in key_value_pair[0]: continue k, v = map(str.strip, key_value_pair) mem_info[k] = int(v[:-3]) / 1e3 # Convert from kB to MB return mem_info['VmRSS'] # Return resident set size only. if __name__ == "__main__": try: while True: rss = get_memory_usage() print(f"Current RSS memory usage is {rss:.2f}MB.") sleep(5) except KeyboardInterrupt: pass ``` 这段小程序每隔五秒钟就会打印当前正在运行实例的实际驻留集合大小(Resident Set Size)。当然也可以很容易地将其转换成 C++ 或其他支持 POSIX API 的编程语言版本以便更好地适应于基于 Qt 构建的应用框架之中[^1]。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值