.webp)
管道和无名管道
AI-摘要
Tianli GPT
AI初始化中...
介绍自己
生成本文简介
推荐相关文章
前往主页
前往tianli博客
管道
1. 管道概述
管道(pipe) 又称之为无名管道。
无名管道是一个特殊的文件类型,在应用程序执行的过程中存在,利用两个【文件描述符】来实现管道操作。利用 write 和 read 系统调用函数,借助于两个文件描述符,实现管道操作。

无名管道采用的通信方式/协议是最古老的 UNIX IPC 方式
- 半双工,数据在同一个时间,有且只允许单向通信
- 数据有且只允许,从一端读取,另一端写入
- 管道中的数据遵循 FIFO (先进先出) 原则,一旦数据从管道的一端读取之后,当前被读取数据在管道中消失。
- 管道中的数据无格式要求,取决于当前用户设置的通信数据格式,数据要求和数据形式。
- 无名管道不是一个【普通文件】是内核中的一个特殊标记文件,存在于内存中
- 进程打开的管道,在系统中有明确的限制个数和限制缓冲区容量,当前 Ubuntu 18.04 中,默认管道缓冲区位 512bytes ,个数 8
2. 无名管道操作
2.1 相关函数 pipe
函数文档
#include <unistd.h> int pipe(int pipefd[2]);
- 功能:
- 当前进程创建一个管道,参数要求是一个 int 类容量为 2 的数组
- 参数解释
int pipefd[2]
: 要求是一个 int 类型的容量为 2 的数组,管道创建完毕之后,对应的文件描述符存储在下标 0 和下标 1 的位置- 同时限制
pipefd[0]
管道读打开- 同时限制
pipefd[1]
管道写打开- 返回值
- 管道创建成功返回 0
- 管道创建失败返回 -1
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
#include <sys/types.h>
#include <fcntl.h>
int main(int argc, char const *argv[])
{
int pipefd[2] = {0};
/*
进程占用的默认文件描述符有
#define STDIN_FILENO 0
#define STDOUT_FILENO 1
#define STDERR_FILENO 2
默认情况下,用户可以使用的最小文件描述符为 3
*/
int ret = pipe(pipefd);
if (0 == ret)
{
printf("pipefd[0] read : %d, pipefd[1] write : %d\n", pipefd[0], pipefd[1]);
}
else
{
abort();
}
/*
父进程通过 pipefd[1] 写入 write 数据,子进程通过 pipefd[0] 读取 read 数据
*/
pid_t pid = -1;
pid = fork();
if (-1 == pid)
{
perror("Fork Failed!\n");
}
if (0 == pid)
{
// 子进程 【子进程通过 pipefd[0] 读取 read 数据】
char buf[32] = "";
read(pipefd[0], buf, sizeof(buf));
printf("Son Process get data : %s\n", buf);
_exit(0);
}
else
{
// 父进程 【父进程通过 pipefd[1] 写入 write 数据】
char data[] = "Hello World!";
printf("Parent Process write data to pipe : %s\n", data);
// sleep(2);
write(pipefd[1], data, sizeof(data));
/*
char *data = "Hello World!";
sizeof(data); 8(64位) 4(32位)
sizeof("Hello World!"); 13
char data[] = "Hello World!";
sizeof(data);
sizeof("Hello World!");
*/
_exit(0);
}
return 0;
}
2.2 dup 和 dup2
【拷贝文件描述符】
函数文档
#include <unistd.h> int dup(int oldfd);
- 函数功能:
- 复制当前现有的文件描述符,得到一个新的文件描述,同时新的文件描述符是目前进程中可以使用的最小文件描述符。
- 函数参数:
int oldfd :
现有的文件描述符,如果文件描述符已关闭 或者存在,【ERROR】- 返回值:
- 得到的新的文件描述符,与
oldfd
指向同一个文件资源,操作新的 fd 和 oldfd 是一致的。- 如果执行失败,返回 -1
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main(int argc, char const *argv[])
{
int fd = open("./1.txt", O_CREAT | O_WRONLY | O_APPEND, 0666);
if (-1 == fd)
{
perror("open failed!\n");
}
printf("fd : %d\n", fd);
/*
当前文件对应的文件描述符,进行复制,得到新的文件描述符,同时
新的文件描述,具备原文件描述符的所有内容。
*/
int new_fd = dup(fd);
write(fd, "1. Hello World!\n", strlen("1. Hello World!\n"));
write(new_fd, "2. Hello World!\n", strlen("2. Hello World!\n"));
/*
当前 old_fd 关闭之后,对于通过 dup 函数拷贝的新的 文件描述符没有任何的影响
新的文件描述符依然可以使用
【注意】
考虑到文件资源问题,需要对当前新的文件描述 new_fd 同样进行 close 操作
*/
close(fd);
write(new_fd, "3. Hello World!\n", strlen("3. Hello World!\n"));
close(new_fd);
return 0;
}
函数文档
#include <unistd.h> int dup2(int oldfd, int newfd);
- 函数功能:
- 将已有的文件描述 oldfd 【复制】/【重定向】到 newfd ,
- 如果 newfd 已存在,将覆盖原本的文件描述符属性,同时将原本文件描述对应的资源进行关闭。
- 【少见】如果 newfd 不存在,则根据当进程中,最小文件描述符情况分配新的文件描述符,功能类似于 dup
- 函数参数:
int oldfd :
现有的文件描述符,如果文件描述符已关闭 或者存在,【ERROR】int newfd :
已有的文件描述符,重定向之后,newfd 原本资源被关闭,newfd 和 oldfd 资源一致。- 返回值:
- 若调用成功,返回新的文件描述符(即
newfd
)。- 如果执行失败,返回 -1
3. 有名管道操作
3.1 基本概述
- 半双工,数据在同一个时间,有且只允许单向通信
- 数据有且只允许,从一端读取,另一端写入
- 管道中的数据遵循 FIFO (先进先出) 原则,一旦数据从管道的一端读取之后,当前被读取数据在管道中消失。
- 管道中的数据无格式要求,取决于当前用户设置的通信数据格式,数据要求和数据形式。
- 有名管道是利用 Linux 中的管道文件,实现管道操作,在硬盘中真实存在对应的文件内容。
- 进程打开的管道,在系统中有明确的限制个数和限制缓冲区容量,当前 Ubuntu 18.04 中,默认管道缓冲区位 512bytes ,个数 8
3.2 相关函数
函数文档
#include <sys/types.h> #include <sys/stat.h> int mkfifo(const char *pathname, mode_t mode);
- 功能
- 在用户指定的路径中,创建一个管道文件,并且指定当前管道文件的操作权限。
- 参数解释
const char *pathname
: 指定管道文件所在路径和对应名称。一般情况下,管道文件没有对应的后缀名mode_t mode
: 设置当管道文件的相关操作权限,主要是拥有者,同组用户和访客的 RWX 操作权限,可以利用 八进制 权限方式限制- 返回值类型
- 如果命名管道创建成功,
mkfifo
函数返回 0。
- **如果创建过程中出现错误,函数返回 -1,并设置 **
errno
来指示具体的错误类型。- 可能的错误类型
EACCESS
:调用进程没有足够的权限在指定的目录下创建命名管道。EEXIST
:指定的路径名已经存在,可能是一个普通文件、目录或其他类型的文件。ENAMETOOLONG
:路径名太长。ENOENT
:指定路径中的某个目录不存在。ENOTDIR
:路径中的某个组件不是一个目录。EROFS
:指定的路径位于只读文件系统中。
3.3 案例代码
创建管道
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
int main(int argc, char const *argv[])
{
int ret = mkfifo("./fifo", 0666);
if (!ret)
{
printf("Create Pipe Successful!\n");
}
return 0;
}
写入数据到管道
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#define PATH_NAME "./fifo"
int main(int argc, char const *argv[])
{
// 打开管道,利用 write 函数向管道中写入数据
printf("Write Data to Pipe!\n");
// 1. 利用 open 函数打开管道,打开方式为只写方式 O_WRONLY
int fd = open(PATH_NAME, O_WRONLY);
char * data = "Hello World!\n";
// 2. 利用 write 函数写入数据到管道中
write(fd, data, strlen(data));
// 3. 关闭 文件描述符
close(fd);
return 0;
}
从管道中读取数据
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#define PATH_NAME "./fifo"
#define BUFFER_SIZE 64
int main(int argc, char const *argv[])
{
// 打开管道,利用 read 函数从管道中读取数据
printf("Read Data From Pipe!\n");
// 1. 利用 open 函数打开管道,打开方式为只读方式 O_RDONLY
int fd = open(PATH_NAME, O_RDONLY);
char buf[BUFFER_SIZE];
memset(buf, 0, BUFFER_SIZE);
// 2. 利用 read 函数从管道文件中读取数据
read(fd, buf, BUFFER_SIZE);
printf("Data : %s\n", buf);
// 3. 关闭 文件描述符
close(fd);
return 0;
}
- 感谢你赐予我前进的力量
赞赏者名单
因为你们的支持让我意识到写文章的价值🙏
本文是原创文章,采用 CC BY-NC-ND 4.0 协议,完整转载必须注明来自 卡卡罗特
评论
匿名评论
隐私政策
你无需删除空行,直接评论以获取最佳展示效果