Truyền thông giữa các quy trình - Đường ống
Đường ống là một phương tiện thông tin liên lạc giữa hai hoặc nhiều quá trình liên quan hoặc liên quan đến nhau. Nó có thể là trong một quy trình hoặc là giao tiếp giữa quy trình con và quy trình cha. Giao tiếp cũng có thể ở nhiều cấp độ chẳng hạn như giao tiếp giữa cha mẹ, con cái và cháu ngoại, v.v. Giao tiếp đạt được bằng một quá trình viết vào ống và đọc khác từ ống. Để thực hiện lệnh gọi hệ thống ống, hãy tạo hai tệp, một tệp để ghi vào tệp và tệp khác để đọc từ tệp.
Cơ chế đường ống có thể được xem với một kịch bản thời gian thực như đổ đầy nước vào đường ống vào một thùng chứa nào đó, ví dụ như một cái xô và ai đó lấy nó ra, chẳng hạn như bằng một cái cốc. Quá trình làm đầy không gì khác ngoài việc ghi vào đường ống và quá trình đọc không gì khác ngoài việc lấy ra từ đường ống. Điều này ngụ ý rằng một đầu ra (nước) được đầu vào cho đầu kia (xô).
#include<unistd.h>
int pipe(int pipedes[2]);
Lệnh gọi hệ thống này sẽ tạo ra một đường ống cho giao tiếp một chiều, tức là nó tạo ra hai bộ mô tả, một bộ đầu tiên được kết nối để đọc từ đường ống và một bộ còn lại được kết nối để ghi vào đường ống.
Ống mô tả [0] dùng để đọc và ống dẫn [1] dùng để viết. Bất cứ thứ gì được viết vào đường ống [1] đều có thể được đọc từ đường ống [0].
Lệnh gọi này sẽ trả về 0 khi thành công và -1 trong trường hợp thất bại. Để biết nguyên nhân của lỗi, hãy kiểm tra với biến errno hoặc hàm perror ().
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int open(const char *pathname, int flags);
int open(const char *pathname, int flags, mode_t mode);
Mặc dù các thao tác cơ bản đối với tệp là đọc và ghi, điều cần thiết là phải mở tệp trước khi thực hiện các thao tác và đóng tệp sau khi hoàn thành các thao tác cần thiết. Thông thường, theo mặc định, 3 bộ mô tả được mở cho mọi quy trình, được sử dụng cho đầu vào (đầu vào tiêu chuẩn - stdin), đầu ra (đầu ra tiêu chuẩn - stdout) và lỗi (lỗi tiêu chuẩn - stderr) có các bộ mô tả tệp 0, 1 và 2 tương ứng.
Lệnh gọi hệ thống này sẽ trả về một bộ mô tả tệp được sử dụng cho các thao tác đọc / ghi / tìm kiếm tệp khác (lseek). Thông thường bộ mô tả tệp bắt đầu từ 3 và tăng lên một con số khi số tệp được mở.
Các đối số được truyền cho lệnh gọi hệ thống mở là tên đường dẫn (đường dẫn tương đối hoặc tuyệt đối), cờ đề cập đến mục đích của việc mở tệp (giả sử, mở để đọc, O_RDONLY, để ghi, O_WRONLY, để đọc và ghi, O_RDWR, để nối vào tệp hiện có O_APPEND, để tạo tệp, nếu không tồn tại với O_CREAT, v.v.) và chế độ bắt buộc cung cấp quyền đọc / ghi / thực thi cho người dùng hoặc chủ sở hữu / nhóm / người khác. Chế độ có thể được đề cập với các biểu tượng.
Đọc - 4, Viết - 2 và Thực hiện - 1.
Ví dụ: Giá trị bát phân (bắt đầu bằng 0), 0764 ngụ ý chủ sở hữu có quyền đọc, ghi và thực thi, nhóm có quyền đọc và ghi, người khác có quyền đọc. Điều này cũng có thể được biểu diễn dưới dạng S_IRWXU | S_IRGRP | S_IWGRP | S_IROTH, ngụ ý hoặc hoạt động của 0700 | 0040 | 0020 | 0004 → 0764.
Lệnh gọi hệ thống này, khi thành công, trả về id bộ mô tả tệp mới và -1 trong trường hợp có lỗi. Nguyên nhân của lỗi có thể được xác định bằng biến errno hoặc hàm perror ().
#include<unistd.h>
int close(int fd)
Lệnh gọi hệ thống ở trên đóng bộ mô tả tệp đã mở. Điều này có nghĩa là tệp không còn được sử dụng và các tài nguyên liên quan có thể được sử dụng lại bởi bất kỳ quá trình nào khác. Lệnh gọi hệ thống này trả về 0 khi thành công và -1 trong trường hợp lỗi. Nguyên nhân của lỗi có thể được xác định bằng biến errno hoặc hàm perror ().
#include<unistd.h>
ssize_t read(int fd, void *buf, size_t count)
Lệnh gọi hệ thống ở trên là để đọc từ tệp được chỉ định với các đối số của bộ mô tả tệp fd, bộ đệm thích hợp với bộ nhớ được cấp phát (tĩnh hoặc động) và kích thước của bộ đệm.
Id bộ mô tả tệp là để xác định tệp tương ứng, được trả về sau khi gọi lệnh gọi hệ thống open () hoặc pipe (). Tệp cần được mở trước khi đọc từ tệp. Nó tự động mở trong trường hợp thực hiện lệnh gọi hệ thống pipe ().
Lệnh gọi này sẽ trả về số byte đã đọc (hoặc số không trong trường hợp gặp phần cuối của tệp) khi thành công và -1 trong trường hợp không thành công. Số byte trả về có thể nhỏ hơn số byte được yêu cầu, đề phòng trường hợp không có dữ liệu hoặc tệp bị đóng. Số lỗi thích hợp được đặt trong trường hợp không thành công.
Để biết nguyên nhân của lỗi, hãy kiểm tra với biến errno hoặc hàm perror ().
#include<unistd.h>
ssize_t write(int fd, void *buf, size_t count)
Lệnh gọi hệ thống ở trên là để ghi vào tệp được chỉ định với các đối số của bộ mô tả tệp fd, một bộ đệm thích hợp với bộ nhớ được cấp phát (tĩnh hoặc động) và kích thước của bộ đệm.
Id bộ mô tả tệp là để xác định tệp tương ứng, được trả về sau khi gọi lệnh gọi hệ thống open () hoặc pipe ().
Tệp cần được mở trước khi ghi vào tệp. Nó tự động mở trong trường hợp thực hiện lệnh gọi hệ thống pipe ().
Lệnh gọi này sẽ trả về số byte được ghi (hoặc số không trong trường hợp không có gì được viết) khi thành công và -1 trong trường hợp thất bại. Số lỗi thích hợp được đặt trong trường hợp không thành công.
Để biết nguyên nhân của lỗi, hãy kiểm tra với biến errno hoặc hàm perror ().
Chương trình mẫu
Sau đây là một số chương trình ví dụ.
Example program 1 - Chương trình viết và đọc hai tin nhắn bằng cách sử dụng pipe.
Thuật toán
Step 1 - Tạo đường ống.
Step 2 - Gửi tin nhắn đến đường ống.
Step 3 - Lấy thông điệp từ đường ống và ghi nó vào đầu ra tiêu chuẩn.
Step 4 - Gửi tin nhắn khác đến đường ống.
Step 5 - Lấy thông điệp từ đường ống và ghi nó vào đầu ra tiêu chuẩn.
Note - Việc lấy lại tin nhắn cũng có thể được thực hiện sau khi gửi tất cả tin nhắn.
Source Code: simplepipe.c
#include<stdio.h>
#include<unistd.h>
int main() {
int pipefds[2];
int returnstatus;
char writemessages[2][20]={"Hi", "Hello"};
char readmessage[20];
returnstatus = pipe(pipefds);
if (returnstatus == -1) {
printf("Unable to create pipe\n");
return 1;
}
printf("Writing to pipe - Message 1 is %s\n", writemessages[0]);
write(pipefds[1], writemessages[0], sizeof(writemessages[0]));
read(pipefds[0], readmessage, sizeof(readmessage));
printf("Reading from pipe – Message 1 is %s\n", readmessage);
printf("Writing to pipe - Message 2 is %s\n", writemessages[0]);
write(pipefds[1], writemessages[1], sizeof(writemessages[0]));
read(pipefds[0], readmessage, sizeof(readmessage));
printf("Reading from pipe – Message 2 is %s\n", readmessage);
return 0;
}
Note- Tốt nhất, tình trạng trả lại cần được kiểm tra cho mọi cuộc gọi hệ thống. Để đơn giản hóa quy trình, việc kiểm tra không được thực hiện cho tất cả các cuộc gọi.
Các bước thực hiện
Tổng hợp
gcc -o simplepipe simplepipe.c
Thực thi / Đầu ra
Writing to pipe - Message 1 is Hi
Reading from pipe – Message 1 is Hi
Writing to pipe - Message 2 is Hi
Reading from pipe – Message 2 is Hell
Example program 2 - Chương trình viết và đọc hai tin nhắn thông qua đường ống sử dụng các tiến trình cha và con.
Thuật toán
Step 1 - Tạo đường ống.
Step 2 - Tạo quy trình con.
Step 3 - Quy trình mẹ ghi vào đường ống.
Step 4 - Tiến trình con lấy thông điệp từ đường ống và ghi nó vào đầu ra chuẩn.
Step 5 - Lặp lại bước 3 và bước 4 một lần nữa.
Source Code: pipewithprocesses.c
#include<stdio.h>
#include<unistd.h>
int main() {
int pipefds[2];
int returnstatus;
int pid;
char writemessages[2][20]={"Hi", "Hello"};
char readmessage[20];
returnstatus = pipe(pipefds);
if (returnstatus == -1) {
printf("Unable to create pipe\n");
return 1;
}
pid = fork();
// Child process
if (pid == 0) {
read(pipefds[0], readmessage, sizeof(readmessage));
printf("Child Process - Reading from pipe – Message 1 is %s\n", readmessage);
read(pipefds[0], readmessage, sizeof(readmessage));
printf("Child Process - Reading from pipe – Message 2 is %s\n", readmessage);
} else { //Parent process
printf("Parent Process - Writing to pipe - Message 1 is %s\n", writemessages[0]);
write(pipefds[1], writemessages[0], sizeof(writemessages[0]));
printf("Parent Process - Writing to pipe - Message 2 is %s\n", writemessages[1]);
write(pipefds[1], writemessages[1], sizeof(writemessages[1]));
}
return 0;
}
Các bước thực hiện
Compilation
gcc pipewithprocesses.c –o pipewithprocesses
Execution
Parent Process - Writing to pipe - Message 1 is Hi
Parent Process - Writing to pipe - Message 2 is Hello
Child Process - Reading from pipe – Message 1 is Hi
Child Process - Reading from pipe – Message 2 is Hello
Giao tiếp hai chiều sử dụng đường ống
Giao tiếp đường ống chỉ được xem là giao tiếp một chiều, tức là quá trình mẹ ghi và quá trình con đọc hoặc ngược lại nhưng không phải cả hai. Tuy nhiên, điều gì sẽ xảy ra nếu cả cha mẹ và con cái cần viết và đọc đồng thời từ các đường ống, giải pháp là giao tiếp hai chiều bằng cách sử dụng các đường ống. Cần có hai đường ống để thiết lập giao tiếp hai chiều.
Sau đây là các bước để đạt được giao tiếp hai chiều -
Step 1- Tạo hai đường ống. Đầu tiên là để cha mẹ viết và con đọc, ví dụ như pipe1. Thứ hai là để đứa trẻ viết và cha mẹ đọc, nói như pipe2.
Step 2 - Tạo quy trình con.
Step 3 - Đóng các đầu cuối không mong muốn vì chỉ cần một đầu cho mỗi giao tiếp.
Step 4 - Đóng các đầu cuối không mong muốn trong tiến trình mẹ, đọc phần cuối của pipe1 và ghi phần cuối của pipe2.
Step 5 - Đóng các kết thúc không mong muốn trong tiến trình con, ghi cuối pipe1 và đọc kết thúc pipe2.
Step 6 - Thực hiện các giao tiếp theo yêu cầu.
Chương trình mẫu
Sample program 1 - Đạt được thông tin liên lạc hai chiều bằng cách sử dụng đường ống.
Thuật toán
Step 1 - Tạo pipe1 để tiến trình mẹ viết và tiến trình con đọc.
Step 2 - Tạo pipe2 để tiến trình con viết và tiến trình mẹ đọc.
Step 3 - Đóng các đầu không mong muốn của đường ống từ phía cha và con.
Step 4 - Quy trình cha viết tin nhắn và quy trình con đọc và hiển thị trên màn hình.
Step 5 - Quy trình con viết tin nhắn và quy trình mẹ đọc và hiển thị trên màn hình.
Source Code: twowayspipe.c
#include<stdio.h>
#include<unistd.h>
int main() {
int pipefds1[2], pipefds2[2];
int returnstatus1, returnstatus2;
int pid;
char pipe1writemessage[20] = "Hi";
char pipe2writemessage[20] = "Hello";
char readmessage[20];
returnstatus1 = pipe(pipefds1);
if (returnstatus1 == -1) {
printf("Unable to create pipe 1 \n");
return 1;
}
returnstatus2 = pipe(pipefds2);
if (returnstatus2 == -1) {
printf("Unable to create pipe 2 \n");
return 1;
}
pid = fork();
if (pid != 0) // Parent process {
close(pipefds1[0]); // Close the unwanted pipe1 read side
close(pipefds2[1]); // Close the unwanted pipe2 write side
printf("In Parent: Writing to pipe 1 – Message is %s\n", pipe1writemessage);
write(pipefds1[1], pipe1writemessage, sizeof(pipe1writemessage));
read(pipefds2[0], readmessage, sizeof(readmessage));
printf("In Parent: Reading from pipe 2 – Message is %s\n", readmessage);
} else { //child process
close(pipefds1[1]); // Close the unwanted pipe1 write side
close(pipefds2[0]); // Close the unwanted pipe2 read side
read(pipefds1[0], readmessage, sizeof(readmessage));
printf("In Child: Reading from pipe 1 – Message is %s\n", readmessage);
printf("In Child: Writing to pipe 2 – Message is %s\n", pipe2writemessage);
write(pipefds2[1], pipe2writemessage, sizeof(pipe2writemessage));
}
return 0;
}
Các bước thực hiện
Tổng hợp
gcc twowayspipe.c –o twowayspipe
Chấp hành
In Parent: Writing to pipe 1 – Message is Hi
In Child: Reading from pipe 1 – Message is Hi
In Child: Writing to pipe 2 – Message is Hello
In Parent: Reading from pipe 2 – Message is Hello