读取生活进程内存而不中断它(/ proc / kcore是一个选项)

时间:2012-10-19 15:09:43

标签: linux memory

我想探索生命过程的记忆,当我这样做时,过程一定不会受到干扰 - 所以将gdb附加到进程(这会阻止它)不是一种选择。 因此,我想从/ proc / kcore获取此信息(如果您知道另一种方法,请告诉我)。 所以我做了一个小实验。我创建了一个名为TEST的文件,里面只有“EXTRATESTEXTRA”。 然后我用较少的

打开它
$ less TEST

我用

获得了这个过程的PID
$ ps aux | grep TEST
user    7785  0.0  0.0  17944   992 pts/8    S+   16:15   0:00 less TEST
user    7798  0.0  0.0  13584   904 pts/9    S+   16:16   0:00 grep TEST

然后我使用此脚本创建所有文件的转储:

#!/bin/bash
grep rw-p /proc/$1/maps | sed -n 's/^\([0-9a-f]*\)-\([0-9a-f]*\) .*$/\1 \2/p' | while read start stop; do gdb --batch --pid $1 -ex "dump memory $1-$start-$stop.dump 0x$start 0x$stop"; done

(我在这个网站上发现了https://serverfault.com/questions/173999/dump-a-linux-processs-memory-to-file

$ sudo ./dump_all_pid_memory.sh 7785

在此之后,我在所有转储文件中查找“TRATESTEX”:

$ grep -a -o -e '...TRATESTEX...' ./*.dump
./7785-00624000-00628000.dump:HEXTRATESTEXTRA
./7785-00b8f000-00bb0000.dump:EXTRATESTEXTRA
./7785-00b8f000-00bb0000.dump:EXTRATESTEXTRA

所以我得出结论,必须在0x00624000和0x00628000之间出现这个字符串。 因此我将偏移量转换为十进制数,并使用dd从/ proc / kcore获取内存:

$ sudo dd if="/proc/kcore" of="./y.txt" skip="0" count="1638400" bs=1

令我惊讶的是,文件y.txt充满了零(我没有找到我在其中寻找的字符串)。

作为一个额外的惊喜,我使用不同的测试文件同时运行了一个simmilar测试,发现我正在使用的另一个测试字符串 (同时运行较少的两个进程)应该在同一位置找到(转储和greping给出相同的偏移量)。 所以必须有一些我不明白的东西。

  • 是不是/ proc / pid / maps应该显示内存的偏移量(即:如果它说“XXX”在偏移量0x10,另一个程序不能使用相同的偏移量我对吗? - 这是我第二次惊讶的来源)

  • 如何读取/ proc / kmap以获取属于我知道的pid进程的内存?

编辑 - 为未来的绊脚石(见下面的答案)

总结答案并添加我自己的评论: - / proc / pid / maps显示内存的各个部分,因为进程看到它(每个进程都不同,在linux上搜索内存映射),所以不同的进程似乎可以使用相同的内存部分(因为它看起来从他们的角度来看)。您可以从/ proc / pid / mem中读取此处指定的部分作为超级用户(或者像gdb这样的父进程使用ptrace) - /proc/kcore中的内存与/proc/pid/mem中进程的内存中的内存不同 - 所以要在/proc/kcore中搜索进程的内存,必须弄清楚进程的内存映射到内核内存(很多乱七八糟的东西,耗费时间) 因此,要获取进程内存,首先要读取/proc/pid/maps允许从哪个区域读/写的区域,然后从/proc/pid/mem转储复制区域。下面的脚本转储所有可写区域(来源:https://unix.stackexchange.com/questions/6301/how-do-i-read-from-proc-pid-mem-under-linux)。编辑:修改后的工作python脚本被移动到它自己的答案,因此它可以被评论与问题不同。

5 个答案:

答案 0 :(得分:24)

如果您具有root访问权限且位于Linux系统上,则可以使用以下linux脚本(改编自Gilles' excellent unix.stackexchange.com answer以及上述问题中最初给出的答案,但包括SyntaxErrors而不是pythonic):

#!/usr/bin/env python

import re
import sys

def print_memory_of_pid(pid, only_writable=True):
    """ 
    Run as root, take an integer PID and return the contents of memory to STDOUT
    """
    memory_permissions = 'rw' if only_writable else 'r-'
    sys.stderr.write("PID = %d" % pid)
    with open("/proc/%d/maps" % pid, 'r') as maps_file:
        with open("/proc/%d/mem" % pid, 'r', 0) as mem_file:
            for line in maps_file.readlines():  # for each mapped region
                m = re.match(r'([0-9A-Fa-f]+)-([0-9A-Fa-f]+) ([-r][-w])', line)
                if m.group(3) == memory_permissions: 
                    sys.stderr.write("\nOK : \n" + line+"\n")
                    start = int(m.group(1), 16)
                    if start > 0xFFFFFFFFFFFF:
                        continue
                    end = int(m.group(2), 16)
                    sys.stderr.write( "start = " + str(start) + "\n")
                    mem_file.seek(start)  # seek to region start
                    chunk = mem_file.read(end - start)  # read region contents
                    print chunk,  # dump contents to standard output
                else:
                    sys.stderr.write("\nPASS : \n" + line+"\n")

if __name__ == '__main__': # Execute this code when run from the commandline.
    try:
        assert len(sys.argv) == 2, "Provide exactly 1 PID (process ID)"
        pid = int(sys.argv[1])
        print_memory_of_pid(pid)
    except (AssertionError, ValueError) as e:
        print "Please provide 1 PID as a commandline argument."
        print "You entered: %s" % ' '.join(sys.argv)
        raise e

如果将其保存为write_mem.py,则可以运行此命令(使用python2.6或2.7)或在python2.5中提前运行(如果添加from __future__ import with_statement):

sudo python write_mem.py 1234 > pid1234_memory_dump

将pid1234内存转储到文件pid1234_memory_dump。

答案 1 :(得分:15)

对于过程1234,您可以通过顺序读取/proc/1234/maps(文本伪文件)来获取其存储器映射,并通过例如读取虚拟存储器来读取虚拟存储器。 read(2) - 或mmap(2) /proc/1234/mem稀疏伪文件的相应段。

但是,我相信你无法避免某种同步(可能与gdb一样,因为mmap确实如此),因为进程1234可以(并且确实)随时改变其地址空间(与gdb&相关系统调用)。

如果受监控的流程1234不是任意的,但是如果您可以改进它以便以某种方式与监控流程进行通信,则情况会有所不同。

我不明白你为什么这么问。并且watch能够{{1}}某个位置而不会停止该过程。

答案 2 :(得分:2)

从3.2版内核开始。您可以使用process_vm_readv系统调用来无中断地读取进程内存。

let string = "Network\t\t\t10.0.0.0/8\nClass\t\t\t\tA\nRequired Hosts:\n2\n\nRequired hosts\t2\nAvailable hosts\t2\nSubnet\t\t\t\t10.0.0.0/30\nRange start\t\t10.0.0.1\nRange end\t\t\t10.0.0.2\nBroadcast\t\t\t10.0.0.3\nMask\t\t\t\t255.255.255.252\n\n"

var result = 0
let scanner = Scanner(string: string)
scanner.scanUpToCharacters(from: .decimalDigits, into: nil)
scanner.scanInt(&result)
print(result) // 10
     

这些系统调用在地址空间之间传输数据。          调用过程(“本地过程”)以及由          pid(“远程进程”)。数据直接在          两个进程的地址空间,无需通过内核          空间。

答案 3 :(得分:0)

您必须使用/ proc // mem来读取进程内存,我不建议尝试读取/ proc / kcore或任何内核内存函数(这非常耗时)

答案 4 :(得分:-2)

我通过发出以下命令来实现这一目标

[root @ stage1~] #echo" PID [MySql]的内存使用情况:&#34 ;;对于{Private,Rss,Shared,Swap,Pss}中的mem;执行grep $ mem / proc / ps aux |grep mysql |awk '{print $2}'|head -n 1 / smaps | awk -v mem_type =" $ mem" ' {i = i + $ 2} END {print mem_type,"内存使用情况:" i}' ;完成

结果输出

PID [MySql]的内存使用情况:

私人内存使用量:204

Rss内存使用情况:1264

共享内存使用量:1060

交换内存使用量:0

Pss内存使用量:423