利用父子进程
创建管道利用pipe函数
// 1.创建管道
int pipefd[2] = {0}; //[0] 读端 ,[1]写端
int n = pipe(pipefd);
assert(n != -1); // debug 在release下会裁减
(void)n;//防止在release下报错
cout << "fd[0]:" << pipefd[0] << endl; // 3
cout << "fd[1]:" << pipefd[1] << endl; // 4
pipe该函数内会创建一个管道文件,我们需要手动传入一个整型数组保存两个元素。
当函数返回继续检查。注意我们的pipefd[0]为对管道读端,pipe[1]为对管道写端,保存的是文件描述符fd。
pid_t id = fork();
assert(id != -1);
//父进程写入,子进程读取
if (id == 0)
{
//子进程
//构建单项通信
int end=0;
close(pipefd[1]); //关闭子进程prpefd[1]。
//char buff[1024];
while (1)
{
//子进程工作
}
close(pipefd[0]);
exit(0);
}
fork()创建子进程通过id判断是否为子进程,如果是0子进程就进入 if 判断式。
子进程继承父进程的进程一众数据结构,那么files_struct也是一样的,所以我们通过fd也可以访问文件。子进程读取数据,就要将写端关闭(也可不关),close(pipefd[1]);然后进行通信工作。
结束后关闭读端,然后结束子进程。
// 父进程
//构建单项通信
close(pipefd[0]);
char send_buff[1024 ];
while (1)
{
//父进程写入工作
}
//工作结束
close(pipefd[1]);
//回收子进程
pid_t ret = waitpid(id, nullptr, 0);
assert(ret != -1);
return 0;
}
父进程关闭读端(也可以不关闭),创建缓冲区,进行写入工作,当工作结束关闭对管道的写端。
然后等待子进程结束回收资源。判断是否正确回收。结束进程。
有一些情况:管道文件,提供了协同工作的机理。读写同步。
写段关闭,读端进程会读取到文件结尾,退出循环。
读端关闭,写端进程被操作系统强行嗝屁。(子进程变为孤儿进程)。
完整代码
#include <iostream>
#include <unistd.h>
#include <cstdio>
#include <cstring>
#include <assert.h>
#include <string>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>
using namespace std;
int main()
{
// 1.创建管道
int pipefd[2] = {0}; //[0] 读端 ,[1]写端
int n = pipe(pipefd);
assert(n != -1); // debug 在release下会裁减
(void)n;
#ifdef DEBUG
cout << "fd[0]:" << pipefd[0] << endl; // 3
cout << "fd[1]:" << pipefd[1] << endl; // 4
#endif
pid_t id = fork();
assert(id != -1);
//父进程写入,子进程读取
if (id == 0)
{
//子进程
//构建单项通信
int end=0;
close(pipefd[1]); //关闭子进程prpefd[1]。
char buff[1024];
while (1)
{
ssize_t s = read(pipefd[0], buff, sizeof(buff) - 1);
if (s > 0)
{
buff[s] = '\0';
cout << "child:pid[" << getpid() << "]father#" << buff << endl;
}
else if (s == 0)
{
printf("写入方关闭写端\n");
break;
}
cout<<"end:"<<end<<endl;
if(end++==5)
{
cout<<"写端提前关闭"<<endl;
break;
}
}
cout<<"子进程关闭读端"<<endl;
close(pipefd[0]);
// close(pipefd[0]);
exit(0);
}
// 父进程
//构建单项通信
close(pipefd[0]);
string parent("我是父进程!!!我在发信息");
int cnt = 0;
char send_buff[1024];
while (1)
{
memset(send_buff,0,sizeof(send_buff));
snprintf(send_buff, sizeof(send_buff), "%s[%d次]:ppid=%d", parent.c_str(), cnt++, getpid());
// 3.3写入管道
sleep(1);
int m = write(pipefd[1], send_buff, strlen(send_buff));
cout << cnt << endl;
if (m < 0)
{
printf("写入失败\n");
}
sleep(1);
}
printf("父进程关闭写端\n");
close(pipefd[1]);
sleep(10);
pid_t ret = waitpid(id, nullptr, 0);
assert(ret != -1);
return 0;
}
非父子进程管道通信。
创建两个进程,通过管道进行通信链接。
首先先介绍,mkfifo函数,显示的创建一个管道文件,
int mkfifo(const char *pathname, mode_t mode);
pathname:创建管道的文件名字以及路径。
mode:管道文件权限设置。
我们的两个进程需要同时访问同一个管道文件,所以我们创建的路径一定要一致。我们让两个源代码使用同一头文件。
//pipe.hpp
#ifndef _COMM_H_
#define _COMM_H_
#include<cstdio>
#include<cstring>
#include<iostream>
#include<unistd.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include"Log.hpp"
#define MODE 0666
#define SIZE 128
using namespace std;
string ipcPath("./fifi.ipc");//可绝对可相对,为了简便我们使用相对路径
#endif
创建一个string 类,管道文件保存路径,两个包含该头文件的源代码就可以访问到相同的管道文件了。
我们在读取数据方创建管道文件(也可以在发送数据方)
int main()
{
//创建管道
if(mkfifo(ipcPath.c_str(),MODE)<0)
{
perror("mkfifo fail!\n");
exit(-1);
}
Log("管道创建成功",Debug)<<"stap 1"<<endl;//日志信息
创建管道,会在当前目录下生成,管道文件
当前进程链接管道。
//链接管道
int fd=open(ipcPath.c_str(),O_RDONLY);
if(fd==-1)
{
perror("open");
exit(-2);
}
Log("管道打开成功",Debug)<<"stap 2"<<endl;//日志信息
如果管道写端没有被访问,会在管道内阻塞读端进程
写端进程运行
读端停止阻塞,继续运行。
//使用管道开始通信
char buffer[SIZE];
while(1)
{
memset(buffer,0,sizeof(buffer));
ssize_t s=read(fd,buffer,sizeof(buffer)-1);
if(s>0)
{
buffer[s]='\0';
cout<<buffer<<endl;
}
else if(s==0)
{
printf("file read end,end receive colse\n");
break;
}
else
{
printf("read error!!!\n");
exit(-3);
}
}
开始读取工作。
当我们关闭写端。
close(fd);
Log("管道关闭成功",Debug)<<"stap 3"<<endl;
unlink(ipcPath.c_str());
Log("管道删除成功",Debug)<<"stap 4"<<endl;
return 0;
}
我们先关闭fd文件描述符,在删除管道文件。
读端完整代码
#include"commt.hpp"
int main()
{
//创建管道
if(mkfifo(ipcPath.c_str(),MODE)<0)
{
perror("mkfifo fail!\n");
exit(-1);
}
Log("管道创建成功",Debug)<<"stap 1"<<endl;
//链接管道
int fd=open(ipcPath.c_str(),O_RDONLY);
if(fd==-1)
{
perror("open");
exit(-2);
}
Log("管道打开成功",Debug)<<"stap 2"<<endl;
//使用管道开始通信
char buffer[SIZE];
while(1)
{
memset(buffer,0,sizeof(buffer));
ssize_t s=read(fd,buffer,sizeof(buffer)-1);
if(s>0)
{
buffer[s]='\0';
cout<<buffer<<endl;
}
else if(s==0)
{
printf("file read end,end receive colse\n");
break;
}
else
{
printf("read error!!!\n");
exit(-3);
}
}
close(fd);
Log("管道关闭成功",Debug)<<"stap 3"<<endl;
unlink(ipcPath.c_str());
Log("管道删除成功",Debug)<<"stap 4"<<endl;
return 0;
}
相比读端我们的写端代码量轻松的多。
#include"commt.hpp"
int main()
{
//打开管道
int fd=open(ipcPath.c_str(),O_WRONLY);
if(fd<-1)
{
perror("open fail!\n");
exit(-1);
}
string buffer;
while(1)
{
cout<<"Plase Enter Message Line >";
std::getline(std::cin,buffer);
write(fd,buffer.c_str(),buffer.size());
}
close(fd);
return 0;
}
写端只需要普通的文件操作,要注意的是需要打开同一个管道文件,进行写入。在运行结束的时候关闭fd即可,删除管道文件在读端进程完成。