系列文章目录
文章一: Linux—进程通信(一)—进程通信概述
文章二: Linux—进程通信(二)—管道
文章三: Linux—进程通信(三)—信号通信
文章四: Linux—进程通信(四)—IPC通信之共享内存
文章五: Linux—进程通信(五)—IPC通信之消息队列
文章六: Linux—进程通信(六)—IPC通信之信号灯
文章目录
前言
本文主要介绍了有名管道和无名管道。
给出了Linux系统下的相关函数,并且编写了一部分例子
管道是一种特殊的文件,存在于内核中,是由队列来实现的。
一、无名管道
1.函数
管道的创建函数
函数原型;
int pipe(int fd[2])
头文件:
#include<unistd.h>
功能:
创建管道,为系统调用函数
参数:
1.参数是得到的文件描述符,特殊的是有两个文件描述符:fd[0]和fd[1]。
2.之所以有两个文件描述符,是因为管道是一个队列,出队是读文件,入队是写文件,就有两个文件描述符
3.fd[0]是读文件描述符,即出队
4.fd[1]是写文件描述符,即入队
返回值:
成功是0,失败是-1
注意:
1.管道是创建在内存中的,进程结束,空间释放,管道就不存在了。
2.管道中的东西,读完了就删除了,因为对于队列来说,出队意味着删除。
3.管道中没有东西可读,则会阻塞。
4.管道有一定的大小,如果写满就会处于写阻塞状态。
5.无名管道不能进行非父子进程之间的通信。
读,写,关闭函数
与传统文件操作函数一致:
1.读管道:read
2.写管道:write
3.关闭管道:close
具体的内容可以参考前面的文件操作系列文章。
例子1:验证读阻塞
#include<unistd.h>
#include<stdio.h>
#include<stdlib.h>
int main()
{
int fd[2];
int ret;
char writebuf[]="hello linux";
char readbuf[128]={0};
ret=pipe(fd);
if(ret<0)
{
printf("create pipe failure\n");
return -1;
}
printf("create pipe success fd[0]=%d,fd[1]=%d\n",fd[0],fd[1]);
write(fd[1],writebuf,sizeof(writebuf));
read(fd[0],readbuf,128);
printf("readbuf=%s\n",readbuf);
memset(readbuf,0,128);
read(fd[0],readbuf,128);
printf("second read after\n");
close(fd[0]);
close(fd[1]);
return 0;
}
执行结果是“second read after”这条语句没打印出来,程序阻塞在那里。
这是可以使用命令:
sudo ps -axj
来查看进程的运行状态,发现是S+状态,即睡眠状态,说明进程已经阻塞。
例子2:验证管道写满会不会写阻塞
#include<unistd.h>
#include<stdio.h>
#include<stdlib.h>
int main()
{
int fd[2];
int ret;
int i=0;
char writebuf[]="hello linux";
char readbuf[128]={0};
ret=pipe(fd);
if(ret<0)
{
printf("create pipe failure\n");
return -1;
}
printf("create pipe success fd[0]=%d,fd[1]=%d\n",fd[0],fd[1]);
//这里的5457是在阻塞的边界上
//如果是5456,那么就不会阻塞
while(i<5457)
{
write(fd[1],writebuf,sizeof(writebuf));
i++;
}
printf("write pipe end\n");
close(fd[0]);
close(fd[1]);
return 0;
}
执行结果是不会打印“write pipe end”,程序处于S+状态,阻塞。
例子3:无名管道的进程通信
#include<sys/types.h>
#include<unistd.h>
#include<stdio.h>
#include<stdlib.h>
int main()
{
pid_t pid;
int fd[2];
int ret;
char process_inter=0;
ret=pipe(fd);
if(ret<0)
{
printf("create pipe failure\n");
return -1;
}
printf("create pipe success\n");
pid=fork();
if(pid==0)
{
int i=0;
read(fd[0],&process_inter,1);
while(process_inter==0);
for(i=0;i<5;i++)
{
printf("this is child process i=%d\n",i);
usleep(100);
}
}
if(pid>0)
{
int i=0;
for(i=0;i<5;i++)
{
printf("this is child process i=%d\n",i);
usleep(100);
}
sleep(5);
process_inter=1;
write(fd[1],&process_inter,1);
}
return 0;
}
执行结果:父进程先运行,大概5秒后,子进程后运行。
二、有名管道
1.为什么需要有名管道?
很直观的意义上来讲:
有名管道可以进行非父子进程之间的通信,这是无名管道所做不到的。根本原因就是无名管道在用户空间没有名,不能让用户空间的两个无亲缘关系的进程调用。
2.什么是有名管道
文件系统中存在这样一个文件结点,每个文件结点都有一个inode号,而且这时一个特殊的文件类型:管道类型
这里进行一个补充:Linux系统中的7种文件类型
1.普通文件
2.目录文件
3.链接文件
4.管道文件
5.Soket(套接字)文件
6.字符设备文件
7.块设备文件
3.有名管道的函数
有名管道的创建
函数形式:
int mkfifo(const char *filename,mode_t mode);
函数功能:
创建管道文件
参数:
1.第一个参数是文件的文件名
2.第二个参数是文件的权限,与具体的umask值有关(可以参考前面的文件操作系列文章)
返回值:
创建成功返回0,创建失败返回-1
注意:
1.管道文件只有inode号,不占磁盘空间,和套接字,字符设备文件块设备文件一样。
2.普通文件和符号链接文件及目录文件,不仅有inode号,还占磁盘空间。
3.mkfifo用来创建管道文件的节点,没有在内核中创建管道。
4.只有通过open函数打开这个文件时,才会在内核空间创建管道。
5.无名管道的规则也适用有名管道
打开,读,写,关闭函数
与传统文件操作函数一致:
1.打开管道:open
2.读管道:read
3.写管道:write
4.关闭管道:close
具体的内容可以参考前面的文件操作系列文章。
例子1:实现无亲缘关系进程间的通信
进程1:
#include<sys/types.h>
#include<unistd.h>
#include<stdio.h>
#include<stdlib.h>
#include<fcntl.h>
int main()
{
int i=0;
char process_inter=0;
int fd;
fd=open("./myfifo",O_WRONLY);
if(fd<0)
{
printf("open myfifo failure\n");
return -1;
}
printf("open myfifo success\n");
for(i=0;i<5;i++)
{
printf("this is first process i=%d\n",i);
usleep(100);
}
process_inter=1;
sleep(5);
write(fd,&process_inter,1);
while(1);
return 0;
}
进程2:
#include<sys/types.h>
#include<unistd.h>
#include<stdio.h>
#include<stdlib.h>
#include<fcntl.h>
int main()
{
int i=0;
char process_inter=0;
int fd;
fd=open("./myfifo",O_RDONLY);
if(fd<0)
{
printf("open myfifo failure\n");
return -1;
}
printf("open myfifo success\n");
read(fd,&process_inter,1);
while(process==0);
for(i=0;i<5;i++)
{
printf("this is first process i=%d\n",i);
usleep(100);
}
process_inter=1;
write(fd,&process_inter,1);
while(1);
return 0;
}
执行步骤:
先打开程序1,再迅速打开程序2.
执行结果描述:
1.刚开始程序1什么都不显示,包括创建管道成功的信息也不显示。这是因为创建的管道没有读端,程序处于阻塞状态。
2.当程序2开始执行后,程序1会立即执行全部内容,程序2会打印出创建管道成功的语句,然后阻塞。
3.当过大概5秒后,程序2也顺利执行。这样两个进程间实现通信。
总结
对于open close read write函数有疑问的读者,可以参考下面的文章: