使用管道时,一个进程的输出可成为另外一个进程的输入。
命名管道(Named pipe或FIFO)是一种类似于管道的特殊文件,但在文件系统上有一个名称,它允许以先进先出(FIFO, first in, first out)的方式存储有限数量的数据。它的使用类似于消息传递,其中一个进程发送一条信息,其它进程接收它。数据以FIFO方式以高吞吐速度进入管道。但是,队列一次可以容纳的最大数据大小为16页(pages)或65536字节。它实际上使用了一块内核内存。
命名管道是在文件系统中作为一个特殊的设备文件而存在。不同祖先的进程之间可以通过命名管道共享数据。当共享命名管道的进程执行完所有的I/O操作以后,命名管道将继续保存在文件系统中,以便以后使用,除非调用unlink。通过命名管道,不相关的进程也能交换数据。一旦已经用mkfifo函数创建了一个FIFO,就可用open打开它。实际上,一般的文件I/O函数(close、read、write、unlink等)都可用于FIFO。
只要FIFO有空间,write函数就是非阻塞的,但read会阻塞当前线程。
命名管道总结:
(1).同步(用于单向管道);
(2).队列大小为16页(page),每页4096字节。只要数据消耗足够快,数据大小就没有限制;
(3).单个管道的单向通信;
(4).以线性方式读写;
(5).自动内存管理。
注:以上内容主要来自网络整理。
如果父进程和子进程之间互相发送接收数据,需要两根管道,如以下测试代码:
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <string.h>
#include <error.h>
#include <iostream>
#include <thread>
#include <cctype>typedef struct message {int pid;char ch;
} message;int main(int argc, char **argv)
{const char *named_pipe1 = "/tmp/named_pipe1", *named_pipe2 = "/tmp/named_pipe22";unlink(named_pipe1); // deletes a name from the file systemunlink(named_pipe2);if (mkfifo(named_pipe1, 0666) < 0 || mkfifo(named_pipe2, 0666) < 0) { // make a FIFO special file(a named pipe), if the file exists, the call will failfprintf(stderr, "fail to mkfifo: %s\n", strerror(errno));return -1;}struct stat buffer1, buffer2;if (stat(named_pipe1, &buffer1) != 0 || stat(named_pipe1, &buffer2) != 0) { // retrieve information about the file pointed to by pathnamefprintf(stderr, "fail to stat: %s\n", strerror(errno));return -1;}pid_t pid = fork();if (pid < 0) {fprintf(stderr, "fail to fork\n");return -1;}if (pid == 0) { // child processauto fd1 = open(named_pipe1, O_RDONLY); // read onlyauto fd2 = open(named_pipe2, O_WRONLY); // write onlyif (fd1 < 0 || fd2 < 0) {fprintf(stderr, "fail to open: %d, %s\n", pid, strerror(errno));exit(1);}for (int i = 0; i < 5; ++i) {message msg;auto ret = read(fd1, &msg, sizeof(msg));if (ret < 0) {fprintf(stderr, "fail to read: %d, %d %s\n", pid, i, strerror(errno));exit(1);}msg.ch = std::toupper(msg.ch);ret = write(fd2, &msg, sizeof(msg));if (ret < 0) {fprintf(stderr, "fail to write: %d, %d, %s\n", pid, i, strerror(errno));exit(1);}std::this_thread::sleep_for(std::chrono::milliseconds(100));}close(fd1);close(fd2);exit(0);}if (pid > 0) { // parent processauto fd1 = open(named_pipe1, O_WRONLY); // write onlyauto fd2 = open(named_pipe2, O_RDONLY); // read onlyif (fd1 < 0 || fd2 < 0) {fprintf(stderr, "fail to open: %d, %s\n", pid, strerror(errno));exit(1);}for (unsigned char i = 0; i < 5; ++i) {message msg = {pid, 'a'+i};auto ret = write(fd1, &msg, sizeof(msg));if (ret < 0) {fprintf(stderr, "fail to write: %d, %d, %s\n", pid, i, strerror(errno));exit(1);}fprintf(stdout, "src char: %c\n", msg.ch);ret = read(fd2, &msg, sizeof(msg));if (ret < 0) {fprintf(stderr, "fail to read: %d, %d, %s\n", pid, i, strerror(errno));exit(1);}fprintf(stdout, "dst char: %c\n", msg.ch);std::this_thread::sleep_for(std::chrono::milliseconds(100));}close(fd1);close(fd2);int status;auto pid2 = wait(&status); // system call suspends execution of the calling thread until one of its children terminatesfprintf(stdout, "process ID of the terminated child: %d\n", pid2);if (WIFEXITED(status)) { // returns true if the child terminated normallyfprintf(stdout, "child process ended with: exit(%d)\n", WEXITSTATUS(status));}if (WIFSIGNALED(status)) { // returns true if the child process was terminated by a signalfprintf(stderr, "child process ended with: kill -%d\n", WTERMSIG(status));}}unlink(named_pipe1); // deletes a name from the file systemunlink(named_pipe2);fprintf(stdout, "====== test finish ======\n");return 0;
}
build.sh内容如下:
#! /bin/bashif [ -d build ]; thenecho "build directory already exists, it does not need to be created again"
elsemkdir -p build
ficd build
cmake ..
makerc=$?
if [[ ${rc} != 0 ]];thenecho "#### ERROR: please check ####"exit ${rc}
fiecho "==== build finish ===="
CMakeLists.txt内容如下:
cmake_minimum_required(VERSION 3.22)
project(samples_multi_process)set(CMAKE_BUILD_TYPE Release) # only works under linux
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -O2 -std=c++17")file(GLOB samples ${PROJECT_SOURCE_DIR}/test_*.cpp)
#message(STATUS "samples: ${samples}")foreach(sample ${samples})string(REGEX MATCH "[^/]+$" name ${sample})string(REPLACE ".cpp" "" exec_name ${name})#message(STATUS "exec name: ${exec_name}")add_executable(${exec_name} ${sample})
endforeach()
执行结果如下图所示:
GitHub:https://github.com/fengbingchun/Linux_Code_Test