Làm thế nào các đường ống làm việc trong Linux


25

Tôi đã đọc về cách các đường ống được triển khai trong nhân Linux và muốn xác thực sự hiểu biết của tôi. Nếu tôi không chính xác, câu trả lời với lời giải thích chính xác sẽ được chọn.

  • Linux có một VFS gọi là pipefs được gắn trong kernel (không phải trong không gian người dùng)
  • Pipefs có một siêu khối duy nhất và được gắn tại gốc của nó ( pipe:), cùng với/
  • Pipefs không thể được xem trực tiếp không giống như hầu hết các hệ thống tập tin
  • Mục để pipefs là thông qua các pipe(2)syscall
  • Tòa nhà pipe(2)được sử dụng bởi các trình bao cho đường ống với |toán tử (hoặc thủ công từ bất kỳ quy trình nào khác) tạo ra một tệp mới trong các ống dẫn hoạt động khá giống với một tệp bình thường
  • Tệp ở phía bên trái của toán tử đường ống đã được stdoutchuyển hướng đến tệp tạm thời được tạo trong pipefs
  • Tệp ở phía bên phải của toán tử đường ống stdinđược đặt thành tệp trên đường ống
  • Pipefs được lưu trữ trong bộ nhớ và thông qua một số phép thuật hạt nhân, không nên phân trang

Là giải thích về cách ống (ví dụ ls -la | less) hoạt động khá chính xác?

Một điều tôi không hiểu là làm thế nào một cái gì đó như bash sẽ thiết lập một quy trình ' stdinhoặc stdoutcho bộ mô tả tệp được trả về pipe(2). Tôi chưa thể tìm thấy bất cứ điều gì về điều đó.


Lưu ý rằng bạn đang nói về hai lớp khác nhau đáng kể có cùng tên. Cuộc pipe()gọi kernel cùng với máy móc hỗ trợ nó ( pipefs, v.v.) ở mức thấp hơn nhiều so với |toán tử được cung cấp trong trình bao của bạn. Cái sau thường được thực hiện bằng cái trước, nhưng nó không phải như vậy.
Greg Hewgill

Vâng, tôi đặc biệt đề cập đến các hoạt động cấp thấp hơn, với giả định rằng |nhà điều hành chỉ gọi pipe(2)như là một quá trình như bash.
Brandon Wamboldt

Câu trả lời:


19

Phân tích của bạn cho đến nay nói chung là chính xác. Cách một shell có thể đặt stdin của một quá trình thành một bộ mô tả đường ống có thể là (mã giả):

pipe(p) // create a new pipe with two handles p[0] and p[1]
fork() // spawn a child process
    close(p[0]) // close the write end of the pipe in the child
    dup2(p[1], 0) // duplicate the pipe descriptor on top of fd 0 (stdin)
    close(p[1]) // close the other pipe descriptor
    exec() // run a new process with the new descriptors in place

Cảm ơn! Chỉ tò mò tại sao dup2cuộc gọi là cần thiết, và bạn không thể chỉ định trực tiếp mô tả đường ống cho stdin?
Brandon Wamboldt

3
Người gọi không được chọn giá trị số của bộ mô tả tệp khi nó được tạo pipe(). Cuộc dup2()gọi cho phép người gọi sao chép bộ mô tả tệp vào một giá trị số cụ thể (cần thiết vì 0, 1, 2 là stdin, stdout, stderr). Đó là hạt nhân tương đương với "gán trực tiếp cho stdin". Lưu ý rằng biến toàn cục của thư viện thời gian chạy C stdinlà a FILE *, không liên quan đến kernel (mặc dù nó được khởi tạo để được kết nối với mô tả 0).
Greg Hewgill

Câu trả lời chính xác! Tôi có một chút mất mát trong các chi tiết. Chỉ tự hỏi tại sao bạn đóng (p [1]) trước khi chạy exec ()? Khi dup2 trở lại, p [1] sẽ trỏ đến fd 0? Sau đó đóng (p [1]) để đóng bộ mô tả tệp 0. Sau đó, làm thế nào chúng ta có thể đọc từ stdin của tiến trình con?
dùng1559897

@ user1559897: Cuộc dup2gọi không thay đổi p[1]. Thay vào đó, nó làm cho hai tay cầm p[1]0trỏ đến cùng một đối tượng kernel (đường ống). Vì tiến trình con không cần hai tay cầm stdin (và sẽ không biết tay cầm được đánh số nào p[1]là dù sao), p[1]đã bị đóng trước đó exec.
Greg Hewgill

@GregHewgill Gotchu. Cám ơn!
dùng1559897
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.