Làm thế nào để kiểm tra xem có tồn tại một quy trình với một pid nhất định trong Python không?


109

Có cách nào để kiểm tra xem một pid có tương ứng với một quy trình hợp lệ không? Tôi đang nhận được một pid từ một nguồn khác với từ đó os.getpid()và tôi cần kiểm tra xem liệu quá trình với pid đó có tồn tại trên máy hay không.

Tôi cần nó có sẵn trong Unix và Windows. Tôi cũng đang kiểm tra xem PID KHÔNG được sử dụng hay không.


2
Windows là một hệ điều hành không chuẩn. Những thứ này KHÔNG phải là hàng xách tay. Biết bạn không thể có cả hai, ưu tiên của bạn là gì? Chọn một ưu tiên và chỉnh sửa câu hỏi.
S.Lott

26
@ S.Lott Windows là một hệ điều hành không chuẩn Đây là một trong những nhận xét ngớ ngẩn nhất mà tôi từng thấy trên SO ...
Piotr Dobrogost

2
@Piotr Dobrogost: Bạn có thể cung cấp mã xử lý Windows tiêu chuẩn unix và không phải POSIX tiêu chuẩn POSIX không? Nếu vậy, vui lòng cung cấp câu trả lời (a) giải quyết vấn đề và (b) nói rõ rằng Windows tuân thủ bằng cách nào đó với tiêu chuẩn POSIX.
S.Lott

3
@PiotrDobrogost Tôi nghĩ nhận xét của S.Lott nghiêng về chi tiết triển khai và hỗ trợ API hơn là thị phần.
Roy Tinker

3
Windows chắc chắn có ít điểm chung với các hệ điều hành phổ biến khác so với các hệ điều hành còn lại. (Bất kỳ ai làm công việc phát triển web đều có thể ví nó như một sản phẩm khét tiếng tương tự của Microsoft.) Nhưng trả lời @ S.Lott: Tôi hiếm khi viết mã Python cho Windows không được cho là cũng hoạt động trên Linux, OSX, BSD, v.v., vì vậy tôi Thành thật mà nói, đừng nghĩ rằng 'chọn làm ưu tiên' là lời khuyên hữu ích, đặc biệt là vì Python đã loại bỏ những khác biệt nền tảng càng nhiều càng tốt.
Michael Scheper

Câu trả lời:


162

Việc gửi tín hiệu 0 tới một pid sẽ tạo ra ngoại lệ OSError nếu pid không chạy và không làm gì khác.

import os

def check_pid(pid):        
    """ Check For the existence of a unix pid. """
    try:
        os.kill(pid, 0)
    except OSError:
        return False
    else:
        return True

3
Hoạt động chắc chắn trên linux và OSX, tôi không thể nói cho Windows. Nó không giết quá trình theo nghĩa mà bạn đang hỏi, nó gửi tín hiệu 0, về cơ bản là "Bạn có đang chạy không?".
mluebke

10
Điều này chắc chắn không hoạt động trên Windows, vì không có tín hiệu giống như UNIX.
Alexander Lebedev

13
Để hoàn tất, bạn cũng nên kiểm tra số lỗi để đảm bảo nó là 3 (bắt ngoại lệ và kiểm tra đối số đầu tiên). Điều này rất quan trọng nếu tiến trình đích tồn tại nhưng bạn không có quyền gửi tín hiệu (vì bất kỳ lý do gì).
haridsv

8
Được hỗ trợ bởi windows bây giờ. docs.python.org/library/os.html?highlight=os.kill#os.kill
michael

15
os.kill được hỗ trợ trên Windows, nhưng os.kill(pid, 0)giống như os.kill(pid, signal.CTRL_C_EVENT)nó có thể chấm dứt quá trình (hoặc không thành công). Tôi nhận được một OSErrornơi errno==EINVALkhi tôi thử điều này trên một quy trình con.
Jason R. Coombs

76

Hãy xem psutilmô-đun:

psutil (hệ thống python và tiện ích quy trình) là một thư viện đa nền tảng để truy xuất thông tin về các quy trình đang chạyviệc sử dụng hệ thống (CPU, bộ nhớ, đĩa, mạng) bằng Python. [...] Nó hiện hỗ trợ Linux , Windows , OSX , FreeBSDSun Solaris , cả kiến trúc 32 bit64 bit , với các phiên bản Python từ 2.6 đến 3.4 (người dùng Python 2.4 và 2.5 có thể sử dụng phiên bản 2.1.3) . PyPy cũng được biết là hoạt động.

Nó có một chức năng được gọi là pid_exists()bạn có thể sử dụng để kiểm tra xem một tiến trình với pid đã cho có tồn tại hay không.

Đây là một ví dụ:

import psutil
pid = 12345
if psutil.pid_exists(pid):
    print("a process with pid %d exists" % pid)
else:
    print("a process with pid %d does not exist" % pid)

Để tham khảo:



Tôi đã gặp một trường hợp trong đó pid_exists không còn đáng tin cậy trên Windows. Nó đã báo cáo sai các pids chết khi đang được sử dụng. Đây là lời nhắc luôn cập nhật mô-đun psutil của bạn - đặc biệt là khi nâng cấp hệ điều hành.
Damon Brodie

62

mã mluebke không đúng 100%; kill () cũng có thể tăng EPERM (quyền truy cập bị từ chối) trong trường hợp đó, điều đó rõ ràng có nghĩa là một quá trình tồn tại. Điều này được cho là hoạt động:

(được chỉnh sửa theo bình luận của Jason R. Coombs)

import errno
import os

def pid_exists(pid):
    """Check whether pid exists in the current process table.
    UNIX only.
    """
    if pid < 0:
        return False
    if pid == 0:
        # According to "man 2 kill" PID 0 refers to every process
        # in the process group of the calling process.
        # On certain systems 0 is a valid PID but we have no way
        # to know that in a portable fashion.
        raise ValueError('invalid PID 0')
    try:
        os.kill(pid, 0)
    except OSError as err:
        if err.errno == errno.ESRCH:
            # ESRCH == No such process
            return False
        elif err.errno == errno.EPERM:
            # EPERM clearly means there's a process to deny access to
            return True
        else:
            # According to "man 2 kill" possible error values are
            # (EINVAL, EPERM, ESRCH)
            raise
    else:
        return True

Bạn không thể thực hiện việc này trên Windows trừ khi bạn sử dụng pywin32, ctypes hoặc mô-đun mở rộng C. Nếu bạn đồng ý với việc phụ thuộc vào lib bên ngoài, bạn có thể sử dụng psutil :

>>> import psutil
>>> psutil.pid_exists(2353)
True

haridsv gợi ý bài kiểm tra nên là e.errno! = 3; có lẽ e.errno! = errno.ESRCH
Jason R. Coombs

Tại sao? ESRCH có nghĩa là "không có quy trình như vậy".
Giampaolo Rodolà

2
Đúng. Vì ESRCH có nghĩa là "không có quá trình như vậy", errno! = ESRCH có nghĩa là "không phải không có quá trình như vậy" hoặc "quá trình tồn tại", rất giống với tên của hàm. Bạn đã đề cập cụ thể đến EPERM, nhưng các mã lỗi có thể có khác ngụ ý gì? Có vẻ như không chính xác khi chỉ ra một mã lỗi có liên quan lỏng lẻo đến mục đích của việc kiểm tra, trong khi ESRCH có vẻ liên quan chặt chẽ.
Jason R. Coombs

Bạn đúng. Tôi đã chỉnh sửa mã hiện phản ánh những gì bạn đề xuất.
Giampaolo Rodolà

trong python3 os.kill () ném ProcessLookupError
martinkunev

19

Các câu trả lời liên quan đến việc gửi 'tín hiệu 0' tới quy trình sẽ chỉ hoạt động nếu quy trình được đề cập thuộc sở hữu của người dùng đang chạy thử nghiệm . Nếu không, bạn sẽ nhận được quyềnOSError truy cập đến hạn , ngay cả khi pid tồn tại trong hệ thống.

Để vượt qua giới hạn này, bạn có thể kiểm tra xem có /proc/<pid>tồn tại hay không:

import os

def is_running(pid):
    if os.path.isdir('/proc/{}'.format(pid)):
        return True
    return False

Rõ ràng là điều này chỉ áp dụng cho các hệ thống dựa trên linux.


Sai lầm. PermissionErrorcó nghĩa là pid tồn tại , bạn nhận được ProcessLookupErrornếu pid không tồn tại.
jfs

Sự OSErrorcho phép do bị từ chối có thể được phân biệt với những cái khác - bằng cách xem xét errno hoặc thông qua việc nắm bắt các trường hợp ngoại lệ PermissionError/ chuyên biệt hơn ProcessLookupErrorxuất phát từ đó OSError. Hơn nữa, bạn chỉ gặp lỗi quyền nếu quá trình tồn tại. Vì vậy, ví dụ của bạn chỉ là một phương pháp thay thế hoạt động trên Linux và một số Unices khác nhưng nó không hoàn thiện hơn việc gọi đúng cách os.kill(pid, 0).
maxschlepzig

Giải pháp này không phải là nền tảng corss. OP muốn nó có sẵn trên Unix và Windows. Các /procprocfs chỉ thoát trên Linux, thậm chí không thoát trên BSD hoặc OSX.
Charles

Phương pháp này không thành công nếu / proc được gắn kết hidepid = 2, quá trình sẽ không hiển thị trong danh sách nếu nó thuộc sở hữu của người dùng khác.
Perkins

8

Trong Python 3.3+, bạn có thể sử dụng tên ngoại lệ thay vì hằng số errno. Phiên bản Posix :

import os

def pid_exists(pid): 
    if pid < 0: return False #NOTE: pid == 0 returns True
    try:
        os.kill(pid, 0) 
    except ProcessLookupError: # errno.ESRCH
        return False # No such process
    except PermissionError: # errno.EPERM
        return True # Operation not permitted (i.e., process exists)
    else:
        return True # no error, we can send a signal to the process

7

Hãy xem ở đây để biết cách dành riêng cho windows để nhận danh sách đầy đủ các quy trình đang chạy với ID của chúng. Nó sẽ giống như

from win32com.client import GetObject
def get_proclist():
    WMI = GetObject('winmgmts:')
    processes = WMI.InstancesOf('Win32_Process')
    return [process.Properties_('ProcessID').Value for process in processes]

Sau đó, bạn có thể xác minh pid bạn nhận được trong danh sách này. Tôi không có ý kiến ​​về chi phí hiệu suất, vì vậy bạn nên kiểm tra điều này nếu bạn định thực hiện xác minh pid thường xuyên.

Đối với * NIx, chỉ cần sử dụng giải pháp của mluebke.


Điều này làm việc tốt cho tôi. Tôi muốn kiểm tra tên quy trình nên đã hoán đổi "ProcessID" cho "Name" và cũng chuyển đổi nó thành danh sách kiểm tra để trả về True hoặc False.
JamesR

6

Xây dựng dựa trên ntrrgc, tôi đã củng cố phiên bản windows để nó kiểm tra mã thoát quy trình và kiểm tra quyền:

def pid_exists(pid):
    """Check whether pid exists in the current process table."""
    if os.name == 'posix':
        import errno
        if pid < 0:
            return False
        try:
            os.kill(pid, 0)
        except OSError as e:
            return e.errno == errno.EPERM
        else:
            return True
    else:
        import ctypes
        kernel32 = ctypes.windll.kernel32
        HANDLE = ctypes.c_void_p
        DWORD = ctypes.c_ulong
        LPDWORD = ctypes.POINTER(DWORD)
        class ExitCodeProcess(ctypes.Structure):
            _fields_ = [ ('hProcess', HANDLE),
                ('lpExitCode', LPDWORD)]

        SYNCHRONIZE = 0x100000
        process = kernel32.OpenProcess(SYNCHRONIZE, 0, pid)
        if not process:
            return False

        ec = ExitCodeProcess()
        out = kernel32.GetExitCodeProcess(process, ctypes.byref(ec))
        if not out:
            err = kernel32.GetLastError()
            if kernel32.GetLastError() == 5:
                # Access is denied.
                logging.warning("Access is denied to get pid info.")
            kernel32.CloseHandle(process)
            return False
        elif bool(ec.lpExitCode):
            # print ec.lpExitCode.contents
            # There is an exist code, it quit
            kernel32.CloseHandle(process)
            return False
        # No exit code, it's running.
        kernel32.CloseHandle(process)
        return True

Trên thực tế, theo msdn.microsoft.com/en-us/library/windows/desktop/… , GetExistCodeProcessyêu cầu quyền PROCESS_QUERY_INFORMATIONPROCESS_QUERY_LIMITED_INFORMATIONquyền truy cập.
phụ nữ

Lưu ý rằng mã là sai. GetExitCodeProcessnhận một xử lý và một con trỏ và trong mẫu này, nó nhận một ExitCodeProcesscấu trúc làm tham số thứ hai khi nó chỉ nên là một con trỏ.
Fabio Zadrozny

Sau OpenProcess, bạn nên kiểm tra GetLastError. ERROR_ACCESS_DENIED có nghĩa là quá trình tồn tại! Đây là một ví dụ hoàn chỉnh bằng cách sử dụng điều này: gist.github.com/ociule/8a48d2a6b15f49258a87b5f55be29250
ociule

4

Kết hợp câu trả lời của Giampaolo Rodolà cho POSIXcâu trả lời của tôi cho Windows, tôi nhận được câu này:

import os
if os.name == 'posix':
    def pid_exists(pid):
        """Check whether pid exists in the current process table."""
        import errno
        if pid < 0:
            return False
        try:
            os.kill(pid, 0)
        except OSError as e:
            return e.errno == errno.EPERM
        else:
            return True
else:
    def pid_exists(pid):
        import ctypes
        kernel32 = ctypes.windll.kernel32
        SYNCHRONIZE = 0x100000

        process = kernel32.OpenProcess(SYNCHRONIZE, 0, pid)
        if process != 0:
            kernel32.CloseHandle(process)
            return True
        else:
            return False

Phiên bản windows không hoạt động đối với tôi trên windows 8.1. Bạn phải kiểm tra GetExitCodeProcessvà đảm bảo rằng bạn thậm chí có quyền truy cập.
bay cao tốc

Chỉ sử dụng kernel32.OpenProcesslà không đủ. Như ở đây đã chỉ ra "nếu quá trình thoát gần đây, một pid có thể vẫn tồn tại cho xử lý." Nếu kernel32.OpenProcesstrả về giá trị khác 0, chúng ta vẫn cần kernel32.GetExitCodeProcesskiểm tra thêm mã thoát.
Meo meo

2

Trong Windows, bạn có thể thực hiện theo cách này:

import ctypes
PROCESS_QUERY_INFROMATION = 0x1000
def checkPid(pid):
    processHandle = ctypes.windll.kernel32.OpenProcess(PROCESS_QUERY_INFROMATION, 0,pid)
    if processHandle == 0:
        return False
    else:
        ctypes.windll.kernel32.CloseHandle(processHandle)
    return True

Trước hết, trong đoạn mã này, bạn cố gắng xử lý quá trình với pid đã cho. Nếu xử lý hợp lệ, sau đó đóng xử lý cho tiến trình và trả về True; nếu không, bạn trả về False. Tài liệu cho OpenProcess: https://msdn.microsoft.com/en-us/library/windows/desktop/ms684320%28v=vs.85%29.aspx


2

Điều này sẽ hoạt động đối với Linux, chẳng hạn nếu bạn muốn kiểm tra xem banshee có đang chạy hay không ... (banshee là một trình phát nhạc)

import subprocess

def running_process(process):
    "check if process is running. < process > is the name of the process."

    proc = subprocess.Popen(["if pgrep " + process + " >/dev/null 2>&1; then echo 'True'; else echo 'False'; fi"], stdout=subprocess.PIPE, shell=True)

    (Process_Existance, err) = proc.communicate()
    return Process_Existance

# use the function
print running_process("banshee")

Phương pháp này rõ ràng là kém hơn so với việc sử dụng os.kill(pid, 0)hoặc nhìn vào /proc/{pid}. Thay vì thực thi một syscall, mã của bạn fork một phần tử con, thực thi một trình bao trong con đó, trình bao thông dịch tập lệnh shell nhỏ không cần thiết của bạn, trình bao này phân tách một con khác thực thi pgrep và cuối cùng pgrep lặp lại /proc. Câu trả lời của bạn không trả lời câu hỏi đã đăng. OP đã yêu cầu một phương pháp được cung cấp một PID. Phương pháp của bạn yêu cầu một tên quy trình.
maxschlepzig

-2

Tôi muốn nói rằng hãy sử dụng PID cho bất kỳ mục đích nào bạn đang lấy nó và xử lý các lỗi một cách khéo léo. Nếu không, đó là một cuộc đua cổ điển (PID có thể hợp lệ khi bạn kiểm tra nó hợp lệ, nhưng biến mất ngay sau đó)


Lẽ ra tôi phải cụ thể hơn - Tôi đang kiểm tra tính HỢP LỆ. Vì vậy, về cơ bản tôi muốn xem liệu pid KHÔNG được sử dụng hay không.
Evan Fosmark

1
Nhưng bạn sẽ làm gì với câu trả lời đó? Ngay sau khi bạn có được kiến ​​thức đó, một cái gì đó có thể sử dụng pid đó.
Damien_The_Un Believer 20/02/09

@Damien_The_Un Believer - không sao cả nếu có thứ gì đó đang sử dụng nó sau khi tôi có được kiến ​​thức đó và tôi hiểu bạn đang nói gì về điều kiện chủng tộc, nhưng tôi có thể đảm bảo với bạn rằng điều đó không áp dụng cho trường hợp của tôi.
Evan Fosmark
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.