AOSP Expert & Production Engineering
3 min read

Pipes

Anonymous Pipes: pipe()

Pipes represent the oldest and simplest form of Unix IPC. An anonymous pipe provides a unidirectional data channel that can be used for communication between related processes (typically a parent and a child).

The pipe() system call takes an array of two integers. It opens a pipe and populates the array with two file descriptors: fd[0] for reading and fd[1] for writing.

#include <unistd.h>
#include <stdio.h>
#include <string.h>

void pipe_example() {
    int pipefd[2];
    if (pipe(pipefd) == -1) {
        perror("pipe");
        return;
    }

    pid_t cpid = fork();
    if (cpid == -1) {
        perror("fork");
        return;
    }

    if (cpid == 0) {    // Child reads from pipe
        close(pipefd[1]); // Close unused write end
        char buf;
        while (read(pipefd[0], &buf, 1) > 0) {
            write(STDOUT_FILENO, &buf, 1);
        }
        write(STDOUT_FILENO, "\n", 1);
        close(pipefd[0]);
    } else {            // Parent writes to pipe
        close(pipefd[0]); // Close unused read end
        const char *msg = "Hello from parent via pipe!";
        write(pipefd[1], msg, strlen(msg));
        close(pipefd[1]); // Reader will see EOF
    }
}

Named Pipes (FIFO): mkfifo

Anonymous pipes are limited to processes with a common ancestor. Named pipes (FIFOs) solve this by creating a special file on the filesystem. Any process with appropriate permissions can open the FIFO for reading or writing.

#include <sys/types.h>
#include <sys/stat.h>

int create_fifo() {
    // Creates a FIFO named "my_fifo"
    return mkfifo("/data/local/tmp/my_fifo", 0666);
}

Processes open the FIFO using standard open(). A process opening a FIFO for writing will block until another process opens it for reading, and vice versa, unless the O_NONBLOCK flag is used.

Use in Android: logd pipe, adb pipe

While Binder is the primary IPC mechanism in Android for complex RPC communication, pipes and FIFOs are still heavily utilized for stream oriented data transfer and signaling.

ADB Pipe

The Android Debug Bridge (adb) relies on pipes and sockets. When you run a shell command via adb shell, the adbd daemon on the device forks a process and redirects its standard input, output, and error streams into pipes connected back to the daemon. This allows adbd to stream the process's output over the network socket back to the host machine.

Logd Pipe

The logging system in Android (logd) historically used sockets, but pipes are often employed in internal logging mechanics or when redirecting logs. For example, when an application crashes, debuggerd connects to the crashed process via ptrace and uses a pipe to stream the crash dump data efficiently to the system log or a tombstone file.

You can inspect open pipes for a process using lsof via the adb shell:

adb shell
su
lsof -p <pid> | grep FIFO