طريقة سريعة لتحديد ما إذا كانت PID موجودة على (Windows)؟

StackOverflow https://stackoverflow.com/questions/592256

  •  09-09-2019
  •  | 
  •  

سؤال

أدرك "سريع" هو شخصي بعض الشيء لذلك سأشرح مع بعض السياق. أنا أعمل على وحدة بيثون تسمى PSUTIL. للحصول على معلومات عملية القراءة بطريقة عبر النظام الأساسي. واحدة من الوظائف هي pid_exists(pid) وظيفة لتحديد ما إذا كانت PID في قائمة العملية الحالية.

الآن أنا أفعل هذه الطريقة الواضحة، باستخدام enumprocesses () لسحب قائمة العملية، ثم التوقعات من خلال القائمة والبحث عن PID. ومع ذلك، فإن بعض المعايير البسيطة تظهر أن هذا أبطأ بشكل كبير من وظيفة PID_Exists على المنصات المستندة إلى UNIX (Linux، OS X، FreeBSD) حيث نستخدمها kill(pid, 0) مع إشارة 0 لتحديد ما إذا كانت PID موجودة. يبين الاختبار الإضافي أنه يعين أن يتناول طوال الوقت تقريبا.

أي شخص يعرف طريقة أسرع من استخدام المعهد لتحديد ما إذا كانت PID موجودة؟ حاولت openprocess () والتحقق من وجود خطأ في فتح عملية غير موجودة، ولكن تحولت هذه إلى أن تكون أكثر من 4X أبطأ من التكرار من خلال قائمة المعينين، لذلك هذا الأمر كذلك. أي اقتراحات أخرى (أفضل)؟

ملاحظة: هذه هي مكتبة بيثون تهدف إلى تجنب تبعيات الطرف الثالث ليب مثل ملحقات pywin32. أحتاج إلى حل أسرع من التعليمات البرمجية الحالية، وهذا لا يعتمد على pywin32 أو الوحدات الأخرى غير موجودة في توزيع بيثون قياسي.

تعديل: للتوضيح - نحن ندرك جيدا أن هناك ظروف السباق الكامنة في قراءة عملية القراءة. نرفع الاستثناءات إذا تجاوزت العملية أثناء جمع البيانات أو نواجهها إلى مشاكل أخرى. لا تهدف وظيفة PID_EXTOS () إلى استبدال معالجة الأخطاء المناسبة.

تحديث: على ما يبدو أن معاييري السابقة كانت معيبة - كتبت بعض تطبيقات الاختبار البسيطة في C و enumprocesses باستمرار يخرج بشكل أبطأ و OpenProcess (بالتزامن مع GetProcessExitcode في حالة صالحة PID، لكن العملية توقفت) هي في الواقع كثيرا أسرع ليس أبطأ.

هل كانت مفيدة؟

المحلول

openprocess. يمكن أن أقول لك ث / س تعداد الكل. ليس لدي أي فكرة عن مدى السرعة.

تعديل: لاحظ أنك تحتاج أيضا GetExitCodeProcess للتحقق من حالة العملية حتى لو حصلت على مقبض من OpenProcess.

نصائح أخرى

هناك حالة سباق متأصلة في استخدام PID_EXTISTON: بحلول الوقت الذي يحصل فيه برنامج الاتصال على الإجابة، قد تكون العملية قد اختفت بالفعل، أو قد تم إنشاء عملية جديدة مع المعرف المستحلول. أود أن أجرؤ على القول أن أي تطبيق يستخدم هذه الوظيفة معيبة حسب التصميم وأن تحسين هذه الوظيفة لا يستحق الجهد.

اتضح أن معاييري بوضوح كانت معيبة بطريقة أو بأخرى، حيث أن الاختبار لاحقا يكشف عن OpenProcess و GetExitCodeProcess أسرع بكثير من استخدام المعاد النووية بعد كل شيء. لست متأكدا مما حدث لكني فعلت بعض الاختبارات الجديدة والتحقق من ذلك هو الحل الأسرع:

int pid_is_running(DWORD pid)
{
    HANDLE hProcess;
    DWORD exitCode;

    //Special case for PID 0 System Idle Process
    if (pid == 0) {
        return 1;
    }

    //skip testing bogus PIDs
    if (pid < 0) {
        return 0;
    }

    hProcess = handle_from_pid(pid);
    if (NULL == hProcess) {
        //invalid parameter means PID isn't in the system
        if (GetLastError() == ERROR_INVALID_PARAMETER) { 
            return 0;
        }

        //some other error with OpenProcess
        return -1;
    }

    if (GetExitCodeProcess(hProcess, &exitCode)) {
        CloseHandle(hProcess);
        return (exitCode == STILL_ACTIVE);
    }

    //error in GetExitCodeProcess()
    CloseHandle(hProcess);
    return -1;
}

لاحظ أنك تحتاج إلى استخدام GetExitCodeProcess() لأن OpenProcess() سينجح في العمليات التي ماتت مؤخرا حتى لا تتمكن من تحمل مقبض عملية صالح يعني أن العملية قيد التشغيل.

لاحظ أيضا ذلك OpenProcess() ينجح على pids التي هي في حدود 3 من أي pid صالح (انظر لماذا تنجح OpenProcess حتى عندما أضيف ثلاثة إلى معرف العملية؟)

أود كود آخر وظيفة جاي بهذه الطريقة.

int pid_is_running(DWORD pid){
    HANDLE hProcess;
    DWORD exitCode;
    //Special case for PID 0 System Idle Process
    if (pid == 0) {
        return 1;
    }
    //skip testing bogus PIDs
    if (pid < 0) {
        return 0;
    }
    hProcess = handle_from_pid(pid);
    if (NULL == hProcess) {
        //invalid parameter means PID isn't in the system
        if (GetLastError() == ERROR_INVALID_PARAMETER) {
             return 0;
        }
        //some other error with OpenProcess
        return -1;
    }
    DWORD dwRetval = WaitForSingleObject(hProcess, 0);
    CloseHandle(hProcess); // otherwise you'll be losing handles

    switch(dwRetval) {
    case WAIT_OBJECT_0;
        return 0;
    case WAIT_TIMEOUT;
        return 1;
    default:
        return -1;
    }
}

الاختلاف الرئيسي هو إغلاق مقبض العملية (مهم عندما يعمل عميل هذه الوظيفة لفترة طويلة) واستراتيجية الكشف عن الإنهاء العملية. يمنحك WaitforsingleObject الفرصة للانتظار لفترة من الوقت (تغيير 0 إلى قيمة معلمة وظيفة) حتى تنتهي العملية.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top