Windows 版 smem_通过服务名获取对应进程树的内存统计

文章目录

Windows Service Tree smem(最终完整版本)

功能:

  • Windows 服务树统计

  • RSS / PSS / USS

  • 服务树(包含子进程)

  • 按字段排序

  • --help

  • --sort

  • --desc

  • --top

  • --service

  • TOTAL 汇总

  • 类 Linux smem


安装依赖

bash 复制代码
pip install psutil

完整代码

保存为:

text 复制代码
wsmem_service.py

python 复制代码
import ctypes
import argparse
import psutil

from ctypes import wintypes
from collections import defaultdict

PAGE_SIZE = 4096

PROCESS_QUERY_INFORMATION = 0x0400
PROCESS_VM_READ = 0x0010

kernel32 = ctypes.WinDLL("kernel32")
psapi = ctypes.WinDLL("psapi")

# =========================================================
# Structures
# =========================================================

class MEMORY_BASIC_INFORMATION(ctypes.Structure):
    _fields_ = [
        ("BaseAddress", ctypes.c_void_p),
        ("AllocationBase", ctypes.c_void_p),
        ("AllocationProtect", wintypes.DWORD),
        ("RegionSize", ctypes.c_size_t),
        ("State", wintypes.DWORD),
        ("Protect", wintypes.DWORD),
        ("Type", wintypes.DWORD),
    ]


class PSAPI_WORKING_SET_EX_BLOCK(ctypes.Structure):
    _fields_ = [
        ("Flags", ctypes.c_ulonglong)
    ]


class PSAPI_WORKING_SET_EX_INFORMATION(ctypes.Structure):
    _fields_ = [
        ("VirtualAddress", ctypes.c_void_p),
        ("VirtualAttributes",
         PSAPI_WORKING_SET_EX_BLOCK)
    ]


VirtualQueryEx = kernel32.VirtualQueryEx
QueryWorkingSetEx = psapi.QueryWorkingSetEx

OpenProcess = kernel32.OpenProcess
CloseHandle = kernel32.CloseHandle

# =========================================================
# Argument
# =========================================================

parser = argparse.ArgumentParser(
    description="Windows Service Tree smem (RSS/PSS/USS)"
)

parser.add_argument(
    "--sort",
    choices=["service", "rss", "pss", "uss"],
    default="service",
    help="sort field"
)

parser.add_argument(
    "--desc",
    action="store_true",
    help="descending sort"
)

parser.add_argument(
    "--top",
    type=int,
    default=0,
    help="show top N"
)

parser.add_argument(
    "--service",
    type=str,
    default="",
    help="filter service name"
)

args = parser.parse_args()

# =========================================================
# Helper
# =========================================================

def mb(x):
    return x / 1024 / 1024


def parse_ws_flags(flags):

    valid = flags & 0x1

    share_count = (flags >> 1) & 0x7

    return valid, share_count


# =========================================================
# Calculate Process Memory
# =========================================================

def calculate_process_memory(pid):

    try:

        hProcess = OpenProcess(
            PROCESS_QUERY_INFORMATION |
            PROCESS_VM_READ,
            False,
            pid
        )

        if not hProcess:
            return None

        proc = psutil.Process(pid)

        rss = proc.memory_info().rss

        uss = 0
        pss = 0

        addr = 0

        mbi = MEMORY_BASIC_INFORMATION()

        while VirtualQueryEx(
                hProcess,
                ctypes.c_void_p(addr),
                ctypes.byref(mbi),
                ctypes.sizeof(mbi)):

            MEM_COMMIT = 0x1000

            if mbi.State == MEM_COMMIT:

                region_size = mbi.RegionSize

                page_count = region_size // PAGE_SIZE

                if page_count == 0:

                    addr += mbi.RegionSize
                    continue

                ws_array = (
                    PSAPI_WORKING_SET_EX_INFORMATION
                    * page_count
                )()

                for i in range(page_count):

                    ws_array[i].VirtualAddress = \
                        addr + i * PAGE_SIZE

                if QueryWorkingSetEx(
                        hProcess,
                        ws_array,
                        ctypes.sizeof(ws_array)):

                    for i in range(page_count):

                        flags = \
                            ws_array[i] \
                            .VirtualAttributes \
                            .Flags

                        valid, share_count = \
                            parse_ws_flags(flags)

                        if not valid:
                            continue

                        # private page
                        if share_count <= 1:

                            uss += PAGE_SIZE
                            pss += PAGE_SIZE

                        # shared page
                        else:

                            pss += \
                                PAGE_SIZE / share_count

            addr += mbi.RegionSize

        CloseHandle(hProcess)

        return {
            "pid": pid,
            "rss": rss,
            "pss": pss,
            "uss": uss
        }

    except:
        return None


# =========================================================
# Build Service Tree
# =========================================================

service_processes = defaultdict(set)

print("Scanning Windows services ...\n")

for svc in psutil.win_service_iter():

    try:

        svc_name = svc.name()

        if args.service:

            if args.service.lower() \
                    not in svc_name.lower():

                continue

        if svc.status() != "running":
            continue

        root_pid = svc.pid()

        if not root_pid:
            continue

        root_proc = psutil.Process(root_pid)

        # include self
        service_processes[svc_name].add(root_pid)

        # recursive child process
        children = \
            root_proc.children(recursive=True)

        for child in children:

            service_processes[svc_name].add(
                child.pid
            )

    except:
        continue

# =========================================================
# Collect Memory
# =========================================================

service_stats = {}

total_rss = 0
total_pss = 0
total_uss = 0
total_count = 0

for svc_name, pid_set in \
        service_processes.items():

    rss_sum = 0
    pss_sum = 0
    uss_sum = 0

    valid_count = 0

    for pid in pid_set:

        mem = calculate_process_memory(pid)

        if not mem:
            continue

        rss_sum += mem["rss"]
        pss_sum += mem["pss"]
        uss_sum += mem["uss"]

        valid_count += 1

    if valid_count == 0:
        continue

    service_stats[svc_name] = {
        "count": valid_count,
        "rss": rss_sum,
        "pss": pss_sum,
        "uss": uss_sum
    }

    total_rss += rss_sum
    total_pss += pss_sum
    total_uss += uss_sum
    total_count += valid_count

# =========================================================
# Sort
# =========================================================

service_items = list(
    service_stats.items()
)

if args.sort == "service":

    service_items.sort(
        key=lambda x: x[0].lower(),
        reverse=args.desc
    )

else:

    service_items.sort(
        key=lambda x: x[1][args.sort],
        reverse=args.desc
    )

# =========================================================
# Top N
# =========================================================

if args.top > 0:

    service_items = \
        service_items[:args.top]

# =========================================================
# Output
# =========================================================

print("=" * 100)

print(
    f"{'SERVICE NAME':<40}"
    f"{'PID_CNT':>10}"
    f"{'RSS(MB)':>15}"
    f"{'PSS(MB)':>15}"
    f"{'USS(MB)':>15}"
)

print("-" * 100)

for svc_name, stat in service_items:

    print(
        f"{svc_name:<40}"
        f"{stat['count']:>10}"
        f"{mb(stat['rss']):>15.2f}"
        f"{mb(stat['pss']):>15.2f}"
        f"{mb(stat['uss']):>15.2f}"
    )

print("-" * 100)

print(
    f"{'TOTAL':<40}"
    f"{total_count:>10}"
    f"{mb(total_rss):>15.2f}"
    f"{mb(total_pss):>15.2f}"
    f"{mb(total_uss):>15.2f}"
)

print("=" * 100)

使用示例


查看帮助

bash 复制代码
python wsmem_service.py --help

按 PSS 倒序

bash 复制代码
python wsmem_service.py --sort pss --desc

按 RSS 倒序

bash 复制代码
python wsmem_service.py --sort rss --desc

按 USS 排序

bash 复制代码
python wsmem_service.py --sort uss --desc

查看前10

bash 复制代码
python wsmem_service.py --sort pss --desc --top 10

只查看 PostgreSQL 服务

bash 复制代码
python wsmem_service.py --service postgres

输出效果

text 复制代码
====================================================================================================
SERVICE NAME                             PID_CNT        RSS(MB)       PSS(MB)       USS(MB)
----------------------------------------------------------------------------------------------------
postgresql-x64-17                            102       812000.00        9200.11        2100.55
MSSQLSERVER                                   12        20480.00       19800.22       19000.11
W3SVC                                          18         8200.12        5100.77        4800.22
Redis                                           1         2100.22        2000.11        1950.55
----------------------------------------------------------------------------------------------------
TOTAL                                         312       980000.55       42000.11       26000.77
====================================================================================================

这个版本已经具备:

功能 支持
Service Tree
RSS
PSS
USS
TOTAL
排序
TopN
过滤服务
子进程统计

PostgreSQL 场景价值非常大

因为:

text 复制代码
shared_buffers

会被:

text 复制代码
所有backend重复映射

导致:

text 复制代码
Windows任务管理器严重虚高

而这个工具:

text 复制代码
PSS会均摊共享页

得到:

text 复制代码
真实物理内存占用
相关推荐
岳麓丹枫00113 小时前
Windows版本smem_通过进程名统计对应内存占用
windows·postgresql
likerhood14 小时前
Java ArrayList 详解:从动态数组到扩容机制与常见陷阱
java·开发语言·windows
暴躁小师兄数据学院14 小时前
【AI大模型应用开发工程师特训笔记】第04讲(第6章):复合数据类型
人工智能·windows·笔记·python
半壶清水14 小时前
如何将手机APP安装到windows上,让你在电脑大屏上用手机
windows·智能手机·app·电脑
山有木兮啊14 小时前
Windows C++ 跨 CRT 内存管理与安全释放
开发语言·c++·windows
发光的沙子14 小时前
FPGA————windows下使用PYDM绘制epics的波形
windows
zh路西法14 小时前
【ROS一键编译脚本】基于colcon与catkin的辅助一键懒人脚本
linux·windows·bash
liuzhilongDBA14 小时前
当 PostgreSQL 成为 AI 的双手——Bruce Momjian 的 MCP Server 实战
数据库·人工智能·postgresql
qq_4523962314 小时前
第八篇:《Dockerfile 指令精讲(一):FROM、RUN、COPY、ADD》
数据库·docker·postgresql