Như Dashogun và Charlie Martin lưu ý, đây là một câu hỏi lớn. Một số phần của câu trả lời của họ là không chính xác, vì vậy tôi cũng sẽ trả lời.
Tôi quan tâm đến việc viết các mô-đun chương trình riêng biệt chạy dưới dạng các chuỗi độc lập mà tôi có thể nối với các đường ống.
Hãy cảnh giác với việc cố gắng sử dụng đường ống làm cơ chế giao tiếp giữa các luồng của một quy trình. Bởi vì bạn sẽ có cả đọc và viết kết thúc của đường ống mở trong một quá trình duy nhất, bạn sẽ không bao giờ nhận được chỉ báo EOF (không byte).
Nếu bạn thực sự đề cập đến các quy trình, thì đây là cơ sở của phương pháp Unix cổ điển để xây dựng các công cụ. Nhiều chương trình Unix chuẩn là các bộ lọc đọc từ đầu vào tiêu chuẩn, biến đổi nó bằng cách nào đó và ghi kết quả vào đầu ra tiêu chuẩn. Ví dụ: tr
, sort
, grep
và cat
là tất cả các bộ lọc, nhưng chỉ một vài bộ lọc. Đây là một mô hình tuyệt vời để làm theo khi dữ liệu bạn đang thao tác cho phép nó. Không phải tất cả các thao tác dữ liệu đều có lợi cho cách tiếp cận này, nhưng có rất nhiều thao tác.
Động lực là tôi có thể viết và kiểm tra từng mô-đun hoàn toàn độc lập, thậm chí có thể viết chúng bằng các ngôn ngữ khác nhau hoặc chạy các mô-đun khác nhau trên các máy khác nhau.
Điểm tốt. Lưu ý rằng không có cơ chế ống thực sự giữa các máy, mặc dù bạn có thể gần với các chương trình như rsh
hoặc (tốt hơn) ssh
. Tuy nhiên, trong nội bộ, các chương trình như vậy có thể đọc dữ liệu cục bộ từ các đường ống và gửi dữ liệu đó đến các máy từ xa, nhưng chúng giao tiếp giữa các máy trên các ổ cắm, không sử dụng đường ống.
Có nhiều khả năng ở đây. Tôi đã sử dụng đường ống trong một thời gian, nhưng tôi không quen với các sắc thái của hành vi của nó.
OK; đặt câu hỏi là một cách tốt để học. Thí nghiệm là một thử nghiệm khác, tất nhiên.
Có vẻ như đầu nhận sẽ chặn việc chờ đầu vào, điều mà tôi mong đợi, nhưng khối kết thúc gửi đôi khi có thể chờ ai đó đọc từ luồng không?
Có. Có giới hạn về kích thước của bộ đệm ống. Về mặt cổ điển, điều này khá nhỏ - 4096 hoặc 5120 là các giá trị chung. Bạn có thể thấy rằng Linux hiện đại sử dụng một giá trị lớn hơn. Bạn có thể sử dụng fpathconf()
và _PC_PIPE_BUF để tìm kích thước của bộ đệm ống. POSIX chỉ yêu cầu bộ đệm là 512 (nghĩa là, _POSIX_PIPE_BUF là 512).
Nếu tôi viết eOF cho luồng, tôi có thể tiếp tục viết cho luồng đó cho đến khi đóng không?
Về mặt kỹ thuật, không có cách nào để viết EOF vào luồng; bạn đóng bộ mô tả đường ống để chỉ EOF. Nếu bạn đang nghĩ đến control-D hoặc control-Z như một ký tự EOF, thì đó chỉ là các ký tự thông thường như các đường ống liên quan - chúng chỉ có hiệu ứng như EOF khi gõ vào một terminal đang chạy ở chế độ chuẩn , hoặc bình thường).
Có sự khác biệt nào về hành vi được đặt tên và đường ống chưa đặt tên?
Có, và không. Sự khác biệt lớn nhất là các đường ống không được đặt tên phải được thiết lập bởi một quá trình và chỉ có thể được sử dụng bởi quá trình đó và trẻ em chia sẻ quy trình đó như một tổ tiên chung. Ngược lại, các ống được đặt tên có thể được sử dụng bởi các quá trình không liên kết trước đó. Sự khác biệt lớn tiếp theo là hậu quả của việc đầu tiên; với một đường ống chưa đặt tên, bạn lấy lại hai bộ mô tả tập tin từ một hàm duy nhất (hệ thống) thành pipe()
, nhưng bạn mở một FIFO hoặc một đường ống có tên bằng cách sử dụng hàm open()
thông thường.(Một người nào đó phải tạo một FIFO với cuộc gọi mkfifo()
trước khi bạn có thể mở nó; các đường ống chưa đặt tên không cần bất kỳ thiết lập nào trước đó.) Tuy nhiên, một khi bạn có một bộ mô tả tập tin mở ra, có sự khác biệt rất nhỏ giữa một đường ống có tên và một đường ống chưa đặt tên .
Có vấn đề gì ở cuối ống tôi mở đầu tiên với các đường ống có tên không?
Không. Quy trình đầu tiên để mở FIFO sẽ (thường) chặn cho đến khi có quá trình với đầu kia mở. Nếu bạn mở nó để đọc và viết (theo cách thông thường nhưng có thể) thì bạn sẽ không bị chặn; nếu bạn sử dụng cờ O_NONBLOCK, bạn sẽ không bị chặn.
Hành vi của các đường ống có nhất quán giữa các hệ thống Linux khác nhau không?
Có. Tôi đã không nghe nói hoặc gặp bất kỳ vấn đề với đường ống trên bất kỳ hệ thống mà tôi đã sử dụng chúng.
Hành vi của các đường ống có phụ thuộc vào vỏ tôi đang sử dụng hoặc cách tôi định cấu hình không?
Không: ống và FIFO độc lập với vỏ mà bạn sử dụng.
Có bất kỳ câu hỏi nào khác mà tôi nên hỏi hoặc các vấn đề tôi cần biết nếu tôi muốn sử dụng ống theo cách này?
Chỉ cần nhớ rằng bạn phải đóng đầu đọc của một đường ống trong quá trình sẽ viết và phần cuối của đường ống trong quá trình sẽ đọc. Nếu bạn muốn giao tiếp hai chiều trên đường ống, hãy sử dụng hai ống riêng biệt. Nếu bạn tạo ra sự sắp xếp phức tạp của hệ thống ống nước, hãy cẩn thận về bế tắc - điều đó là có thể. Tuy nhiên, đường ống tuyến tính không bế tắc (mặc dù nếu quy trình đầu tiên không bao giờ đóng đầu ra của nó, các quy trình hạ nguồn có thể đợi vô thời hạn).
Tôi đã quan sát cả hai bên trên và trong nhận xét cho các câu trả lời khác rằng bộ đệm ống được giới hạn theo kiểu cổ điển với kích thước khá nhỏ. @Charlie Martin phản luận rằng một số phiên bản của Unix có bộ đệm ống động và chúng có thể khá lớn.
Tôi không chắc chắn bạn đang nghĩ gì. Tôi đã sử dụng các chương trình thử nghiệm mà sau trên Solaris, AIX, HP-UX, MacOS X, Linux và Cygwin/Windows XP (kết quả dưới đây):
#include <unistd.h>
#include <signal.h>
#include <stdio.h>
#include <fcntl.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
static const char *arg0;
static void err_syserr(char *str)
{
int errnum = errno;
fprintf(stderr, "%s: %s - (%d) %s\n", arg0, str, errnum, strerror(errnum));
exit(1);
}
int main(int argc, char **argv)
{
int pd[2];
pid_t kid;
size_t i = 0;
char buffer[2] = "a";
int flags;
arg0 = argv[0];
if (pipe(pd) != 0)
err_syserr("pipe() failed");
if ((kid = fork()) < 0)
err_syserr("fork() failed");
else if (kid == 0)
{
close(pd[1]);
pause();
}
/* else */
close(pd[0]);
if (fcntl(pd[1], F_GETFL, &flags) == -1)
err_syserr("fcntl(F_GETFL) failed");
flags |= O_NONBLOCK;
if (fcntl(pd[1], F_SETFL, &flags) == -1)
err_syserr("fcntl(F_SETFL) failed");
while (write(pd[1], buffer, sizeof(buffer)-1) == sizeof(buffer)-1)
{
putchar('.');
if (++i % 50 == 0)
printf("%u\n", (unsigned)i);
}
if (i % 50 != 0)
printf("%u\n", (unsigned)i);
kill(kid, SIGINT);
return 0;
}
Tôi tò mò muốn được có được kết quả thêm từ các nền tảng khác. Dưới đây là các kích thước tôi tìm thấy. Tất cả các kết quả đều lớn hơn tôi mong đợi, tôi phải thú nhận, nhưng Charlie và tôi có thể tranh luận về ý nghĩa của 'khá lớn' khi nói đến kích thước bộ đệm.
- 8196 - HP-UX 11,23 cho IA-64 (fcntl (F_SETFL) thất bại)
- 16384 - Solaris 10
- 16384 - hệ điều hành MacOS X 10.5 (O_NONBLOCK đã không làm việc, mặc dù fcntl (F_SETFL) đã không thất bại)
- 32768 - AIX 5.3
- 65536 - Cygwin/Windows XP (O_NONBLOCK đã không làm việc, mặc dù fcntl (F_SETFL) đã không thất bại)
- 65536 - SuSE Linux 10 (và CentOS) (fcntl (F_SETFL) thất bại)
Một điểm rõ ràng từ các thử nghiệm này là O_NONBLOCK hoạt động với các đường ống trên một số nền tảng chứ không phải trên các nền tảng khác.
Chương trình tạo đường ống và dĩa. Đứa trẻ đóng đầu ghi của đường ống, và sau đó đi ngủ cho đến khi nó nhận được tín hiệu - đó là những gì pause() làm. Sau đó, cha mẹ sẽ đóng đầu đọc của đường ống và đặt các cờ trên bộ mô tả ghi để nó không chặn trên một nỗ lực viết trên một đường ống đầy đủ. Sau đó nó lặp lại, viết một ký tự tại một thời điểm, và in một dấu chấm cho mỗi ký tự được viết, và đếm và dòng mới mỗi 50 ký tự. Khi nó phát hiện một vấn đề viết (bộ đệm đầy đủ, kể từ khi đứa trẻ không đọc một điều), nó dừng vòng lặp, viết số cuối cùng, và giết chết đứa trẻ.
Nhìn trộm nội dung của đường ống có chỉ số stat() không đáng tin cậy trên tất cả các nền tảng. –
Kết thúc bằng văn bản có thể chặn nếu đệm ống đầy - nó không phải là rất lớn. –
Ống chéo máy ... không tồn tại? Cách tiếp cận gần nhất có lẽ là một ổ cắm, nhưng điều đó không giống như một đường ống. –