进程间通信方式的种类:
(1)管道(Pipe)
(2)信号(Signal)
(3)消息队列(Messge Queue)
(4)共享内存(Shared memory)
(5)信号量(Semaphore)
(6)套接字(Socket)
管道的有两个端点,一个端点接受信息输入,一端信息输出;管道中的信息一旦流出,则不复存在了,即度写一次数据只能读一次数据!
管道提供了简单的流控制机制,即读写阻塞功能。
管道又分为无名管道、标准管道、有名管道
一、无名管道
1、无名管道只能用于具有亲缘关系的进程之间的通信(也就是父子进程或者兄弟进程之间)。
2、它是一个半双工的通信模式,具有固定的读端和写端。
3、无名管道也可以看成是一种特殊的文件,对于它的读写也可以使用普通的read()和write()等函数。但是它不是普通的文件,并不属于其他任何文件系统,并且只存在于内核的内存空间中。
步骤:
1、创建管道可以通过调用pipe()函数,并且创建两个文件描述fds[0]和fds[1],其中fds[0]固定用于读管道,而fd[1]固定用于写管道
2、read()和write()等函数对管道的读和写
3、关闭时只需使用普通的close()函数关闭读和写管道
例子:
父进程用匿名管道向其子进程发送“Hello!”,延时一秒,子进程接收后并显示出来;
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<sys/types.h>
#include<string.h>
#define BUFFER_SIZE 10
int main()
{
int pipe_fd[2];
pid_t pid;
char buf[BUFFER_SIZE];
char data[]="Hello!";
memset((void*)buf,0,sizeof(buf));
if(pipe(pipe_fd)==-1)
{
printf("pipe error!\n");
return -1;
}
pid=fork();
if(-1==pid)
{
printf("fork error!\n");
return -1;
}
else if(0==pid)
{
//子进程
close(pipe_fd[1]);
sleep(1);
if(read(pipe_fd[0],buf,BUFFER_SIZE))
{
printf("In child process:\nThe data read from pipe is: %s\n",buf);
}
close(pipe_fd[0]);
exit(0);
}
//父进程
close(pipe_fd[0]);
if(write(pipe_fd[1],data,strlen(data)))
{
printf("In parent process:\nThe datawrite to pipe is: %s\n",data);
}
close(pipe_fd[1]);
wait();
exit(0);
return 0;
}
二、标准流管道
标准就是,标准I/O流的意思,即标准流管道可以操作于标准I/O文件流流。
这种基于文件流的管道主要是用来创建一个连接到另一个进程的管道,这里的“另一个进程”也就是一个可以进行一定操作的可执行文件,例如,用户执行“ls -l”或者自己编写的程序“./pipe”等。由于这一类操作很常用,因此标准流管道就将一系列的创建过程合并到一个函数popen()中完成。
步骤:
1、用popen()创建的管道必须使用标准I/O函数进行操作,但不能使用前面的read()、write()一类不带缓冲的I/O函数;
2、其中,command为需要和创建管道的进程进行通信的另一个可执行文件;
mode为w时,数据从创建管道的进程流向command进程;
mode为r时,数据从command进程流向创建管道的进程。
2、使用函数pclose()来关闭该管道流。该函数关闭标准I/O流,并等待命令执行结束;
例子:
创建hello可执行文件
进程用标准管道接受进程“./hello”的输出信息,并打印信息
#include<stdio.h>
int main()
{
printf("Hello world!");
return 0;
}
gcc hello.c -o hello
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#define BUFFER_SIZE 256
int main()
{
FILE *fp;
int num;
char buf[BUFFER_SIZE];
memset((void *)buf,0,sizeof(buf));//清空数组
fp=popen("./hello","r");//读命令"./hello"的标准输出
if(fp==NULL)
{
printf("popen error!\n");
exit(0);
}
if((num=fread(buf,1,BUFFER_SIZE,fp))>0)
{
printf("Size: %d, pipe read is: %s\n",num,buf);
}
pclose(fp);
return 0;
}
三、有名管道
有名管道进程通信要用到一个文件,所以有名可以理解为有名字的文件。
前面介绍的管道是无名管道,它只能用于具有亲缘关系的进程之间,这就大大地限制了管道的使用。
有名管道的出现突破了这种限制,它可以使互不相关的两个进程实现彼此通信。该管道可以通过路径名来指出,并且在文件系统中是可见的。在建立了管道之后,两个进程就可以把它当作普通文件一样进行读写操作,使用非常方便。
步骤:
1、使用函数mkfifo()创建有名管道,并且设置权限
设置文件的权限,可用8进制数值来表示,如0666表示当前用户,用户组,其他用户均能可读可写
2、使用open()、read()和write()这些函数对管道文件进行读写,读写时候可以加上O_RDONLY、O_WRONLY(读写阻塞)或者O_NONBLOCK(非阻塞)
若该管道是阻塞打开,①则写操作将一直阻塞到数据可以被写入;②则读操作将一直阻塞到有数据可以被读出;
3、用close()管道文件
例子
进程1能够把从键盘接收字符写入管道文件myfifo中,进程2以阻塞方式打开管道文件(文件不存在就创建),当管道文件有数据时,把数据显示出来
进程1
fifo_write.c
#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<string.h>
#include<errno.h>
#include<fcntl.h>
#include<limits.h>
#define MYFIFO "./myfifo" //文件路径
#define BUFFER_SIZE PIPE_BUF //PIPE_BUF在limits.h定义
int main(int argc,char **argv)
{
int fd_myfifo;
char buf[BUFFER_SIZE];
memset((void *)buf,0,sizeof(buf));
if(access(MYFIFO,F_OK)==-1)//如果文件不存在
{
printf("Please create fifo!\n");
return -1;
}
//已阻塞方式写
if((fd_myfifo=open(MYFIFO,O_WRONLY))==-1)
{
printf("write open myfifo error\n");
return -1;
}
//将命令行中字符串拷贝到buf
printf("Please input the data:");
scanf("%s",buf);
if(write(fd_myfifo,buf,strlen(buf))>0)
{
printf("In write process,transfered: %s\n",buf);
}
close(fd_myfifo);
return -1;
}
进程2
read_fifo.c
#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<string.h>
#include<errno.h>
#include<fcntl.h>
#include<limits.h>
#define MYFIFO "./myfifo"
#define BUFFER_SIZE PIPE_BUF //PIPE_BUF在limits.h定义
int main()
{
int fd_myfifo;
char buf[BUFFER_SIZE];
//creat fifo
if(access(MYFIFO,F_OK)==-1)//detect fifo is or not
{
if((mkfifo(MYFIFO, 0666)<0 )&& (errno != EEXIST))
{
printf("mkfifo error!\n");
return -1;
}
}
//open fifo,要已阻塞的方式读,要等有数据读操作才返回
fd_myfifo = open("./myfifo",O_RDONLY);
if(fd_myfifo==-1)
{
printf("open myfifo error\n");
return -1;
}
//receive words from write process
printf("when received 'quit' to exit\n");
do{
memset((void *)buf,0,sizeof(buf));//clear buf to zero
if(read(fd_myfifo,buf,BUFFER_SIZE)>0)
{
printf("In read process,received: %s\n",buf);
}
}while(strncmp(buf,"quit",4));//接收到quit时结束
printf("\n");
close(fd_myfifo);
return -1;
}
总结:匿名管道主要用于有血缘关系的进程间的通信,有名管道可用于没有血缘关系的进程间的通信;标准流管道提供了两个进程进行连接的一种简便方法。