本文简要概述了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;
}
- 最后,程序的运行结果如下: