Linux进程间通信之有名无名管道
一:无名管道
通信原理:
管道文件是一个特殊的文件,是由队列来实现的。
在文件IO中创建一个文件或打开一个文件是由open函数来实现的,它不能创建管道文件。只能用pipe函数来创建管道。(open函数只能创建普通文件)
函数形式:int pipe(int fd[2])
功能:创建管道,为系统调用:unistd.h
参数:就是得到的文件描述符。可见有两个文件描述符:fd[0]和fd[1],管道有一个读端fd[0]用来读和一个写端fd[1]用来写,这个规定不能变。(有了文件描述符后就可以对文件进行write read)
返回值:成功是0,出错是-1;
例1:pipe函数使用。
#include "unistd.h"
#include "stdio.h"
#include "stdlib.h"
#include "string.h"
int main()
{
int fd[2];
int ret;
char writebuf[]="hello linux";
char readbuf[128]={0};
ret=pipe(fd);
if(ret <0)
{
printf("creat pipe failure\n");
return -1;
}
printf("creat pipe sucess fd[0]=%d,fd[1]=%d\n",fd[0],fd[1]);
write(fd[1],writebuf,sizeof(writebuf));
//start read from pipe
read(fd[0],readbuf,128);
printf("readbuf=%s\n",readbuf);
//second read from pipe
memset(readbuf,0,128);
//会阻塞在这里
read(fd[0],readbuf,128);
printf("second read after\n");
close(fd[0]);
close(fd[1]);
return 0;
}
注意:
管道中的东西,读完了就删除了;队列
如果管道中没有东西可读,则会读阻塞。
例2:验证读阻塞。例3:验证写阻塞:可以计算出内核开辟的管道有多大。
5456个writebuf时不写阻塞 5457个writebuf 时会阻塞
#include "unistd.h"
#include "stdio.h"
#include "stdlib.h"
#include "string.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("creat pipe failure\n");
return -1;
}
printf("creat pipe sucess fd[0]=%d,fd[1]=%d\n",fd[0],fd[1]);
while( i < 5457)
{
write(fd[1],writebuf,sizeof(writebuf));
i++;
}
printf("write pipe end\n");
close(fd[0]);
close(fd[1]);
return 0;
}
例4:实现进程通信。
#include "unistd.h"
#include "stdio.h"
#include "sys/types.h"
#include "stdlib.h"
int main()
{
pid_t pid;
int fd[2];
int ret;
char process_inter=0;
//应该在fork函数之前就创建管道,这样父子进程才用的是同一管道
ret=pipe(fd);
if(ret < 0)
{
printf("creat pipe failure\n");
return -1;
}
printf("creat pipe sucess\n");
pid = fork();
if(pid ==0)//child process code second
{
int i=0;
read(fd[0],&process_inter,1);//if pipe empty sleep
while(process_inter ==0);
for(i=0;i<5;i++)
{
printf("this is child process i=%d\n",i);
usleep(100);
}
}
if(pid >0)//parent process code first
{
int i=0;
for(i=0;i<5;i++)
{
printf("this is parent process i=%d\n",i);
usleep(100);
}
process_inter=1;
sleep(5);
write(fd[1],&process_inter,1);
}
while(1);
return 0;
}
无名管道的缺点:只能实现父子进程(有亲缘关系进程)之间的通信。
无名管道:文件系统中无文件名
有名管道:文件系统中有文件名
二.有名管道
正由于这无名管道的缺点,对无名管道进行改进:有名管道。
所谓的有名,即文件系统中存在这个一样文件节点,每一个文件节点都有一个inode号
而且这是一个特殊的文件类型:p管道类型。
1.创建这个文件节点,不可以通过open 函数,open 函数只能创建普通文件,不能创建特殊文件(管道-mkdifo,套接字-socket,字符设备文件-mknod,块设备文件-mknod,符号链接文件-ln –s,目录文件mkdir)
2.管道文件只有inode号,不占磁盘块空间,和套接字、字符设备文件、块设备文件一样。普通文件和符号链接文件及目录文件,不仅有inode号,还占磁盘块空间。
3. mkfifo 用来创建管道文件的节点,没有在内核中创建管道。
只有通过open 函数打开这个文件时才会在内核空间创建管道。
4.mkfifo
函数形式:int mkfifo(const char *filename,mode_t mode);
功能:创建管道文件
参数:管道文件文件名,权限,创建的文件权限仍然和umask有关系。
返回值:创建成功返回0,创建失败返回-1。
例1:mkfifo的用法。
例子:通过管道实现无亲缘关系进程间通信。
mkfifo.c
#include "stdio.h"
#include "unistd.h"
#include "stdlib.h"
#include <sys/stat.h>
#include <sys/types.h>
int main()
{
int ret;
ret=mkfifo("./myfifo",0777);
if(ret <0)
{
printf("creat myfifo failure\n");
return -1;
}
printf("creat myfifo sucess\n");
return 0;
}
注:ubuntu下的共享文件夹下是无法创建有名管道的。
注:管道文件只有inode号,不占磁盘块空间,和套接字、字符设备文件、块设备文件一样。普通文件和符号链接文件及目录文件,不仅有inode号,还占磁盘块空间。
进程A写有名管道
#include "unistd.h"
#include "stdio.h"
#include "sys/types.h"
#include "stdlib.h"
#include "fcntl.h"
int main()
{
int fd;
int i;
char process_inter=0;
fd=open("./myfifo",O_WRONLY);
if(fd <0)
{
printf("open myfifo failure\n");
return -1;
}
printf("open myfifo sucess\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;
}
进程B读管道
#include "unistd.h"
#include "stdio.h"
#include "sys/types.h"
#include "stdlib.h"
#include "fcntl.h"
int main()
{
int fd;
int i;
int process_inter=0;
fd=open("./myfifo",O_RDONLY);
if(fd <0)
{
printf("open myfifo failure\n");
return -1;
}
printf("open myfifo sucess\n");
read(fd,&process_inter,1);
while(process_inter == 0);
for(i=0;i<5;i++)
{
printf("this is second process i=%d\n",i);
usleep(100);
}
while(1);
return 0;
}