管道和无名管道
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 协议,完整转载必须注明来自 卡卡罗特
        
     评论
            
                匿名评论
                隐私政策
            
            
                你无需删除空行,直接评论以获取最佳展示效果
            
        
            
        

                                    
                                    