قياس استهلاك CPU والذاكرة من داخل عملية C++: دليل شامل

مدرب بايثون أونلاين للمبتدئين

تعلم Python بسهولة دون إرهاق نظري. حل مهام عملية مع التحقق التلقائي واكتب الكود مباشرة في المتصفح.

ابدأ الدورة

نقطة العمى في الأداء: لماذا لا تعرف عمليتك مقدار ما تستهلكه؟

\n\n

تخيل أنك كتبت تطبيقًا معقدًا بلغة C++، وهو يعمل لكنه يبدأ في التباطؤ. تقوم بتشغيل top أو htop أو مدير المهام — وتجد أن عمليتك تستهلك 90% من المعالج أو غيغابايتات من الذاكرة. ولكن كيف تجعل العملية نفسها تدرك ذلك، وتقوم مثلاً بتقليل الحمل ديناميكيًا أو مسح الذاكرة المؤقتة؟

\n\n

هذا ليس مجرد فضول أكاديمي. في بيئات الإنتاج، على الخوادم بدون واجهة رسومية، لا يمكنك الاعتماد على الأدوات الخارجية. يجب أن يراقب البرنامج نفسه بنفسه. هذا هو بالضبط ما يحله السؤال الشهير على Stack Overflow: "How to determine CPU and memory consumption from inside a process". دعنا نستعرضه بأمثلة عملية بلغة C++.

\n\n

المعضلة عبر المنصات: Linux مقابل Windows

\n\n

تقدم أنظمة التشغيل المختلفة إمكانية الوصول إلى إحصائيات العمليات بطرق مختلفة. تحت غطاء Linux يوجد نظام الملفات الافتراضي /proc، بينما يستخدم Windows واجهة Win32 API. سنكتب كلاسًا شاملاً يعمل على كلا النظامين.

\n\n

المقاييس الرئيسية التي سنحصل عليها:

\n
    \n
  • استخدام المعالج (CPU) (كنسبة مئوية أو كسور من النواة)
  • \n
  • الذاكرة الفعلية (RSS) — الذاكرة الواقعية المشغولة
  • \n
  • الذاكرة الافتراضية (VSZ) — الحجم الإجمالي للمساحة العنوانية
  • \n
\n\n

قراءة CPU على Linux: سحر /proc/self/stat

\n\n

للحصول على وقت معالج العملية، نقرأ الملف /proc/self/stat. يحتوي على عشرات الحقول، نحن مهتمون بالحقول 13-16 (utime, stime, cutime, cstime) — الوقت بوحدات النبض (jiffies). لتحويلها إلى نسب مئوية، نحتاج إلى معرفة تردد النبض (sysconf(_SC_CLK_TCK)) وإجمالي وقت تشغيل النظام.

\n\n
#ifdef __linux__\n#include <unistd.h>\n#include <fstream>\n#include <sstream>\n#include <string>\n#include <vector>\n\ndouble getCpuUsageLinux() {\n    static unsigned long long prevTotal = 0, prevIdle = 0;\n    static double prevProcessTicks = 0;\n    static bool first = true;\n\n    // وقت العملية\n    std::ifstream procStat("/proc/self/stat");\n    std::string line;\n    std::getline(procStat, line);\n    std::istringstream iss(line);\n    std::vector<std::string> fields;\n    std::string field;\n    while (iss >> field) fields.push_back(field);\n\n    unsigned long utime = std::stoul(fields[13]);\n    unsigned long stime = std::stoul(fields[14]);\n    unsigned long cutime = std::stoul(fields[15]);\n    unsigned long cstime = std::stoul(fields[16]);\n    double processTicks = utime + stime + cutime + cstime;\n\n    // وقت النظام\n    std::ifstream statFile("/proc/stat");\n    std::getline(statFile, line);\n    statFile.close();\n    std::istringstream iss2(line);\n    std::string cpuLabel;\n    iss2 >> cpuLabel;\n    unsigned long long user, nice, system, idle, iowait, irq, softirq, steal;\n    iss2 >> user >> nice >> system >> idle >> iowait >> irq >> softirq >> steal;\n    unsigned long long total = user + nice + system + idle + iowait + irq + softirq + steal;\n\n    if (first) {\n        prevTotal = total;\n        prevIdle = idle;\n        prevProcessTicks = processTicks;\n        first = false;\n        return 0.0;\n    }\n\n    unsigned long long totalDelta = total - prevTotal;\n    double processDelta = processTicks - prevProcessTicks;\n    double cpuPercent = (processDelta / totalDelta) * 100.0;\n\n    prevTotal = total;\n    prevIdle = idle;\n    prevProcessTicks = processTicks;\n\n    return cpuPercent;\n}\n#endif\n
\n\n

يحسب هذا الكود النسبة المئوية لوحدة المعالجة المركزية التي تشغلها العملية منذ أول استدعاء. لاحظ: نحن نأخذ الفرق بين قياسين — هذه هي الطريقة القياسية لتحقيق الدقة.

\n\n

الذاكرة على Linux: RSS و VSZ

\n\n

للذاكرة، نقرأ الملف /proc/self/status. نحن مهتمون بالأسطر

المدونات

توصيات الكتب