Pipe Development Guide

[ English | 简体中文 ]

I. Overview

Pipes are widely used in systems for various purposes, including the following common types:

  1. Pipe (Anonymous Pipe):

    • Used to create a pipe that allows inter-process communication (IPC).
    • The created pipe contains two file descriptors: one for reading (read end), and the other for writing (write end).
  2. popen/pclose calls:

    • Creates a pipe connected to another process, allowing reading from its output or sending data to its input.
  3. FIFO (Named Pipe):

    • Allows data exchange between unrelated processes.
    • Creating a FIFO is similar to creating a file and requires specifying a path.

II. API Interfaces

1、pipe Function Usage Instructions

Usage Instructions

The following are the function definitions for pipe and pipe2

#include <unistd.h>
// Returns: 0 if OK, −1 on error
int pipe(int fd[2]);
int pipe2(int fd[2], int flags);
  • Pipe File Descriptors:
    • fd[0]:The read end of the pipe.
    • fd[1]:The write end of the pipe.
  • Notes:
    • Writing to a closed read end: When data is written to a pipe whose read end is closed, a SIGPIPE signal is generated. If the signal is ignored or returned from the signal handler, write returns -1 and sets errno to EPIPE.
    • Multi-process writing: When multiple processes write to a pipe simultaneously, data may interleave.

Configuration Enabling

Below are the configuration options for enabling pipe functionality:

CONFIG_PIPES=y
CONFIG_DEV_PIPE_SIZE>0

Example Code

Below is an example code using pipes for inter-thread communication:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>

#define BUFFER_SIZE 1024

void* write_thread(void* arg) {
    int* pipefd = (int*)arg;
    char buffer[] = "Hello, pipe!";
    write(pipefd[1], buffer, sizeof(buffer));

    return NULL;
}

void* read_thread(void* arg) {
    int* pipefd = (int*)arg;
    char buffer[BUFFER_SIZE];
    ssize_t bytes_read = read(pipefd[0], buffer, sizeof(buffer) - 1);
    if (bytes_read > 0) {
        buffer[bytes_read] = '\0';
        printf("Read from pipe: %s\n", buffer);
    }
 
    return NULL;
}

int main() {
    int pipefd[2];
    pthread_t writer, reader;

    if (pipe(pipefd) == -1) {
        perror("pipe");
        exit(EXIT_FAILURE);
    }

    if (pthread_create(&writer, NULL, write_thread, (void*)pipefd) != 0) {
        perror("pthread_create writer");
        exit(EXIT_FAILURE);
    }

    if (pthread_create(&reader, NULL, read_thread, (void*)pipefd) != 0) {
        perror("pthread_create reader");
        exit(EXIT_FAILURE);
    }

    pthread_join(writer, NULL);
    pthread_join(reader, NULL);

    close(pipefd[0]);
    close(pipefd[1]);
    return 0;
}

2、popen/pclose Function Usage Instructions

Usage Instructions

#include <stdio.h>
// Returns: file pointer if OK, NULL on error
FILE *popen(const char *cmdstring, const char *type);
// Returns: termination status of cmdstring, or −1 on error
int pclose(FILE *fp);

The popen function creates a new process using posix_spawn to execute the specified command string (cmdstring) and redirects its input or output. The specific behavior depends on the value of the type parameter:

  • If type is r, the file pointer connects to the standard output (stdout) of cmdstring.
  • If type is w, the file pointer connects to the standard input (stdin) of cmdstring.

Configuration Enabling

CONFIG_SYSTEM_POPEN=y

Example Code

#include <stdio.h>
#include <stdlib.h>

nt main() {
    FILE *pipe;
    char *command = "ls";
    char buffer[128];

    pipe = popen(command, "r");
    if (pipe == NULL) {
        fprintf(stderr, "popen failed.\n");
        return -1;
    }

    while (fgets(buffer, sizeof(buffer), pipe) != NULL) {
        /* Processing each line of output */
        printf("%s", buffer);
    }

    if (pclose(pipe) == -1) {
        fprintf(stderr, "pclose failed.\n");
        return -1;
    }

    return 0;
}

3、FIFO Usage Instructions

Usage Instructions

FIFO (Named Pipe) is a special type of file used for communication between unrelated processes. Below are the function definitions for creating and using FIFOs:

#include <sys/stat.h>
// Both return: 0 if OK, −1 on error
int mkfifo(const char *path, mode_t mode);
int mkfifoat(int dirfd, const char *path, mode_t mode);
  • mode Parameter: Specifies the file permissions of the FIFO, same as the mode parameter in the open function.
  • path Parameter in mkfifoat:
    • If specified as an absolute path, the dirfd parameter is ignored, and behavior is similar to mkfifo.
    • If specified as a relative path, it is relative to the directory opened by dirfd.
    • If specified as a relative path and dirfd is AT_FDCWD, the path is relative to the current directory.
  • Notes on Opening FIFO:
    • If non-blocking flag O_NONBLOCK is not set:
    • When opened as read-only (O_RDONLY), the process will block until another process opens the FIFO for writing.
    • When opened as write-only (O_WRONLY), the process will block until another process opens the FIFO for reading.
    • It is not recommended to open FIFO with O_RDWR (read-write mode), as this may cause the read to never encounter an end-of-file (EOF). Non-blocking mode should be used to avoid blocking behavior.

Configuration Enabling

CONFIG_PIPES=y
CONFIG_DEV_FIFO_SIZE>0

Example Code

Below is an example code using FIFO for inter-thread communication:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <pthread.h>
#include <sys/types.h>
#include <sys/stat.h>

#define FIFO_NAME "/var/myfifo"

void* writer_thread(void* arg) {
    int fd;
    char buf[] = "Hello, FIFO!";

    /* Open the FIFO for writing */
    fd = open(FIFO_NAME, O_WRONLY);
    if (fd == -1) {
        perror("open");
        exit(EXIT_FAILURE);
    }

    /* Write data to the FIFO */
    if (write(fd, buf, sizeof(buf)) == -1) {
        perror("write");
        exit(EXIT_FAILURE);
    }

    /* Close the FIFO */
    close(fd);

    return NULL;
}

void* reader_thread(void* arg) {
    int fd;
    char buf[1024];

    /* Open the FIFO for reading */
    fd = open(FIFO_NAME, O_RDONLY);
    if (fd == -1) {
        perror("open");
        exit(EXIT_FAILURE);
    }

    /* Read data from the FIFO */
    if (read(fd, buf, sizeof(buf)) == -1) {
        perror("read");
        exit(EXIT_FAILURE);
    }

    /* Print the data read from the FIFO */
    printf("Read from FIFO: %s\n", buf);

    /* Close the FIFO */
    close(fd);

    return NULL;
}

int main() {
    pthread_t writer, reader;

    /* Create the FIFO */
    if (mkfifo(FIFO_NAME, 0666) == -1) {
        if (errno != EEXIST) {
            perror("mkfifo");
            exit(EXIT_FAILURE);
        }
    }

    /* Create threads for reading and writing */
    if (pthread_create(&writer, NULL, writer_thread, NULL) != 0) {
        perror("pthread_create writer");
        exit(EXIT_FAILURE);
    }

    if (pthread_create(&reader, NULL, reader_thread, NULL) != 0) {
        perror("pthread_create reader");
        exit(EXIT_FAILURE);
    }

    /* Wait for the threads to finish */
    pthread_join(writer, NULL);
    pthread_join(reader, NULL);

    /* Remove the FIFO */
    unlink(FIFO_NAME);

    return 0;
}

III. Inter-Process Isolation Restrictions

In the current openvela environment, due to the lack of support for inter-process isolation, all processes share the same address space. This situation may lead to the following issues:

  • Cross-process use of file descriptors (fd): Due to shared address space, cross-process use of file descriptors may result in unexpected behavior or resource conflicts.

Although hardware limitations prevent address space isolation, the following features remain isolated across processes:

  • File Descriptors (fd): File descriptors are independent for each process and cannot be directly shared across processes.
  • Environment Variables: Environment variables are independent for each process, and modifying one process’s environment variable will not affect other processes.