我可以看到分配为 GEM 缓冲区的内存量吗?

我可以看到分配为 GEM 缓冲区的内存量吗?

我的/proc/meminfo节目大约 500 MB 被分配为Shmem.我想得到更具体的数字。我在这里找到了一个解释:

https://lists.kernelnewbies.org/pipermail/kernelnewbies/2013-July/008628.html

它包括 tmpfs 内存、SysV 共享内存(来自 ipc/shm.c)、POSIX 共享内存(位于 /dev/shm [这是一个 tmpfs] 下)和共享匿名映射(来自 /dev/zero 的 mmap 和 MAP_SHARED:请参阅从 drivers/char/mem.c 调用 shmem_zero_setup():通过 mm/shmem.c 分配页面。

2-> 根据开发人员评论,NR_SHMEM 包括 tmpfs 和 GEM 页面。什么是 GEM 页面?

是的,图形执行管理器使用 shmem 来存储与 GPU 共享的对象:请参阅 drivers/gpu/drm/ 中 shmem_read_mapping_page*() 的使用。

我有关于

  • 用户可见的 tmpfs 中有 50MB,通过df -h -t tmpfs.
  • sysvipc 共享内存中 40MB(10,000 页,4096 字节),通过ipcs -mu.

我想得到一些更积极的核算,500MB 的用途是什么!有没有办法显示创业板总分配情况? (或任何其他可能的贡献者)。

我希望我有一些 GEM 分配,因为我在英特尔图形硬件上运行图形桌面。我的内核版本是4.18.16-200.fc28.x86_64(Fedora Workstation 28)。

答案1

这些在流程图中显示为“drm mm 对象”或者“i915”。您可以在 中看到这一点/proc/<pid>/maps;给定使用 GEM/DRM 的进程的 PID:

awk '/(drm mm object)|i915/ { hypidx = index($1, "-"); from = substr($1, 1, hypidx - 1); to = substr($1, hypidx + 1); sum += strtonum("0x" to) - strtonum("0x" from) } END { print sum }' /proc/${PID}/maps

将显示分配的 GEM 缓冲区的总大小。可以通过输入至少包含一次“drm mm object”或“i915”的所有地图来计算总数;作为根用户:

find /proc -maxdepth 2 -name maps |
xargs grep -E -l "(drm mm object)|i915" |
xargs awk '/(drm mm object)|i915/ { hypidx = index($1, "-"); sum += strtonum("0x" substr($1, hypidx + 1)) - strtonum("0x" substr($1, 1, hypidx - 1)) } END { print sum }'

-maxdepth 2有必要避免查看线程映射)。可能需要一些额外的基于索引节点的重复数据删除。

答案2

编辑:有一个仅用于内核调试目的的界面。它只能通过访问root并且不稳定。如果您不是内核开发人员,它可能会被重写、重命名和/或误导。 (据我所知,它甚至可能是有缺陷的)。但如果您遇到问题,了解问题的存在可能会很有用。

我的i915司机在这里给了我信息:

$ sudo sh -c 'cat /sys/kernel/debug/dri/*/i915_gem_objects'
643 objects, 205852672 bytes
75 unbound objects, 7811072 bytes
568 bound objects, 198041600 bytes
16 purgeable objects, 5750784 bytes
16 mapped objects, 606208 bytes
13 huge-paged objects (2M, 4K) 123764736 bytes
13 display objects (globally pinned), 14954496 bytes
4294967296 [0x0000000010000000] gtt total
Supported page sizes: 2M, 4K

[k]contexts: 16 objects, 548864 bytes (0 active, 548864 inactive, 548864 global, 0 shared, 0 unbound)
systemd-logind: 324 objects, 97374208 bytes (0 active, 115798016 inactive, 23941120 global, 5246976 shared, 3858432 unbound)
Xwayland: 24 objects, 6995968 bytes (0 active, 12169216 inactive, 5283840 global, 5246976 shared, 110592 unbound)
gnome-shell: 246 objects, 89739264 bytes (26517504 active, 120852480 inactive, 63016960 global, 5242880 shared, 3629056 unbound)
Xwayland: 25 objects, 17309696 bytes (0 active, 22503424 inactive, 5304320 global, 5242880 shared, 90112 unbound)

再次强调,谨慎行事。我注意到mapped objects只显示 600KB。我想mapped这里的意思与我的预期不同。为了进行比较,运行下面的 python 脚本来显示映射到用户进程地址空间中的 i915 对象,我看到总共 70MB。

我的输出中的行systemd-logind代表第二个gnome-shell实例,在不同的虚拟控制台上运行。如果我切换到运行文本登录的虚拟控制台,则此文件显示两systemd-logind行,但不显示任何gnome-shell行:-)。


/proc/*/fd/否则,您能做的最好的事情就是通过在和/proc/*/map_files/(或)中查找所有打开的文件来找到一些 shmem 文件/proc/*/maps

通过正确的技巧,似乎可以可靠地识别哪些文件属于隐藏的 shmem 文件系统。

每个共享内存对象都是一个具有名称的文件。这些名称可用于识别哪个内核子系统创建了该文件。

问题是这确实不是显示所有 DRM/GEM 分配。 DRM 缓冲区可以在不映射的情况下存在,只是作为数字句柄。它们与创建它们时所用的打开的 DRM 文件相关联。当程序崩溃或被杀死时,DRM文件将被关闭,并且其所有DRM句柄将被自动清理。 (除非某些其他软件保持文件描述符的副本打开,就像这个老虫子.)

https://www.systutorials.com/docs/linux/man/7-drm-gem/

您可以在 中找到打开的 DRM 文件/proc/*/fd/,但它们显示为分配了零块的零大小文件。

例如,下面的输出显示了一个我无法占用超过 50%/300MB 的系统Shmem

$ grep Shmem: /proc/meminfo
Shmem:            612732 kB

$ df -h -t tmpfs
Filesystem      Size  Used Avail Use% Mounted on
tmpfs           3.9G   59M  3.8G   2% /dev/shm
tmpfs           3.9G  2.5M  3.9G   1% /run
tmpfs           3.9G     0  3.9G   0% /sys/fs/cgroup
tmpfs           3.9G  9.0M  3.9G   1% /tmp
tmpfs           786M   20K  786M   1% /run/user/42
tmpfs           786M  8.0M  778M   2% /run/user/1000
tmpfs           786M  5.7M  781M   1% /run/user/1001

$ sudo ipcs -mu

------ Shared Memory Status --------
segments allocated 20
pages allocated 4226
pages resident  3990
pages swapped   0
Swap performance: 0 attempts     0 successes  

隐藏 shmem 文件系统上的所有打开文件:

$ sudo python3 ~/shm -s
15960   /SYSV*
79140   /i915
7912    /memfd:gdk-wayland
1164    /memfd:pulseaudio
104176

这是注销我的两个登录 GNOME 用户之一的“之前和之后”。如果gnome-shell未映射的 DRM 缓冲区超过 100MB,则可能会出现这种情况。

$ grep Shmem: /proc/meminfo
Shmem:            478780 kB
$ df -t tmpfs -h
Filesystem      Size  Used Avail Use% Mounted on
tmpfs           3.9G  4.0K  3.9G   1% /dev/shm
tmpfs           3.9G  2.5M  3.9G   1% /run
tmpfs           3.9G     0  3.9G   0% /sys/fs/cgroup
tmpfs           3.9G  276K  3.9G   1% /tmp
tmpfs           786M   20K  786M   1% /run/user/42
tmpfs           786M  8.0M  778M   2% /run/user/1000
tmpfs           786M  5.7M  781M   1% /run/user/1001
$ sudo ./shm -s
80  /SYSV*
114716  /i915
1692    /memfd:gdk-wayland
1156    /memfd:pulseaudio
117644

$ grep Shmem: /proc/meminfo
Shmem:            313008 kB
$ df -t tmpfs -h
Filesystem      Size  Used Avail Use% Mounted on
tmpfs           3.9G  4.0K  3.9G   1% /dev/shm
tmpfs           3.9G  2.1M  3.9G   1% /run
tmpfs           3.9G     0  3.9G   0% /sys/fs/cgroup
tmpfs           3.9G  204K  3.9G   1% /tmp
tmpfs           786M   20K  786M   1% /run/user/42
tmpfs           786M  6.8M  780M   1% /run/user/1000
$ sudo ./shm -s
40  /SYSV*
88496   /i915
1692    /memfd:gdk-wayland
624 /memfd:pulseaudio
90852

用于生成上述输出的 Python 脚本:

#!/bin/python3
# Reads Linux /proc.  No str, all bytes.

import sys
import os
import stat
import glob
import collections
import math

# File.
# 'name' is first name encountered, we don't track hardlinks.
Inode = collections.namedtuple('Inode', ['name', 'bytes', 'pids'])

# inode number -> Inode object
inodes = dict()
# pid -> program name
pids = dict()
# filename -> list() of Inodes
filenames = dict()

def add_file(pid, proclink):
    try:
        vfs = os.statvfs(proclink)

        # The tmpfs which reports 0 blocks is an internal shm mount
        # python doesn't admit f_fsid ...
        if vfs.f_blocks != 0:
            return
        filename = os.readlink(proclink)
        # ... but all the shm files are deleted (hack :)
        if not filename.endswith(b' (deleted)'):
            return
        filename = filename[:-10]
        # I tried a consistency check that all our st_dev are the same
        # but actually there can be more than one internal shm mount!
        # i915 added a dedicated "gemfs" so they could control mount options.

        st = os.stat(proclink)

        # hack the second: ignore deleted character devices from devpts
        if stat.S_ISCHR(st.st_mode):
            return

        # Read process name succesfully,
        # before we record file owned by process.
        if pid not in pids:
            pids[pid] = open(b'/proc/' + pid + b'/comm', 'rb').read()[:-1]

        if st.st_ino not in inodes:
            inode_pids = set()
            inode_pids.add(pid)

            inode = Inode(name=filename,
                          bytes=st.st_blocks * 512,
                          pids=inode_pids)
            inodes[st.st_ino] = inode
        else:
            inode = inodes[st.st_ino]
            inode.pids.add(pid)

        # Group SYSV shared memory objects.
        # There could be many, and the rest of the name is just a numeric ID
        if filename.startswith(b'/SYSV'):
            filename = b'/SYSV*'

        filename_inodes = filenames.setdefault(filename, set())
        filename_inodes.add(st.st_ino)

    except FileNotFoundError:
        # File disappeared (race condition).
        # Don't bother to distinguish "file closed" from "process exited".
        pass

summary = False
if sys.argv[1:]:
    if sys.argv[1:] == ['-s']:
        summary = True
    else:
        print("Usage: {0} [-s]".format(sys.argv[0]))
        sys.exit(2)

os.chdir(b'/proc')
for pid in glob.iglob(b'[0-9]*'):
    for f in glob.iglob(pid + b'/fd/*'):
        add_file(pid, f)
    for f in glob.iglob(pid + b'/map_files/*'):
        add_file(pid, f)

def pid_name(pid):
    return pid + b'/' + pids[pid]

def kB(b):
    return str(math.ceil(b / 1024)).encode('US-ASCII')

out = sys.stdout.buffer

total = 0
for (filename, filename_inodes) in sorted(filenames.items(), key=lambda p: p[0]):
    filename_bytes = 0
    for ino in filename_inodes:
        inode = inodes[ino]
        filename_bytes += inode.bytes
        if not summary:
            out.write(kB(inode.bytes))
            out.write(b'\t')
            #out.write(str(ino).encode('US-ASCII'))
            #out.write(b'\t')            
            out.write(inode.name)
            out.write(b'\t')
            out.write(b' '.join(map(pid_name, inode.pids)))
            out.write(b'\n')
    total += filename_bytes
    out.write(kB(filename_bytes))
    out.write(b'\t')
    out.write(filename)
    out.write(b'\n')
out.write(kB(total))
out.write(b'\n')

相关内容