Quy trình con

Source code: Lib/asyncio/subprocess.py, Lib/asyncio/base_subprocess.py


Phần này mô tả các API async/await asyncio cấp cao để tạo và quản lý các quy trình con.

Đây là một ví dụ về cách asyncio có thể chạy lệnh shell và nhận được kết quả của nó

nhập asyncio

chạy không đồng bộ (cmd):
    proc = đang chờ asyncio.create_subprocess_shell(
        cmd,
        thiết bị xuất chuẩn = asyncio.subprocess.PIPE,
        stderr=asyncio.subprocess.PIPE)

    thiết bị xuất chuẩn, stderr = đang chờ proc.communicate()

    print(f'[{cmd!r} đã thoát với {proc.returncode}]')
    nếu thiết bị xuất chuẩn:
        print(f'[stdout]\n{stdout.decode()}')
    nếu lỗi chuẩn:
        print(f'[stderr]\n{stderr.decode()}')

asyncio.run(run('ls /zzz'))

sẽ in:

['ls /zzz' đã thoát với 1]
[stderr]
ls: /zzz: Không  tập tin hoặc thư mục như vậy

Bởi vì tất cả các hàm của quy trình con asyncio đều không đồng bộ và asyncio cung cấp nhiều công cụ để làm việc với các hàm đó nên rất dễ thực thi và giám sát nhiều quy trình con song song. Thực sự là không cần thiết khi sửa đổi ví dụ trên để chạy nhiều lệnh cùng lúc

async def main():
    đang chờ asyncio.gather(
        chạy('ls /zzz'),
        run('ngủ 1; echo "xin chào"'))

asyncio.run(chính())

Xem thêm phần phụ Examples.

Tạo quy trình con

async asyncio.create_subprocess_exec(program, *args, stdin=None, stdout=None, stderr=None, limit=None, **kwds)

Tạo một quy trình con.

Đối số limit đặt giới hạn bộ đệm cho các trình bao bọc StreamReader cho stdoutstderr (nếu subprocess.PIPE được chuyển cho các đối số stdoutstderr).

Trả về một phiên bản Process.

Xem tài liệu của loop.subprocess_exec() để biết các thông số khác.

Nếu đối tượng tiến trình bị thu gom rác trong khi tiến trình vẫn đang chạy thì tiến trình con sẽ bị hủy.

Thay đổi trong phiên bản 3.10: Đã xóa tham số loop.

async asyncio.create_subprocess_shell(cmd, stdin=None, stdout=None, stderr=None, limit=None, **kwds)

Chạy lệnh shell cmd.

Đối số limit đặt giới hạn bộ đệm cho các trình bao bọc StreamReader cho stdoutstderr (nếu subprocess.PIPE được chuyển cho các đối số stdoutstderr).

Trả về một phiên bản Process.

Xem tài liệu của loop.subprocess_shell() để biết các thông số khác.

Nếu đối tượng tiến trình bị thu gom rác trong khi tiến trình vẫn đang chạy thì tiến trình con sẽ bị hủy.

Quan trọng

Trách nhiệm của ứng dụng là đảm bảo rằng tất cả khoảng trắng và ký tự đặc biệt đều được trích dẫn một cách thích hợp để tránh các lỗ hổng shell injection. Hàm shlex.quote() có thể được sử dụng để thoát khỏi khoảng trắng và các ký tự shell đặc biệt trong chuỗi sẽ được sử dụng để xây dựng các lệnh shell một cách chính xác.

Thay đổi trong phiên bản 3.10: Đã xóa tham số loop.

Ghi chú

Các quy trình con có sẵn cho Windows nếu sử dụng ProactorEventLoop. Xem Subprocess Support on Windows để biết chi tiết.

Xem thêm

asyncio cũng có các API low-level sau để hoạt động với các quy trình con: loop.subprocess_exec(), loop.subprocess_shell(), loop.connect_read_pipe(), loop.connect_write_pipe(), cũng như Subprocess TransportsSubprocess Protocols.

Hằng số

asyncio.subprocess.PIPE

Có thể được chuyển đến các tham số stdin, stdout hoặc stderr.

Nếu PIPE được truyền cho đối số stdin, thuộc tính Process.stdin sẽ trỏ đến một phiên bản StreamWriter.

Nếu PIPE được chuyển tới các đối số stdout hoặc stderr, thuộc tính Process.stdoutProcess.stderr sẽ trỏ đến các phiên bản StreamReader.

asyncio.subprocess.STDOUT

Giá trị đặc biệt có thể được sử dụng làm đối số stderr và cho biết rằng lỗi tiêu chuẩn sẽ được chuyển hướng sang đầu ra tiêu chuẩn.

asyncio.subprocess.DEVNULL

Giá trị đặc biệt có thể được sử dụng làm đối số stdin, stdout hoặc stderr để xử lý các hàm tạo. Nó chỉ ra rằng tệp đặc biệt os.devnull sẽ được sử dụng cho luồng quy trình con tương ứng.

Tương tác với các quy trình con

Cả hai hàm create_subprocess_exec()create_subprocess_shell() đều trả về các thể hiện của lớp Process. Process là một trình bao bọc cấp cao cho phép giao tiếp với các quy trình con và theo dõi quá trình hoàn thành của chúng.

class asyncio.subprocess.Process

Một đối tượng bao bọc các tiến trình hệ điều hành được tạo bởi các hàm create_subprocess_exec()create_subprocess_shell().

Lớp này được thiết kế để có API tương tự như lớp subprocess.Popen, nhưng có một số khác biệt đáng chú ý:

  • không giống như Popen, các phiên bản Process không có phương thức tương đương với phương thức poll();

  • phương thức communicate()wait() không có tham số timeout: sử dụng hàm wait_for();

  • phương thức Process.wait() không đồng bộ, trong khi phương thức subprocess.Popen.wait() được triển khai như một vòng lặp bận rộn chặn;

  • tham số universal_newlines không được hỗ trợ.

Lớp này là not thread safe.

Xem thêm phần Subprocess and Threads.

async wait()

Đợi quá trình con kết thúc.

Đặt và trả về thuộc tính returncode.

Ghi chú

Phương pháp này có thể bế tắc khi sử dụng stdout=PIPE hoặc stderr=PIPE và tiến trình con tạo ra nhiều đầu ra đến mức chặn việc chờ bộ đệm ống hệ điều hành chấp nhận nhiều dữ liệu hơn. Sử dụng phương pháp communicate() khi sử dụng ống để tránh tình trạng này.

async communicate(input=None)

Tương tác với quá trình:

  1. gửi dữ liệu tới stdin (nếu input không phải là None);

  2. đóng stdin;

  3. đọc dữ liệu từ stdoutstderr, cho đến khi đạt được EOF;

  4. chờ quá trình kết thúc.

Đối số input tùy chọn là dữ liệu (đối tượng bytes) sẽ được gửi đến tiến trình con.

Trả về một bộ (stdout_data, stderr_data).

Nếu ngoại lệ BrokenPipeError hoặc ConnectionResetError được nêu ra khi ghi input vào stdin thì ngoại lệ đó sẽ bị bỏ qua. Tình trạng này xảy ra khi quá trình thoát ra trước khi tất cả dữ liệu được ghi vào stdin.

Nếu muốn gửi dữ liệu đến tiến trình' stdin, thì tiến trình đó cần được tạo bằng stdin=PIPE. Tương tự, để nhận được bất kỳ thứ gì khác ngoài None trong bộ kết quả, quy trình phải được tạo với các đối số stdout=PIPE và/hoặc stderr=PIPE.

Lưu ý rằng dữ liệu đọc được lưu vào bộ đệm trong bộ nhớ, vì vậy không sử dụng phương pháp này nếu kích thước dữ liệu lớn hoặc không giới hạn.

Thay đổi trong phiên bản 3.12: stdin bị đóng khi input=None cũng vậy.

send_signal(signal)

Gửi tín hiệu signal đến tiến trình con.

Ghi chú

Trên Windows, SIGTERM là bí danh của terminate(). CTRL_C_EVENTCTRL_BREAK_EVENT có thể được gửi đến các tiến trình được bắt đầu bằng tham số creationflags bao gồm CREATE_NEW_PROCESS_GROUP.

terminate()

Dừng quá trình con.

Trên hệ thống POSIX, phương thức này gửi SIGTERM đến tiến trình con.

Trên Windows, hàm API Win32 TerminateProcess() được gọi để dừng tiến trình con.

kill()

Giết tiến trình con.

Trên hệ thống POSIX, phương thức này gửi SIGKILL đến tiến trình con.

Trên Windows phương pháp này là bí danh cho terminate().

stdin

Luồng đầu vào tiêu chuẩn (StreamWriter) hoặc None nếu quy trình được tạo bằng stdin=None.

stdout

Luồng đầu ra tiêu chuẩn (StreamReader) hoặc None nếu quy trình được tạo bằng stdout=None.

stderr

Luồng lỗi tiêu chuẩn (StreamReader) hoặc None nếu quy trình được tạo bằng stderr=None.

Cảnh báo

Sử dụng phương pháp communicate() thay vì process.stdin.write(), await process.stdout.read() hoặc await process.stderr.read(). Điều này tránh được tình trạng bế tắc do các luồng tạm dừng đọc hoặc ghi và chặn tiến trình con.

pid

Số nhận dạng quy trình (PID).

Lưu ý rằng đối với các quy trình được tạo bởi hàm create_subprocess_shell(), thuộc tính này là PID của shell được sinh ra.

returncode

Trả lại mã của quá trình khi nó thoát.

Giá trị None cho biết quá trình này chưa kết thúc.

Đối với các tiến trình được tạo bằng create_subprocess_exec(), giá trị âm -N cho biết rằng tiến trình con đã bị chấm dứt bởi tín hiệu N (chỉ POSIX).

Đối với các quy trình được tạo bằng create_subprocess_shell(), mã trả về phản ánh trạng thái thoát của chính shell (ví dụ: /bin/sh), có thể ánh xạ tín hiệu tới các mã như 128+N. Xem tài liệu về shell (ví dụ: Trạng thái thoát của hướng dẫn sử dụng Bash) để biết chi tiết.

Quy trình con và chủ đề

Theo mặc định, vòng lặp sự kiện asyncio tiêu chuẩn hỗ trợ chạy các quy trình con từ các luồng khác nhau.

Trên các quy trình con của Windows chỉ được cung cấp bởi ProactorEventLoop (mặc định), SelectorEventLoop không hỗ trợ quy trình con.

Lưu ý rằng việc triển khai vòng lặp sự kiện thay thế có thể có những hạn chế riêng; vui lòng tham khảo tài liệu của họ.

Ví dụ

Một ví dụ sử dụng lớp Process để điều khiển một quy trình con và lớp StreamReader để đọc từ đầu ra tiêu chuẩn của nó.

Quy trình con được tạo bởi hàm create_subprocess_exec()

nhập asyncio
hệ thống nhập khẩu

không đồng bộ get_date():
    code = 'nhập ngày giờ dưới dạng dt; print(dt.datetime.now())'

    # Create quy trình con; chuyển hướng đầu ra tiêu chuẩn
    # into một cái ống.
    proc = đang chờ asyncio.create_subprocess_exec(
        sys.executable, '-c', ,
        stdout=asyncio.subprocess.PIPE)

    # Read một dòng đầu ra.
    dữ liệu = đang chờ proc.stdout.readline()
    line = data.decode('ascii').rstrip()

    # Wait để thoát khỏi quy trình con.
    đang chờ proc.wait()
    dòng trở về

ngày = asyncio.run(get_date())
print(f"Ngày hiện tại: {date}")

Xem thêm same example được viết bằng API cấp thấp.