一、进程间通信
进程间通信 IPC(InetProcess Communication):是指在不同进程之间传播或交换信息。
IPC 的方式通常有管道(包括无名管道和命名管道)、消息队列、信号量、共享存储、Socket 和 Streams等。其中 Socket 和 Streams 支持在不同主机上的两个进程 IPC。
二、管道
我们说的管道通常指的是无名管道,是UNIX系统PIC最古老的形式。
有以下几个特点:
1. 它是半双工(即数据只能往一个方向流动,读和写不能同时进行),具有固定的读端和写端;
2. 它只能用于具有亲缘关系的进程之间的通信(即父、子进程之间和兄弟进程之间);
3. 它可以看成是一种特殊的文件,对于它的读写也可以使用普通的read、write 等函数,但是它不是普通的文件,并不属于其它的任何文件系统,并且只存在于内存中。
1.管道的建立
函数原型: int pipe(int fd[2]); //在 <unisyd.h> 头文件中
返回值: 若成功,返回 0,失败返回 -1;
当一个管道建立时,它会创建两个文件描述符:fd[0] 为读;fd[1] 为写,如下图:
要关闭管道,只需要将这两个文件描述符关闭即可。写个父进程读管道数据,子进程来写数据的程序掌握一下 pipe函数:
实验代码:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
int main()
{
int fd[2];
char *read_buf;
pid_t pid;
int status=10;
printf("This is IPC test!\n");
if(pipe(fd)==-1){
printf("pipe creat fail!\n");
}
pid=fork();
if(pid>0){
waitpid(pid,&status,WNOHANG);//非挂起方式等待子进程
printf("This is fathet curret!ID:%d\n",getpid());
close(fd[1]);//关闭写
read(fd[0],read_buf,20);//开始读
printf("Father read:%s\n",read_buf);
}
if(pid==0){
printf("This is child current!ID:%d\n",getpid());
close(fd[0]);//关闭
write(fd[1],"Hello Wrold!",strlen("Hello Wrold!"));
//printf("Child read: %s\n",read_buf);
exit(status);
}
return 0;
}
运行结果:
后面的乱码是因为我们读了20个字节所导致,因为我们真实写的时候,并没有20个字节那么多。
三、命名管道(FIFO)
命名管道也叫FIFO,是一种文件类型。
特点:
1. FIFO可以在无关的进程之间交换数据,与无名管道不同;
2. FIFO有路径与之相关联,它以一种特殊文件形式存在于文件系统中。
函数原型: int mkfifo(const char *pathname,mode_t mode);//存在于 <sys/stat.h> 当中;
其中的 mode 参数与open 的相同,一旦创建了一个 FIFO,就可以哦那个一般的文件 I/O 函数来操作它。
当open 一个 FIFO 时,是否设置非阻塞标志( O_NONBLOCK)的区别:
1. 若没有指定 O_NONBLOCK(默认),只读 open要阻塞到某个其他进程为写而打开次 FIFO,类似的,只写 open 要阻塞到某个其他进程为 读 而打开它;
2. 若指定了 O_NONBLOCK,则只读 open 立即返回 -1 ,如果没有进程已经为 读而打开 FIFO,其 errno 置 ENXIO。
1.创建命名管道 FIFO
创建 FIFO 代码:
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
int main()
{
//int mkfifo(const char *pathname, mode_t mode);
int mkfifo_s=mkfifo("./FIFO_1",0600);//创建一个FIFO 并赋予其可读可写权限
if(mkfifo_s==0){
printf("FIFO cerat succese!\n");
}
if(mkfifo_s==-1){
printf("FIFO cerat fail!\n");
perror("why");
}
return 0;
}
运行结果:
可以看到,我们在没有运行 ./crearfifo之前,当前目录并没有FIFO_1 文件,执行了 ./crearfifo 之后,便创建了一个FIFO_1文件。其实我们可以优化一下这个程序:
程序优化:
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <errno.h>
int main()
{
//int mkfifo(const char *pathname, mode_t mode);
if((mkfifo("./FIFO_1",0600)==-1)&& errno == EEXIST){
printf("Creat FIFO fail!");
perror("why");
}
return 0;
}
这个程序就是把错误信息打印出来,假如文件已经存在,就打印文件已存在信息(file exists);
运行结果:
因为我们刚刚就创建了 FIIOF_1,所以打印信息:file exists ;
3.利用FIFO传递数据
学会了创建FIFO,下面就开始使用它来给两个不相关的进程传递数据。因为命名管道也是管道的一种,依旧没办法读、写 同时进行,而且open FIFO 时,默认是阻塞型,即程序会卡在 read 处,会读不出数据,直到另一个进程 写 入之后且关闭FIFO之后,才能读出来,做个实现负责读数据的程序命名为:FIFO_FWCR3.c ;负责写入的程序命名为:FIFO_FWCR5.c
实验代码:
FIFO_FWCR3.c 不断读取FIFO 中的内容,如果FIFO_FWCR5.c 程序不运行的话,会卡住。
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <errno.h>
#include <fcntl.h>
int main()
{
//int mkfifo(const char *pathname, mode_t mode);
char read_buf[30]={0};
if((mkfifo("./FIFO_1",0600)==-1)&& errno == EEXIST){
printf("Creat FIFO fail!");
perror("why");
}
int fd=open("./FIFO_1",O_RDONLY);
printf("open success\n");
while(1){
sleep(1);
read(fd,read_buf,30);
printf("read :%s\n",read_buf);
}
close(fd);
return 0;
}
FIFO_FWCR5.c 负责给FIFO 写入“FIFO data!”.
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <errno.h>
#include <fcntl.h>
#include <string.h>
int main()
{
//int mkfifo(const char *pathname, mode_t mode);
char *str="FIFO data!";
int cnt=5;
int fd=open("./FIFO_1",O_WRONLY);
printf("open success\n");
while(cnt!=0){
sleep(1);
write(fd,str,strlen(str));
cnt--;
}
close(fd);
return 0;
}
FIFO_FWCR3.c 在FIFO_FWCR5.c 没运行之前的结果:
一直在等待写入,而当 FIFO_FWCR5.c 执行之后,我们再看看 FIFO_FWCR3.c 的结果:
FIFO_FWCR3.c 会不断的读取被FIFO_FWCR5.c 写到 FIFO 中的数据。