1、管道通信
管道通信类似于水流在水管中流动,只能一个方向传输数据。分为无名管道和命名管道(FIFO)。
1.1 无名管道
特点:
- 半双工(数据只能一个方向进行流动)
- 主要应用于父子进程或者兄弟进程
- 不会以文件的形式存在于磁盘之中(存在于内存之中,父子进程结束后,便会消失)
- 管道数据读取之后就没了
- 创建的管道位于内核之中
用到的API函数:
#include <unistd.h>
int pipe(int pipefd[2]); //创建管道,成功返回0;失败返回-1
描述:
- 创建管道需要用到pipe()函数,pipe()函数需要一个有两个元素的数组(pipefd[.2]),返回值是一个整型数。其中pipefd[.2]表示两个文件描述符,pipefd[0]只为读打开,pipefd[1.]只为写打开。
- 在写时,应先把读关闭(close(pipefd[0]) ),反之,在读时应先把写关闭( close(pipefd[1.]))
*[注:数组里面的.只是为了显示完整才加的(第一次使用csdn,不会操作),具体写代码时没有]
具体示例代码:
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
int main()
{
int fd[2]; //定义一个数组---后续的文件描述符
int pid; //定义一个整型,用来描述后续的进程id
//调用pipe函数创建管道,并判断是否创建成功
if(pipe(fd) == -1)
{
printf("创建管道失败\n");
exit(-1);
}
//创建子进程,进行父子进程间的通信
pid = fork(); //pid接收进程的id
if(pid > 0) //pid大于0,表示父进程
{
sleep(3); //父进程睡3秒,让子进程先进行,当然此时子进程应该是堵塞状态
printf("这是父进程\n");
close(fd[0]); //关闭读的文件描述符
//向管道内写
write(fd[1],"this is from father!", strlen("this is from father"));
wait(); //等待子进程退出后,父进程再退出
}
else if(pid == 0) //pid 等于0,表示子进程
{
printf("这是子进程\n");
close(fd[1]); //关闭写的文件描述符
char buf[128] = {0}; //创建一个数组作为读取时的缓冲区
read(fd[0], buf,128); //将读取的数据放在buf 内
printf("get from father: %s\n",buf);
exit(0); //子进程退出
}
else
{
printf("创建进程失败\n");
}
return 0;
}
*[注:管道函数pipe,读写时调用的是 read() write()函数]
1.2 命名管道(FIFO)
特点:
- 可用于无关进程间的通信
- 会以特殊设备文件形式存在磁盘之中
相关API函数
#include <sys/stat.h>
#include <sys/types.h>
int mkfifo(const char* pathname, mode_t mode); //创建管道,成功返回0,失败返回-1
描述:第一个参数 pathname 是管道名,第二个参数 mode 和 open() 函数的 mode 一样 ,意思应该是对管道文件的操作权限
open() 函数的 mode 选择: 0600 可读可写
具体示例代码:
①创建命名管道并判断文件是否存在
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
int main()
{
int ret;
//创建命名管道,“./file”表示在当前路径下建立文件file, 文件权限是0600
ret = mkfifo("./file",0600);
if(ret == 0) //返回值是0,表示创建成功
{
printf("创建成功\n");
}
else if(ret == -1) //返回值是-1,表示创建失败 失败原因有很多,可以man 3 mkfifo查看具体原因
{
printf("创建失败\n");
perror("Why"); //调用perror()函数可以查看错误原因
}
return 0;
}
上述代码运行结果:第一次运行:创建成功
—————————第二次运行:创建失败 Why:File exists (表示文件已经存在)
优化上述代码
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
int main()
{
//创建命名管道,“./file”表示在当前路径下建立文件file, 文件权限是0600 可读可写
//errno表示错误类型
if((mkfifo("./file",0600)) == -1 && errno != EEXIST)
{
printf("创建文件失败\n");
perror("Why"); //调用perror()函数可以查看错误原因
}
else
{
if(errno == EEXIST)
{
printf("文件已存在\n");
}
else
{
printf("创建成功\n");
}
}
return 0;
}
其他函数: perror()函数----判断错误原因 errno检查错误类型
②实现不同进程间通信
读取信息
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <fcntl.h>
int main()
{
//创建命名管道,“./file”表示在当前路径下建立文件file, 文件权限是0600 可读可写
//errno表示错误类型
if((mkfifo("./file",0600)) == -1 && errno != EEXIST)
{
printf("创建文件失败\n");
perror("Why"); //调用perror()函数可以查看错误原因
}
//以只读的方式打开管道文件file 此时默认堵塞,直到另一个进程以只写的方式打开file文件
//如果想要不堵塞加 O_RDONLY|O_NONBLOCK
int fd = open("./file",O_RDONLY);
if(fd != -1)
{
printf("read open success\n");
}
char buf[128] = {0}; //开辟缓冲区
int n_read = read(fd,buf,128); //从管道文件file中读取数据到buf中
printf("read %d byte from write: %s\n",n_read,buf);
close(fd);
return 0;
}
写信息
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
int main()
{
//以只写的方式打开管道文件file 此时会堵塞,直到另一个进程以只读的方式打开file文件
int fd = open("./file",O_WRONLY);
if(fd != -1)
{
printf("write open success\n");
}
char* buf= "this is message from file"; //发送的数据内容
write(fd,buf,strlen(buf)); //写到管道文件file中
close(fd);
return 0;
}
以上是关于管道通信的相关理论以及代码示例
简单来说:
1、无名管道不能满足无关进程间的通信,但命名管道可以满足
2、无名管道不会以文件形式存在磁盘中,但命名管道会以特殊设备文件形式存在磁盘中
3、二者都是调用 read() write()函数进行读写操作!
4、二者的数据都是单一方向流动,并且读数据之后,管道内的数据就没了
这是本人学习笔记,如有侵权,请告之!