Linux无名管道和有名管道

一、无名管道

无名管道是UNIX系统IPC(进程间通信)的最古老形式,所有的UNIX都支持这种通信机制。

1、半双工,数据在同一时刻只能在一个方向上流动。

2、数据只能从管道的一端写入,另一端读出

3、写入管道中的数据遵循先入先出规则

4、管道所传送的数据是无格式的,这要求管道的读出方与写入方必须事先约定好数据的格式

5、管道不是普通文件,不属于某个文件系统,只存在于内存中。

6、管道在内存中对应一个缓冲区。不同的系统其大小不一定相同

7、从管道读数据时一次性操作,数据一旦被独奏,它就从管道中被抛弃,释放空间以便写更多的数据

8、管道没有名字,只能在具有公共祖先的进程(父进程与子进程,或者两个兄弟进程,具有亲缘关系)之间使用

1、pipe函数

#include<unistd.h>

int pipe(int pipefd[2]);

功能:创建无名管道

参数:

        pipefd : 为int型数组的首地址,其存放了管道的文件描述符 pipefd[0] 、pipe[1] 分别固定用于读管道和写管道。一般文件 I/O的函数都可以用来操作管道(lseek()除外)

返回值:

        成功:0

        失败:-1

2、 测试

在下面,用父子进程来使用管道通信测试,第一步创建一个无名管道,第二步创建子进程:

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#define SIZE 64

int main()
{
    int fds[2];
    int ret = -1;
    char buf[SIZE];
    pid_t pid = -1;
    
    //第一步、创建无名管道
    ret = pipe(fds);
    if(ret == -1)
    {
        perror("pipe");
        return 1;
    }

    //第二步、创建子进程
    pid = fork();
    //子进程创建失败
    if(pid == -1)
    {
        perror("fork");
        return 1;
    }
    //子进程  读管道
    if(pid == 0)
    {
        //关闭写端
        close(fds[1]);
        memset(buf, 0, SIZE);
        //读管道内容
        ret = read(fds[0], buf, SIZE);
        if(ret < 0)
        {
            perror("read");
            exit(-1);//子进程退出
        }
        printf("child process buf: %s\n", buf);
        //关闭读端
        close(fds[0]);
        //子进程退出
        exit(0);
    }

    //父进程  写管道
    //关闭读端
    close(fds[0]);
    //写管道
    ret = write(fds[1], "ABCDEFGHIJ", 10);
    if(ret < 0)
    {
        perror("write");
        return 1;
    }
    printf("parent process write len: %d\n", ret);
    //关闭写端
    close(fds[1]);
    //关闭文件描述符
    close(fds[0]);
    close(fds[1]);
    return 0;
}

编译后执行:

这里有一点需要注意的是,其实并不知道父子进程谁先执行,但是读管道如果没有读到数据会发生阻塞,所以在这里子进程总能读到父进程写的内容。

3、管道读写特点

1、 如果写端没有关闭,管道中没有数据,这个时候管道进程去读管道会阻塞

       如果写端没有关闭,管道中有数据,这个时候读管道进程会将数据读出,下一次读没有数据就会阻塞

2、管道所有的写端关闭,读进程去读管道的内容,读取全部内容后,最后返回0

3、所有读端没有关闭,如果管道被写满了,写管道进程写管道会被阻塞

4、所有的读端被关闭,写管道进程写管道会收到一个信号,然后退出 

 4、查看管道大小

使用ulimit -a命令查看,一般默认为4k

或者使用fpathconf函数来查看

#include<unistd.h>

long fpathconf(inf fd, int name);

该函数可以通过name参数查看不同的属性值

参数:

        fd:文件描述符

        name:

                _PC_PIPE_BUF,查看管道缓冲区大小

                _PC_NAME_MAX,文件名字字节数的上限

返回值:

        成功:根据name返回的值的意义也不同。

        失败:-1

5、 将无名管道设置为非阻塞

在上面我们知道管道默认为阻塞的,所以测试代码中,无论父子进程谁先执行,子进程总能在管道中读到数据,可以用以下方式设置为非阻塞

//获取原来的flags
int flags = fcntl(fd[0], F_GETFL);
//设置新的flags
flags |= O_NONBLOCK;// flags = flags | O_NONBLOCK
fcntl(fd[0], F_SETFL, flags);

设置读端为非阻塞之后,如果没读到数据,那么读端就会立马返回,如果只有这一个读端,此时没有了任何读端,根据管道读写特点,写端也会退出。

二、有名管道(FIFO)

无名管道只能用于亲缘关系的进程间通信。为了克服这个缺点,提出了命名管道(FIFO),也叫有名管道、FIFO文件。

命名管道不同于无名管道之处在于它提供了一个路径名与之关联,以FIFO的文件形式存在于文件系统中,这样,即使与FIFO的创建进程不存在亲缘关系的进程,只要可以访问该路径,就能彼此通过FIFO相互通信,因此,通过FIFO不相关的进程也能交换数据

有名管道的创建

使用命令mkfifo fifo_name创建一个管道,fifo_name为管道的名字。

使用函数mkfifo(fifo_name, mode)创建有名管道(可以使用命令man 3 mkfifo查看帮助文档):

#include<sys/types.h>
#include<sys/stat.h>
#include<unistd.h>

#include<sys/types.h>
#include<sys/stat.h>
int main()
{
    //创建一个有名管道
    int ret = mkfifo("fifo", 0644);//第一个参数为管道名称,第二个参数为mode(权限)
    if(ret == -1)
    {
        perror("mkfifo");
        return 1;
    }
    printf("成功创建一个有名管道...\n");
    return 0;
}
有名管道的使用测试

创建两个进程,分别用来读和写:

读进程:

int fd = -1; // 文件描述符
int ret = -1;
char buf[SIZE];
//1、以只读方式打开一个管道文件
fd = open("fifo", O_RDONLY);
if(fd == -1)
{
    perror("open");
    return 1;
}

//2、循环读管道
while(1)
{
    memset(buf, 0, SIZE);
    ret = read(fd, buf, SIZE);
    if(ret <= 0)// 0表示读到末尾,小于0表示读取错误
    {
        perror("read");
        break;
    }
    printf("buf:%s\n", buf);
}

//3、关闭文件
close(fd);
return 0;

写进程:

int i = 0;
int fd = -1; // 文件描述符
int ret = -1;
char buf[SIZE];

//1、以只写方式打开一个管道文件
fd = open("fifo", O_WRONLY);
if(fd == -1)
{
    perror("open");
    return 1;
}

//2、写管道
while(1)
{
    memset(buf, 0, SIZE);
    sprintf(buf, "hello itcast %d", i++); // 将hello itcast i写入buf
    ret = write(fd, buf, strlen(buf)); //将buf写入管道,fd为写端的文件描述符
    if(ret <= 0)
    {
        perror("write");
        break;
    }
    printf("write fifo:%d\n", ret);//如果写成功了则会输出这一行代码
    sleep(1);
}

//3、关闭文件
close(fd);
return 0;

有名管道注意事项

  1. 一个只为读而打开一个管道的进程会阻塞,直到另一个进程为只写打开该管道
  2. 一个只为写而打开一个管道的进程会阻塞,直到另一个进程为只读打开该管道
  • 17
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值