Bagaimana cara memeriksa apakah ada proses dengan pid yang diberikan dengan Python?

109

Apakah ada cara untuk memeriksa untuk melihat apakah pid sesuai dengan proses yang valid? Saya mendapatkan pid dari sumber yang berbeda selain dari os.getpid()dan saya perlu memeriksa untuk melihat apakah proses dengan pid itu tidak ada di mesin.

Saya membutuhkannya untuk tersedia di Unix dan Windows. Saya juga memeriksa untuk melihat apakah PID TIDAK digunakan.

Evan Fosmark
sumber
2
Windows adalah OS non-standar. Hal-hal seperti ini TIDAK portabel. Mengetahui bahwa Anda tidak dapat memiliki keduanya, mana yang menjadi prioritas Anda? Pilih satu sebagai prioritas dan edit pertanyaannya.
S. Lotot
26
@ S.Lott Windows adalah OS non-standar Ini adalah salah satu komentar paling konyol yang pernah saya lihat di SO ...
Piotr Dobrogost
2
@Piotr Dobrogost: Dapatkah Anda memberikan kode yang menangani standar unix POSIX dan standar non-POSIX Windows? Jika demikian, berikan jawaban bahwa (a) menyelesaikan masalah dan (b) menjelaskan bahwa Windows sesuai dengan standar POSIX.
S. Lott
3
@PiotrDobrogost Menurut saya komentar S.Lott lebih banyak tentang detail implementasi dan dukungan API daripada pangsa pasar.
Roy Tinker
3
Windows pasti memiliki sedikit kesamaan dengan OS populer lainnya daripada yang lainnya dengan satu sama lain. (Siapa pun yang melakukan pengembangan web mungkin menyamakannya dengan produk Microsoft yang sama terkenalnya.) Tetapi sebagai tanggapan terhadap @ S. Lott: Saya jarang menulis kode Python untuk Windows yang tidak seharusnya juga berfungsi di Linux, OSX, BSD, dll, jadi saya sejujurnya jangan berpikir 'memilih sebagai prioritas' adalah saran yang berguna, terutama karena Python memisahkan perbedaan platform sebanyak mungkin.
Michael Scheper

Jawaban:

163

Mengirim sinyal 0 ke pid akan memunculkan pengecualian OSError jika pid tidak berjalan, dan tidak melakukan apapun.

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
mluebke.dll
sumber
3
Bekerja dengan pasti di linux dan OSX, saya tidak dapat berbicara untuk Windows. Itu tidak mematikan proses dalam arti yang Anda tanyakan, itu mengirimkan sinyal 0, yang pada dasarnya adalah "Apakah Anda sedang berjalan?".
mluebke
10
Ini jelas tidak berfungsi pada Windows, karena tidak ada yang namanya sinyal mirip UNIX.
Alexander Lebedev
13
Agar lengkap, Anda juga harus memeriksa nomor kesalahan untuk memastikannya adalah 3 (tangkap pengecualian dan periksa argumen pertama). Ini penting jika proses target ada tetapi Anda tidak memiliki izin untuk mengirim sinyal (untuk alasan apa pun).
haridsv
8
Didukung oleh windows sekarang. docs.python.org/library/os.html?highlight=os.kill#os.kill
michael
15
os.kill didukung di Windows, tetapi os.kill(pid, 0)sama seperti os.kill(pid, signal.CTRL_C_EVENT)yang dapat menghentikan proses (atau gagal). Saya mendapatkan OSErrortempat errno==EINVALketika saya mencoba ini pada sebuah subproses.
Jason R. Coombs
76

Lihat psutilmodulnya:

psutil (sistem python dan utilitas proses) adalah pustaka lintas platform untuk mengambil informasi tentang proses yang sedang berjalan dan pemanfaatan sistem (CPU, memori, disk, jaringan) dengan Python. [...] Saat ini mendukung Linux , Windows , OSX , FreeBSD dan Sun Solaris , baik arsitektur 32-bit dan 64-bit , dengan versi Python dari 2.6 hingga 3.4 (pengguna Python 2.4 dan 2.5 dapat menggunakan versi 2.1.3) . PyPy juga dikenal bekerja.

Ini memiliki fungsi yang disebut pid_exists()yang dapat Anda gunakan untuk memeriksa apakah proses dengan pid yang diberikan ada.

Berikut contohnya:

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)

Sebagai referensi:

moooeeeep
sumber
Saya memiliki contoh di mana pid_exists berhenti dapat diandalkan di Windows. Itu salah melaporkan pids mati sebagai sedang digunakan. Ini adalah pengingat untuk terus memperbarui modul psutil Anda dari waktu ke waktu - terutama saat melakukan peningkatan OS.
Damon Brodie
62

kode mluebke tidak 100% benar; kill () juga dapat meningkatkan EPERM (akses ditolak) dalam hal ini berarti ada proses. Ini seharusnya berhasil:

(diedit sesuai komentar 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

Anda tidak dapat melakukan ini di Windows kecuali Anda menggunakan pywin32, ctypes atau modul ekstensi C. Jika Anda setuju dengan bergantung dari lib eksternal Anda dapat menggunakan psutil :

>>> import psutil
>>> psutil.pid_exists(2353)
True
Giampaolo Rodolà
sumber
haridsv menyarankan tes harus e.errno! = 3; mungkin e.errno! = errno.ESRCH
Jason R. Coombs
Mengapa? ESRCH berarti "tidak ada proses seperti itu".
Giampaolo Rodolà
2
Baik. Karena ESRCH berarti "tidak ada proses seperti itu", errno! = ESRCH berarti "tidak ada proses seperti itu" atau "proses yang ada", yang sangat mirip dengan nama fungsinya. Anda menyebutkan secara khusus EPERM, tetapi apa yang disiratkan oleh kode kesalahan lain yang mungkin? Tampaknya salah untuk memilih kode kesalahan yang terkait secara longgar dengan maksud pemeriksaan, sedangkan ESRCH tampaknya terkait erat.
Jason R. Coombs
Kamu benar. Saya mengedit kode yang sekarang mencerminkan apa yang Anda sarankan.
Giampaolo Rodolà
di python3 os.kill () throws ProcessLookupError
martinkunev
19

Jawaban yang melibatkan pengiriman 'sinyal 0' ke proses hanya akan berfungsi jika proses tersebut dimiliki oleh pengguna yang menjalankan pengujian . Jika tidak, Anda akan mendapatkan izinOSError karena , meskipun pid ada di sistem.

Untuk melewati batasan ini, Anda dapat memeriksa apakah /proc/<pid>ada:

import os

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

Ini jelas berlaku untuk sistem berbasis linux saja.

Omer Dagan
sumber
salah. PermissionErrorberarti pid ada , Anda mendapatkan ProcessLookupErrorjika pid tidak ada.
jfs
The OSErrorkarena izin ditolak dapat dibedakan dari yang lain - baik melalui melihat errno atau melalui menangkap lebih khusus PermissionError/ ProcessLookupErrorpengecualian yang berasal dari OSError. Selanjutnya, Anda hanya mendapatkan kesalahan izin jika proses tersebut ada. Jadi, contoh Anda hanyalah metode alternatif yang berfungsi di Linux dan beberapa Unix lainnya tetapi tidak lebih lengkap daripada memanggil dengan benar os.kill(pid, 0).
maxschlepzig
Solusi ini bukan platform corss. OP menginginkannya tersedia di Unix dan Windows. The /procprocfs hanya keluar di Linux, bahkan di BSD atau OSX.
Charles
Metode ini gagal jika / proc dipasang hidepid = 2, proses tidak akan ditampilkan dalam daftar jika dimiliki oleh pengguna lain.
Perkins
8

Di Python 3.3+, Anda bisa menggunakan nama pengecualian sebagai ganti konstanta errno. Versi 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
jfs
sumber
7

Lihat di sini untuk cara khusus windows untuk mendapatkan daftar lengkap proses yang berjalan dengan ID mereka. Ini akan menjadi seperti itu

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]

Anda kemudian dapat memverifikasi pid yang Anda dapatkan terhadap daftar ini. Saya tidak tahu tentang biaya kinerja, jadi sebaiknya Anda memeriksa ini jika Anda akan sering melakukan verifikasi pid.

Untuk * NIx, cukup gunakan solusi mluebke.

Alexander Lebedev
sumber
Ini bekerja dengan baik untuk saya. Saya ingin memeriksa nama proses jadi menukar "ProcessID" untuk "Name" dan juga mengubahnya menjadi check in list untuk mengembalikan True atau False.
JamesR
6

Membangun di atas ntrrgc, saya telah meningkatkan versi windows sehingga memeriksa kode keluar proses dan memeriksa izin:

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
speedplane
sumber
Sebenarnya, menurut msdn.microsoft.com/en-us/library/windows/desktop/… , GetExistCodeProcessmembutuhkan PROCESS_QUERY_INFORMATIONdan PROCESS_QUERY_LIMITED_INFORMATIONhak akses.
augustomen
Perhatikan bahwa kodenya salah. GetExitCodeProcessmenerima sebuah pegangan dan sebuah pointer dan dalam contoh ini ia menerima sebuah ExitCodeProcessstruktur sebagai parameter kedua yang seharusnya hanya sebuah pointer.
Fabio Zadrozny
Setelah OpenProcess, ada baiknya untuk memeriksa GetLastError. ERROR_ACCESS_DENIED di sana berarti proses tersebut ada! Berikut adalah contoh lengkap menggunakan ini: gist.github.com/ociule/8a48d2a6b15f49258a87b5f55be29250
ociule
4

Menggabungkan jawaban Giampaolo Rodolà untuk POSIX dan milik saya untuk Windows, saya mendapatkan ini:

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
Alicia
sumber
Versi windows tidak berfungsi untuk saya di windows 8.1. Anda harus memeriksa GetExitCodeProcessdan memastikan bahwa Anda memiliki akses.
speedplane
Hanya menggunakan saja kernel32.OpenProcesstidak cukup. Seperti yang ditunjukkan di sini "jika proses keluar baru-baru ini, pid mungkin masih ada untuk pegangan." Jika kernel32.OpenProcessmengembalikan nilai bukan nol, kita masih perlu kernel32.GetExitCodeProcesslebih jauh untuk memeriksa kode keluar.
Meong
2

Di Windows, Anda dapat melakukannya dengan cara ini:

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

Pertama-tama, dalam kode ini Anda mencoba untuk mendapatkan pegangan untuk proses dengan pid yang diberikan. Jika pegangannya valid, tutup handle untuk proses dan kembalikan True; jika tidak, Anda mengembalikan False. Dokumentasi untuk OpenProcess: https://msdn.microsoft.com/en-us/library/windows/desktop/ms684320%28v=vs.85%29.aspx

Andrei
sumber
2

Ini akan berfungsi untuk Linux, misalnya jika Anda ingin memeriksa apakah banshee sedang berjalan ... (banshee adalah pemutar musik)

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")
BARSPIN
sumber
Metode ini jelas lebih rendah dibandingkan dengan menggunakan os.kill(pid, 0)atau melihat /proc/{pid}. Alih-alih mengeksekusi satu syscall, kode Anda mem-fork seorang anak, mengeksekusi shell pada turunan itu, shell menafsirkan skrip-shell mini Anda yang berlebihan, shell mem-fork turunan lain yang menjalankan pgrep dan akhirnya pgrep melakukan iterasi /proc. Jawaban Anda tidak menjawab pertanyaan yang diposting. OP meminta metode yang diberi PID. Metode Anda membutuhkan nama proses.
maxschlepzig
-2

Saya akan mengatakan gunakan PID untuk tujuan apa pun Anda mendapatkannya dan menangani kesalahan dengan anggun. Jika tidak, ini adalah balapan klasik (PID mungkin valid jika Anda memeriksa validitasnya, tetapi segera hilang nanti)

Damien_The_Tidak percaya
sumber
Saya seharusnya lebih spesifik - Saya memeriksa INVALIDITY. Jadi, pada dasarnya saya ingin dapat melihat apakah pid TIDAK digunakan.
Evan Fosmark
1
Tapi apa yang akan Anda lakukan dengan jawaban itu? Segera setelah Anda memperoleh pengetahuan itu, sesuatu mungkin menggunakan pid itu.
Damien_The_Unbeliever
@Damien_The_Unbeliever - tidak apa-apa jika ada sesuatu yang menggunakannya setelah saya mendapatkan pengetahuan itu, dan saya mengerti apa yang Anda katakan tentang kondisi balapan, tetapi saya dapat meyakinkan Anda bahwa itu tidak berlaku untuk situasi saya.
Evan Fosmark