Межпроцессное взаимодействие - именованные каналы
Каналы предназначались для связи между связанными процессами. Можем ли мы использовать каналы для несвязанного взаимодействия процессов, скажем, мы хотим выполнять клиентскую программу с одного терминала и серверную программу с другого терминала? Ответ - нет. Тогда как мы можем добиться взаимодействия несвязанных процессов, простой ответ - именованные каналы. Несмотря на то, что это работает для связанных процессов, нет смысла использовать именованные каналы для связи связанных процессов.
Мы использовали одну трубу для односторонней связи и две трубы для двусторонней связи. Применяется ли то же условие для именованных каналов. Ответ - нет, мы можем использовать один именованный канал, который можно использовать для двусторонней связи (связь между сервером и клиентом, а также между клиентом и сервером одновременно), поскольку именованный канал поддерживает двунаправленную связь.
Другое название именованного канала - 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 предназначено для указания информации об устройстве, такой как старший и младший номера устройств.
Тип файла | Описание | Тип файла | Описание |
---|---|---|---|
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 или функции perror ().
#include <sys/types.h>
#include <sys/stat.h>
int mkfifo(const char *pathname, mode_t mode)
Эта библиотечная функция создает специальный файл FIFO, который используется для именованного канала. Аргументы этой функции - имя файла и режим. Имя файла может быть абсолютным или относительным. Если полный путь (или абсолютный путь) не указан, файл будет создан в текущей папке выполняющегося процесса. Информация о режиме файла описана в системном вызове mknod ().
Этот вызов вернет ноль в случае успеха и -1 в случае неудачи. Чтобы узнать причину сбоя, проверьте с помощью переменной errno или функции perror ().
Давайте рассмотрим программу запуска сервера на одном терминале и запуска клиента на другом терминале. Программа будет выполнять только одностороннюю связь. Клиент принимает ввод пользователя и отправляет сообщение на сервер, сервер печатает сообщение на выходе. Процесс продолжается до тех пор, пока пользователь не введет строку «конец».
Давайте разберемся в этом на примере -
Step 1 - Создайте два процесса, один из которых является фифосервером, а другой - фифоклиентом.
Step 2 - Серверный процесс выполняет следующее -
Создает именованный канал (используя системный вызов mknod ()) с именем «MYFIFO», если он не создан.
Открывает именованный канал только для чтения.
Здесь создан FIFO с разрешениями на чтение и запись для владельца. Прочтите для группы и никаких разрешений для других.
Бесконечно ждет сообщения от Клиента.
Если сообщение, полученное от клиента, не является «концом», печатает сообщение. Если сообщение «конец», закрывает FIFO и завершает процесс.
Step 3 - Клиентский процесс выполняет следующее -
Открывает именованный канал только для записи.
Принимает строку от пользователя.
Проверяет, вводит ли пользователь «конец» или иное, чем «конец». В любом случае он отправляет сообщение на сервер. Однако, если строка «конец», это закрывает FIFO, а также завершает процесс.
Повторяется бесконечно, пока пользователь не введет строку «конец».
Теперь давайте посмотрим на файл сервера 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 с разрешениями на чтение и запись для владельца. Прочтите для группы и никаких разрешений для других.
Бесконечно ждет сообщения от клиента.
Если сообщение, полученное от клиента, не является «концом», печатает сообщение и меняет строку. Перевернутая строка отправляется обратно клиенту. Если сообщение «конец», закрывает FIFO и завершает процесс.
Step 3 - Клиентский процесс выполняет следующее -
Открывает именованный канал для чтения и записи.
Принимает строку от пользователя.
Проверяет, вводит ли пользователь «конец» или иное, чем «конец». В любом случае он отправляет сообщение на сервер. Однако, если строка «конец», это закрывает FIFO, а также завершает процесс.
Если сообщение отправлено как «не конец», он ожидает сообщения (перевернутой строки) от клиента и печатает перевернутую строку.
Повторяется бесконечно, пока пользователь не введет строку «конец».
Теперь давайте посмотрим на пример кода сервера 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