2009-02-26 55 views
8

Tôi nhận ra "nhanh" hơi chủ quan nên tôi sẽ giải thích với một số ngữ cảnh. Tôi đang làm việc trên một mô-đun Python gọi là psutil để đọc thông tin về quy trình theo cách đa nền tảng. Một trong các hàm là hàm pid_exists(pid) để xác định xem PID có nằm trong danh sách quy trình hiện tại hay không.Cách nhanh chóng để xác định xem một PID có tồn tại trên (Windows) không?

Ngay bây giờ tôi đang làm điều này một cách rõ ràng, sử dụng EnumProcesses() để kéo danh sách quy trình, sau đó liên kết thông qua danh sách và tìm kiếm PID. Tuy nhiên, một số điểm chuẩn đơn giản cho thấy điều này chậm hơn đáng kể so với chức năng pid_exists trên nền tảng UNIX (Linux, OS X, FreeBSD), nơi chúng tôi đang sử dụng kill(pid, 0) với tín hiệu 0 để xác định xem có tồn tại PID hay không. Thử nghiệm bổ sung cho thấy đó là EnumProcesses chiếm hầu hết thời gian.

Bất cứ ai biết cách nhanh hơn sử dụng EnumProcesses để xác định xem có tồn tại PID không? Tôi đã thử OpenProcess() và kiểm tra lỗi khi mở quá trình không tồn tại, nhưng điều này hóa ra là chậm hơn 4 lần so với lặp qua danh sách EnumProcesses, vì vậy điều đó cũng tốt. Bất kỳ đề xuất nào khác (tốt hơn)?

LƯU Ý: Đây là thư viện Python nhằm tránh các phụ thuộc lib của bên thứ ba như phần mở rộng pywin32. Tôi cần một giải pháp nhanh hơn mã hiện tại của chúng tôi và điều đó không phụ thuộc vào pywin32 hoặc các mô-đun khác không có trong bản phân phối Python chuẩn.

EDIT: Để làm rõ - chúng tôi biết rõ rằng có các điều kiện chủng tộc vốn có trong quá trình đọc iformation. Chúng tôi nêu ra các ngoại lệ nếu quá trình này biến mất trong quá trình thu thập dữ liệu hoặc chúng tôi gặp phải các vấn đề khác. Hàm pid_exists() không có ý định thay thế xử lý lỗi thích hợp.

CẬP NHẬT: Rõ ràng các tiêu chuẩn trước đó của tôi đã bị biến dị - tôi đã viết một số ứng dụng đơn giản kiểm tra trong C và EnumProcesses liên tục đi ra chậm hơn và OpenProcess (kết hợp với GetProcessExitCode trong trường hợp PID là hợp lệ nhưng quá trình này đã ngừng) là thực sự nhiều hơn nhanh hơn không chậm hơn.

Trả lời

8

OpenProcess có thể cho bạn biết cách liệt kê tất cả. Tôi không biết nhanh như thế nào.

EDIT: lưu ý rằng bạn cũng cần GetExitCodeProcess để xác minh trạng thái của quy trình ngay cả khi bạn có tay cầm từ OpenProcess.

+0

Hóa ra mặc dù thử nghiệm trước đó của tôi, đây là cách tốt nhất để đi sau khi tất cả. Xem câu trả lời của tôi để biết chi tiết nếu quan tâm. – Jay

4

Có một điều kiện chủng tộc vốn có khi sử dụng chức năng pid_exists: khi chương trình gọi được sử dụng câu trả lời, quá trình có thể đã biến mất hoặc quá trình mới với id được truy vấn có thể đã được tạo. Tôi dám nói rằng bất kỳ ứng dụng nào sử dụng chức năng này là thiếu sót bởi thiết kế và tối ưu hóa chức năng này do đó không đáng để nỗ lực.

+0

Có, có một điều kiện chủng tộc vốn có trong bất kỳ ứng dụng giống ps nào, kể cả thư viện của chúng tôi. Tuy nhiên, chức năng này vẫn có trường hợp sử dụng hợp lệ. Lưu ý rằng chúng tôi * cũng * tăng ngoại lệ nếu tại bất kỳ thời điểm nào trong quá trình thu thập dữ liệu không thành công vì quá trình này đã biến mất. – Jay

3

Chỉ ra rằng điểm chuẩn của tôi rõ ràng là thiếu sót bằng cách nào đó, như kiểm tra sau đó cho thấy OpenProcess và GetExitCodeProcess nhanh hơn nhiều so với sử dụng EnumProcesses sau khi tất cả. Tôi không chắc chắn những gì đã xảy ra nhưng tôi đã làm một số xét nghiệm mới và xác nhận đây là giải pháp nhanh hơn:

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; 
} 

Lưu ý rằng bạn cần phải sử dụng GetExitCodeProcess()OpenProcess() sẽ thành công trên các quy trình đã qua đời gần đây, do đó bạn có thể' t giả định một xử lý hợp lệ có nghĩa là quá trình đang chạy.

Cũng lưu ý rằng OpenProcess() thành công cho PID có vòng 3 của bất kỳ PID hợp lệ (Xem Why does OpenProcess succeed even when I add three to the process ID?)

+0

Cảm ơn ghi chú cuối cùng đó, tôi đã đập đầu vào bàn làm việc tại sao một PID hoàn toàn không tồn tại trở lại đúng sự thật. –

3

Tôi muốn mã chức năng mới nhất của Jay theo cách này.

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; 
    } 
} 

Sự khác biệt chính là đóng trình xử lý (quan trọng khi máy khách đang chạy trong một thời gian dài) và chiến lược phát hiện chấm dứt quá trình. WaitForSingleObject cho bạn cơ hội chờ một lúc (thay đổi giá trị 0 thành giá trị tham số hàm) cho đến khi quá trình kết thúc.

+0

Chúng tôi không muốn chờ trong trường hợp này (các cuộc gọi chức năng khác sẽ phát hiện nếu quá trình đã tự đóng và nâng một ngoại lệ lên Python). Tuy nhiên, bạn đã đúng về việc đóng các quy trình xử lý ... mã "thực" của chúng tôi đóng các chốt nhưng tôi quên làm điều đó trong mẫu mà tôi đã đăng. – Jay

Các vấn đề liên quan