จะตรวจสอบได้อย่างไรว่ามีกระบวนการกับ pid ที่กำหนดใน Python หรือไม่?


109

มีวิธีตรวจสอบว่า pid สอดคล้องกับกระบวนการที่ถูกต้องหรือไม่? ฉันได้รับ pid จากแหล่งอื่นที่ไม่ใช่จากos.getpid()และฉันต้องตรวจสอบเพื่อดูว่าไม่มีกระบวนการกับ pid นั้นในเครื่องหรือไม่

ฉันต้องการให้มันพร้อมใช้งานใน Unix และ Windows ฉันกำลังตรวจสอบเพื่อดูว่า PID ไม่ได้ใช้งานหรือไม่


2
Windows เป็นระบบปฏิบัติการที่ไม่ได้มาตรฐาน สิ่งเหล่านี้ไม่สามารถพกพาได้ การรู้ว่าคุณไม่สามารถมีทั้งสองอย่างได้อะไรคือความสำคัญของคุณ? เลือกหนึ่งรายการเป็นลำดับความสำคัญและแก้ไขคำถาม
ล็อต

26
@ S.Lott Windows เป็นระบบปฏิบัติการที่ไม่ได้มาตรฐานนี่เป็นหนึ่งในคำพูดที่งี่เง่าที่สุดที่ฉันเคยเห็นใน SO ...
Piotr Dobrogost

2
@Piotr Dobrogost: คุณสามารถระบุรหัสที่จัดการกับ Windows unix มาตรฐาน POSIX และไม่ใช่ POSIX มาตรฐานได้หรือไม่? ในกรณีนี้โปรดให้คำตอบว่า (a) แก้ปัญหาได้และ (b) ทำให้ชัดเจนว่า Windows เป็นไปตามมาตรฐาน POSIX
ล็อต

3
@PiotrDobrogost ฉันคิดว่าคำพูดของ S.Lott เกี่ยวกับรายละเอียดการใช้งานและการรองรับ API มากกว่าส่วนแบ่งการตลาด
Roy Tinker

3
Windows มีความเหมือนกันกับระบบปฏิบัติการยอดนิยมอื่น ๆ น้อยกว่าระบบปฏิบัติการอื่น ๆ ที่เหลือ (ใครก็ตามที่พัฒนาเว็บอาจเปรียบได้กับผลิตภัณฑ์ Microsoft ที่น่าอับอายเหมือนกัน) แต่ในการตอบกลับ @ S.Lott: ฉันไม่ค่อยเขียนโค้ด Python สำหรับ Windows ซึ่งไม่ควรใช้กับ Linux, OSX, BSD และอื่น ๆ ดังนั้นฉัน อย่าคิดว่า 'เลือกเป็นลำดับความสำคัญ' เป็นคำแนะนำที่เป็นประโยชน์โดยเฉพาะอย่างยิ่งเนื่องจาก Python แยกความแตกต่างของแพลตฟอร์มออกไปให้มากที่สุดเท่าที่จะทำได้
Michael Scheper

คำตอบ:


164

การส่งสัญญาณ 0 ไปยัง pid จะเพิ่มข้อยกเว้น OSError หาก pid ไม่ทำงานและไม่ทำอะไรเลย

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
ใช้งานได้แน่นอนใน linux และ OSX ฉันไม่สามารถพูดกับ Windows ได้ มันไม่ได้ฆ่ากระบวนการในแง่ที่คุณกำลังถามมันส่งสัญญาณ 0 ซึ่งโดยพื้นฐานแล้ว "คุณกำลังทำงานอยู่หรือเปล่า"
mluebke

10
สิ่งนี้ใช้ไม่ได้กับ Windows อย่างแน่นอนเนื่องจากไม่มีสัญญาณเหมือน UNIX
Alexander Lebedev

13
เพื่อให้เสร็จสมบูรณ์คุณควรตรวจสอบหมายเลขข้อผิดพลาดเพื่อให้แน่ใจว่าเป็น 3 (ตรวจจับข้อยกเว้นและตรวจสอบอาร์กิวเมนต์แรก) นี่เป็นสิ่งสำคัญหากกระบวนการเป้าหมายมีอยู่ แต่คุณไม่มีสิทธิ์ส่งสัญญาณ (ไม่ว่าด้วยเหตุผลใดก็ตาม)
haridsv

8
รองรับโดย windows ในขณะนี้ docs.python.org/library/os.html?highlight=os.kill#os.kill
michael

15
os.kill ได้รับการสนับสนุนบน Windows แต่os.kill(pid, 0)เหมือนกับos.kill(pid, signal.CTRL_C_EVENT)ที่อาจยุติกระบวนการ (หรือล้มเหลว) ฉันได้รับOSErrorที่errno==EINVALเมื่อฉันลองทำในกระบวนการย่อย
Jason R.Combs

77

ดูpsutilโมดูล:

psutil (ระบบ python และยูทิลิตี้กระบวนการ) เป็นไลบรารีข้ามแพลตฟอร์มสำหรับการดึงข้อมูลเกี่ยวกับกระบวนการที่กำลังทำงานและการใช้ระบบ (CPU, หน่วยความจำ, ดิสก์, เครือข่าย) ใน Python [... ] ปัจจุบันรองรับLinux , Windows , OSX , FreeBSDและSun Solarisทั้งสถาปัตยกรรม32 บิตและ64 บิตโดยมีเวอร์ชัน Python ตั้งแต่2.6 ถึง 3.4 (ผู้ใช้ Python 2.4 และ 2.5 อาจใช้เวอร์ชัน 2.1.3) . PyPy เป็นที่รู้จักกันในการทำงาน

มีฟังก์ชันที่เรียกpid_exists()ว่าคุณสามารถใช้เพื่อตรวจสอบว่ามีกระบวนการกับ pid ที่กำหนดหรือไม่

นี่คือตัวอย่าง:

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)

สำหรับการอ้างอิง:



ฉันมีอินสแตนซ์ที่ pid_exists หยุดความน่าเชื่อถือบน Windows มีรายงานผิดพลาดว่ามีการใช้งาน นี่คือการแจ้งเตือนให้อัปเดตโมดูล psutil เป็นครั้งคราวโดยเฉพาะอย่างยิ่งเมื่อทำการอัปเกรดระบบปฏิบัติการ
Damon Brodie

62

รหัส mluebke ไม่ถูกต้อง 100% kill () ยังสามารถเพิ่ม EPERM (การเข้าถึงถูกปฏิเสธ) ซึ่งในกรณีนี้หมายความว่ามีกระบวนการอยู่ สิ่งนี้ควรจะใช้งานได้:

(แก้ไขตามความคิดเห็นของ Jason R.Combs)

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

คุณไม่สามารถทำได้บน Windows เว้นแต่คุณจะใช้ pywin32, ctypes หรือโมดูลส่วนขยาย C หากคุณตกลงโดยขึ้นอยู่กับ lib ภายนอกคุณสามารถใช้psutil :

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

haridsv แนะนำการทดสอบควรเป็น e.errno! = 3; บางที e.errno! = errno.ESRCH
Jason

ทำไม? ESRCH หมายถึง "ไม่มีกระบวนการดังกล่าว"
Giampaolo Rodolà

2
ขวา. เนื่องจาก ESRCH หมายถึง "ไม่มีกระบวนการดังกล่าว" errno! = ESRCH หมายถึง "ไม่มีกระบวนการดังกล่าว" หรือ "กระบวนการนี้มีอยู่" ซึ่งคล้ายกับชื่อของฟังก์ชันมาก คุณพูดถึง EPERM โดยเฉพาะ แต่รหัสข้อผิดพลาดอื่น ๆ ที่เป็นไปได้บ่งบอกถึงอะไร ดูเหมือนว่าจะไม่ถูกต้องในการแยกรหัสข้อผิดพลาดซึ่งเกี่ยวข้องอย่างหลวม ๆ กับจุดประสงค์ของการตรวจสอบในขณะที่ ESRCH ดูเหมือนจะเกี่ยวข้องอย่างใกล้ชิด
Jason R.Combs

คุณถูก. ฉันแก้ไขโค้ดซึ่งตอนนี้แสดงถึงสิ่งที่คุณแนะนำ
Giampaolo Rodolà

ใน python3 os.kill () พ่น ProcessLookupError
martinkunev

20

คำตอบที่เกี่ยวข้องกับการส่งสัญญาณ 0 'ถึงกระบวนการที่จะทำงานเฉพาะในกรณีที่กระบวนการในคำถามเป็นเจ้าของโดยผู้ใช้ที่ใช้ทดสอบ มิฉะนั้นคุณจะได้รับOSErrorเนื่องจากสิทธิ์แม้ว่า pid จะมีอยู่ในระบบก็ตาม

ในการข้ามข้อ จำกัด นี้คุณสามารถตรวจสอบว่า/proc/<pid>มีอยู่หรือไม่:

import os

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

สิ่งนี้ใช้กับระบบที่ใช้ linux เท่านั้นเห็นได้ชัดว่า


ไม่ถูกต้อง. PermissionErrorหมายความว่ามี pid อยู่คุณจะได้รับProcessLookupErrorถ้าไม่มี pid
jfs

OSErrorเนื่องจากการอนุญาตปฏิเสธจะแตกต่างจากคนอื่น ๆ - ทั้งผ่านการมองหาที่ errno หรือผ่านการจับความเชี่ยวชาญมากขึ้นPermissionError/ ProcessLookupErrorข้อยกเว้นซึ่งเป็นผลมาจากOSErrorข้อยกเว้นซึ่งเป็นผลมาจากยิ่งไปกว่านั้นคุณจะได้รับข้อผิดพลาดการอนุญาตหากมีกระบวนการอยู่เท่านั้น ดังนั้นตัวอย่างของคุณเป็นเพียงวิธีการอื่นที่ทำงานบนลินุกซ์และอื่น ๆ บาง Unices os.kill(pid, 0)แต่มันไม่ได้สมบูรณ์มากขึ้นกว่าการเรียกอย่างถูกต้อง
maxschlepzig

โซลูชันนี้ไม่ใช่แพลตฟอร์ม corss OP ต้องการให้พร้อมใช้งานบน Unix และ Windows /procprocfs เพียงทางออกบนลินุกซ์ไม่ได้แม้ใน BSD หรือ OSX
Charles

วิธีนี้จะล้มเหลวหากติดตั้ง / proc hidepid = 2 กระบวนการจะไม่ปรากฏในรายการหากเป็นของผู้ใช้รายอื่น
Perkins

8

ใน Python 3.3+ คุณสามารถใช้ชื่อข้อยกเว้นแทนค่าคงที่ผิดพลาดได้ รุ่น 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

ดูที่นี่หาวิธีหน้าต่างเฉพาะในการรับรายการเต็มรูปแบบของกระบวนการทำงานที่มีรหัสของพวกเขา มันจะเป็นสิ่งที่ชอบ

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]

จากนั้นคุณสามารถตรวจสอบ pid ที่คุณได้รับจากรายการนี้ ฉันไม่รู้เกี่ยวกับต้นทุนประสิทธิภาพดังนั้นคุณควรตรวจสอบสิ่งนี้ให้ดีกว่าหากคุณจะทำการยืนยัน pid บ่อยๆ

สำหรับ * NIx ให้ใช้สารละลายของ mluebke


นี้ทำงานได้ดีสำหรับฉัน. ฉันต้องการตรวจสอบชื่อกระบวนการดังนั้นจึงสลับ "ProcessID" เป็น "ชื่อ" และแปลงเป็นรายการตรวจสอบในรายการเพื่อส่งคืน True หรือ False
JamesR

6

จากการสร้าง ntrrgc ฉันได้เพิ่มเวอร์ชันของ windows ดังนั้นจึงตรวจสอบรหัสออกของกระบวนการและตรวจสอบสิทธิ์:

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

อันที่จริงตามmsdn.microsoft.com/en-us/library/windows/desktop/... , GetExistCodeProcessต้องใช้PROCESS_QUERY_INFORMATIONและPROCESS_QUERY_LIMITED_INFORMATIONการเข้าถึงสิทธิ
augustomen

โปรดทราบว่ารหัสไม่ถูกต้อง GetExitCodeProcessได้รับที่จับและตัวชี้และในตัวอย่างนี้จะได้รับExitCodeProcessโครงสร้างเป็นพารามิเตอร์ที่สองเมื่อควรเป็นเพียงตัวชี้เท่านั้น
Fabio Zadrozny

หลังจาก OpenProcess ควรตรวจสอบ GetLastError ERROR_ACCESS_DENIED หมายความว่ากระบวนการนี้มีอยู่จริง! นี่คือตัวอย่างที่สมบูรณ์โดยใช้สิ่งนี้: gist.github.com/ociule/8a48d2a6b15f49258a87b5f55be29250
ociule

4

การรวมคำตอบของ Giampaolo Rodolàสำหรับ POSIXและของฉันสำหรับ Windowsฉันได้รับสิ่งนี้:

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

เวอร์ชัน windows ใช้ไม่ได้สำหรับฉันบน windows 8.1 คุณต้องตรวจสอบGetExitCodeProcessและให้แน่ใจว่าคุณสามารถเข้าถึงได้
เครื่องบินเร็ว

การใช้kernel32.OpenProcessเพียงอย่างเดียวไม่เพียงพอ ดังที่ได้กล่าวไว้ในที่นี้ว่า"หากกระบวนการออกไปเมื่อเร็ว ๆ นี้อาจมี pid สำหรับที่จับ" หากkernel32.OpenProcessส่งคืนค่าที่ไม่ใช่ศูนย์เรายังคงต้องkernel32.GetExitCodeProcessตรวจสอบรหัสทางออกเพิ่มเติม
Meow

2

ใน Windows คุณสามารถทำได้ด้วยวิธีนี้:

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

ก่อนอื่นในรหัสนี้คุณต้องพยายามจัดการกระบวนการด้วย pid ที่กำหนด หากแฮนเดิลถูกต้องให้ปิดแฮนเดิลสำหรับกระบวนการและส่งคืน True มิฉะนั้นคุณจะส่งคืน False เอกสารประกอบสำหรับ OpenProcess: https://msdn.microsoft.com/en-us/library/windows/desktop/ms684320%28v=vs.85%29.aspx


2

สิ่งนี้จะใช้ได้กับ Linux เช่นถ้าคุณต้องการตรวจสอบว่า banshee กำลังทำงานอยู่หรือไม่ ... (banshee เป็นเครื่องเล่นเพลง)

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")

วิธีการนี้จะเห็นได้ชัดว่าจะด้อยกว่าเมื่อเทียบกับทั้งการใช้หรือกำลังมองหาที่os.kill(pid, 0) /proc/{pid}แทนการดำเนินการอย่างใดอย่างหนึ่ง syscall รหัสของคุณส้อมเด็กผู้บริหารเชลล์ในเด็กที่เปลือกตีความฟุ่มเฟือยมินิเชลล์สคริปต์ของคุณเปลือกส้อมเด็กอื่นที่ผู้บริหาร pgrep และ iterates ที่สุด pgrep /procมากกว่า คำตอบของคุณไม่ตอบคำถามที่โพสต์ OP ขอวิธีการให้ PID วิธีการของคุณต้องใช้ชื่อกระบวนการ
maxschlepzig

0

โค้ดต่อไปนี้ใช้ได้กับทั้ง Linux และ Windows และไม่ขึ้นอยู่กับโมดูลภายนอก

import os
import subprocess
import platform
import re

def pid_alive(pid:int):
    """ Check For whether a pid is alive """


    system = platform.uname().system
    if re.search('Linux', system, re.IGNORECASE):
        try:
            os.kill(pid, 0)
        except OSError:
            return False
        else:
            return True
    elif re.search('Windows', system, re.IGNORECASE):
        out = subprocess.check_output(["tasklist","/fi",f"PID eq {pid}"]).strip()
        # b'INFO: No tasks are running which match the specified criteria.'

        if re.search(b'No tasks', out, re.IGNORECASE):
            return False
        else:
            return True
    else:
        raise RuntimeError(f"unsupported system={system}")

สามารถเพิ่มได้อย่างง่ายดายในกรณีที่คุณต้องการ

  1. แพลตฟอร์มอื่น ๆ
  2. ภาษาอื่น ๆ

-2

ฉันจะบอกว่าใช้ PID เพื่อจุดประสงค์ใดก็ตามที่คุณได้รับและจัดการข้อผิดพลาดอย่างสง่างาม มิฉะนั้นจะเป็นการแข่งขันแบบคลาสสิก (PID อาจใช้ได้เมื่อคุณตรวจสอบว่าถูกต้อง แต่จะหายไปในภายหลัง)


ฉันควรจะเจาะจงมากกว่านี้ - ฉันกำลังตรวจสอบความไม่ถูกต้อง โดยพื้นฐานแล้วฉันต้องการที่จะดูว่า pid ไม่ได้ใช้งานหรือไม่
Evan Fosmark

1
แต่คุณจะทำอย่างไรกับคำตอบนั้น? ทันทีหลังจากที่คุณได้รับความรู้บางอย่างอาจใช้ pid นั้น
Damien_The_Unbeliever

@Damien_The_Unbeliever - ไม่เป็นไรถ้ามีบางอย่างใช้มันหลังจากที่ฉันได้รับความรู้นั้นและฉันเข้าใจสิ่งที่คุณพูดเกี่ยวกับสภาพการแข่งขัน แต่ฉันมั่นใจได้ว่ามันไม่ได้ใช้กับสถานการณ์ของฉัน
Evan Fosmark
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.