前言:本篇主要總結介紹了Linux進程間通信方式之一管道技術。包括管道(無名有名)的基本概念、相關API的介紹、及Demo代碼示例。
關於管道
管道是UNIX系統最古老的一種 IPC 方式,所有UNIX系統都提供這種通信機制。
管道 | 接口 | 特性 |
---|---|---|
無名管道 | pipe | 半雙工爲主、父子進程間通信 |
命名管道 FIFO | mkfifo 和 mkfifoat | 多個不相關進程間通信 |
- int pipe(int pipefd[2])
該函數經由參數 pipefd 返回兩個文件描述符:pipefd[0] 爲讀打開,pipefd[1] 爲寫打開。pipefd[1] 的輸出是 pipefd[0] 的輸入 - int mkfifo(const char *pathname, mode_t mode)
- int mkfifoat(int dirfd, const char *pathname, mode_t mode)
使用 mkfifo 和 mkfifoat 兩個接口來創建 FIFO 文件,其中參數 mode 與 open 函數中的 mode 相同;
mkfifoat 與 mkfifo 相似,其中 fd 用來在 fd 文件描述符表示的目錄相關的位置創建一個 FIFO。
父子進程間的管道通信
一般,進程先調用 pipe,再調 fork,從而創建從父進程到子進程的 IPC 管道,如圖所示:
下面來創建一個父子進程間的通信管道,子進程通過該管道像父進程傳送數據,父進程使用該數據。Demo 程序如下:
#define BUFSIZE 512
static void PipeDemo()
{
int fd[2];
pid_t pid;
char buf[BUFSIZE] = {0};
int readsize = 0;
if(pipe(fd) < 0) {
printf("pipe failed!\n");
return;
}
if((pid = fork()) < 0) {
printf("fork failed!\n");
return;
}
else if(pid == 0) {
close(fd[0]);
printf("Child Process Say Hello to Father Process\n");
write(fd[1], "Hello Father\n", 13);
}
else {
close(fd[1]);
readsize = read(fd[0], buf, BUFSIZE);
if(strcmp(buf, "Hello Father\n") == 0) {
printf("OK, Good Boy\n");
}
}
exit(0);
}
該程序測試執行後打印如下:
函數 popen 和 pclose
- FILE *popen(const char *command, const char *type)
函數 popen 先執行 fork,然後調用 exec 執行 command,並且返回一個標準 I/O 文件指針。
如果 type 是 “r”,則文件指針鏈接到 command 的標準輸出;
如果 type 是 “w”,則文件指針鏈接到 command 的標準輸入; - int pclose(FILE *stream)
on success, returns the exit status of the command; if wait4(2) returns an error, or some other error is detected, -1 is returned.
下面使用 popen 執行一個 ls 命令,並打印結果:
static void PopenDemo()
{
FILE *fd = NULL;
char buf[BUFSIZE] = {0};
size_t n = 0;
fd = popen("ls -l", "r");
n = fread(buf, BUFSIZE, 1, fd);
printf("%s", buf);
if(pclose(fd) == -1) {
printf("pclose failed.");
}
}
執行結果如下:
FIFO 命名管道
FIFO 是一種文件類型,通過FIFO,不相干的進程之間也能交換數據。
下面就使用 mkfifo 函數來創建一個 FIFO 文件,並用與不同進程間通信。
該Demo程序通過 fifo 接收其他進程的消息,並作出迴應
// fifo.c create 2020/5/26
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdlib.h>
static void ModifyFIFO()
{
const char *fifofile = "/tmp/fifo";
if(access(fifofile, F_OK) != 0) {
if(mkfifo(fifofile, 0755) != 0) {
printf("mkfifo failed.");
exit(0);
}
}
int fd = open(fifofile, O_RDWR);
if(fd < 0) {
printf("open fifo failed.");
exit(0);
}
char buf[512] = {0};
int size = 0;
while(1) {
memset(buf, 0, 512);
size = read(fd, buf, 512);
if(size > 0) {
printf("%s\n", buf);
}
if(strstr(buf, "exit")) {
break;
}
else if(strstr(buf, "Hello Dad")) {
write(fd, "OK, Good boy~", 13);
}
}
}
int main()
{
ModifyFIFO();
return 0;
}
不同進程之間執行效果如下: