การสื่อสารระหว่างกระบวนการ - ท่อที่มีชื่อ
ท่อมีไว้สำหรับการสื่อสารระหว่างกระบวนการที่เกี่ยวข้อง เราสามารถใช้ไพพ์สำหรับการสื่อสารในกระบวนการที่ไม่เกี่ยวข้องกันได้หรือไม่เช่นเราต้องการเรียกใช้โปรแกรมไคลเอนต์จากเทอร์มินัลหนึ่งและโปรแกรมเซิร์ฟเวอร์จากเทอร์มินัลอื่น คำตอบคือไม่แล้วเราจะบรรลุกระบวนการสื่อสารที่ไม่เกี่ยวข้องกันได้อย่างไรคำตอบง่ายๆก็คือท่อที่มีชื่อ แม้ว่าสิ่งนี้จะใช้ได้กับกระบวนการที่เกี่ยวข้อง แต่ก็ไม่มีความหมายในการใช้ไปป์ที่มีชื่อสำหรับการสื่อสารในกระบวนการที่เกี่ยวข้อง
เราใช้ท่อหนึ่งท่อสำหรับการสื่อสารทางเดียวและสองท่อสำหรับการสื่อสารแบบสองทิศทาง ใช้เงื่อนไขเดียวกันสำหรับท่อที่มีชื่อหรือไม่ คำตอบคือไม่เราสามารถใช้ไปป์ชื่อเดียวที่สามารถใช้สำหรับการสื่อสารสองทาง (การสื่อสารระหว่างเซิร์ฟเวอร์และไคลเอนต์รวมทั้งไคลเอนต์และเซิร์ฟเวอร์ในเวลาเดียวกัน) เนื่องจาก Named Pipe รองรับการสื่อสารแบบสองทิศทาง
อีกชื่อหนึ่งของท่อที่มีชื่อคือ FIFO (First-In-First-Out). ให้เราดูการเรียกระบบ (mknod ()) เพื่อสร้างไปป์ที่มีชื่อซึ่งเป็นไฟล์พิเศษชนิดหนึ่ง
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
int mknod(const char *pathname, mode_t mode, dev_t dev);
การเรียกระบบนี้จะสร้างไฟล์พิเศษหรือโหนดระบบไฟล์เช่นไฟล์ธรรมดาไฟล์อุปกรณ์หรือ FIFO อาร์กิวเมนต์สำหรับการเรียกระบบคือชื่อพา ธ โหมดและ dev ชื่อพา ธ พร้อมกับคุณสมบัติของโหมดและข้อมูลอุปกรณ์ ชื่อพา ธ เป็นแบบสัมพัทธ์หากไม่ได้ระบุไดเร็กทอรีจะถูกสร้างในไดเร็กทอรีปัจจุบัน โหมดที่ระบุคือโหมดของไฟล์ที่ระบุประเภทไฟล์เช่นชนิดของไฟล์และโหมดไฟล์ดังที่กล่าวไว้ในตารางต่อไปนี้ ฟิลด์ dev คือการระบุข้อมูลอุปกรณ์เช่นหมายเลขอุปกรณ์หลักและหมายเลขรอง
ประเภทไฟล์ | คำอธิบาย | ประเภทไฟล์ | คำอธิบาย |
---|---|---|---|
S_IFBLK | บล็อกพิเศษ | S_IFREG | ไฟล์ปกติ |
S_IFCHR | อักขระพิเศษ | S_IFDIR | ไดเรกทอรี |
S_IFIFO | FIFO พิเศษ | S_IFLNK | ลิงค์สัญลักษณ์ |
โหมดไฟล์ | คำอธิบาย | โหมดไฟล์ | คำอธิบาย |
---|---|---|---|
S_IRWXU | อ่านเขียนดำเนินการ / ค้นหาโดยเจ้าของ | S_IWGRP | เขียนอนุญาตกลุ่ม |
S_IRUSR | อ่านอนุญาตเจ้าของ | S_IXGRP | สิทธิ์ดำเนินการ / ค้นหากลุ่ม |
S_IWUSR | เขียนอนุญาตเจ้าของ | S_IRWXO | อ่านเขียนดำเนินการ / ค้นหาโดยผู้อื่น |
S_IXUSR | สิทธิ์ดำเนินการ / ค้นหาเจ้าของ | S_IROTH | อ่านอนุญาตอื่น ๆ |
S_IRWXG | อ่านเขียนดำเนินการ / ค้นหาตามกลุ่ม | S_IWOTH | เขียนอนุญาตอื่น ๆ |
S_IRGRP | อ่านอนุญาตกลุ่ม | S_IXOTH | สิทธิ์ดำเนินการ / ค้นหาอื่น ๆ |
โหมดไฟล์ยังสามารถแสดงในรูปแบบฐานแปดเช่น 0XYZ โดยที่ X แทนเจ้าของ Y หมายถึงกลุ่มและ Z แทนผู้อื่น ค่าของ X, Y หรือ Z สามารถอยู่ในช่วง 0 ถึง 7 ค่าสำหรับการอ่านเขียนและดำเนินการคือ 4, 2, 1 ตามลำดับ หากจำเป็นต้องใช้ร่วมกันระหว่างการอ่านเขียนและดำเนินการจากนั้นเพิ่มค่าตามนั้น
พูดว่าถ้าเราพูดถึง 0640 หมายความว่าอ่านและเขียน (4 + 2 = 6) สำหรับเจ้าของอ่าน (4) สำหรับกลุ่มและไม่มีสิทธิ์ (0) สำหรับผู้อื่น
การโทรนี้จะคืนค่าเป็นศูนย์เมื่อสำเร็จและ -1 ในกรณีที่ล้มเหลว หากต้องการทราบสาเหตุของความล้มเหลวให้ตรวจสอบด้วยฟังก์ชัน errno variable หรือ perror ()
#include <sys/types.h>
#include <sys/stat.h>
int mkfifo(const char *pathname, mode_t mode)
ฟังก์ชันไลบรารีนี้สร้างไฟล์พิเศษ FIFO ซึ่งใช้สำหรับไพพ์ที่มีชื่อ อาร์กิวเมนต์ของฟังก์ชันนี้คือชื่อไฟล์และโหมด ชื่อไฟล์อาจเป็นพา ธ สัมบูรณ์หรือพา ธ สัมพัทธ์ หากไม่ได้ระบุชื่อพา ธ แบบเต็ม (หรือพา ธ สัมบูรณ์) ไฟล์จะถูกสร้างขึ้นในโฟลเดอร์ปัจจุบันของกระบวนการดำเนินการ ข้อมูลโหมดไฟล์เป็นไปตามที่อธิบายไว้ในการเรียกระบบ mknod ()
การโทรนี้จะคืนค่าเป็นศูนย์เมื่อสำเร็จและ -1 ในกรณีที่ล้มเหลว หากต้องการทราบสาเหตุของความล้มเหลวให้ตรวจสอบด้วยฟังก์ชัน errno variable หรือ perror ()
ให้เราพิจารณาโปรแกรมสำหรับการเรียกใช้เซิร์ฟเวอร์บนเทอร์มินัลหนึ่งและรันไคลเอนต์บนเทอร์มินัลอื่น โปรแกรมจะทำการสื่อสารทางเดียวเท่านั้น ไคลเอนต์ยอมรับอินพุตของผู้ใช้และส่งข้อความไปยังเซิร์ฟเวอร์เซิร์ฟเวอร์จะพิมพ์ข้อความบนเอาต์พุต กระบวนการนี้จะดำเนินต่อไปจนกว่าผู้ใช้จะเข้าสู่สตริง“ end”
ให้เราเข้าใจสิ่งนี้ด้วยตัวอย่าง -
Step 1 - สร้างสองกระบวนการหนึ่งคือเซิร์ฟเวอร์ที่ห้าและอีกกระบวนการหนึ่งคือห้าเซลล์
Step 2 - กระบวนการเซิร์ฟเวอร์ดำเนินการดังต่อไปนี้ -
สร้างไปป์ที่มีชื่อ (โดยใช้การเรียกระบบ mknod ()) ด้วยชื่อ“ MYFIFO” หากไม่ได้สร้าง
เปิดไปป์ที่มีชื่อเพื่อวัตถุประสงค์ในการอ่านเท่านั้น
ที่นี่สร้าง FIFO พร้อมสิทธิ์ในการอ่านและเขียนสำหรับเจ้าของ อ่านสำหรับกลุ่มและไม่มีสิทธิ์สำหรับผู้อื่น
รอข้อความจากลูกค้าอย่างไม่มีที่สิ้นสุด
หากข้อความที่ได้รับจากไคลเอนต์ไม่ใช่“ end” ให้พิมพ์ข้อความนั้น หากข้อความเป็น "end" ให้ปิด fifo และสิ้นสุดกระบวนการ
Step 3 - กระบวนการลูกค้าดำเนินการดังต่อไปนี้ -
เปิดไปป์ที่มีชื่อเพื่อวัตถุประสงค์ในการเขียนเท่านั้น
ยอมรับสตริงจากผู้ใช้
ตรวจสอบว่าผู้ใช้เข้าสู่“ end” หรืออื่นที่ไม่ใช่“ end” ไม่ว่าจะด้วยวิธีใดระบบจะส่งข้อความไปยังเซิร์ฟเวอร์ อย่างไรก็ตามหากสตริงเป็น "end" สิ่งนี้จะปิด FIFO และสิ้นสุดกระบวนการด้วย
ทำซ้ำไปเรื่อย ๆ จนกว่าผู้ใช้จะเข้าสู่สตริง“ end”
ตอนนี้เรามาดูไฟล์เซิร์ฟเวอร์ FIFO
/* Filename: fifoserver.c */
#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#define FIFO_FILE "MYFIFO"
int main() {
int fd;
char readbuf[80];
char end[10];
int to_end;
int read_bytes;
/* Create the FIFO if it does not exist */
mknod(FIFO_FILE, S_IFIFO|0640, 0);
strcpy(end, "end");
while(1) {
fd = open(FIFO_FILE, O_RDONLY);
read_bytes = read(fd, readbuf, sizeof(readbuf));
readbuf[read_bytes] = '\0';
printf("Received string: \"%s\" and length is %d\n", readbuf, (int)strlen(readbuf));
to_end = strcmp(readbuf, end);
if (to_end == 0) {
close(fd);
break;
}
}
return 0;
}
ขั้นตอนการรวบรวมและดำเนินการ
Received string: "this is string 1" and length is 16
Received string: "fifo test" and length is 9
Received string: "fifo client and server" and length is 22
Received string: "end" and length is 3
ตอนนี้เรามาดูโค้ดตัวอย่างไคลเอ็นต์ FIFO
/* Filename: fifoclient.c */
#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#define FIFO_FILE "MYFIFO"
int main() {
int fd;
int end_process;
int stringlen;
char readbuf[80];
char end_str[5];
printf("FIFO_CLIENT: Send messages, infinitely, to end enter \"end\"\n");
fd = open(FIFO_FILE, O_CREAT|O_WRONLY);
strcpy(end_str, "end");
while (1) {
printf("Enter string: ");
fgets(readbuf, sizeof(readbuf), stdin);
stringlen = strlen(readbuf);
readbuf[stringlen - 1] = '\0';
end_process = strcmp(readbuf, end_str);
//printf("end_process is %d\n", end_process);
if (end_process != 0) {
write(fd, readbuf, strlen(readbuf));
printf("Sent string: \"%s\" and string length is %d\n", readbuf, (int)strlen(readbuf));
} else {
write(fd, readbuf, strlen(readbuf));
printf("Sent string: \"%s\" and string length is %d\n", readbuf, (int)strlen(readbuf));
close(fd);
break;
}
}
return 0;
}
ลองดูที่ผลลัพธ์ที่มาถึง
ขั้นตอนการรวบรวมและดำเนินการ
FIFO_CLIENT: Send messages, infinitely, to end enter "end"
Enter string: this is string 1
Sent string: "this is string 1" and string length is 16
Enter string: fifo test
Sent string: "fifo test" and string length is 9
Enter string: fifo client and server
Sent string: "fifo client and server" and string length is 22
Enter string: end
Sent string: "end" and string length is 3
การสื่อสารสองทางโดยใช้ท่อที่มีชื่อ
การสื่อสารระหว่างท่อหมายถึงทิศทางเดียว ท่อถูก จำกัด การสื่อสารทางเดียวโดยทั่วไปและต้องการอย่างน้อยสองท่อสำหรับการสื่อสารสองทาง ท่อมีไว้สำหรับกระบวนการที่เกี่ยวข้องกันเท่านั้น ไม่สามารถใช้ไพพ์สำหรับการสื่อสารในกระบวนการที่ไม่เกี่ยวข้องกันได้เช่นหากเราต้องการดำเนินการกระบวนการหนึ่งจากเทอร์มินัลหนึ่งและอีกกระบวนการหนึ่งจากเทอร์มินัลอื่นจะเป็นไปไม่ได้กับไปป์ เรามีวิธีง่ายๆในการสื่อสารระหว่างสองกระบวนการพูดกระบวนการที่ไม่เกี่ยวข้องกันอย่างง่ายๆหรือไม่? คำตอบคือใช่ ท่อที่มีชื่อมีไว้สำหรับการสื่อสารระหว่างกระบวนการที่ไม่เกี่ยวข้องกันตั้งแต่สองกระบวนการขึ้นไปและยังสามารถสื่อสารแบบสองทิศทางได้อีกด้วย
แล้วเราได้เห็นการสื่อสารแบบทิศทางเดียวระหว่างไปป์ที่มีชื่อนั่นคือข้อความจากไคลเอนต์ไปยังเซิร์ฟเวอร์ ตอนนี้ให้เราดูที่การสื่อสารแบบสองทิศทางคือไคลเอนต์ส่งข้อความไปยังเซิร์ฟเวอร์และเซิร์ฟเวอร์ที่รับข้อความและส่งข้อความกลับไปยังไคลเอนต์โดยใช้ไปป์ชื่อเดียวกัน
ต่อไปนี้เป็นตัวอย่าง -
Step 1 - สร้างสองกระบวนการหนึ่งคือ fifoserver_twoway และอีกกระบวนการหนึ่งคือ fifoclient_twoway
Step 2 - กระบวนการเซิร์ฟเวอร์ดำเนินการดังต่อไปนี้ -
สร้างไปป์ที่มีชื่อ (โดยใช้ฟังก์ชันไลบรารี mkfifo ()) ด้วยชื่อ“ fifo_twoway” ในไดเร็กทอรี / tmp หากไม่ได้สร้าง
เปิดไปป์ที่มีชื่อเพื่อวัตถุประสงค์ในการอ่านและเขียน
ที่นี่สร้าง FIFO พร้อมสิทธิ์ในการอ่านและเขียนสำหรับเจ้าของ อ่านสำหรับกลุ่มและไม่มีสิทธิ์สำหรับผู้อื่น
รอข้อความจากลูกค้าอย่างไม่มีที่สิ้นสุด
หากข้อความที่ได้รับจากไคลเอนต์ไม่ใช่“ end” ให้พิมพ์ข้อความและย้อนกลับสตริง สตริงที่ย้อนกลับจะถูกส่งกลับไปยังไคลเอนต์ หากข้อความเป็น "end" ให้ปิด fifo และสิ้นสุดกระบวนการ
Step 3 - กระบวนการลูกค้าดำเนินการดังต่อไปนี้ -
เปิดไปป์ที่มีชื่อเพื่อวัตถุประสงค์ในการอ่านและเขียน
ยอมรับสตริงจากผู้ใช้
ตรวจสอบว่าผู้ใช้เข้าสู่“ end” หรืออื่นที่ไม่ใช่“ end” ไม่ว่าจะด้วยวิธีใดระบบจะส่งข้อความไปยังเซิร์ฟเวอร์ อย่างไรก็ตามหากสตริงเป็น "end" สิ่งนี้จะปิด FIFO และสิ้นสุดกระบวนการด้วย
หากข้อความถูกส่งโดยไม่ใช่“ end” ข้อความนั้นจะรอข้อความ (สตริงที่ย้อนกลับ) จากไคลเอนต์และพิมพ์สตริงที่ย้อนกลับ
ทำซ้ำเรื่อย ๆ จนกว่าผู้ใช้จะเข้าสู่สตริง“ end”
ตอนนี้เรามาดูโค้ดตัวอย่างเซิร์ฟเวอร์ FIFO
/* Filename: fifoserver_twoway.c */
#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#define FIFO_FILE "/tmp/fifo_twoway"
void reverse_string(char *);
int main() {
int fd;
char readbuf[80];
char end[10];
int to_end;
int read_bytes;
/* Create the FIFO if it does not exist */
mkfifo(FIFO_FILE, S_IFIFO|0640);
strcpy(end, "end");
fd = open(FIFO_FILE, O_RDWR);
while(1) {
read_bytes = read(fd, readbuf, sizeof(readbuf));
readbuf[read_bytes] = '\0';
printf("FIFOSERVER: Received string: \"%s\" and length is %d\n", readbuf, (int)strlen(readbuf));
to_end = strcmp(readbuf, end);
if (to_end == 0) {
close(fd);
break;
}
reverse_string(readbuf);
printf("FIFOSERVER: Sending Reversed String: \"%s\" and length is %d\n", readbuf, (int) strlen(readbuf));
write(fd, readbuf, strlen(readbuf));
/*
sleep - This is to make sure other process reads this, otherwise this
process would retrieve the message
*/
sleep(2);
}
return 0;
}
void reverse_string(char *str) {
int last, limit, first;
char temp;
last = strlen(str) - 1;
limit = last/2;
first = 0;
while (first < last) {
temp = str[first];
str[first] = str[last];
str[last] = temp;
first++;
last--;
}
return;
}
ขั้นตอนการรวบรวมและดำเนินการ
FIFOSERVER: Received string: "LINUX IPCs" and length is 10
FIFOSERVER: Sending Reversed String: "sCPI XUNIL" and length is 10
FIFOSERVER: Received string: "Inter Process Communication" and length is 27
FIFOSERVER: Sending Reversed String: "noitacinummoC ssecorP retnI" and length is 27
FIFOSERVER: Received string: "end" and length is 3
ตอนนี้เรามาดูโค้ดตัวอย่างไคลเอ็นต์ FIFO
/* Filename: fifoclient_twoway.c */
#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#define FIFO_FILE "/tmp/fifo_twoway"
int main() {
int fd;
int end_process;
int stringlen;
int read_bytes;
char readbuf[80];
char end_str[5];
printf("FIFO_CLIENT: Send messages, infinitely, to end enter \"end\"\n");
fd = open(FIFO_FILE, O_CREAT|O_RDWR);
strcpy(end_str, "end");
while (1) {
printf("Enter string: ");
fgets(readbuf, sizeof(readbuf), stdin);
stringlen = strlen(readbuf);
readbuf[stringlen - 1] = '\0';
end_process = strcmp(readbuf, end_str);
//printf("end_process is %d\n", end_process);
if (end_process != 0) {
write(fd, readbuf, strlen(readbuf));
printf("FIFOCLIENT: Sent string: \"%s\" and string length is %d\n", readbuf, (int)strlen(readbuf));
read_bytes = read(fd, readbuf, sizeof(readbuf));
readbuf[read_bytes] = '\0';
printf("FIFOCLIENT: Received string: \"%s\" and length is %d\n", readbuf, (int)strlen(readbuf));
} else {
write(fd, readbuf, strlen(readbuf));
printf("FIFOCLIENT: Sent string: \"%s\" and string length is %d\n", readbuf, (int)strlen(readbuf));
close(fd);
break;
}
}
return 0;
}
ขั้นตอนการรวบรวมและดำเนินการ
FIFO_CLIENT: Send messages, infinitely, to end enter "end"
Enter string: LINUX IPCs
FIFOCLIENT: Sent string: "LINUX IPCs" and string length is 10
FIFOCLIENT: Received string: "sCPI XUNIL" and length is 10
Enter string: Inter Process Communication
FIFOCLIENT: Sent string: "Inter Process Communication" and string length is 27
FIFOCLIENT: Received string: "noitacinummoC ssecorP retnI" and length is 27
Enter string: end
FIFOCLIENT: Sent string: "end" and string length is 3