181 lines
4.3 KiB
C
181 lines
4.3 KiB
C
|
/* pipe-example-1.c */
|
|||
|
|
|||
|
#include <stdint.h>
|
|||
|
#include <stdio.h>
|
|||
|
#include <string.h>
|
|||
|
#include <unistd.h>
|
|||
|
#include <sys/wait.h>
|
|||
|
|
|||
|
enum
|
|||
|
{
|
|||
|
MSG_STRING = 1,
|
|||
|
MSG_END
|
|||
|
};
|
|||
|
|
|||
|
enum
|
|||
|
{
|
|||
|
MESSAGE_MAGIC = 0xAFAF, // magic signature value
|
|||
|
MAX_MESSAGE_LEN = 4096 // maximum message length
|
|||
|
};
|
|||
|
|
|||
|
struct __attribute__((packed)) message_header
|
|||
|
{
|
|||
|
uint16_t magic; // magic signature
|
|||
|
uint16_t type; // type of the message
|
|||
|
uint16_t data_len; // length of data
|
|||
|
};
|
|||
|
|
|||
|
enum
|
|||
|
{
|
|||
|
// maximum data length
|
|||
|
MAX_MESSAGE_DATA_LEN = MAX_MESSAGE_LEN - sizeof(struct message_header)
|
|||
|
};
|
|||
|
|
|||
|
struct __attribute__((packed)) message
|
|||
|
{
|
|||
|
struct message_header header;
|
|||
|
// payload
|
|||
|
uint8_t data[MAX_MESSAGE_DATA_LEN];
|
|||
|
};
|
|||
|
|
|||
|
struct message simple_message(uint16_t type)
|
|||
|
{
|
|||
|
return (struct message) {(struct message_header) {MESSAGE_MAGIC, type, 0}, 0};
|
|||
|
}
|
|||
|
|
|||
|
struct message string_message(const char *str)
|
|||
|
{
|
|||
|
struct message msg = (struct message) {(struct message_header) {MESSAGE_MAGIC, MSG_STRING, strlen(str + 1)}, 0};
|
|||
|
strcpy(msg.data, str);
|
|||
|
return msg;
|
|||
|
}
|
|||
|
|
|||
|
int send(int fd, const struct message *msg)
|
|||
|
{
|
|||
|
/* Check if the input data is not empty */
|
|||
|
if (fd < 0 || msg == NULL)
|
|||
|
return -1;
|
|||
|
|
|||
|
/* Calculate the message size to send */
|
|||
|
int msg_size = sizeof(struct message_header) + msg->header.data_len;
|
|||
|
|
|||
|
/* Check if message payload size is valid */
|
|||
|
if (msg->header.data_len > MAX_MESSAGE_DATA_LEN)
|
|||
|
return -1;
|
|||
|
|
|||
|
/* Write data to the output pipe (we assume it is ready) */
|
|||
|
if (write(fd, msg, msg_size) != msg_size)
|
|||
|
return -2;
|
|||
|
|
|||
|
return 0;
|
|||
|
}
|
|||
|
|
|||
|
int receive(int fd, struct message *msg)
|
|||
|
{
|
|||
|
/* Check if the input data is not empty */
|
|||
|
if (fd < 0 || msg == NULL)
|
|||
|
return -1;
|
|||
|
|
|||
|
/* Try to read header */
|
|||
|
int msg_size = read(fd, &msg->header, sizeof(struct message_header));
|
|||
|
if (msg_size == 0)
|
|||
|
return 0;
|
|||
|
|
|||
|
/* Check header magic */
|
|||
|
if (msg->header.magic != MESSAGE_MAGIC)
|
|||
|
return -2;
|
|||
|
|
|||
|
/* Check if message has payload */
|
|||
|
if (msg->header.data_len > MAX_MESSAGE_DATA_LEN)
|
|||
|
return -2;
|
|||
|
if (msg->header.data_len > 0)
|
|||
|
msg_size += read(fd, &msg->data, msg->header.data_len);
|
|||
|
|
|||
|
/* Return number of bytes read */
|
|||
|
return msg_size;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
int main() {
|
|||
|
// Создадим два конвейера
|
|||
|
int pipes[2][2];
|
|||
|
pipe(pipes[0]);
|
|||
|
pipe(pipes[1]);
|
|||
|
|
|||
|
// Создадим дочерний процесс
|
|||
|
pid_t pid = fork();
|
|||
|
if (pid == 0) {
|
|||
|
// Сохраним нужные дескпиторы конвейеров
|
|||
|
int to_parent_pipe = pipes[1][1];
|
|||
|
int from_parent_pipe = pipes[0][0];
|
|||
|
|
|||
|
// И закроем ненужные
|
|||
|
close(pipes[1][0]);
|
|||
|
close(pipes[0][1]);
|
|||
|
|
|||
|
// Будем читать строки и отправлять их сообщениями
|
|||
|
char str[MAX_MESSAGE_DATA_LEN];
|
|||
|
for (;;)
|
|||
|
{
|
|||
|
// Прочитаем строку
|
|||
|
if (fgets(str, MAX_MESSAGE_DATA_LEN, stdin) == NULL)
|
|||
|
return 0;
|
|||
|
if (strlen(str) <= 1)
|
|||
|
break;
|
|||
|
|
|||
|
// И отпавим её родителю
|
|||
|
struct message msg = string_message(str);
|
|||
|
send(to_parent_pipe, &msg);
|
|||
|
}
|
|||
|
|
|||
|
// Отправим сообщение о завершении ввода
|
|||
|
struct message msg = simple_message(MSG_END);
|
|||
|
send(to_parent_pipe, &msg);
|
|||
|
|
|||
|
// Закроем дескпиторы
|
|||
|
close(to_parent_pipe);
|
|||
|
close(from_parent_pipe);
|
|||
|
|
|||
|
return 0;
|
|||
|
}
|
|||
|
|
|||
|
// Далее выполняется только родительский поток
|
|||
|
// Сохраним нужные дескпиторы конвейеров
|
|||
|
int from_child_pipe = pipes[1][0];
|
|||
|
int to_child_pipe = pipes[0][1];
|
|||
|
|
|||
|
// И закроем ненужные
|
|||
|
close(pipes[1][1]);
|
|||
|
close(pipes[0][0]);
|
|||
|
|
|||
|
// Будем ждать, пока ребенок не пришлет сообщение
|
|||
|
struct message msg;
|
|||
|
while (1)
|
|||
|
{
|
|||
|
int ret;
|
|||
|
while (ret = receive(from_child_pipe, &msg) == 0)
|
|||
|
;
|
|||
|
if (ret < 0)
|
|||
|
return 1;
|
|||
|
|
|||
|
// Если это строка - выведем её
|
|||
|
if (msg.header.type == MSG_STRING)
|
|||
|
printf("Recieved string from child: %s\n", (char *)msg.data);
|
|||
|
// Если конец ввода - прервемся
|
|||
|
else if (msg.header.type == MSG_END)
|
|||
|
{
|
|||
|
printf("Recieved END from child\n");
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// Дождемся завершения ребенка
|
|||
|
waitpid(pid, NULL, 0);
|
|||
|
|
|||
|
// Закроем дескпиторы
|
|||
|
close(from_child_pipe);
|
|||
|
close(to_child_pipe);
|
|||
|
|
|||
|
return 0;
|
|||
|
}
|