linux进程中的有名管道和无名管道

前言:管道是用于进程之间通信的方式,本质是在内存中建立一段缓存区,并且这段缓存区有固定大小,不能超出。有名管道是任意进程之间都可以进行通信的,并且可以以文件的形式存在的,而无名管道只能用于父子进程之间的通信,同时也只在其生命周期内存在。

一、无名管道

通常情况下,无名管道默认是单向的,用于父进程向子进程发送文件描述符等数据,但实际上也是可以双向传递的。无名管道的创建需要用到系统调用pipe();

函数头文件:#include <unistd.h>

函数定义:int pipe(int pipefd[2]);

函数返回值:   成功时返回 0。
                        失败时返回 -1,并设置 errno 以指示错误。

该函数需要传入一个长度为二的数组,之后,pipefd[0]就作为读端文件描述符,pipefd[1]:作为写端文件描述符。通过文件I/O函数就可以在管道中进行读写操作。

来看一段代码:

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/wait.h>

int main()
{
    char buf_w[128] = "";//定义两个数组,用来存放数据;
    char buf_r[128] = ""; 
    int pipefd[2];//存放文件标识符;
    int ret;
    ret = pipe(pipefd);//创建一个无名管道;
    if(ret == -1){
        perror("pipe:");
        exit(EXIT_FAILURE);
    }   
    printf("please input cammond:");
    fgets(buf_w,sizeof(buf_w),stdin);
    buf_w[strlen(buf_w)-1] = '\0';
    pid_t pid = fork(); //创建一个子进程;
    if(pid == -1){
        perror("fork");
        exit(EXIT_FAILURE);
    }else if(pid == 0){ 
            sleep(0.5);//延迟子进程,让主进程先进行写操作;
            close(pipefd[1]);//开始读之前保证写端关闭,避免发生错误;
            ssize_t rbytes = read(pipefd[0],buf_r,sizeof(buf_r));//从管道中读取数据;
            if(rbytes == -1){
                perror("read");
                close(pipefd[0]);
                exit(EXIT_FAILURE);
            }
            printf("command is %s,%ld in total\n",buf_r,rbytes);
            close(pipefd[0]);
            exit(EXIT_SUCCESS);
        }else{                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        
            close(pipefd[0]);//同样先保证读端关闭;
            ssize_t wbytes =  write(pipefd[1],buf_w,strlen(buf_w));//向管道中写入数据;
            if(wbytes == -1){
                perror("write");
                close(pipefd[1]);
                exit(EXIT_FAILURE);
            }
            close(pipefd[1]);
            printf("wbytes is %ld\n",wbytes);
            wait(NULL);//等待子进程读取完成;
        }
}
~                  

上面代码在进行写的时候,为避免错误,会先将读端关闭,读端同理。还有打印wbytes时的占位符时ld,因为在64位操作系统下,ssize_t类型本质上就是long int类型,但在32位的才做系统下,它是int 类型。 

下面的运行结果:

虽然我们read函数传入参数读取的size是sizeof(buf_r)也就是128个,但只要遇到结束符就会停止,所以一共读了是11个是没有问题的。 

此外,还需要注意的是当我们不进行写操作时,也就是说当管道为空时,读操作就会堵塞。

二、有名管道

有名管道可以在任意两个进程中进行通信,但是没有打开写进程时,会堵塞读进程。相反没有打开读进程时,写进程也会阻塞。

有名管道的创建时通过mkfifo函数:

函数头文件              #include <sys/types.h>
                                #include <sys/stat.h>
函数原型:                 int mkfifo(const char *pathname, mode_t mode);
函数参数:                 pathname:有名管道路径名
                                mode:有名管道文件访问权限, 常用0644
函数返回值:             成功:返回0
                                失败:返回-1,并设置 errno
下面时一段两个进程通过有名管道发送信息write的示例:
#include <stdio.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <signal.h>

#define PATH_NAME "/home/linux/path_name"

int main()
{
    int ret = access(PATH_NAME,F_OK);//判断文件是否存在;
    if(ret == -1) 
    {   
        int res = mkfifo(PATH_NAME,0644);/*如果不存在就创建,0644实际上是8进制数,
0代表的文件的类型,后面的三位则代表着着拥有者、所属组和其他人的读、写和执行权限。
举个例子:上面的6代表的二进制数是110,那拥有者的权限就是可读可写,并没有执行权限。*/
        if(res == -1){
            perror("res");
            exit(EXIT_FAILURE);
        }        
    }   
    int fd = open(PATH_NAME,O_WRONLY);
    if(fd == -1)  
    {   
        perror("open");
        exit(EXIT_FAILURE);
    }       
    char buf[256]={"Hello pipe"};
    ssize_t wbytes = write(fd,buf,sizeof(buf));//写入数据;
    if(wbytes == -1)
    {   
        perror("write");
        close(fd);
        exit(EXIT_FAILURE);
    }   
    printf("wbytes = %ld\n",wbytes);
    close(fd);
    return 0;
}       

read部分:

#include <stdlib.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <signal.h>

#define PATH_NAME "/home/linux/path_name"

int main()
{
    int fd = open(PATH_NAME,O_RDONLY);
    if(fd == -1) 
    {   
        perror("read");
        exit(EXIT_FAILURE);
    }   
    char buf[256]={0};
    ssize_t rbytes = read(fd,buf,sizeof(buf));//将数据读到buf中;
    if(rbytes == -1) 
    {   
        perror("read");
        exit(EXIT_FAILURE);
    }   
    printf("buf = %s\n",buf);
    close(fd);
    return 0;
}         

 通过write函数想管道中写入数据,然后再read的函数的进程中将数据读出来。运行结果如下:

先运行write部分:

发现它处于堵塞状态,因为我们的read部分还没有打开,暂时还无法进行写操作,那再运行read部分:

 可以看到,代码成功运行,并且read部分已经将数据读出来了,是因为write在read打开的时候便将数据传了进去,read也成功接收,再看write部分:

 发现也已经显示写入的字符数。

再次之后还有关闭的情况:

  1. 写端关闭:当写端(即写入管道的一端)关闭后,如果读端(即从管道读取数据的一端)尝试继续读取,它将不会阻塞,而是会立即返回0,表示到达了文件末尾(EOF)。这是因为管道中没有更多的数据可读,写端已经关闭。

  2. 读端关闭:当读端关闭后,写端如果继续写入,通常会收到一个SIGPIPE信号,导致进程默认终止,除非进程已经处理了这个信号。在这种情况下,写入操作不会阻塞,而是会因为管道没有读端而失败。

  3. 阻塞和非阻塞模式:如果管道的一端以非阻塞模式打开,那么当另一端关闭时,操作不会阻塞,而是会立即返回,并且errno会被设置为EAGAINEPIPE

这些东西一定要注意。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值