Liunx系统进程通信方式——匿名管道与有名管道(附:进程间的简易聊天室)

本文简要概述了Linux系统中,进程间相互通信的两种方法。其中,匿名管道适合有亲缘关系的两进程通信;而有名管道可以允许任意两进程之间的通信。

在介绍了匿名/有名管道的原理以及相关系统调用后,利用有名管道与多线程实现了本机两个进程间的实时聊天系统。

一、匿名管道

1.匿名管道简介

  • 管道是半双工的。一端写入,一端读取
  • 数据一旦从管道中取出,就会从管道中删除,释放空间以便于写入更多的数据。
  • 匿名管道只能在具有公共祖先的进程间通信
  • 管道的是环形队列的数据结构。
  • 头文件以及函数声明如下
#include <unistd.h>

int pipe(int pipefd[2]);
  • 数组pipefd返回管道两端的文件描述符。pipefd[0]指向读端,pipefd[1]指向写端

The array pipefd is used to return two file descriptors referring to the ends of the pipe.
pipefd[0] refers to the read end of the pipe. pipefd[1] refers to the write end of the pipe.

  • 创建成功返回0,失败或是错误返回-1

2. 简单示例程序

  • 以下示例程序中,创建了子进程以及用于两进程通信的匿名管道(注意,管道需要在子进程前创建)。主进程不断对管道read,一旦子进程对管道write数据后,主进程读出并打印。
#include <unistd.h>
#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main()
{
    int pipefd[2];
    int ret = pipe(pipefd);
    if(ret == -1){
        perror("pipe:");
        exit(0);
    }
    pid_t pid = fork();
    if(pid > 0){
        //读取端
        while(1){
            char buf[1024];
	        memset(buf, 0, sizeof(buf));
            int len = read(pipefd[0], buf, sizeof(buf));
            printf("parent recive:%s\n", buf);
        }
    }
    else if(pid == 0){
        //写入端
        while(1){
            char str[1024];
            scanf("%s", str);
            write(pipefd[1], str, strlen(str));
        }
    }
}

二、有名管道

1. 有名管道简介

  • 有名管道又叫FIFO,实质上是一种特殊的文件,两个没有亲缘关系的进程通过读写FIFO实现通信

  • 可以在终端通过mkfifo命令,创建有名管道。

  • 也可以通过与命令同名的mkfifo系统调用,在程序中创建有名管道。

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

int mkfifo(const char *pathname, mode_t mode);
  • 很明显,pathname代表创建的有名管道文件的路径,mode代表文件权限,是一个八进制数。
  • 有名管道特点:
    • 写管道的进程会被阻塞,直到读管道进程启动
    • 读管道的进程会被阻塞,直到写管道进程启动
    • 当某一端断开连接后,会发出信号,迫使另一端断开连接。

2.示例程序(两进程间的聊天系统)

  • 我们利用进程间的有名管道通信,完成了一个简易的聊天系统。

  • 将该cpp源文件编译后,生成chat程序。

  • 在终端启动chat时,需要给出两个参数,分别代表当前终端的"昵称"与对方终端的"昵称"。启动程序的格式如下:./chat A B。

    执行该命令后,程序会在当前目录下创建两个有名管道:AtoB与BtoA,分别用于双方的收发消息。

  • 当前进程会创建一个子进程,其中,当前进程执行sendProcess()函数,用于不断监听终端有无数据输入。当有数据输入时,将数据写入AtoB有名管道中。

    而当前进程的子进程,执行recvProcess()函数,用于不断读取BtoA有名管道,一旦对方写入了数据,则会立即读取并打印到终端。

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

#include <iostream>
#include <string>
#include <cstring>
using namespace std;

void createFIFO(string sendFIFOName, string recvFIFOName);
void sendProcess(string sendFIFOName);
void recvProcess(string recvFIFOName);

int main(int argc, char* argv[])
{
    if(argc < 2){
        cout << "参数不足!" << endl;
        exit(0);
    }
    string sendFIFOName = string(argv[1]) + "to" + string(argv[2]);
    string recvFIFOName = string(argv[2]) + "to" + string(argv[1]);
    int fd = fork();
    if(fd > 0){
        sendProcess(sendFIFOName);
    }
    else{
        recvProcess(recvFIFOName);
    }
    return 0;
}

void createFIFO(string sendFIFOName, string recvFIFOName){
    if(access(sendFIFOName.c_str(), F_OK) == 0){
        cout << sendFIFOName << "已经存在!可以正常发信息" << endl;
    }
    else{
        int ret = mkfifo(sendFIFOName.c_str(), 0664);
        if(ret == -1){
            perror(("mkfifo'" + sendFIFOName).c_str());
            exit(0);
        }
    }
    if(access(recvFIFOName.c_str(), F_OK) == 0){
        cout << recvFIFOName << "已经存在!可以正常收信息" << endl;
    }
    else{
        int ret = mkfifo(recvFIFOName.c_str(), 0664);
        if(ret == -1){
            perror(("mkfifo'" + recvFIFOName).c_str());
            exit(0);
        }
    }
}

void sendProcess(string sendFIFOName){
    const char* sendName = sendFIFOName.c_str();
    int fd = open(sendName, O_WRONLY);
    while(true){
        string buf;
        cin >> buf;
        if(buf == "exit")
            break;
        write(fd, buf.c_str(), sizeof(buf.c_str()));
    }
}

void recvProcess(string recvFIFOName){
    const char* recvName = recvFIFOName.c_str();
    int fd = open(recvName, O_RDONLY);
    while(true){
        char buf[1024];
        memset(buf, '\0', 1024);
        int len = read(fd, buf, sizeof(buf));
        if(len == 0){
            break;
        }
        cout << "收到了对方的消息:" <<  buf << endl;
    }
    return;
}
  • 最后,程序的运行结果如下:
    在这里插入图片描述
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值