Linux 进程间通信 - 匿名管道/命名管道

进程之间具有独立性. 其他的进程无法访问本进程的资源和内容.
那如果想要让进程访问一些某个进程内的内容, 我们该怎么做?

这时就需要通过进程间通信, 来完成两个进程之间的内容交互.
管道就是用来完成进程间通信的一种方式.

管道又分为匿名管道, 和命名管道.

管道

管道它允许两个进程之间的信息传递, 其中一个进程的输出直接作为另一个进程的输入.

管道是一种单向通信机制, 数据只能沿一个方向流动.

管道本质就是一个文件, 进程间通信就是让两个进程看见相同的空间 (文件)

并且管道都是位于内核区的, 而不是用户区

上面我们也提过, 管道只能单向通信, 即一个进程要么只能读, 要么只能写

如果想要实现两个进程都能进行"交流', 可以在创建一个管道, 这次读写的身份反过来.

匿名管道

匿名管道: 顾名思义这个管道文件没有名字

匿名管道通常用于父子进程之间进行通信

int pipefd[2]: 当创建好管道后, 会将管道读写端文件描述符返回

pipefd[0]: 管道读端的描述符.        pipefd[1]: 管道写端的描述符

返回值: 创建成功返回 0, 创建失败返回 -1

int main()
{
    //创建管道
    int pipefd[2]={0}; //0下标表示读取端,1下标表示写入端
    int n = pipe(pipefd);
    assert(n!=-1);
    pid_t id = fork();
    assert(id!=-1);

    if(id==0)//子进程, 构建单向通信
    {
        close(pipefd[1]);
    }

    //父进程写入,子进程读取
    close(pipefd[0]);

    return 0;
}

在上面的代码中, 我们先创建了匿名管道, 然后再创建子进程, 这样管道的两个文件描述符 pipefd[2] 就会被自动拷贝给子进程. 然后让父子进程分别关闭读端和写端.

当然, 上面的代码只实现了单项通信, 想实现双向通信就需要多创建一个管道.
既然管道就是一个文件, 那么读写数据也就是使用 read / write 函数.

int main()
{
    //创建管道
    int pipefd[2]={0}; //0下标表示读取端,1下标表示写入端
    int n = pipe(pipefd);
    assert(n!=-1);
    pid_t id = fork();
    assert(id!=-1);

    if(id==0)//子进程, 构建单向通信
    {
        close(pipefd[1]);
        char buffer[1024];
        while(1)
        {
            ssize_t s = read(pipefd[0],buffer,sizeof(buffer)-1);
            if(s>0)
            {
                buffer[s]=0;
                cout<<"father# "<<buffer<<endl;
            }
            else//read的返回值等于0代表父进程的管道文件已经close了
            {
                cout<<"写入结束,子进程退出";
                break;
            }
        }
        exit(0);
    }

    //父进程写入,子进程读取
    close(pipefd[0]);
    string str = "给子进程发信息";
    int count=0;
    char send_buffer[1024];
    snprintf(send_buffer,sizeof(send_buffer),"%s[%d]",str.c_str(),getpid());//往缓冲区里写入数据
    //写入到管道中
    write(pipefd[1],send_buffer,sizeof(send_buffer));
    
    // 关闭文件描述符, 释放资源
    close(pipefd[1]);
    pid_t ret = waitpid(id,NULL,0); // 等待子进程
    return 0;
}

匿名管道的特点: 

  • 管道常用于父子进程间的通信
  • 管道是面向字节流的服务
  • 管道是基于文件的,管道的生命周期随进程
  • 管道是单向通信的
  • 写快读慢,写满管道后不能再写了
  • 写慢读快,管道没有数据时,读端要等待
  • 写端关闭,读端会读到0,标识结束
  • 读端关闭,写端继续写会终止进程

命名管道

匿名管道必须在有血缘关系的进程间才能使用.
如果两个进程之间没有血缘关系, 可以通过创建命名管道来进行通信.

命名管道可以通过使用命令创建, 也可以在程序中使用系统调用函数来创建.

1. 使用命令创建

mkfifo filename

mkfifo 用于创建命名管道

filename 被创建出来的管道的名称

2. 通过函数调用创建

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

int mkfifo(const char *pathname, mode_t mode);

int main()
{
    mkfifo("fifo", 0644); // 创建一个管道, 名称为 fifo
    return 0;
}

当命名管道存在以后, 使用就和匿名管道是类似的.

读端代码:写端代码:

string ipcpath = "./fifo.ipc";
int main()
{
    //client不用自己创建管道文件,只需获取文件即可
    int fd = open(ipcpath.c_str(),O_WRONLY);
    if(fd<0)
    {
        perror("open");
        exit(1);
    }
    //通信过程
    string buffer;
    cout<<"please Enter message: ";
    getline(cin,buffer);
    write(fd,buffer.c_str(),buffer.size());
    close(fd);
    return 0;
}

命名管道打开的的规则:

  • 如果没有进程打开命名管道进行写操作, 那么尝试从命名管道读数据的进程将被阻塞
  • 如果没有进程打开命名管道进行读操作, 那么尝试向命名管道写数据的进程将被阻塞

进程可以在打开命名管道时设置非阻塞标志 (O_NONBLOCK) , 这样在没有对应读/写操作时, read()write()调用将立即返回, 而不是阻塞等待.

命名管道的使用, 和文件的使用是非常相似的, 会文件操作就会命名管道的使用.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值