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